fortrangoingonforty/sniffly / 3e8784d

Browse files

packaging things and fixes

Authored by espadonne
SHA
3e8784d4d85893bce8994ef8e5de8a42d9712c44
Parents
d1ed96b
Tree
19b9619

6 changed files

StatusFile+-
M app/main.f90 84 17
A scripts/bundle-libs.sh 157 0
A scripts/package-macos-fixed.sh 170 0
A scripts/package-macos.sh 180 0
A scripts/simple-bundle.sh 119 0
A src/version.f90.in 10 0
app/main.f90modified
@@ -9,35 +9,78 @@
99
 
1010
 program sniffly_main
1111
   use gtk_app
12
-  use file_system, only: get_absolute_path
12
+  use file_system, only: get_absolute_path, is_directory
13
+  use version
1314
   implicit none
1415
 
15
-  integer :: exit_status, nargs
16
-  character(len=512) :: arg, scan_dir
16
+  integer :: exit_status, nargs, iostat_val
17
+  character(len=512) :: arg, scan_dir, home_dir
1718
   character(len=:), allocatable :: abs_path
19
+  logical :: has_terminal
1820
 
19
-  ! Print banner
20
-  print '(A)', "======================================"
21
-  print '(A)', "    Sniffly v0.1.0 - Alpha"
22
-  print '(A)', "    Disk Space Analyzer"
23
-  print '(A)', "    Pure Fortran + GTK4"
24
-  print '(A)', "======================================"
25
-  print '(A)', ""
21
+  ! Check if we have a terminal (if not, skip print statements to avoid hanging)
22
+  ! When launched from Finder, print statements can cause the app to hang
23
+  inquire(unit=6, opened=has_terminal, iostat=iostat_val)
24
+  if (iostat_val /= 0) has_terminal = .false.
25
+
26
+  ! Print banner (only if terminal is available)
27
+  if (has_terminal) then
28
+    print '(A)', "======================================"
29
+    print '(A)', "    " // SNIFFLY_NAME // " v" // SNIFFLY_VERSION
30
+    print '(A)', "    " // SNIFFLY_DESCRIPTION
31
+    print '(A)', "    Pure Fortran + GTK4"
32
+    print '(A)', "======================================"
33
+    print '(A)', ""
34
+  end if
2635
 
2736
   ! Parse command-line arguments
2837
   nargs = command_argument_count()
2938
 
3039
   if (nargs > 0) then
3140
     call get_command_argument(1, arg)
41
+
42
+    ! Handle flags
43
+    if (trim(arg) == '--help' .or. trim(arg) == '-h') then
44
+      call print_usage()
45
+      stop 0
46
+    else if (trim(arg) == '--version' .or. trim(arg) == '-v') then
47
+      print '(A)', SNIFFLY_NAME // " version " // SNIFFLY_VERSION
48
+      stop 0
49
+    end if
50
+
51
+    ! Validate directory path
52
+    if (index(trim(arg), '--') == 1) then
53
+      if (has_terminal) then
54
+        print '(A)', "ERROR: Unknown option: " // trim(arg)
55
+        print '(A)', "Try 'sniffly --help' for more information"
56
+      end if
57
+      stop 1
58
+    end if
59
+
3260
     ! Expand relative paths to absolute paths
3361
     abs_path = get_absolute_path(trim(arg))
3462
     scan_dir = abs_path
35
-    print '(A,A)', "Directory to scan: ", trim(scan_dir)
63
+
64
+    ! Check if directory exists
65
+    if (.not. is_directory(scan_dir)) then
66
+      if (has_terminal) print '(A)', "ERROR: Not a valid directory: " // trim(scan_dir)
67
+      stop 1
68
+    end if
69
+
70
+    if (has_terminal) print '(A,A)', "Directory to scan: ", trim(scan_dir)
3671
     call sniffly_set_scan_path(scan_dir)
3772
   else
38
-    print '(A)', "Usage: sniffly [directory]"
39
-    print '(A)', "No directory specified, will use default"
40
-    print '(A)', ""
73
+    ! No directory specified - use home directory
74
+    call get_environment_variable("HOME", home_dir)
75
+    if (len_trim(home_dir) == 0) then
76
+      home_dir = "/Users"  ! Fallback for macOS
77
+    end if
78
+    scan_dir = trim(home_dir)
79
+    if (has_terminal) then
80
+      print '(A)', "No directory specified, starting with home directory"
81
+      print '(A,A)', "Directory to scan: ", trim(scan_dir)
82
+    end if
83
+    call sniffly_set_scan_path(scan_dir)
4184
   end if
4285
 
4386
   ! Run the GTK application (enters main loop)
@@ -45,11 +88,35 @@ program sniffly_main
4588
 
4689
   ! Exit with appropriate status
4790
   if (exit_status /= 0) then
48
-    print '(A,I0)', "ERROR: Application exited with status ", exit_status
91
+    if (has_terminal) print '(A,I0)', "ERROR: Application exited with status ", exit_status
4992
     stop 1
5093
   end if
5194
 
