# 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: 1. **.app bundles** - Directory structure that looks like a single file 2. **.dmg files** - Disk images containing the .app bundle ### Steps to Create a macOS .app Bundle #### 1. Create the Bundle Structure ```bash # 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 ```bash # Copy the compiled binary cp build/sniffly Sniffly.app/Contents/MacOS/sniffly ``` #### 3. Create Info.plist Create `Sniffly.app/Contents/Info.plist`: ```xml CFBundleExecutable sniffly CFBundleIdentifier org.fortrangoingonforty.sniffly CFBundleName Sniffly CFBundleDisplayName Sniffly CFBundleVersion 0.1.0 CFBundleShortVersionString 0.1.0 CFBundlePackageType APPL CFBundleSignature SNIF CFBundleIconFile sniffly LSMinimumSystemVersion 11.0 NSHighResolutionCapable NSRequiresAquaSystemAppearance ``` #### 4. Create an Application Icon You need a `.icns` file. Create one from a PNG using: ```bash # 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)** ```bash # User must run: brew install gtk4 # Then the app will find libraries via dylib search paths ``` **Option B: Bundle libraries (recommended for distribution)** ```bash # 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`** ```bash # 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 ```bash # 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) ```bash # 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 ```bash # 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: ```bash # 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 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) ```bash # 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 - 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`: ```yaml 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 1. Build the app: `meson compile -C build` 2. Create .app bundle structure 3. Use `dylibbundler` to bundle GTK4 libraries 4. Create .dmg with `hdiutil` 5. (Optional) Code sign and notarize **Result**: `Sniffly-0.1.0-macOS.dmg` ### Linux 1. Build the app: `meson compile -C build` 2. Create AppDir structure 3. Use `appimagetool` to create AppImage 4. 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: 1. **User installation**: Require users to install GTK4 (`brew install gtk4` on macOS, `apt install libgtk-4-1` on Debian) 2. **Bundle libraries**: Include GTK4 in your app bundle (increases size ~100MB but works anywhere) 3. **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)