Sniffly Packaging Guide
This document explains how to package Sniffly for distribution on macOS and Linux.
Current State
Sniffly is built using:
- Meson build system
- GTK4 for the GUI
- gtk-fortran bindings
- gfortran compiler (GCC Fortran)
The executable is currently located at build/sniffly after running meson compile -C build.
macOS Packaging (.dmg)
Overview
On macOS, applications are distributed as:
- .app bundles - Directory structure that looks like a single file
- .dmg files - Disk images containing the .app bundle
Steps to Create a macOS .app Bundle
1. Create the Bundle Structure
# Create the app bundle directories
mkdir -p Sniffly.app/Contents/MacOS
mkdir -p Sniffly.app/Contents/Resources
mkdir -p Sniffly.app/Contents/Frameworks
2. Copy the Executable
# Copy the compiled binary
cp build/sniffly Sniffly.app/Contents/MacOS/sniffly
3. Create Info.plist
Create Sniffly.app/Contents/Info.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>sniffly</string>
<key>CFBundleIdentifier</key>
<string>org.fortrangoingonforty.sniffly</string>
<key>CFBundleName</key>
<string>Sniffly</string>
<key>CFBundleDisplayName</key>
<string>Sniffly</string>
<key>CFBundleVersion</key>
<string>0.1.0</string>
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>SNIF</string>
<key>CFBundleIconFile</key>
<string>sniffly</string>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSRequiresAquaSystemAppearance</key>
<false/>
</dict>
</plist>
4. Create an Application Icon
You need a .icns file. Create one from a PNG using:
# Create iconset directory
mkdir sniffly.iconset
# Create different sizes (you'll need a source image)
# 16x16, 32x32, 128x128, 256x256, 512x512, 1024x1024
sips -z 16 16 icon-1024.png --out sniffly.iconset/icon_16x16.png
sips -z 32 32 icon-1024.png --out sniffly.iconset/icon_16x16@2x.png
sips -z 32 32 icon-1024.png --out sniffly.iconset/icon_32x32.png
sips -z 64 64 icon-1024.png --out sniffly.iconset/icon_32x32@2x.png
sips -z 128 128 icon-1024.png --out sniffly.iconset/icon_128x128.png
sips -z 256 256 icon-1024.png --out sniffly.iconset/icon_128x128@2x.png
sips -z 256 256 icon-1024.png --out sniffly.iconset/icon_256x256.png
sips -z 512 512 icon-1024.png --out sniffly.iconset/icon_256x256@2x.png
sips -z 512 512 icon-1024.png --out sniffly.iconset/icon_512x512.png
sips -z 1024 1024 icon-1024.png --out sniffly.iconset/icon_512x512@2x.png
# Convert to .icns
iconutil -c icns sniffly.iconset -o Sniffly.app/Contents/Resources/sniffly.icns
5. Bundle GTK4 Libraries
This is the tricky part! The app needs GTK4 libraries. You have two options:
Option A: Use Homebrew's GTK4 (requires user to have GTK4 installed)
# User must run: brew install gtk4
# Then the app will find libraries via dylib search paths
Option B: Bundle libraries (recommended for distribution)
# Copy GTK4 libraries into the bundle
cp -r /opt/homebrew/lib/libgtk-4*.dylib Sniffly.app/Contents/Frameworks/
cp -r /opt/homebrew/lib/libglib-2.0*.dylib Sniffly.app/Contents/Frameworks/
cp -r /opt/homebrew/lib/libgobject-2.0*.dylib Sniffly.app/Contents/Frameworks/
# ... and all other GTK4 dependencies
# Use install_name_tool to fix library paths
install_name_tool -change /opt/homebrew/lib/libgtk-4.dylib \
@executable_path/../Frameworks/libgtk-4.dylib \
Sniffly.app/Contents/MacOS/sniffly
Better Option: Use dylibbundler
# Install dylibbundler
brew install dylibbundler
# Automatically bundle all dependencies
dylibbundler -od -b \
-x Sniffly.app/Contents/MacOS/sniffly \
-d Sniffly.app/Contents/Frameworks/ \
-p @executable_path/../Frameworks/
6. Create the .dmg
# Create a temporary directory for DMG contents
mkdir dmg_temp
cp -r Sniffly.app dmg_temp/
# Optional: Create a symbolic link to /Applications for easy drag-install
ln -s /Applications dmg_temp/Applications
# Create the DMG
hdiutil create -volname "Sniffly" \
-srcfolder dmg_temp \
-ov -format UDZO \
Sniffly-0.1.0-macOS.dmg
# Clean up
rm -rf dmg_temp
7. Code Signing (Optional but Recommended)
# Sign the app (requires Apple Developer account)
codesign --deep --force --verify --verbose \
--sign "Developer ID Application: Your Name" \
Sniffly.app
# Notarize with Apple (required for distribution outside App Store)
xcrun notarytool submit Sniffly-0.1.0-macOS.dmg \
--apple-id "your@email.com" \
--password "app-specific-password" \
--team-id "YOUR_TEAM_ID" \
--wait
# Staple the notarization ticket
xcrun stapler staple Sniffly-0.1.0-macOS.dmg
Linux Packaging
Option 1: AppImage (Recommended - Works on All Distros)
AppImage is a universal package format that works across all Linux distributions.
Create AppImage Structure
# Create AppDir structure
mkdir -p Sniffly.AppDir/usr/bin
mkdir -p Sniffly.AppDir/usr/lib
mkdir -p Sniffly.AppDir/usr/share/applications
mkdir -p Sniffly.AppDir/usr/share/icons/hicolor/256x256/apps
# Copy executable
cp build/sniffly Sniffly.AppDir/usr/bin/
# Copy icon
cp assets/sniffly.png Sniffly.AppDir/usr/share/icons/hicolor/256x256/apps/sniffly.png
cp assets/sniffly.png Sniffly.AppDir/sniffly.png
# Create .desktop file
cat > Sniffly.AppDir/sniffly.desktop << 'EOF'
[Desktop Entry]
Type=Application
Name=Sniffly
Comment=Disk Space Analyzer
Exec=sniffly
Icon=sniffly
Categories=Utility;System;
Terminal=false
EOF
# Copy to required location
cp Sniffly.AppDir/sniffly.desktop Sniffly.AppDir/usr/share/applications/
# Create AppRun script
cat > Sniffly.AppDir/AppRun << 'EOF'
#!/bin/bash
SELF=$(readlink -f "$0")
HERE=${SELF%/*}
export PATH="${HERE}/usr/bin:${PATH}"
export LD_LIBRARY_PATH="${HERE}/usr/lib:${LD_LIBRARY_PATH}"
export XDG_DATA_DIRS="${HERE}/usr/share:${XDG_DATA_DIRS}"
exec "${HERE}/usr/bin/sniffly" "$@"
EOF
chmod +x Sniffly.AppDir/AppRun
# Download appimagetool
wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x appimagetool-x86_64.AppImage
# Build AppImage
./appimagetool-x86_64.AppImage Sniffly.AppDir Sniffly-0.1.0-x86_64.AppImage
Note: AppImage requires GTK4 to be installed on the system. For a fully self-contained AppImage, you'd need to bundle GTK4 libraries.
Option 2: Debian Package (.deb)
For Debian/Ubuntu users:
# Create package structure
mkdir -p sniffly-0.1.0/DEBIAN
mkdir -p sniffly-0.1.0/usr/bin
mkdir -p sniffly-0.1.0/usr/share/applications
mkdir -p sniffly-0.1.0/usr/share/icons/hicolor/256x256/apps
# Copy files
cp build/sniffly sniffly-0.1.0/usr/bin/
cp assets/sniffly.png sniffly-0.1.0/usr/share/icons/hicolor/256x256/apps/
cp assets/sniffly.desktop sniffly-0.1.0/usr/share/applications/
# Create control file
cat > sniffly-0.1.0/DEBIAN/control << 'EOF'
Package: sniffly
Version: 0.1.0
Section: utils
Priority: optional
Architecture: amd64
Depends: libgtk-4-1, libglib2.0-0
Maintainer: FortranGoingOnForty <your@email.com>
Description: Disk Space Analyzer
A fast, beautiful disk space analyzer built with Fortran and GTK4.
Visualizes disk usage with interactive treemaps.
EOF
# Build the package
dpkg-deb --build sniffly-0.1.0
Option 3: RPM Package (Fedora/RedHat)
# Create RPM build directories
mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
# Create spec file
cat > ~/rpmbuild/SPECS/sniffly.spec << 'EOF'
Name: sniffly
Version: 0.1.0
Release: 1%{?dist}
Summary: Disk Space Analyzer
License: MIT
URL: https://github.com/FortranGoingOnForty/sniffly
Source0: %{name}-%{version}.tar.gz
BuildRequires: meson, gcc-gfortran, gtk4-devel
Requires: gtk4
%description
A fast, beautiful disk space analyzer built with Fortran and GTK4.
%prep
%setup -q
%build
meson setup build
meson compile -C build
%install
install -Dm755 build/sniffly %{buildroot}%{_bindir}/sniffly
install -Dm644 assets/sniffly.desktop %{buildroot}%{_datadir}/applications/sniffly.desktop
install -Dm644 assets/sniffly.png %{buildroot}%{_datadir}/icons/hicolor/256x256/apps/sniffly.png
%files
%{_bindir}/sniffly
%{_datadir}/applications/sniffly.desktop
%{_datadir}/icons/hicolor/256x256/apps/sniffly.png
%changelog
* Mon Jan 01 2024 Your Name <your@email.com> - 0.1.0-1
- Initial release
EOF
# Create source tarball
tar czf ~/rpmbuild/SOURCES/sniffly-0.1.0.tar.gz ../sniffly/
# Build RPM
rpmbuild -ba ~/rpmbuild/SPECS/sniffly.spec
Automated Packaging with CI/CD
GitHub Actions Example
Create .github/workflows/release.yml:
name: Release Build
on:
push:
tags:
- 'v*'
jobs:
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: |
brew install meson gtk4 gcc
- name: Build
run: |
meson setup build
meson compile -C build
- name: Create DMG
run: |
# Add DMG creation steps here
- name: Upload Release Asset
uses: actions/upload-artifact@v3
with:
name: Sniffly-macOS.dmg
path: Sniffly-*.dmg
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y meson gfortran libgtk-4-dev
- name: Build
run: |
meson setup build
meson compile -C build
- name: Create AppImage
run: |
# Add AppImage creation steps here
- name: Upload Release Asset
uses: actions/upload-artifact@v3
with:
name: Sniffly-Linux-x86_64.AppImage
path: Sniffly-*.AppImage
Quick Start Summary
macOS
- Build the app:
meson compile -C build - Create .app bundle structure
- Use
dylibbundlerto bundle GTK4 libraries - Create .dmg with
hdiutil - (Optional) Code sign and notarize
Result: Sniffly-0.1.0-macOS.dmg
Linux
- Build the app:
meson compile -C build - Create AppDir structure
- Use
appimagetoolto create AppImage - Or create .deb/.rpm for specific distros
Result: Sniffly-0.1.0-x86_64.AppImage or .deb/.rpm
Distribution Checklist
- Create application icon (1024x1024 PNG)
- Create .desktop file for Linux
- Test on clean system without development tools
- Document system requirements (GTK4, etc.)
- Add installation instructions to README
- Create GitHub releases page
- Consider Homebrew formula for macOS
- Consider Flatpak for Linux (alternative to AppImage)
Notes on GTK4 Dependencies
Sniffly requires GTK4 at runtime. You have three options:
- User installation: Require users to install GTK4 (
brew install gtk4on macOS,apt install libgtk-4-1on Debian) - Bundle libraries: Include GTK4 in your app bundle (increases size ~100MB but works anywhere)
- Hybrid: Bundle on macOS, rely on system packages on Linux
For the best user experience, option 2 (bundling) is recommended for macOS, while option 1 (system packages) works well for Linux where GTK4 is commonly available.
Future Improvements
- Flatpak: Cross-distro sandboxed app (like macOS .app)
- Snap: Alternative Linux universal package
- Homebrew Cask: Easy installation on macOS (
brew install --cask sniffly) - AUR Package: For Arch Linux users
- Windows: Cross-compile with MinGW-w64 (if desired)
View source
| 1 | # Sniffly Packaging Guide |
| 2 | |
| 3 | This document explains how to package Sniffly for distribution on macOS and Linux. |
| 4 | |
| 5 | ## Current State |
| 6 | |
| 7 | Sniffly is built using: |
| 8 | - **Meson** build system |
| 9 | - **GTK4** for the GUI |
| 10 | - **gtk-fortran** bindings |
| 11 | - **gfortran** compiler (GCC Fortran) |
| 12 | |
| 13 | The executable is currently located at `build/sniffly` after running `meson compile -C build`. |
| 14 | |
| 15 | --- |
| 16 | |
| 17 | ## macOS Packaging (.dmg) |
| 18 | |
| 19 | ### Overview |
| 20 | On macOS, applications are distributed as: |
| 21 | 1. **.app bundles** - Directory structure that looks like a single file |
| 22 | 2. **.dmg files** - Disk images containing the .app bundle |
| 23 | |
| 24 | ### Steps to Create a macOS .app Bundle |
| 25 | |
| 26 | #### 1. Create the Bundle Structure |
| 27 | |
| 28 | ```bash |
| 29 | # Create the app bundle directories |
| 30 | mkdir -p Sniffly.app/Contents/MacOS |
| 31 | mkdir -p Sniffly.app/Contents/Resources |
| 32 | mkdir -p Sniffly.app/Contents/Frameworks |
| 33 | ``` |
| 34 | |
| 35 | #### 2. Copy the Executable |
| 36 | |
| 37 | ```bash |
| 38 | # Copy the compiled binary |
| 39 | cp build/sniffly Sniffly.app/Contents/MacOS/sniffly |
| 40 | ``` |
| 41 | |
| 42 | #### 3. Create Info.plist |
| 43 | |
| 44 | Create `Sniffly.app/Contents/Info.plist`: |
| 45 | |
| 46 | ```xml |
| 47 | <?xml version="1.0" encoding="UTF-8"?> |
| 48 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
| 49 | <plist version="1.0"> |
| 50 | <dict> |
| 51 | <key>CFBundleExecutable</key> |
| 52 | <string>sniffly</string> |
| 53 | <key>CFBundleIdentifier</key> |
| 54 | <string>org.fortrangoingonforty.sniffly</string> |
| 55 | <key>CFBundleName</key> |
| 56 | <string>Sniffly</string> |
| 57 | <key>CFBundleDisplayName</key> |
| 58 | <string>Sniffly</string> |
| 59 | <key>CFBundleVersion</key> |
| 60 | <string>0.1.0</string> |
| 61 | <key>CFBundleShortVersionString</key> |
| 62 | <string>0.1.0</string> |
| 63 | <key>CFBundlePackageType</key> |
| 64 | <string>APPL</string> |
| 65 | <key>CFBundleSignature</key> |
| 66 | <string>SNIF</string> |
| 67 | <key>CFBundleIconFile</key> |
| 68 | <string>sniffly</string> |
| 69 | <key>LSMinimumSystemVersion</key> |
| 70 | <string>11.0</string> |
| 71 | <key>NSHighResolutionCapable</key> |
| 72 | <true/> |
| 73 | <key>NSRequiresAquaSystemAppearance</key> |
| 74 | <false/> |
| 75 | </dict> |
| 76 | </plist> |
| 77 | ``` |
| 78 | |
| 79 | #### 4. Create an Application Icon |
| 80 | |
| 81 | You need a `.icns` file. Create one from a PNG using: |
| 82 | |
| 83 | ```bash |
| 84 | # Create iconset directory |
| 85 | mkdir sniffly.iconset |
| 86 | |
| 87 | # Create different sizes (you'll need a source image) |
| 88 | # 16x16, 32x32, 128x128, 256x256, 512x512, 1024x1024 |
| 89 | sips -z 16 16 icon-1024.png --out sniffly.iconset/icon_16x16.png |
| 90 | sips -z 32 32 icon-1024.png --out sniffly.iconset/icon_16x16@2x.png |
| 91 | sips -z 32 32 icon-1024.png --out sniffly.iconset/icon_32x32.png |
| 92 | sips -z 64 64 icon-1024.png --out sniffly.iconset/icon_32x32@2x.png |
| 93 | sips -z 128 128 icon-1024.png --out sniffly.iconset/icon_128x128.png |
| 94 | sips -z 256 256 icon-1024.png --out sniffly.iconset/icon_128x128@2x.png |
| 95 | sips -z 256 256 icon-1024.png --out sniffly.iconset/icon_256x256.png |
| 96 | sips -z 512 512 icon-1024.png --out sniffly.iconset/icon_256x256@2x.png |
| 97 | sips -z 512 512 icon-1024.png --out sniffly.iconset/icon_512x512.png |
| 98 | sips -z 1024 1024 icon-1024.png --out sniffly.iconset/icon_512x512@2x.png |
| 99 | |
| 100 | # Convert to .icns |
| 101 | iconutil -c icns sniffly.iconset -o Sniffly.app/Contents/Resources/sniffly.icns |
| 102 | ``` |
| 103 | |
| 104 | #### 5. Bundle GTK4 Libraries |
| 105 | |
| 106 | This is the tricky part! The app needs GTK4 libraries. You have two options: |
| 107 | |
| 108 | **Option A: Use Homebrew's GTK4 (requires user to have GTK4 installed)** |
| 109 | ```bash |
| 110 | # User must run: brew install gtk4 |
| 111 | # Then the app will find libraries via dylib search paths |
| 112 | ``` |
| 113 | |
| 114 | **Option B: Bundle libraries (recommended for distribution)** |
| 115 | |
| 116 | ```bash |
| 117 | # Copy GTK4 libraries into the bundle |
| 118 | cp -r /opt/homebrew/lib/libgtk-4*.dylib Sniffly.app/Contents/Frameworks/ |
| 119 | cp -r /opt/homebrew/lib/libglib-2.0*.dylib Sniffly.app/Contents/Frameworks/ |
| 120 | cp -r /opt/homebrew/lib/libgobject-2.0*.dylib Sniffly.app/Contents/Frameworks/ |
| 121 | # ... and all other GTK4 dependencies |
| 122 | |
| 123 | # Use install_name_tool to fix library paths |
| 124 | install_name_tool -change /opt/homebrew/lib/libgtk-4.dylib \ |
| 125 | @executable_path/../Frameworks/libgtk-4.dylib \ |
| 126 | Sniffly.app/Contents/MacOS/sniffly |
| 127 | ``` |
| 128 | |
| 129 | **Better Option: Use `dylibbundler`** |
| 130 | ```bash |
| 131 | # Install dylibbundler |
| 132 | brew install dylibbundler |
| 133 | |
| 134 | # Automatically bundle all dependencies |
| 135 | dylibbundler -od -b \ |
| 136 | -x Sniffly.app/Contents/MacOS/sniffly \ |
| 137 | -d Sniffly.app/Contents/Frameworks/ \ |
| 138 | -p @executable_path/../Frameworks/ |
| 139 | ``` |
| 140 | |
| 141 | #### 6. Create the .dmg |
| 142 | |
| 143 | ```bash |
| 144 | # Create a temporary directory for DMG contents |
| 145 | mkdir dmg_temp |
| 146 | cp -r Sniffly.app dmg_temp/ |
| 147 | |
| 148 | # Optional: Create a symbolic link to /Applications for easy drag-install |
| 149 | ln -s /Applications dmg_temp/Applications |
| 150 | |
| 151 | # Create the DMG |
| 152 | hdiutil create -volname "Sniffly" \ |
| 153 | -srcfolder dmg_temp \ |
| 154 | -ov -format UDZO \ |
| 155 | Sniffly-0.1.0-macOS.dmg |
| 156 | |
| 157 | # Clean up |
| 158 | rm -rf dmg_temp |
| 159 | ``` |
| 160 | |
| 161 | #### 7. Code Signing (Optional but Recommended) |
| 162 | |
| 163 | ```bash |
| 164 | # Sign the app (requires Apple Developer account) |
| 165 | codesign --deep --force --verify --verbose \ |
| 166 | --sign "Developer ID Application: Your Name" \ |
| 167 | Sniffly.app |
| 168 | |
| 169 | # Notarize with Apple (required for distribution outside App Store) |
| 170 | xcrun notarytool submit Sniffly-0.1.0-macOS.dmg \ |
| 171 | --apple-id "your@email.com" \ |
| 172 | --password "app-specific-password" \ |
| 173 | --team-id "YOUR_TEAM_ID" \ |
| 174 | --wait |
| 175 | |
| 176 | # Staple the notarization ticket |
| 177 | xcrun stapler staple Sniffly-0.1.0-macOS.dmg |
| 178 | ``` |
| 179 | |
| 180 | --- |
| 181 | |
| 182 | ## Linux Packaging |
| 183 | |
| 184 | ### Option 1: AppImage (Recommended - Works on All Distros) |
| 185 | |
| 186 | AppImage is a universal package format that works across all Linux distributions. |
| 187 | |
| 188 | #### Create AppImage Structure |
| 189 | |
| 190 | ```bash |
| 191 | # Create AppDir structure |
| 192 | mkdir -p Sniffly.AppDir/usr/bin |
| 193 | mkdir -p Sniffly.AppDir/usr/lib |
| 194 | mkdir -p Sniffly.AppDir/usr/share/applications |
| 195 | mkdir -p Sniffly.AppDir/usr/share/icons/hicolor/256x256/apps |
| 196 | |
| 197 | # Copy executable |
| 198 | cp build/sniffly Sniffly.AppDir/usr/bin/ |
| 199 | |
| 200 | # Copy icon |
| 201 | cp assets/sniffly.png Sniffly.AppDir/usr/share/icons/hicolor/256x256/apps/sniffly.png |
| 202 | cp assets/sniffly.png Sniffly.AppDir/sniffly.png |
| 203 | |
| 204 | # Create .desktop file |
| 205 | cat > Sniffly.AppDir/sniffly.desktop << 'EOF' |
| 206 | [Desktop Entry] |
| 207 | Type=Application |
| 208 | Name=Sniffly |
| 209 | Comment=Disk Space Analyzer |
| 210 | Exec=sniffly |
| 211 | Icon=sniffly |
| 212 | Categories=Utility;System; |
| 213 | Terminal=false |
| 214 | EOF |
| 215 | |
| 216 | # Copy to required location |
| 217 | cp Sniffly.AppDir/sniffly.desktop Sniffly.AppDir/usr/share/applications/ |
| 218 | |
| 219 | # Create AppRun script |
| 220 | cat > Sniffly.AppDir/AppRun << 'EOF' |
| 221 | #!/bin/bash |
| 222 | SELF=$(readlink -f "$0") |
| 223 | HERE=${SELF%/*} |
| 224 | export PATH="${HERE}/usr/bin:${PATH}" |
| 225 | export LD_LIBRARY_PATH="${HERE}/usr/lib:${LD_LIBRARY_PATH}" |
| 226 | export XDG_DATA_DIRS="${HERE}/usr/share:${XDG_DATA_DIRS}" |
| 227 | exec "${HERE}/usr/bin/sniffly" "$@" |
| 228 | EOF |
| 229 | |
| 230 | chmod +x Sniffly.AppDir/AppRun |
| 231 | |
| 232 | # Download appimagetool |
| 233 | wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage |
| 234 | chmod +x appimagetool-x86_64.AppImage |
| 235 | |
| 236 | # Build AppImage |
| 237 | ./appimagetool-x86_64.AppImage Sniffly.AppDir Sniffly-0.1.0-x86_64.AppImage |
| 238 | ``` |
| 239 | |
| 240 | **Note**: AppImage requires GTK4 to be installed on the system. For a fully self-contained AppImage, you'd need to bundle GTK4 libraries. |
| 241 | |
| 242 | ### Option 2: Debian Package (.deb) |
| 243 | |
| 244 | For Debian/Ubuntu users: |
| 245 | |
| 246 | ```bash |
| 247 | # Create package structure |
| 248 | mkdir -p sniffly-0.1.0/DEBIAN |
| 249 | mkdir -p sniffly-0.1.0/usr/bin |
| 250 | mkdir -p sniffly-0.1.0/usr/share/applications |
| 251 | mkdir -p sniffly-0.1.0/usr/share/icons/hicolor/256x256/apps |
| 252 | |
| 253 | # Copy files |
| 254 | cp build/sniffly sniffly-0.1.0/usr/bin/ |
| 255 | cp assets/sniffly.png sniffly-0.1.0/usr/share/icons/hicolor/256x256/apps/ |
| 256 | cp assets/sniffly.desktop sniffly-0.1.0/usr/share/applications/ |
| 257 | |
| 258 | # Create control file |
| 259 | cat > sniffly-0.1.0/DEBIAN/control << 'EOF' |
| 260 | Package: sniffly |
| 261 | Version: 0.1.0 |
| 262 | Section: utils |
| 263 | Priority: optional |
| 264 | Architecture: amd64 |
| 265 | Depends: libgtk-4-1, libglib2.0-0 |
| 266 | Maintainer: FortranGoingOnForty <your@email.com> |
| 267 | Description: Disk Space Analyzer |
| 268 | A fast, beautiful disk space analyzer built with Fortran and GTK4. |
| 269 | Visualizes disk usage with interactive treemaps. |
| 270 | EOF |
| 271 | |
| 272 | # Build the package |
| 273 | dpkg-deb --build sniffly-0.1.0 |
| 274 | ``` |
| 275 | |
| 276 | ### Option 3: RPM Package (Fedora/RedHat) |
| 277 | |
| 278 | ```bash |
| 279 | # Create RPM build directories |
| 280 | mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} |
| 281 | |
| 282 | # Create spec file |
| 283 | cat > ~/rpmbuild/SPECS/sniffly.spec << 'EOF' |
| 284 | Name: sniffly |
| 285 | Version: 0.1.0 |
| 286 | Release: 1%{?dist} |
| 287 | Summary: Disk Space Analyzer |
| 288 | License: MIT |
| 289 | URL: https://github.com/FortranGoingOnForty/sniffly |
| 290 | Source0: %{name}-%{version}.tar.gz |
| 291 | |
| 292 | BuildRequires: meson, gcc-gfortran, gtk4-devel |
| 293 | Requires: gtk4 |
| 294 | |
| 295 | %description |
| 296 | A fast, beautiful disk space analyzer built with Fortran and GTK4. |
| 297 | |
| 298 | %prep |
| 299 | %setup -q |
| 300 | |
| 301 | %build |
| 302 | meson setup build |
| 303 | meson compile -C build |
| 304 | |
| 305 | %install |
| 306 | install -Dm755 build/sniffly %{buildroot}%{_bindir}/sniffly |
| 307 | install -Dm644 assets/sniffly.desktop %{buildroot}%{_datadir}/applications/sniffly.desktop |
| 308 | install -Dm644 assets/sniffly.png %{buildroot}%{_datadir}/icons/hicolor/256x256/apps/sniffly.png |
| 309 | |
| 310 | %files |
| 311 | %{_bindir}/sniffly |
| 312 | %{_datadir}/applications/sniffly.desktop |
| 313 | %{_datadir}/icons/hicolor/256x256/apps/sniffly.png |
| 314 | |
| 315 | %changelog |
| 316 | * Mon Jan 01 2024 Your Name <your@email.com> - 0.1.0-1 |
| 317 | - Initial release |
| 318 | EOF |
| 319 | |
| 320 | # Create source tarball |
| 321 | tar czf ~/rpmbuild/SOURCES/sniffly-0.1.0.tar.gz ../sniffly/ |
| 322 | |
| 323 | # Build RPM |
| 324 | rpmbuild -ba ~/rpmbuild/SPECS/sniffly.spec |
| 325 | ``` |
| 326 | |
| 327 | --- |
| 328 | |
| 329 | ## Automated Packaging with CI/CD |
| 330 | |
| 331 | ### GitHub Actions Example |
| 332 | |
| 333 | Create `.github/workflows/release.yml`: |
| 334 | |
| 335 | ```yaml |
| 336 | name: Release Build |
| 337 | |
| 338 | on: |
| 339 | push: |
| 340 | tags: |
| 341 | - 'v*' |
| 342 | |
| 343 | jobs: |
| 344 | build-macos: |
| 345 | runs-on: macos-latest |
| 346 | steps: |
| 347 | - uses: actions/checkout@v3 |
| 348 | - name: Install dependencies |
| 349 | run: | |
| 350 | brew install meson gtk4 gcc |
| 351 | - name: Build |
| 352 | run: | |
| 353 | meson setup build |
| 354 | meson compile -C build |
| 355 | - name: Create DMG |
| 356 | run: | |
| 357 | # Add DMG creation steps here |
| 358 | - name: Upload Release Asset |
| 359 | uses: actions/upload-artifact@v3 |
| 360 | with: |
| 361 | name: Sniffly-macOS.dmg |
| 362 | path: Sniffly-*.dmg |
| 363 | |
| 364 | build-linux: |
| 365 | runs-on: ubuntu-latest |
| 366 | steps: |
| 367 | - uses: actions/checkout@v3 |
| 368 | - name: Install dependencies |
| 369 | run: | |
| 370 | sudo apt-get update |
| 371 | sudo apt-get install -y meson gfortran libgtk-4-dev |
| 372 | - name: Build |
| 373 | run: | |
| 374 | meson setup build |
| 375 | meson compile -C build |
| 376 | - name: Create AppImage |
| 377 | run: | |
| 378 | # Add AppImage creation steps here |
| 379 | - name: Upload Release Asset |
| 380 | uses: actions/upload-artifact@v3 |
| 381 | with: |
| 382 | name: Sniffly-Linux-x86_64.AppImage |
| 383 | path: Sniffly-*.AppImage |
| 384 | ``` |
| 385 | |
| 386 | --- |
| 387 | |
| 388 | ## Quick Start Summary |
| 389 | |
| 390 | ### macOS |
| 391 | 1. Build the app: `meson compile -C build` |
| 392 | 2. Create .app bundle structure |
| 393 | 3. Use `dylibbundler` to bundle GTK4 libraries |
| 394 | 4. Create .dmg with `hdiutil` |
| 395 | 5. (Optional) Code sign and notarize |
| 396 | |
| 397 | **Result**: `Sniffly-0.1.0-macOS.dmg` |
| 398 | |
| 399 | ### Linux |
| 400 | 1. Build the app: `meson compile -C build` |
| 401 | 2. Create AppDir structure |
| 402 | 3. Use `appimagetool` to create AppImage |
| 403 | 4. Or create .deb/.rpm for specific distros |
| 404 | |
| 405 | **Result**: `Sniffly-0.1.0-x86_64.AppImage` or `.deb`/`.rpm` |
| 406 | |
| 407 | --- |
| 408 | |
| 409 | ## Distribution Checklist |
| 410 | |
| 411 | - [ ] Create application icon (1024x1024 PNG) |
| 412 | - [ ] Create .desktop file for Linux |
| 413 | - [ ] Test on clean system without development tools |
| 414 | - [ ] Document system requirements (GTK4, etc.) |
| 415 | - [ ] Add installation instructions to README |
| 416 | - [ ] Create GitHub releases page |
| 417 | - [ ] Consider Homebrew formula for macOS |
| 418 | - [ ] Consider Flatpak for Linux (alternative to AppImage) |
| 419 | |
| 420 | --- |
| 421 | |
| 422 | ## Notes on GTK4 Dependencies |
| 423 | |
| 424 | Sniffly requires GTK4 at runtime. You have three options: |
| 425 | |
| 426 | 1. **User installation**: Require users to install GTK4 (`brew install gtk4` on macOS, `apt install libgtk-4-1` on Debian) |
| 427 | 2. **Bundle libraries**: Include GTK4 in your app bundle (increases size ~100MB but works anywhere) |
| 428 | 3. **Hybrid**: Bundle on macOS, rely on system packages on Linux |
| 429 | |
| 430 | For the best user experience, option 2 (bundling) is recommended for macOS, while option 1 (system packages) works well for Linux where GTK4 is commonly available. |
| 431 | |
| 432 | --- |
| 433 | |
| 434 | ## Future Improvements |
| 435 | |
| 436 | - **Flatpak**: Cross-distro sandboxed app (like macOS .app) |
| 437 | - **Snap**: Alternative Linux universal package |
| 438 | - **Homebrew Cask**: Easy installation on macOS (`brew install --cask sniffly`) |
| 439 | - **AUR Package**: For Arch Linux users |
| 440 | - **Windows**: Cross-compile with MinGW-w64 (if desired) |