52
-  print '(A)', ""
53
-  print '(A)', "Sniffly closed successfully. Goodbye!"
95
+  if (has_terminal) then
96
+    print '(A)', ""
97
+    print '(A)', "Sniffly closed successfully. Goodbye!"
98
+  end if
99
+
100
+contains
101
+
102
+  subroutine print_usage()
103
+    print '(A)', ""
104
+    print '(A)', "Usage: sniffly [OPTIONS] [DIRECTORY]"
105
+    print '(A)', ""
106
+    print '(A)', "A fast, visual disk space analyzer built with Fortran and GTK4"
107
+    print '(A)', ""
108
+    print '(A)', "Options:"
109
+    print '(A)', "  -h, --help       Show this help message and exit"
110
+    print '(A)', "  -v, --version    Show version information and exit"
111
+    print '(A)', ""
112
+    print '(A)', "Arguments:"
113
+    print '(A)', "  DIRECTORY        Directory to analyze (default: home directory)"
114
+    print '(A)', ""
115
+    print '(A)', "Examples:"
116
+    print '(A)', "  sniffly                   # Start with home directory"
117
+    print '(A)', "  sniffly /Users/username   # Analyze specific directory"
118
+    print '(A)', "  sniffly .                 # Analyze current directory"
119
+    print '(A)', ""
120
+  end subroutine print_usage
54121
 
55122
 end program sniffly_main
scripts/bundle-libs.shadded
@@ -0,0 +1,157 @@
1
+#!/bin/bash
2
+# Complete library bundling script for Sniffly
3
+# Handles all GTK4 dependencies properly
4
+
5
+set -e
6
+
7
+APP_BUNDLE="Sniffly.app"
8
+FRAMEWORKS="${APP_BUNDLE}/Contents/Frameworks"
9
+MACOS_BIN="${APP_BUNDLE}/Contents/MacOS/sniffly-bin"
10
+
11
+echo "=== Sniffly Library Bundling ==="
12
+echo ""
13
+
14
+# Create frameworks directory
15
+mkdir -p "$FRAMEWORKS"
16
+
17
+echo "Step 1: Copying critical GTK4 libraries..."
18
+# Main GTK4
19
+cp -v /opt/homebrew/opt/gtk4/lib/libgtk-4.1.dylib "$FRAMEWORKS/" 2>/dev/null || echo "  libgtk-4.1.dylib not found (may be bundled already)"
20
+
21
+# GLib family
22
+cp -v /opt/homebrew/opt/glib/lib/libglib-2.0.0.dylib "$FRAMEWORKS/" 2>/dev/null || true
23
+cp -v /opt/homebrew/opt/glib/lib/libgio-2.0.0.dylib "$FRAMEWORKS/" 2>/dev/null || true
24
+cp -v /opt/homebrew/opt/glib/lib/libgobject-2.0.0.dylib "$FRAMEWORKS/" 2>/dev/null || true
25
+cp -v /opt/homebrew/opt/glib/lib/libgmodule-2.0.0.dylib "$FRAMEWORKS/" 2>/dev/null || true
26
+
27
+# GTK-Fortran binding
28
+cp -v /usr/local/lib/libgtk-4-fortran.4.8.0.dylib "$FRAMEWORKS/" 2>/dev/null || true
29
+
30
+# Pango
31
+cp -v /opt/homebrew/opt/pango/lib/libpango-1.0.0.dylib "$FRAMEWORKS/" 2>/dev/null || true
32
+cp -v /opt/homebrew/opt/pango/lib/libpangocairo-1.0.0.dylib "$FRAMEWORKS/" 2>/dev/null || true
33
+cp -v /opt/homebrew/opt/pango/lib/libpangoft2-1.0.0.dylib "$FRAMEWORKS/" 2>/dev/null || true
34
+
35
+# GdkPixbuf
36
+cp -v /opt/homebrew/opt/gdk-pixbuf/lib/libgdk_pixbuf-2.0.0.dylib "$FRAMEWORKS/" 2>/dev/null || true
37
+
38
+# Cairo
39
+cp -v /opt/homebrew/opt/cairo/lib/libcairo.2.dylib "$FRAMEWORKS/" 2>/dev/null || true
40
+cp -v /opt/homebrew/opt/cairo/lib/libcairo-gobject.2.dylib "$FRAMEWORKS/" 2>/dev/null || true
41
+
42
+# HarfBuzz
43
+cp -v /opt/homebrew/opt/harfbuzz/lib/libharfbuzz.0.dylib "$FRAMEWORKS/" 2>/dev/null || true
44
+
45
+# Graphene
46
+cp -v /opt/homebrew/opt/graphene/lib/libgraphene-1.0.0.dylib "$FRAMEWORKS/" 2>/dev/null || true
47
+
48
+# Epoxy (OpenGL wrapper)
49
+cp -v /opt/homebrew/opt/libepoxy/lib/libepoxy.0.dylib "$FRAMEWORKS/" 2>/dev/null || true
50
+
51
+# Gettext (internationalization)
52
+cp -v /opt/homebrew/opt/gettext/lib/libintl.8.dylib "$FRAMEWORKS/" 2>/dev/null || true
53
+
54
+# Additional dependencies
55
+cp -v /opt/homebrew/opt/fribidi/lib/libfribidi.0.dylib "$FRAMEWORKS/" 2>/dev/null || true
56
+cp -v /opt/homebrew/opt/fontconfig/lib/libfontconfig.1.dylib "$FRAMEWORKS/" 2>/dev/null || true
57
+cp -v /opt/homebrew/opt/freetype/lib/libfreetype.6.dylib "$FRAMEWORKS/" 2>/dev/null || true
58
+
59
+# GFortran runtime (dylibbundler can't handle this due to header padding)
60
+echo "Manually copying GFortran runtime..."
61
+cp -v /opt/homebrew/opt/gcc/lib/gcc/current/libgfortran.5.dylib "$FRAMEWORKS/" 2>/dev/null || \
62
+  cp -v /usr/local/lib/gcc/current/libgfortran.5.dylib "$FRAMEWORKS/" 2>/dev/null || true
63
+cp -v /opt/homebrew/opt/gcc/lib/gcc/current/libgcc_s.1.1.dylib "$FRAMEWORKS/" 2>/dev/null || \
64
+  cp -v /usr/local/lib/gcc/current/libgcc_s.1.1.dylib "$FRAMEWORKS/" 2>/dev/null || true
65
+cp -v /opt/homebrew/opt/gcc/lib/gcc/current/libquadmath.0.dylib "$FRAMEWORKS/" 2>/dev/null || \
66
+  cp -v /usr/local/lib/gcc/current/libquadmath.0.dylib "$FRAMEWORKS/" 2>/dev/null || true
67
+
68
+# Fix paths in sniffly-bin to point to bundled gfortran
69
+echo "Fixing GFortran library paths in binary..."
70
+install_name_tool -change /opt/homebrew/opt/gcc/lib/gcc/current/libgfortran.5.dylib @executable_path/../Frameworks/libgfortran.5.dylib "$MACOS_BIN" 2>/dev/null || true
71
+install_name_tool -change /usr/local/lib/gcc/current/libgfortran.5.dylib @executable_path/../Frameworks/libgfortran.5.dylib "$MACOS_BIN" 2>/dev/null || true
72
+
73
+echo ""
74
+echo "Step 2: Running dylibbundler for ALL dependencies..."
75
+# Use -of to overwrite and fix ALL dylibs recursively
76
+dylibbundler -of -b \
77
+    -x "$MACOS_BIN" \
78
+    -d "$FRAMEWORKS/" \
79
+    -p @executable_path/../Frameworks/ \
80
+    -s /usr/local/lib \
81
+    -s /opt/homebrew/lib \
82
+    -s /opt/homebrew/opt/gtk4/lib \
83
+    -s /opt/homebrew/opt/glib/lib \
84
+    -s /opt/homebrew/opt/pango/lib \
85
+    -s /opt/homebrew/opt/cairo/lib \
86
+    -s /opt/homebrew/opt/harfbuzz/lib \
87
+    -s /opt/homebrew/opt/gdk-pixbuf/lib \
88
+    -s /opt/homebrew/opt/graphene/lib \
89
+    -s /opt/homebrew/opt/libepoxy/lib \
90
+    -s /opt/homebrew/opt/gettext/lib \
91
+    -s /opt/homebrew/opt/fribidi/lib \
92
+    -s /opt/homebrew/opt/fontconfig/lib \
93
+    -s /opt/homebrew/opt/freetype/lib \
94
+    -s /opt/homebrew/opt/libpng/lib \
95
+    -s /opt/homebrew/opt/graphite2/lib \
96
+    -s /opt/homebrew/opt/pcre2/lib \
97
+    -s /opt/homebrew/opt/brotli/lib \
98
+    -s /opt/homebrew/opt/bzip2/lib \
99
+    -s /opt/homebrew/opt/zstd/lib 2>&1
100
+
101
+echo ""
102
+echo "Step 2.5: Fixing library interdependencies..."
103
+# dylibbundler should have done this, but let's make sure all bundled libs reference each other correctly
104
+for lib in "$FRAMEWORKS"/*.dylib; do
105
+    if [ -f "$lib" ]; then
106
+        # Fix any remaining /opt/homebrew references
107
+        install_name_tool -change /opt/homebrew/opt/glib/lib/libglib-2.0.0.dylib @executable_path/../Frameworks/libglib-2.0.0.dylib "$lib" 2>/dev/null || true
108
+        install_name_tool -change /opt/homebrew/opt/glib/lib/libgio-2.0.0.dylib @executable_path/../Frameworks/libgio-2.0.0.dylib "$lib" 2>/dev/null || true
109
+        install_name_tool -change /opt/homebrew/opt/glib/lib/libgobject-2.0.0.dylib @executable_path/../Frameworks/libgobject-2.0.0.dylib "$lib" 2>/dev/null || true
110
+        install_name_tool -change /opt/homebrew/opt/glib/lib/libgmodule-2.0.0.dylib @executable_path/../Frameworks/libgmodule-2.0.0.dylib "$lib" 2>/dev/null || true
111
+        install_name_tool -change /opt/homebrew/opt/cairo/lib/libcairo.2.dylib @executable_path/../Frameworks/libcairo.2.dylib "$lib" 2>/dev/null || true
112
+        install_name_tool -change /opt/homebrew/opt/cairo/lib/libcairo-gobject.2.dylib @executable_path/../Frameworks/libcairo-gobject.2.dylib "$lib" 2>/dev/null || true
113
+        install_name_tool -change /opt/homebrew/opt/pango/lib/libpango-1.0.0.dylib @executable_path/../Frameworks/libpango-1.0.0.dylib "$lib" 2>/dev/null || true
114
+        install_name_tool -change /opt/homebrew/opt/pango/lib/libpangocairo-1.0.0.dylib @executable_path/../Frameworks/libpangocairo-1.0.0.dylib "$lib" 2>/dev/null || true
115
+        install_name_tool -change /opt/homebrew/opt/gtk4/lib/libgtk-4.1.dylib @executable_path/../Frameworks/libgtk-4.1.dylib "$lib" 2>/dev/null || true
116
+        install_name_tool -change /opt/homebrew/opt/gettext/lib/libintl.8.dylib @executable_path/../Frameworks/libintl.8.dylib "$lib" 2>/dev/null || true
117
+        install_name_tool -change /opt/homebrew/opt/harfbuzz/lib/libharfbuzz.0.dylib @executable_path/../Frameworks/libharfbuzz.0.dylib "$lib" 2>/dev/null || true
118
+        install_name_tool -change /opt/homebrew/opt/fribidi/lib/libfribidi.0.dylib @executable_path/../Frameworks/libfribidi.0.dylib "$lib" 2>/dev/null || true
119
+        install_name_tool -change /opt/homebrew/opt/fontconfig/lib/libfontconfig.1.dylib @executable_path/../Frameworks/libfontconfig.1.dylib "$lib" 2>/dev/null || true
120
+        install_name_tool -change /opt/homebrew/opt/freetype/lib/libfreetype.6.dylib @executable_path/../Frameworks/libfreetype.6.dylib "$lib" 2>/dev/null || true
121
+        install_name_tool -change /opt/homebrew/opt/gdk-pixbuf/lib/libgdk_pixbuf-2.0.0.dylib @executable_path/../Frameworks/libgdk_pixbuf-2.0.0.dylib "$lib" 2>/dev/null || true
122
+        install_name_tool -change /opt/homebrew/opt/graphene/lib/libgraphene-1.0.0.dylib @executable_path/../Frameworks/libgraphene-1.0.0.dylib "$lib" 2>/dev/null || true
123
+        install_name_tool -change /opt/homebrew/opt/libepoxy/lib/libepoxy.0.dylib @executable_path/../Frameworks/libepoxy.0.dylib "$lib" 2>/dev/null || true
124
+    fi
125
+done
126
+
127
+echo "  Fixing library ID names..."
128
+# Also fix the library ID names themselves
129
+for lib in "$FRAMEWORKS"/*.dylib; do
130
+    if [ -f "$lib" ]; then
131
+        libname=$(basename "$lib")
132
+        install_name_tool -id @executable_path/../Frameworks/"$libname" "$lib" 2>/dev/null || true
133
+    fi
134
+done
135
+
136
+echo ""
137
+echo "Step 3: Verifying bundle..."
138
+TOTAL_LIBS=$(ls -1 "$FRAMEWORKS" | wc -l | tr -d ' ')
139
+echo "Total libraries bundled: $TOTAL_LIBS"
140
+
141
+# Check for critical libraries
142
+echo ""
143
+echo "Critical libraries check:"
144
+for lib in libgtk-4 libglib-2.0 libgtk-4-fortran libcairo libpango; do
145
+    if ls "$FRAMEWORKS"/${lib}* 1> /dev/null 2>&1; then
146
+        echo "  ✓ ${lib}"
147
+    else
148
+        echo "  ✗ ${lib} MISSING!"
149
+    fi
150
+done
151
+
152
+echo ""
153
+echo "Step 4: Checking binary dependencies..."
154
+otool -L "$MACOS_BIN" | grep -E "(gtk|glib|cairo)" | head -5
155
+
156
+echo ""
157
+echo "=== Bundling Complete ==="
scripts/package-macos-fixed.shadded
@@ -0,0 +1,170 @@
1
+#!/bin/bash
2
+# Sniffly macOS Packaging Script - Fixed for GTK4 resources
3
+# Creates a fully standalone .app bundle and .dmg
4
+
5
+set -e
6
+
7
+GREEN='\033[0;32m'
8
+BLUE='\033[0;34m'
9
+RED='\033[0;31m'
10
+NC='\033[0m'
11
+
12
+echo -e "${BLUE}========================================${NC}"
13
+echo -e "${BLUE}  Sniffly macOS Packaging (Fixed)${NC}"
14
+echo -e "${BLUE}========================================${NC}"
15
+
16
+# Read version
17
+VERSION=$(cat VERSION)
18
+echo -e "${GREEN}Version: ${VERSION}${NC}"
19
+
20
+# Check build
21
+if [ ! -f "build/sniffly" ]; then
22
+    echo -e "${RED}ERROR: build/sniffly not found${NC}"
23
+    exit 1
24
+fi
25
+
26
+# Clean old bundle
27
+APP_NAME="Sniffly"
28
+APP_BUNDLE="${APP_NAME}.app"
29
+rm -rf "${APP_BUNDLE}"
30
+
31
+# Create structure
32
+CONTENTS="${APP_BUNDLE}/Contents"
33
+MACOS="${CONTENTS}/MacOS"
34
+RESOURCES="${CONTENTS}/Resources"
35
+FRAMEWORKS="${CONTENTS}/Frameworks"
36
+
37
+echo -e "${BLUE}Step 1: Creating bundle structure...${NC}"
38
+mkdir -p "${MACOS}"
39
+mkdir -p "${RESOURCES}"
40
+mkdir -p "${FRAMEWORKS}"
41
+
42
+# Copy binary (rename to -bin, we'll create a launcher)
43
+echo -e "${BLUE}Step 2: Copying executable...${NC}"
44
+cp build/sniffly "${MACOS}/sniffly-bin"
45
+chmod +x "${MACOS}/sniffly-bin"
46
+
47
+# Create launcher script
48
+echo -e "${BLUE}Step 3: Creating launcher script...${NC}"
49
+cat > "${MACOS}/sniffly" << 'EOF'
50
+#!/bin/bash
51
+# Sniffly launcher - sets up GTK4 environment
52
+
53
+# Get the app bundle path
54
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
55
+BUNDLE_DIR="$(dirname "$(dirname "$DIR")")"
56
+RESOURCES_DIR="${BUNDLE_DIR}/Contents/Resources"
57
+
58
+# Set GTK4 environment variables
59
+export GSETTINGS_SCHEMA_DIR="${RESOURCES_DIR}/share/glib-2.0/schemas"
60
+export GTK_DATA_PREFIX="${RESOURCES_DIR}"
61
+export GTK_EXE_PREFIX="${RESOURCES_DIR}"
62
+export GTK_PATH="${RESOURCES_DIR}"
63
+export XDG_DATA_DIRS="${RESOURCES_DIR}/share:${XDG_DATA_DIRS:-}"
64
+
65
+# Run the actual binary with all arguments
66
+exec "${DIR}/sniffly-bin" "$@"
67
+EOF
68
+chmod +x "${MACOS}/sniffly"
69
+
70
+# Create Info.plist
71
+echo -e "${BLUE}Step 4: Creating Info.plist...${NC}"
72
+cat > "${CONTENTS}/Info.plist" << EOF
73
+<?xml version="1.0" encoding="UTF-8"?>
74
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
75
+<plist version="1.0">
76
+<dict>
77
+    <key>CFBundleExecutable</key>
78
+    <string>sniffly</string>
79
+    <key>CFBundleIdentifier</key>
80
+    <string>org.fortrangoingonforty.sniffly</string>
81
+    <key>CFBundleName</key>
82
+    <string>${APP_NAME}</string>
83
+    <key>CFBundleDisplayName</key>
84
+    <string>${APP_NAME}</string>
85
+    <key>CFBundleVersion</key>
86
+    <string>${VERSION}</string>
87
+    <key>CFBundleShortVersionString</key>
88
+    <string>${VERSION}</string>
89
+    <key>CFBundlePackageType</key>
90
+    <string>APPL</string>
91
+    <key>CFBundleSignature</key>
92
+    <string>SNIF</string>
93
+    <key>CFBundleIconFile</key>
94
+    <string>sniffly</string>
95
+    <key>LSMinimumSystemVersion</key>
96
+    <string>11.0</string>
97
+    <key>NSHighResolutionCapable</key>
98
+    <true/>
99
+    <key>NSRequiresAquaSystemAppearance</key>
100
+    <false/>
101
+    <key>LSApplicationCategoryType</key>
102
+    <string>public.app-category.utilities</string>
103
+</dict>
104
+</plist>
105
+EOF
106
+
107
+# Bundle GTK4 resources
108
+echo -e "${BLUE}Step 5: Bundling GTK4 resources...${NC}"
109
+mkdir -p "${RESOURCES}/share/glib-2.0/schemas"
110
+mkdir -p "${RESOURCES}/share/gtk-4.0"
111
+
112
+# Copy GLib schemas
113
+if [ -d "/opt/homebrew/share/glib-2.0/schemas" ]; then
114
+    echo "  Copying GLib schemas..."
115
+    cp -r /opt/homebrew/share/glib-2.0/schemas/* "${RESOURCES}/share/glib-2.0/schemas/"
116
+fi
117
+
118
+# Copy GTK4 data
119
+if [ -d "/opt/homebrew/share/gtk-4.0" ]; then
120
+    echo "  Copying GTK4 data..."
121
+    cp -r /opt/homebrew/share/gtk-4.0/* "${RESOURCES}/share/gtk-4.0/"
122
+fi
123
+
124
+# Bundle libraries
125
+echo -e "${BLUE}Step 6: Bundling libraries...${NC}"
126
+./scripts/bundle-libs.sh
127
+
128
+# Create PkgInfo
129
+echo -n "APPLSNIF" > "${CONTENTS}/PkgInfo"
130
+
131
+# Verify
132
+echo -e "${BLUE}Step 7: Verifying bundle...${NC}"
133
+if [ -f "${MACOS}/sniffly" ] && [ -f "${MACOS}/sniffly-bin" ]; then
134
+    echo -e "${GREEN}✓ App bundle created successfully${NC}"
135
+else
136
+    echo -e "${RED}ERROR: Bundle creation failed${NC}"
137
+    exit 1
138
+fi
139
+
140
+# Create DMG
141
+echo -e "${BLUE}Step 8: Creating DMG...${NC}"
142
+DMG_NAME="Sniffly-${VERSION}-macOS.dmg"
143
+DMG_TEMP="dmg_temp"
144
+
145
+rm -rf "${DMG_TEMP}"
146
+mkdir -p "${DMG_TEMP}"
147
+cp -r "${APP_BUNDLE}" "${DMG_TEMP}/"
148
+ln -s /Applications "${DMG_TEMP}/Applications"
149
+
150
+hdiutil create -volname "${APP_NAME}" \
151
+    -srcfolder "${DMG_TEMP}" \
152
+    -ov -format UDZO \
153
+    "${DMG_NAME}"
154
+
155
+rm -rf "${DMG_TEMP}"
156
+
157
+DMG_SIZE=$(du -h "${DMG_NAME}" | cut -f1)
158
+
159
+echo -e "${GREEN}========================================${NC}"
160
+echo -e "${GREEN}✓ Packaging complete!${NC}"
161
+echo -e "${GREEN}========================================${NC}"
162
+echo ""
163
+echo -e "${BLUE}Output files:${NC}"
164
+echo -e "  App Bundle: ${APP_BUNDLE}"
165
+echo -e "  DMG:        ${DMG_NAME} (${DMG_SIZE})"
166
+echo ""
167
+echo -e "${BLUE}Next steps:${NC}"
168
+echo -e "  1. Test: open ${APP_BUNDLE}"
169
+echo -e "  2. Test DMG: open ${DMG_NAME}"
170
+echo ""
scripts/package-macos.shadded
@@ -0,0 +1,180 @@
1
+#!/bin/bash
2
+# Sniffly macOS Packaging Script
3
+# Creates a .app bundle and .dmg for distribution
4
+#
5
+# Usage: ./scripts/package-macos.sh
6
+
7
+set -e  # Exit on error
8
+
9
+# Colors for output
10
+GREEN='\033[0;32m'
11
+BLUE='\033[0;34m'
12
+RED='\033[0;31m'
13
+NC='\033[0m' # No Color
14
+
15
+echo -e "${BLUE}========================================${NC}"
16
+echo -e "${BLUE}  Sniffly macOS Packaging Script${NC}"
17
+echo -e "${BLUE}========================================${NC}"
18
+
19
+# Read version from VERSION file
20
+if [ ! -f "VERSION" ]; then
21
+    echo -e "${RED}ERROR: VERSION file not found${NC}"
22
+    exit 1
23
+fi
24
+
25
+VERSION=$(cat VERSION)
26
+echo -e "${GREEN}Version: ${VERSION}${NC}"
27
+
28
+# Check if build exists
29
+if [ ! -f "build/sniffly" ]; then
30
+    echo -e "${RED}ERROR: build/sniffly not found. Run 'meson compile -C build' first${NC}"
31
+    exit 1
32
+fi
33
+
34
+# Create app bundle structure
35
+APP_NAME="Sniffly"
36
+APP_BUNDLE="${APP_NAME}.app"
37
+CONTENTS="${APP_BUNDLE}/Contents"
38
+MACOS="${CONTENTS}/MacOS"
39
+RESOURCES="${CONTENTS}/Resources"
40
+FRAMEWORKS="${CONTENTS}/Frameworks"
41
+
42
+echo -e "${BLUE}Step 1: Creating app bundle structure...${NC}"
43
+rm -rf "${APP_BUNDLE}"
44
+mkdir -p "${MACOS}"
45
+mkdir -p "${RESOURCES}"
46
+mkdir -p "${FRAMEWORKS}"
47
+
48
+# Copy executable directly - no library bundling needed
49
+echo -e "${BLUE}Step 2: Copying executable...${NC}"
50
+cp build/sniffly "${MACOS}/sniffly"
51
+chmod +x "${MACOS}/sniffly"
52
+
53
+# Create Info.plist
54
+echo -e "${BLUE}Step 3: Creating Info.plist...${NC}"
55
+cat > "${CONTENTS}/Info.plist" << EOF
56
+<?xml version="1.0" encoding="UTF-8"?>
57
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
58
+<plist version="1.0">
59
+<dict>
60
+    <key>CFBundleExecutable</key>
61
+    <string>sniffly</string>
62
+    <key>CFBundleIdentifier</key>
63
+    <string>org.fortrangoingonforty.sniffly</string>
64
+    <key>CFBundleName</key>
65
+    <string>${APP_NAME}</string>
66
+    <key>CFBundleDisplayName</key>
67
+    <string>${APP_NAME}</string>
68
+    <key>CFBundleVersion</key>
69
+    <string>${VERSION}</string>
70
+    <key>CFBundleShortVersionString</key>
71
+    <string>${VERSION}</string>
72
+    <key>CFBundlePackageType</key>
73
+    <string>APPL</string>
74
+    <key>CFBundleSignature</key>
75
+    <string>SNIF</string>
76
+    <key>CFBundleIconFile</key>
77
+    <string>sniffly</string>
78
+    <key>LSMinimumSystemVersion</key>
79
+    <string>11.0</string>
80
+    <key>NSHighResolutionCapable</key>
81
+    <true/>
82
+    <key>NSRequiresAquaSystemAppearance</key>
83
+    <false/>
84
+    <key>LSApplicationCategoryType</key>
85
+    <string>public.app-category.utilities</string>
86
+    <key>LSUIElement</key>
87
+    <false/>
88
+    <key>NSPrincipalClass</key>
89
+    <string>NSApplication</string>
90
+</dict>
91
+</plist>
92
+EOF
93
+
94
+# Check for icon file
95
+echo -e "${BLUE}Step 4: Checking for icon...${NC}"
96
+if [ -f "assets/sniffly.icns" ]; then
97
+    echo -e "${GREEN}Found icon file, copying...${NC}"
98
+    cp assets/sniffly.icns "${RESOURCES}/sniffly.icns"
99
+else
100
+    echo -e "${RED}WARNING: No icon file found at assets/sniffly.icns${NC}"
101
+    echo -e "${RED}         App will use default icon${NC}"
102
+fi
103
+
104
+# Bundle GTK4 libraries manually (simple approach)
105
+echo -e "${BLUE}Step 5: Bundling GTK4 libraries...${NC}"
106
+if [ -f "./scripts/simple-bundle.sh" ]; then
107
+    ./scripts/simple-bundle.sh
108
+else
109
+    echo -e "${RED}ERROR: simple-bundle.sh not found${NC}"
110
+    exit 1
111
+fi
112
+
113
+# Create PkgInfo file
114
+echo -e "${BLUE}Step 6: Creating PkgInfo...${NC}"
115
+echo -n "APPLSNIF" > "${CONTENTS}/PkgInfo"
116
+
117
+# Verify app bundle
118
+echo -e "${BLUE}Step 7: Verifying app bundle...${NC}"
119
+if [ -f "${MACOS}/sniffly" ] && [ -f "${CONTENTS}/Info.plist" ]; then
120
+    echo -e "${GREEN}✓ App bundle created successfully${NC}"
121
+else
122
+    echo -e "${RED}ERROR: App bundle creation failed${NC}"
123
+    exit 1
124
+fi
125
+
126
+# Create DMG
127
+echo -e "${BLUE}Step 8: Creating DMG...${NC}"
128
+DMG_NAME="Sniffly-${VERSION}-macOS.dmg"
129
+DMG_TEMP="dmg_temp"
130
+
131
+rm -rf "${DMG_TEMP}"
132
+mkdir -p "${DMG_TEMP}"
133
+
134
+# Copy app bundle
135
+cp -r "${APP_BUNDLE}" "${DMG_TEMP}/"
136
+
137
+# Create symbolic link to Applications folder
138
+ln -s /Applications "${DMG_TEMP}/Applications"
139
+
140
+# Optional: Add background and custom window settings
141
+# (requires additional tools like create-dmg)
142
+
143
+# Create the DMG
144
+echo -e "${GREEN}Creating ${DMG_NAME}...${NC}"
145
+hdiutil create -volname "${APP_NAME}" \
146
+    -srcfolder "${DMG_TEMP}" \
147
+    -ov -format UDZO \
148
+    "${DMG_NAME}"
149
+
150
+# Clean up
151
+rm -rf "${DMG_TEMP}"
152
+
153
+# Get DMG size
154
+DMG_SIZE=$(du -h "${DMG_NAME}" | cut -f1)
155
+
156
+echo -e "${GREEN}========================================${NC}"
157
+echo -e "${GREEN}✓ Packaging complete!${NC}"
158
+echo -e "${GREEN}========================================${NC}"
159
+echo ""
160
+echo -e "${BLUE}Output files:${NC}"
161
+echo -e "  App Bundle: ${APP_BUNDLE}"
162
+echo -e "  DMG:        ${DMG_NAME} (${DMG_SIZE})"
163
+echo ""
164
+echo -e "${BLUE}Next steps:${NC}"
165
+echo -e "  1. Test the app: open ${APP_BUNDLE}"
166
+echo -e "  2. Test the DMG: open ${DMG_NAME}"
167
+echo -e "  3. Upload ${DMG_NAME} to your distribution server"
168
+echo ""
169
+
170
+# Optional: Code signing check
171
+if command -v codesign &> /dev/null; then
172
+    echo -e "${BLUE}Code Signing Status:${NC}"
173
+    codesign -dv "${APP_BUNDLE}" 2>&1 | grep -E "Signature|TeamIdentifier" || echo "  Not signed"
174
+    echo ""
175
+    echo -e "${BLUE}To sign the app (requires Apple Developer account):${NC}"
176
+    echo -e '  codesign --deep --force --sign "Developer ID Application: Your Name" Sniffly.app'
177
+    echo ""
178
+fi
179
+
180
+echo -e "${GREEN}Done!${NC}"
scripts/simple-bundle.shadded
@@ -0,0 +1,119 @@
1
+#!/bin/bash
2
+# Simple library bundling - copy libs and update binary paths only
3
+# Don't touch the bundled libraries themselves
4
+
5
+set -e
6
+
7
+APP_BUNDLE="Sniffly.app"
8
+FRAMEWORKS="${APP_BUNDLE}/Contents/Frameworks"
9
+MACOS_BIN="${APP_BUNDLE}/Contents/MacOS/sniffly"
10
+
11
+echo "=== Simple Library Bundling ==="
12
+
13
+# Copy all GTK4-related libraries recursively
14
+echo "Step 1: Copying GTK4 libraries..."
15
+mkdir -p "$FRAMEWORKS"
16
+
17
+# Function to copy a library and its dependencies
18
+copy_lib_and_deps() {
19
+    local lib=$1
20
+    local libname=$(basename "$lib")
21
+
22
+    if [ -f "$FRAMEWORKS/$libname" ]; then
23
+        return  # Already copied
24
+    fi
25
+
26
+    if [ ! -f "$lib" ]; then
27
+        return  # Doesn't exist
28
+    fi
29
+
30
+    echo "  Copying $libname"
31
+    cp "$lib" "$FRAMEWORKS/"
32
+    chmod 644 "$FRAMEWORKS/$libname"
33
+
34
+    # Get dependencies and copy them too
35
+    otool -L "$lib" | grep "/opt/homebrew" | awk '{print $1}' | while read dep; do
36
+        if [ -f "$dep" ]; then
37
+            copy_lib_and_deps "$dep"
38
+        fi
39
+    done
40
+}
41
+
42
+# Start with the main libraries the binary needs
43
+for lib in libgtk-4-fortran libgtk-4 libcairo libgio libglib libgfortran; do
44
+    find /opt/homebrew -name "${lib}*.dylib" -o -name "${lib}*.*.dylib" 2>/dev/null | head -1 | while read libpath; do
45
+        if [ -n "$libpath" ]; then
46
+            copy_lib_and_deps "$libpath"
47
+        fi
48
+    done
49
+done
50
+
51
+# Also copy from /usr/local for gtk-4-fortran
52
+if [ -f "/usr/local/lib/libgtk-4-fortran.4.8.0.dylib" ]; then
53
+    copy_lib_and_deps "/usr/local/lib/libgtk-4-fortran.4.8.0.dylib"
54
+fi
55
+
56
+echo ""
57
+echo "Step 2: Updating binary to use bundled libraries..."
58
+# Only update the main binary's paths - don't touch the libraries
59
+for lib in "$FRAMEWORKS"/*.dylib; do
60
+    if [ -f "$lib" ]; then
61
+        libname=$(basename "$lib")
62
+        # Try to change the binary's reference to this library
63
+        install_name_tool -change "/opt/homebrew/opt/*/lib/$libname" "@executable_path/../Frameworks/$libname" "$MACOS_BIN" 2>/dev/null || true
64
+        install_name_tool -change "/usr/local/lib/$libname" "@executable_path/../Frameworks/$libname" "$MACOS_BIN" 2>/dev/null || true
65
+    fi
66
+done
67
+
68
+# Update specific known paths in the binary
69
+install_name_tool -change "@rpath/libgtk-4-fortran.4.8.0.dylib" "@executable_path/../Frameworks/libgtk-4-fortran.4.8.0.dylib" "$MACOS_BIN" 2>/dev/null || true
70
+install_name_tool -change "/opt/homebrew/opt/gtk4/lib/libgtk-4.1.dylib" "@executable_path/../Frameworks/libgtk-4.1.dylib" "$MACOS_BIN" 2>/dev/null || true
71
+install_name_tool -change "/opt/homebrew/opt/cairo/lib/libcairo.2.dylib" "@executable_path/../Frameworks/libcairo.2.dylib" "$MACOS_BIN" 2>/dev/null || true
72
+install_name_tool -change "/opt/homebrew/opt/glib/lib/libgio-2.0.0.dylib" "@executable_path/../Frameworks/libgio-2.0.0.dylib" "$MACOS_BIN" 2>/dev/null || true
73
+install_name_tool -change "/opt/homebrew/opt/glib/lib/libglib-2.0.0.dylib" "@executable_path/../Frameworks/libglib-2.0.0.dylib" "$MACOS_BIN" 2>/dev/null || true
74
+install_name_tool -change "/opt/homebrew/opt/gcc/lib/gcc/current/libgfortran.5.dylib" "@executable_path/../Frameworks/libgfortran.5.dylib" "$MACOS_BIN" 2>/dev/null || true
75
+
76
+echo ""
77
+echo "Step 3: Fixing bundled libraries to reference each other..."
78
+# Fix all the bundled libraries to reference each other instead of /opt/homebrew
79
+for lib in "$FRAMEWORKS"/*.dylib; do
80
+    if [ -f "$lib" ]; then
81
+        libname=$(basename "$lib")
82
+
83
+        # Fix the library's ID
84
+        install_name_tool -id "@executable_path/../Frameworks/$libname" "$lib" 2>/dev/null || true
85
+
86
+        # Fix only the actual dependencies (much faster than trying all combinations)
87
+        otool -L "$lib" | grep "/opt/homebrew\|/usr/local" | awk '{print $1}' | while read dep_path; do
88
+            dep_name=$(basename "$dep_path")
89
+            if [ -f "$FRAMEWORKS/$dep_name" ]; then
90
+                install_name_tool -change "$dep_path" "@executable_path/../Frameworks/$dep_name" "$lib" 2>/dev/null || true
91
+            fi
92
+        done
93
+    fi
94
+done
95
+
96
+echo ""
97
+echo "Step 4: Re-signing everything (CRITICAL)..."
98
+# After install_name_tool modifications, code signatures are invalid
99
+# macOS will SIGKILL the app if signatures don't match
100
+# Use ad-hoc signing (no certificate needed)
101
+echo "  Re-signing main binary..."
102
+codesign --remove-signature "$MACOS_BIN" 2>/dev/null || true
103
+codesign -s - -f "$MACOS_BIN"
104
+
105
+echo "  Re-signing bundled libraries..."
106
+for lib in "$FRAMEWORKS"/*.dylib; do
107
+    if [ -f "$lib" ]; then
108
+        codesign --remove-signature "$lib" 2>/dev/null || true
109
+        codesign -s - -f "$lib" 2>/dev/null || true
110
+    fi
111
+done
112
+
113
+echo ""
114
+echo "Step 5: Verifying..."
115
+TOTAL_LIBS=$(ls -1 "$FRAMEWORKS" | wc -l | tr -d ' ')
116
+echo "Total libraries bundled: $TOTAL_LIBS"
117
+
118
+echo ""
119
+echo "=== Bundling Complete ==="
src/version.f90.inadded
@@ -0,0 +1,10 @@
1
+! Auto-generated version module
2
+! Do not edit - generated from version.f90.in by Meson
3
+module version
4
+  implicit none
5
+
6
+  character(len=*), parameter :: SNIFFLY_VERSION = "@VERSION@"
7
+  character(len=*), parameter :: SNIFFLY_NAME = "Sniffly"
8
+  character(len=*), parameter :: SNIFFLY_DESCRIPTION = "Disk Space Analyzer"
9
+
10
+end module version