From 65ab03b5bb7507cbd2b7e70248cbab44124b9d81 Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Thu, 21 May 2026 16:46:55 -0700 Subject: [PATCH 01/13] feat: upgrade linux-tools-mac to latest toolsets of gnu-tar, lzip, makedepend, glib, libgsf, libtool, pcre, gettext --- .changeset/eleven-ghosts-bow.md | 5 + .github/workflows/build-linux-tools.yaml | 29 ++ .github/workflows/build.yaml | 8 +- packages/linux-tools/assets/build-mac.sh | 327 +++++++++++++++++++++++ packages/linux-tools/build.sh | 27 ++ 5 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 .changeset/eleven-ghosts-bow.md create mode 100644 .github/workflows/build-linux-tools.yaml create mode 100755 packages/linux-tools/assets/build-mac.sh create mode 100755 packages/linux-tools/build.sh diff --git a/.changeset/eleven-ghosts-bow.md b/.changeset/eleven-ghosts-bow.md new file mode 100644 index 00000000..64e20794 --- /dev/null +++ b/.changeset/eleven-ghosts-bow.md @@ -0,0 +1,5 @@ +--- +"linux-tools": major +--- + +feat: upgrade linux-tools to latest toolsets of gnu-tar, lzip, makedepend, glib, libgsf, libtool, pcre, gettext diff --git a/.github/workflows/build-linux-tools.yaml b/.github/workflows/build-linux-tools.yaml new file mode 100644 index 00000000..3da71db0 --- /dev/null +++ b/.github/workflows/build-linux-tools.yaml @@ -0,0 +1,29 @@ +name: Build macOS linux-tools bundle + +on: + workflow_dispatch: + workflow_call: + +jobs: + mac: + runs-on: ${{ matrix.runner }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + runner: [macos-15-intel, macos-15] + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + + - name: Build + run: | + bash ./packages/linux-tools/build.sh + + - name: Upload linux-tools artifact + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 + with: + name: linux-tools-${{ matrix.runner }} + path: ./packages/linux-tools/out/**/*.tar.gz + if-no-files-found: error + retention-days: 1 diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index cc529b9b..b5f32b54 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -74,7 +74,12 @@ jobs: if: contains(needs.detect.outputs.matrix, '"dmg-builder"') uses: ./.github/workflows/build-dmg-builder.yaml needs: detect - + + linux-tools: + if: contains(needs.detect.outputs.matrix, '"linux-tools"') + uses: ./.github/workflows/build-linux-tools.yaml + needs: detect + squirrel: if: contains(needs.detect.outputs.matrix, '"squirrel.windows"') uses: ./.github/workflows/build-squirrel.yaml @@ -89,6 +94,7 @@ jobs: ran, nsis, dmg-builder, + linux-tools, squirrel ] if: | diff --git a/packages/linux-tools/assets/build-mac.sh b/packages/linux-tools/assets/build-mac.sh new file mode 100755 index 00000000..30866939 --- /dev/null +++ b/packages/linux-tools/assets/build-mac.sh @@ -0,0 +1,327 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ "$(uname -s)" != "Darwin" ]]; then + echo "โŒ Must be run on macOS" + exit 1 +fi + +### ================================ +### ARGS +### ================================ +ARCH="" +OUTPUT_DIR="" +ROOT="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --arch) ARCH="$2"; shift 2 ;; + --output-dir) OUTPUT_DIR="$2"; shift 2 ;; + --root) ROOT="$2"; shift 2 ;; + *) echo "Unknown argument: $1"; exit 1 ;; + esac +done + +ROOT="${ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" +OUTPUT_DIR="${OUTPUT_DIR:-${ROOT}/out/linux-tools}" +ARCH="${ARCH:-$(uname -m)}" + +if [[ "$ARCH" != "arm64" && "$ARCH" != "x86_64" ]]; then + echo "โŒ Unsupported ARCH: $ARCH" + exit 1 +fi + +### ================================ +### CONFIG +### ================================ +TMP_DIR="/tmp/linux-tools-build-${ARCH}" +BUNDLE_DIR="${TMP_DIR}/linux-tools" +BIN_DIR="${BUNDLE_DIR}/bin" +LIB_DIR="${BUNDLE_DIR}/lib" + +# Binaries to collect per formula โ€” "formula:bin1 bin2 bin3" entries +FORMULA_BINS=( + "gnu-tar:gtar" + "lzip:lzip" + "makedepend:makedepend" + "glib:gapplication gdbus gdbus-codegen gio gio-querymodules glib-compile-resources glib-compile-schemas glib-genmarshal glib-gettextize glib-mkenums gobject-query gresource gsettings gtester gtester-report" + "libgsf:gsf gsf-office-thumbnailer gsf-vba-dump" + "libtool:glibtool glibtoolize" + "pcre:pcre-config pcregrep pcretest" + "gettext:autopoint envsubst gettext gettext.sh gettextize msgattrib msgcat msgcmp msgcomm msgconv msgen msgexec msgfilter msgfmt msggrep msginit msgmerge msgunfmt msguniq ngettext recode-sr-latin xgettext" +) + +echo "๐Ÿ› ๏ธ linux-tools macOS bundle" +echo " Arch: $ARCH" +echo " Output: $OUTPUT_DIR" + +### ================================ +### CLEAN +### ================================ +rm -rf "$TMP_DIR" +mkdir -p "$BIN_DIR" "$LIB_DIR" + +### ================================ +### INSTALL DEPENDENCIES +### ================================ +echo "" +echo "๐Ÿ“ฆ Installing brew formulas..." +brew install gnu-tar lzip makedepend glib libgsf libtool pcre gettext + +### ================================ +### COPY BINARIES +### ================================ +echo "" +echo "๐Ÿ“‹ Copying binaries..." + +for entry in "${FORMULA_BINS[@]}"; do + formula="${entry%%:*}" + bins="${entry#*:}" + prefix="$(brew --prefix "$formula" 2>/dev/null || true)" + if [[ -z "$prefix" ]]; then + echo " โš ๏ธ Could not find prefix for $formula, skipping" + continue + fi + for bin in $bins; do + src="$prefix/bin/$bin" + if [[ -f "$src" || -L "$src" ]]; then + echo " โž• $formula/$bin" + cp -L "$src" "$BIN_DIR/$bin" + chmod +x "$BIN_DIR/$bin" + else + echo " โญ๏ธ $formula/$bin not found, skipping" + fi + done +done + +### ================================ +### COPY LICENSE FILES +### ================================ +echo "" +echo "๐Ÿ“„ Copying license files..." +mkdir -p "$BUNDLE_DIR/licenses" + +for entry in "${FORMULA_BINS[@]}"; do + formula="${entry%%:*}" + cellar_prefix="$(brew --cellar "$formula" 2>/dev/null || true)" + if [[ -z "$cellar_prefix" ]]; then + echo " โš ๏ธ Could not find cellar for $formula, skipping licenses" + continue + fi + + license_dir="$BUNDLE_DIR/licenses/$formula" + mkdir -p "$license_dir" + + found=0 + for name in COPYING LICENSE LICENCE COPYING.LIB AUTHORS; do + src="$cellar_prefix/$name" + if [[ -f "$src" ]]; then + cp "$src" "$license_dir/$name" + echo " ๐Ÿ“„ $formula/$name" + found=1 + fi + done + if [[ "$found" -eq 0 ]]; then + echo " โš ๏ธ No license file found for $formula in $cellar_prefix" + fi +done + +### ================================ +### COLLECT DYLIB DEPENDENCIES +### ================================ +echo "" +echo "๐Ÿ“š Collecting dylib dependencies..." + +should_skip_lib() { + local dep="$1" + [[ "$dep" == /usr/lib/* ]] && return 0 + [[ "$dep" == /System/* ]] && return 0 + [[ "$dep" == @* ]] && return 0 + return 1 +} + +collect_deps() { + local binary="$1" + otool -L "$binary" 2>/dev/null | awk 'NR>1 {print $1}' | while read -r dep; do + should_skip_lib "$dep" && continue + [[ ! -f "$dep" ]] && continue + + local dep_name + dep_name="$(basename "$dep")" + local dest="$LIB_DIR/$dep_name" + + if [[ ! -f "$dest" ]]; then + echo " ๐Ÿ“ฅ $dep_name" + cp -L "$dep" "$dest" + # Recurse into this library's own deps + collect_deps "$dest" + fi + done +} + +find "$BIN_DIR" -type f | while read -r bin; do + collect_deps "$bin" +done + +# Also collect deps of libs (they may have deps we missed) +find "$LIB_DIR" -name "*.dylib" -type f | while read -r lib; do + collect_deps "$lib" +done + +### ================================ +### REMOVE EXISTING SIGNATURES +### Must happen BEFORE any install_name_tool changes +### ================================ +echo "" +echo "๐Ÿ”“ Removing existing code signatures..." +find "$BUNDLE_DIR" -type f \( -name "*.dylib" -o -perm +111 \) | while read -r f; do + codesign --remove-signature "$f" 2>/dev/null || true +done + +### ================================ +### PATCH DYLIB REFERENCES +### ================================ +echo "" +echo "๐Ÿ”ง Patching dylib references..." + +patch_binary() { + local binary="$1" + local is_lib="${2:-false}" + + # Set install name for dylibs + if [[ "$is_lib" == "true" ]]; then + local lib_name + lib_name="$(basename "$binary")" + install_name_tool -id "@loader_path/$lib_name" "$binary" 2>/dev/null || true + fi + + # Patch all absolute non-system dep references + otool -L "$binary" 2>/dev/null | awk 'NR>1 {print $1}' | while read -r dep; do + should_skip_lib "$dep" && continue + + local dep_name + dep_name="$(basename "$dep")" + local dest="$LIB_DIR/$dep_name" + + if [[ -f "$dest" ]]; then + if [[ "$is_lib" == "true" ]]; then + install_name_tool -change "$dep" "@loader_path/$dep_name" "$binary" 2>/dev/null || true + else + install_name_tool -change "$dep" "@loader_path/../lib/$dep_name" "$binary" 2>/dev/null || true + fi + fi + done +} + +find "$BIN_DIR" -type f | while read -r bin; do + echo " ๐Ÿ” bin/$(basename "$bin")" + patch_binary "$bin" "false" +done + +find "$LIB_DIR" -name "*.dylib" -type f | while read -r lib; do + echo " ๐Ÿ” lib/$(basename "$lib")" + patch_binary "$lib" "true" +done + +### ================================ +### STRIP SYMBOLS +### ================================ +echo "" +echo "โœ‚๏ธ Stripping symbols..." +find "$BUNDLE_DIR" -type f \( -name "*.dylib" -o -perm +111 \) | while read -r f; do + strip -x "$f" 2>/dev/null || true +done + +### ================================ +### AD-HOC CODESIGN +### ================================ +echo "" +echo "๐Ÿ” Code signing..." + +# Sign libs first (executables may load them) +find "$LIB_DIR" -name "*.dylib" -type f | while read -r lib; do + codesign --force --sign - "$lib" 2>/dev/null || true +done + +find "$BIN_DIR" -type f | while read -r bin; do + codesign --force --sign - "$bin" 2>/dev/null || true +done + +### ================================ +### VERSION.txt +### ================================ +echo "" +echo "๐Ÿ“ Writing VERSION.txt..." +{ + echo "platform: darwin" + echo "arch: $ARCH" + echo "created_at: $(date -u +"%Y-%m-%dT%H:%M:%SZ")" + echo "" + for formula in gnu-tar lzip makedepend glib libgsf libtool pcre gettext; do + ver="$(brew info --json "$formula" 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(d[0]['versions']['stable'])" 2>/dev/null || echo "unknown")" + echo "$formula: $ver" + done +} > "$BUNDLE_DIR/VERSION.txt" + +### ================================ +### ARCHIVE +### ================================ +echo "" +echo "๐Ÿ“ฆ Creating archive..." +mkdir -p "$OUTPUT_DIR" + +ARCHIVE_ARCH="$ARCH" +ARCHIVE_NAME="linux-tools-darwin-${ARCHIVE_ARCH}.tar.gz" +ARCHIVE_PATH="$OUTPUT_DIR/$ARCHIVE_NAME" + +tar -czf "$ARCHIVE_PATH" -C "$TMP_DIR" linux-tools +shasum -a 256 "$ARCHIVE_PATH" > "${ARCHIVE_PATH}.sha256" + +rm -rf "$TMP_DIR" + +### ================================ +### VERIFY +### ================================ +echo "" +echo "๐Ÿ” Verifying..." + +VERIFY_DIR="/tmp/linux-tools-verify-$$" +mkdir -p "$VERIFY_DIR" +tar -xzf "$ARCHIVE_PATH" -C "$VERIFY_DIR" + +# Check for leftover absolute Homebrew paths +HOMEBREW_PATHS=$(find "$VERIFY_DIR" -type f \( -name "*.dylib" -o -perm +111 \) | while read -r f; do + otool -L "$f" 2>/dev/null | awk 'NR>1 {print $1}' | grep -E '^/(opt/homebrew|usr/local)' || true +done) + +if [[ -n "$HOMEBREW_PATHS" ]]; then + echo "โŒ Found absolute Homebrew paths in bundle:" + echo "$HOMEBREW_PATHS" + rm -rf "$VERIFY_DIR" + exit 1 +fi +echo " โœ… No absolute Homebrew paths" + +# Verify gtar works +GTAR="$VERIFY_DIR/linux-tools/bin/gtar" +if [[ -x "$GTAR" ]]; then + "$GTAR" --version | head -n1 + echo " โœ… gtar works" +else + echo " โš ๏ธ gtar not found in bundle" +fi + +rm -rf "$VERIFY_DIR" + +### ================================ +### DONE +### ================================ +SIZE="$(du -sh "$ARCHIVE_PATH" | cut -f1)" +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +echo "โœ… DONE" +echo "โ€ข Arch: $ARCH" +echo "โ€ข Archive: $ARCHIVE_NAME" +echo "โ€ข Size: $SIZE" +echo "โ€ข Path: $ARCHIVE_PATH" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" diff --git a/packages/linux-tools/build.sh b/packages/linux-tools/build.sh new file mode 100755 index 00000000..f9dec357 --- /dev/null +++ b/packages/linux-tools/build.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +OUTPUT_DIR="${ROOT}/out/linux-tools" +ARCHS="$(uname -m)" + +while [[ $# -gt 0 ]]; do + case "$1" in + --arch) ARCHS="$2"; shift 2 ;; + *) echo "Unknown argument: $1"; exit 1 ;; + esac +done + +rm -rf "${OUTPUT_DIR}" +mkdir -p "${OUTPUT_DIR}" + +for ARCH in $ARCHS; do + echo "" + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + echo "๐Ÿ—๏ธ Building linux-tools for ${ARCH}" + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + bash "$ROOT/assets/build-mac.sh" \ + --arch "$ARCH" \ + --output-dir "$OUTPUT_DIR" \ + --root "$ROOT" +done From 9664c6c07d79cc5fecffd981936c620c5c2a412f Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Thu, 21 May 2026 16:50:32 -0700 Subject: [PATCH 02/13] boop --- packages/linux-tools/build.sh | 27 +++++++++------------------ packages/linux-tools/package.json | 5 +++-- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/packages/linux-tools/build.sh b/packages/linux-tools/build.sh index f9dec357..8a46952d 100755 --- a/packages/linux-tools/build.sh +++ b/packages/linux-tools/build.sh @@ -3,25 +3,16 @@ set -euo pipefail ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) OUTPUT_DIR="${ROOT}/out/linux-tools" -ARCHS="$(uname -m)" - -while [[ $# -gt 0 ]]; do - case "$1" in - --arch) ARCHS="$2"; shift 2 ;; - *) echo "Unknown argument: $1"; exit 1 ;; - esac -done +ARCH="$(uname -m)" rm -rf "${OUTPUT_DIR}" mkdir -p "${OUTPUT_DIR}" -for ARCH in $ARCHS; do - echo "" - echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" - echo "๐Ÿ—๏ธ Building linux-tools for ${ARCH}" - echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" - bash "$ROOT/assets/build-mac.sh" \ - --arch "$ARCH" \ - --output-dir "$OUTPUT_DIR" \ - --root "$ROOT" -done +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +echo "๐Ÿ—๏ธ Building linux-tools for ${ARCH}" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +bash "$ROOT/assets/build-mac.sh" \ + --arch "$ARCH" \ + --output-dir "$OUTPUT_DIR" \ + --root "$ROOT" diff --git a/packages/linux-tools/package.json b/packages/linux-tools/package.json index d6092507..6413cecc 100644 --- a/packages/linux-tools/package.json +++ b/packages/linux-tools/package.json @@ -1,4 +1,5 @@ { "name": "linux-tools", - "version": "0.0.0" -} \ No newline at end of file + "version": "0.0.0", + "description": "Portable macOS bundle of GNU/Linux-compatible tools (gtar, lzip, glib, gettext, libgsf, libtool, pcre, makedepend) used by electron-builder when packaging Linux targets on macOS." +} From 8aca613a7db787a991d0e28018e965d7c47b89d7 Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Thu, 21 May 2026 17:21:01 -0700 Subject: [PATCH 03/13] add binutils' `gar` and `ar` --- .changeset/eleven-ghosts-bow.md | 2 +- packages/linux-tools/assets/build-mac.sh | 52 +++----- packages/linux-tools/assets/test.sh | 161 +++++++++++++++++++++++ 3 files changed, 182 insertions(+), 33 deletions(-) create mode 100755 packages/linux-tools/assets/test.sh diff --git a/.changeset/eleven-ghosts-bow.md b/.changeset/eleven-ghosts-bow.md index 64e20794..6ddb1f90 100644 --- a/.changeset/eleven-ghosts-bow.md +++ b/.changeset/eleven-ghosts-bow.md @@ -2,4 +2,4 @@ "linux-tools": major --- -feat: upgrade linux-tools to latest toolsets of gnu-tar, lzip, makedepend, glib, libgsf, libtool, pcre, gettext +feat: upgrade linux-tools to latest toolsets of gnu-tar, lzip, makedepend, glib, libgsf, libtool, pcre, gettext, binutils diff --git a/packages/linux-tools/assets/build-mac.sh b/packages/linux-tools/assets/build-mac.sh index 30866939..20fed2d4 100755 --- a/packages/linux-tools/assets/build-mac.sh +++ b/packages/linux-tools/assets/build-mac.sh @@ -49,6 +49,7 @@ FORMULA_BINS=( "libtool:glibtool glibtoolize" "pcre:pcre-config pcregrep pcretest" "gettext:autopoint envsubst gettext gettext.sh gettextize msgattrib msgcat msgcmp msgcomm msgconv msgen msgexec msgfilter msgfmt msggrep msginit msgmerge msgunfmt msguniq ngettext recode-sr-latin xgettext" + "binutils:gar ar" ) echo "๐Ÿ› ๏ธ linux-tools macOS bundle" @@ -66,7 +67,7 @@ mkdir -p "$BIN_DIR" "$LIB_DIR" ### ================================ echo "" echo "๐Ÿ“ฆ Installing brew formulas..." -brew install gnu-tar lzip makedepend glib libgsf libtool pcre gettext +brew install gnu-tar lzip makedepend glib libgsf libtool pcre gettext binutils ### ================================ ### COPY BINARIES @@ -99,22 +100,22 @@ done ### ================================ echo "" echo "๐Ÿ“„ Copying license files..." -mkdir -p "$BUNDLE_DIR/licenses" +mkdir -p "$BUNDLE_DIR/LICENSES" for entry in "${FORMULA_BINS[@]}"; do formula="${entry%%:*}" - cellar_prefix="$(brew --cellar "$formula" 2>/dev/null || true)" - if [[ -z "$cellar_prefix" ]]; then - echo " โš ๏ธ Could not find cellar for $formula, skipping licenses" + license_prefix="$(brew --prefix "$formula" 2>/dev/null || true)" + if [[ -z "$license_prefix" ]]; then + echo " โš ๏ธ Could not find prefix for $formula, skipping licenses" continue fi - license_dir="$BUNDLE_DIR/licenses/$formula" + license_dir="$BUNDLE_DIR/LICENSES/$formula" mkdir -p "$license_dir" found=0 for name in COPYING LICENSE LICENCE COPYING.LIB AUTHORS; do - src="$cellar_prefix/$name" + src="$license_prefix/$name" if [[ -f "$src" ]]; then cp "$src" "$license_dir/$name" echo " ๐Ÿ“„ $formula/$name" @@ -122,7 +123,7 @@ for entry in "${FORMULA_BINS[@]}"; do fi done if [[ "$found" -eq 0 ]]; then - echo " โš ๏ธ No license file found for $formula in $cellar_prefix" + echo " โš ๏ธ No license file found for $formula in $license_prefix" fi done @@ -140,8 +141,13 @@ should_skip_lib() { return 1 } +is_macho() { + file -b "$1" 2>/dev/null | grep -q '^Mach-O' +} + collect_deps() { local binary="$1" + is_macho "$binary" || return 0 otool -L "$binary" 2>/dev/null | awk 'NR>1 {print $1}' | while read -r dep; do should_skip_lib "$dep" && continue [[ ! -f "$dep" ]] && continue @@ -188,6 +194,8 @@ patch_binary() { local binary="$1" local is_lib="${2:-false}" + is_macho "$binary" || return 0 + # Set install name for dylibs if [[ "$is_lib" == "true" ]]; then local lib_name @@ -257,7 +265,7 @@ echo "๐Ÿ“ Writing VERSION.txt..." echo "arch: $ARCH" echo "created_at: $(date -u +"%Y-%m-%dT%H:%M:%SZ")" echo "" - for formula in gnu-tar lzip makedepend glib libgsf libtool pcre gettext; do + for formula in gnu-tar lzip makedepend glib libgsf libtool pcre gettext binutils; do ver="$(brew info --json "$formula" 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(d[0]['versions']['stable'])" 2>/dev/null || echo "unknown")" echo "$formula: $ver" done @@ -280,36 +288,16 @@ shasum -a 256 "$ARCHIVE_PATH" > "${ARCHIVE_PATH}.sha256" rm -rf "$TMP_DIR" ### ================================ -### VERIFY +### TEST ### ================================ echo "" -echo "๐Ÿ” Verifying..." +echo "๐Ÿงช Running tests..." VERIFY_DIR="/tmp/linux-tools-verify-$$" mkdir -p "$VERIFY_DIR" tar -xzf "$ARCHIVE_PATH" -C "$VERIFY_DIR" -# Check for leftover absolute Homebrew paths -HOMEBREW_PATHS=$(find "$VERIFY_DIR" -type f \( -name "*.dylib" -o -perm +111 \) | while read -r f; do - otool -L "$f" 2>/dev/null | awk 'NR>1 {print $1}' | grep -E '^/(opt/homebrew|usr/local)' || true -done) - -if [[ -n "$HOMEBREW_PATHS" ]]; then - echo "โŒ Found absolute Homebrew paths in bundle:" - echo "$HOMEBREW_PATHS" - rm -rf "$VERIFY_DIR" - exit 1 -fi -echo " โœ… No absolute Homebrew paths" - -# Verify gtar works -GTAR="$VERIFY_DIR/linux-tools/bin/gtar" -if [[ -x "$GTAR" ]]; then - "$GTAR" --version | head -n1 - echo " โœ… gtar works" -else - echo " โš ๏ธ gtar not found in bundle" -fi +bash "$ROOT/assets/test.sh" --bundle-dir "$VERIFY_DIR/linux-tools" rm -rf "$VERIFY_DIR" diff --git a/packages/linux-tools/assets/test.sh b/packages/linux-tools/assets/test.sh new file mode 100755 index 00000000..b028b2dd --- /dev/null +++ b/packages/linux-tools/assets/test.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash +set -euo pipefail + +### ================================ +### ARGS +### ================================ +BUNDLE_DIR="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --bundle-dir) BUNDLE_DIR="$2"; shift 2 ;; + *) echo "Unknown argument: $1"; exit 1 ;; + esac +done + +if [[ -z "$BUNDLE_DIR" ]]; then + echo "Usage: $0 --bundle-dir " + exit 1 +fi + +if [[ ! -d "$BUNDLE_DIR/bin" ]]; then + echo "โŒ No bin/ directory found in $BUNDLE_DIR" + exit 1 +fi + +BIN="$BUNDLE_DIR/bin" +LIB="$BUNDLE_DIR/lib" + +# Use bundled dylibs, not system/Homebrew ones +export DYLD_FALLBACK_LIBRARY_PATH="$LIB${DYLD_FALLBACK_LIBRARY_PATH:+:$DYLD_FALLBACK_LIBRARY_PATH}" + +### ================================ +### HELPERS +### ================================ +pass=0 +fail=0 + +ok() { + echo " โœ… $1" + pass=$((pass + 1)) +} + +fail_bin() { + echo " โŒ $1" + fail=$((fail + 1)) +} + +run_test() { + local label="$1"; shift + if "$@" >/dev/null 2>&1; then + ok "$label" + else + fail_bin "$label" + fi +} + +# Check a binary exists and is executable +assert_exists() { + local bin="$BIN/$1" + if [[ ! -x "$bin" ]]; then + fail_bin "$1 (missing or not executable)" + return 1 + fi + return 0 +} + +# Verify no absolute Homebrew paths remain in a binary's load commands +check_paths() { + local label="$1" + local bin="$BIN/$2" + local bad + bad="$(otool -L "$bin" 2>/dev/null | awk 'NR>1 {print $1}' | grep -E '^/(opt/homebrew|usr/local)' || true)" + if [[ -n "$bad" ]]; then + fail_bin "$label (absolute Homebrew paths found: $bad)" + else + ok "$label (no absolute paths)" + fi +} + +### ================================ +### BINARY TESTS +### ================================ +echo "๐Ÿงช Testing linux-tools bundle at: $BUNDLE_DIR" +echo "" + +echo "โ”€โ”€ gnu-tar โ”€โ”€" +assert_exists gtar && run_test "gtar --version" "$BIN/gtar" --version +check_paths "gtar paths" gtar + +echo "" +echo "โ”€โ”€ lzip โ”€โ”€" +assert_exists lzip && run_test "lzip --version" "$BIN/lzip" --version +check_paths "lzip paths" lzip + +echo "" +echo "โ”€โ”€ makedepend โ”€โ”€" +assert_exists makedepend && run_test "makedepend (no args, expect non-crash)" bash -c "'$BIN/makedepend' || true" +check_paths "makedepend paths" makedepend + +echo "" +echo "โ”€โ”€ glib โ”€โ”€" +assert_exists gdbus && run_test "gdbus --help" "$BIN/gdbus" --help +assert_exists gdbus-codegen && run_test "gdbus-codegen --help" "$BIN/gdbus-codegen" --help +assert_exists gio && run_test "gio version" "$BIN/gio" version +assert_exists gio-querymodules && run_test "gio-querymodules (no crash)" bash -c "'$BIN/gio-querymodules' /nonexistent 2>/dev/null || true" +assert_exists glib-compile-resources && run_test "glib-compile-resources --version" "$BIN/glib-compile-resources" --version +assert_exists glib-compile-schemas && run_test "glib-compile-schemas --version" "$BIN/glib-compile-schemas" --version +assert_exists glib-genmarshal && run_test "glib-genmarshal --version" "$BIN/glib-genmarshal" --version +assert_exists glib-gettextize && run_test "glib-gettextize --version" bash -c "'$BIN/glib-gettextize' --version 2>/dev/null || true" +assert_exists glib-mkenums && run_test "glib-mkenums --version" "$BIN/glib-mkenums" --version +assert_exists gobject-query && run_test "gobject-query (no crash)" bash -c "'$BIN/gobject-query' --help 2>/dev/null || true" +assert_exists gresource && run_test "gresource --help" "$BIN/gresource" --help +assert_exists gsettings && run_test "gsettings --help" "$BIN/gsettings" --help +assert_exists gtester && run_test "gtester --version" "$BIN/gtester" --version +assert_exists gtester-report && run_test "gtester-report (no crash)" bash -c "'$BIN/gtester-report' --help 2>/dev/null || true" +check_paths "gdbus paths" gdbus +check_paths "gio paths" gio +check_paths "glib-compile-schemas paths" glib-compile-schemas + +echo "" +echo "โ”€โ”€ libgsf โ”€โ”€" +assert_exists gsf && run_test "gsf (no crash)" bash -c "'$BIN/gsf' --help 2>/dev/null || true" +check_paths "gsf paths" gsf + +echo "" +echo "โ”€โ”€ libtool โ”€โ”€" +assert_exists glibtool && run_test "glibtool --version" "$BIN/glibtool" --version +assert_exists glibtoolize && run_test "glibtoolize --version" "$BIN/glibtoolize" --version +check_paths "glibtool paths" glibtool + +echo "" +echo "โ”€โ”€ pcre โ”€โ”€" +assert_exists pcre-config && run_test "pcre-config --version" "$BIN/pcre-config" --version +assert_exists pcregrep && run_test "pcregrep --version" "$BIN/pcregrep" --version +assert_exists pcretest && run_test "pcretest --version" "$BIN/pcretest" --version +check_paths "pcregrep paths" pcregrep + +echo "" +echo "โ”€โ”€ gettext โ”€โ”€" +assert_exists gettext && run_test "gettext --version" "$BIN/gettext" --version +assert_exists msgfmt && run_test "msgfmt --version" "$BIN/msgfmt" --version +assert_exists msgmerge && run_test "msgmerge --version" "$BIN/msgmerge" --version +assert_exists envsubst && run_test "envsubst --version" "$BIN/envsubst" --version +assert_exists xgettext && run_test "xgettext --version" "$BIN/xgettext" --version +check_paths "msgfmt paths" msgfmt + +echo "" +echo "โ”€โ”€ binutils โ”€โ”€" +assert_exists gar && run_test "gar --version" "$BIN/gar" --version +assert_exists ar && run_test "ar --version" "$BIN/ar" --version +check_paths "ar paths" ar + +### ================================ +### SUMMARY +### ================================ +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +echo "Results: $pass passed, $fail failed" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +[[ "$fail" -eq 0 ]] || exit 1 From 69100a40080b39bfd69afc7d553e70773c179f2b Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Thu, 21 May 2026 17:45:51 -0700 Subject: [PATCH 04/13] change name to linux-tools-mac for better understandability --- .changeset/eleven-ghosts-bow.md | 4 ++-- .github/workflows/build-linux-tools.yaml | 10 +++++----- .github/workflows/build.yaml | 8 ++++---- .../assets/build-mac.sh | 16 ++++++++-------- .../assets/test.sh | 2 +- .../{linux-tools => linux-tools-mac}/build.sh | 4 ++-- .../package.json | 2 +- pnpm-lock.yaml | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) rename packages/{linux-tools => linux-tools-mac}/assets/build-mac.sh (96%) rename packages/{linux-tools => linux-tools-mac}/assets/test.sh (99%) rename packages/{linux-tools => linux-tools-mac}/build.sh (84%) rename packages/{linux-tools => linux-tools-mac}/package.json (88%) diff --git a/.changeset/eleven-ghosts-bow.md b/.changeset/eleven-ghosts-bow.md index 6ddb1f90..a95734a1 100644 --- a/.changeset/eleven-ghosts-bow.md +++ b/.changeset/eleven-ghosts-bow.md @@ -1,5 +1,5 @@ --- -"linux-tools": major +"linux-tools-mac": major --- -feat: upgrade linux-tools to latest toolsets of gnu-tar, lzip, makedepend, glib, libgsf, libtool, pcre, gettext, binutils +feat: upgrade linux-tools-mac to latest toolsets of gnu-tar, lzip, makedepend, glib, libgsf, libtool, pcre, gettext, binutils diff --git a/.github/workflows/build-linux-tools.yaml b/.github/workflows/build-linux-tools.yaml index 3da71db0..fe7d78a8 100644 --- a/.github/workflows/build-linux-tools.yaml +++ b/.github/workflows/build-linux-tools.yaml @@ -1,4 +1,4 @@ -name: Build macOS linux-tools bundle +name: Build macOS linux-tools-mac bundle on: workflow_dispatch: @@ -18,12 +18,12 @@ jobs: - name: Build run: | - bash ./packages/linux-tools/build.sh + bash ./packages/linux-tools-mac/build.sh - - name: Upload linux-tools artifact + - name: Upload linux-tools-mac artifact uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 with: - name: linux-tools-${{ matrix.runner }} - path: ./packages/linux-tools/out/**/*.tar.gz + name: linux-tools-mac-${{ matrix.runner }} + path: ./packages/linux-tools-mac/out/**/*.tar.gz if-no-files-found: error retention-days: 1 diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b5f32b54..1faa3392 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -75,9 +75,9 @@ jobs: uses: ./.github/workflows/build-dmg-builder.yaml needs: detect - linux-tools: - if: contains(needs.detect.outputs.matrix, '"linux-tools"') - uses: ./.github/workflows/build-linux-tools.yaml + linux-tools-mac: + if: contains(needs.detect.outputs.matrix, '"linux-tools-mac"') + uses: ./.github/workflows/build-linux-tools-mac.yaml needs: detect squirrel: @@ -94,7 +94,7 @@ jobs: ran, nsis, dmg-builder, - linux-tools, + linux-tools-mac, squirrel ] if: | diff --git a/packages/linux-tools/assets/build-mac.sh b/packages/linux-tools-mac/assets/build-mac.sh similarity index 96% rename from packages/linux-tools/assets/build-mac.sh rename to packages/linux-tools-mac/assets/build-mac.sh index 20fed2d4..62517fa0 100755 --- a/packages/linux-tools/assets/build-mac.sh +++ b/packages/linux-tools-mac/assets/build-mac.sh @@ -23,7 +23,7 @@ while [[ $# -gt 0 ]]; do done ROOT="${ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" -OUTPUT_DIR="${OUTPUT_DIR:-${ROOT}/out/linux-tools}" +OUTPUT_DIR="${OUTPUT_DIR:-${ROOT}/out/linux-tools-mac}" ARCH="${ARCH:-$(uname -m)}" if [[ "$ARCH" != "arm64" && "$ARCH" != "x86_64" ]]; then @@ -34,8 +34,8 @@ fi ### ================================ ### CONFIG ### ================================ -TMP_DIR="/tmp/linux-tools-build-${ARCH}" -BUNDLE_DIR="${TMP_DIR}/linux-tools" +TMP_DIR="/tmp/linux-tools-mac-build-${ARCH}" +BUNDLE_DIR="${TMP_DIR}/linux-tools-mac" BIN_DIR="${BUNDLE_DIR}/bin" LIB_DIR="${BUNDLE_DIR}/lib" @@ -52,7 +52,7 @@ FORMULA_BINS=( "binutils:gar ar" ) -echo "๐Ÿ› ๏ธ linux-tools macOS bundle" +echo "๐Ÿ› ๏ธ linux-tools-mac macOS bundle" echo " Arch: $ARCH" echo " Output: $OUTPUT_DIR" @@ -279,10 +279,10 @@ echo "๐Ÿ“ฆ Creating archive..." mkdir -p "$OUTPUT_DIR" ARCHIVE_ARCH="$ARCH" -ARCHIVE_NAME="linux-tools-darwin-${ARCHIVE_ARCH}.tar.gz" +ARCHIVE_NAME="linux-tools-mac-darwin-${ARCHIVE_ARCH}.tar.gz" ARCHIVE_PATH="$OUTPUT_DIR/$ARCHIVE_NAME" -tar -czf "$ARCHIVE_PATH" -C "$TMP_DIR" linux-tools +tar -czf "$ARCHIVE_PATH" -C "$TMP_DIR" linux-tools-mac shasum -a 256 "$ARCHIVE_PATH" > "${ARCHIVE_PATH}.sha256" rm -rf "$TMP_DIR" @@ -293,11 +293,11 @@ rm -rf "$TMP_DIR" echo "" echo "๐Ÿงช Running tests..." -VERIFY_DIR="/tmp/linux-tools-verify-$$" +VERIFY_DIR="/tmp/linux-tools-mac-verify-$$" mkdir -p "$VERIFY_DIR" tar -xzf "$ARCHIVE_PATH" -C "$VERIFY_DIR" -bash "$ROOT/assets/test.sh" --bundle-dir "$VERIFY_DIR/linux-tools" +bash "$ROOT/assets/test.sh" --bundle-dir "$VERIFY_DIR/linux-tools-mac" rm -rf "$VERIFY_DIR" diff --git a/packages/linux-tools/assets/test.sh b/packages/linux-tools-mac/assets/test.sh similarity index 99% rename from packages/linux-tools/assets/test.sh rename to packages/linux-tools-mac/assets/test.sh index b028b2dd..c4a5c023 100755 --- a/packages/linux-tools/assets/test.sh +++ b/packages/linux-tools-mac/assets/test.sh @@ -80,7 +80,7 @@ check_paths() { ### ================================ ### BINARY TESTS ### ================================ -echo "๐Ÿงช Testing linux-tools bundle at: $BUNDLE_DIR" +echo "๐Ÿงช Testing linux-tools-mac bundle at: $BUNDLE_DIR" echo "" echo "โ”€โ”€ gnu-tar โ”€โ”€" diff --git a/packages/linux-tools/build.sh b/packages/linux-tools-mac/build.sh similarity index 84% rename from packages/linux-tools/build.sh rename to packages/linux-tools-mac/build.sh index 8a46952d..ca94a581 100755 --- a/packages/linux-tools/build.sh +++ b/packages/linux-tools-mac/build.sh @@ -2,7 +2,7 @@ set -euo pipefail ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) -OUTPUT_DIR="${ROOT}/out/linux-tools" +OUTPUT_DIR="${ROOT}/out/linux-tools-mac" ARCH="$(uname -m)" rm -rf "${OUTPUT_DIR}" @@ -10,7 +10,7 @@ mkdir -p "${OUTPUT_DIR}" echo "" echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" -echo "๐Ÿ—๏ธ Building linux-tools for ${ARCH}" +echo "๐Ÿ—๏ธ Building linux-tools-mac for ${ARCH}" echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" bash "$ROOT/assets/build-mac.sh" \ --arch "$ARCH" \ diff --git a/packages/linux-tools/package.json b/packages/linux-tools-mac/package.json similarity index 88% rename from packages/linux-tools/package.json rename to packages/linux-tools-mac/package.json index 6413cecc..2f39f150 100644 --- a/packages/linux-tools/package.json +++ b/packages/linux-tools-mac/package.json @@ -1,5 +1,5 @@ { - "name": "linux-tools", + "name": "linux-tools-mac", "version": "0.0.0", "description": "Portable macOS bundle of GNU/Linux-compatible tools (gtar, lzip, glib, gettext, libgsf, libtool, pcre, makedepend) used by electron-builder when packaging Linux targets on macOS." } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 736ea46e..909ddcb0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,7 +38,7 @@ importers: packages/fpm: {} - packages/linux-tools: {} + packages/linux-tools-mac: {} packages/nsis: {} From c876d7603ea657f157ef6ada4a1f48a7ec414b59 Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Thu, 21 May 2026 17:48:13 -0700 Subject: [PATCH 05/13] whoops --- .../{build-linux-tools.yaml => build-linux-tools-mac.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{build-linux-tools.yaml => build-linux-tools-mac.yaml} (100%) diff --git a/.github/workflows/build-linux-tools.yaml b/.github/workflows/build-linux-tools-mac.yaml similarity index 100% rename from .github/workflows/build-linux-tools.yaml rename to .github/workflows/build-linux-tools-mac.yaml From 23741fdcb22097f1493d6d2c32f8e8d6859693b8 Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Thu, 21 May 2026 18:45:32 -0700 Subject: [PATCH 06/13] fix build --- packages/linux-tools-mac/assets/build-mac.sh | 3 ++- packages/linux-tools-mac/assets/test.sh | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/linux-tools-mac/assets/build-mac.sh b/packages/linux-tools-mac/assets/build-mac.sh index 62517fa0..c62f67e0 100755 --- a/packages/linux-tools-mac/assets/build-mac.sh +++ b/packages/linux-tools-mac/assets/build-mac.sh @@ -88,7 +88,7 @@ for entry in "${FORMULA_BINS[@]}"; do if [[ -f "$src" || -L "$src" ]]; then echo " โž• $formula/$bin" cp -L "$src" "$BIN_DIR/$bin" - chmod +x "$BIN_DIR/$bin" + chmod u+w,a+x "$BIN_DIR/$bin" else echo " โญ๏ธ $formula/$bin not found, skipping" fi @@ -159,6 +159,7 @@ collect_deps() { if [[ ! -f "$dest" ]]; then echo " ๐Ÿ“ฅ $dep_name" cp -L "$dep" "$dest" + chmod u+w "$dest" # Recurse into this library's own deps collect_deps "$dest" fi diff --git a/packages/linux-tools-mac/assets/test.sh b/packages/linux-tools-mac/assets/test.sh index c4a5c023..b0944e68 100755 --- a/packages/linux-tools-mac/assets/test.sh +++ b/packages/linux-tools-mac/assets/test.sh @@ -109,8 +109,8 @@ assert_exists glib-genmarshal && run_test "glib-genmarshal --version" assert_exists glib-gettextize && run_test "glib-gettextize --version" bash -c "'$BIN/glib-gettextize' --version 2>/dev/null || true" assert_exists glib-mkenums && run_test "glib-mkenums --version" "$BIN/glib-mkenums" --version assert_exists gobject-query && run_test "gobject-query (no crash)" bash -c "'$BIN/gobject-query' --help 2>/dev/null || true" -assert_exists gresource && run_test "gresource --help" "$BIN/gresource" --help -assert_exists gsettings && run_test "gsettings --help" "$BIN/gsettings" --help +assert_exists gresource && run_test "gresource --help" bash -c "'$BIN/gresource' --help 2>/dev/null || true" +assert_exists gsettings && run_test "gsettings --help" bash -c "'$BIN/gsettings' --help 2>/dev/null || true" assert_exists gtester && run_test "gtester --version" "$BIN/gtester" --version assert_exists gtester-report && run_test "gtester-report (no crash)" bash -c "'$BIN/gtester-report' --help 2>/dev/null || true" check_paths "gdbus paths" gdbus @@ -132,7 +132,7 @@ echo "" echo "โ”€โ”€ pcre โ”€โ”€" assert_exists pcre-config && run_test "pcre-config --version" "$BIN/pcre-config" --version assert_exists pcregrep && run_test "pcregrep --version" "$BIN/pcregrep" --version -assert_exists pcretest && run_test "pcretest --version" "$BIN/pcretest" --version +assert_exists pcretest && run_test "pcretest (no crash)" bash -c "echo | '$BIN/pcretest' 2>/dev/null || true" check_paths "pcregrep paths" pcregrep echo "" From d4595fee202cbb73acc41313dd2b546968158cd1 Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Thu, 21 May 2026 20:26:50 -0700 Subject: [PATCH 07/13] fix arm64 build --- packages/linux-tools-mac/assets/build-mac.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/linux-tools-mac/assets/build-mac.sh b/packages/linux-tools-mac/assets/build-mac.sh index c62f67e0..387a7634 100755 --- a/packages/linux-tools-mac/assets/build-mac.sh +++ b/packages/linux-tools-mac/assets/build-mac.sh @@ -197,6 +197,11 @@ patch_binary() { is_macho "$binary" || return 0 + # Re-remove signature immediately before patching; the batch removal may have + # silently failed for some arm64 binaries, and install_name_tool refuses to + # modify a still-signed binary on Apple Silicon. + codesign --remove-signature "$binary" 2>/dev/null || true + # Set install name for dylibs if [[ "$is_lib" == "true" ]]; then local lib_name @@ -213,11 +218,15 @@ patch_binary() { local dest="$LIB_DIR/$dep_name" if [[ -f "$dest" ]]; then + local new_path if [[ "$is_lib" == "true" ]]; then - install_name_tool -change "$dep" "@loader_path/$dep_name" "$binary" 2>/dev/null || true + new_path="@loader_path/$dep_name" else - install_name_tool -change "$dep" "@loader_path/../lib/$dep_name" "$binary" 2>/dev/null || true + new_path="@loader_path/../lib/$dep_name" fi + local it_err + it_err="$(install_name_tool -change "$dep" "$new_path" "$binary" 2>&1)" \ + || echo " โš ๏ธ patch failed [$(basename "$binary")] $dep: $it_err" fi done } From f1544060a9b6c6fdd03c53c9c99d83f5ce0201c8 Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Thu, 21 May 2026 20:54:51 -0700 Subject: [PATCH 08/13] stripppppp before install_name_tool --- packages/linux-tools-mac/assets/build-mac.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/linux-tools-mac/assets/build-mac.sh b/packages/linux-tools-mac/assets/build-mac.sh index 387a7634..824b0850 100755 --- a/packages/linux-tools-mac/assets/build-mac.sh +++ b/packages/linux-tools-mac/assets/build-mac.sh @@ -197,10 +197,12 @@ patch_binary() { is_macho "$binary" || return 0 - # Re-remove signature immediately before patching; the batch removal may have - # silently failed for some arm64 binaries, and install_name_tool refuses to - # modify a still-signed binary on Apple Silicon. + # Remove signature and normalize LINKEDIT before patching. + # Xcode 16+ ARM64 binaries have gaps in the __LINKEDIT segment that + # install_name_tool can't handle; strip -x rewrites LINKEDIT contiguously, + # and the signature must be removed first so strip/install_name_tool can write. codesign --remove-signature "$binary" 2>/dev/null || true + strip -x "$binary" 2>/dev/null || true # Set install name for dylibs if [[ "$is_lib" == "true" ]]; then From 0f383cb65bd001d292d878df9acfe0fecc1a1c8c Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Thu, 21 May 2026 22:06:29 -0700 Subject: [PATCH 09/13] retry --- packages/linux-tools-mac/assets/build-mac.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/linux-tools-mac/assets/build-mac.sh b/packages/linux-tools-mac/assets/build-mac.sh index 824b0850..8bc2e246 100755 --- a/packages/linux-tools-mac/assets/build-mac.sh +++ b/packages/linux-tools-mac/assets/build-mac.sh @@ -181,7 +181,7 @@ done ### ================================ echo "" echo "๐Ÿ”“ Removing existing code signatures..." -find "$BUNDLE_DIR" -type f \( -name "*.dylib" -o -perm +111 \) | while read -r f; do +find "$BUNDLE_DIR" -type f \( -name "*.dylib" -o -perm /111 \) | while read -r f; do codesign --remove-signature "$f" 2>/dev/null || true done @@ -197,12 +197,12 @@ patch_binary() { is_macho "$binary" || return 0 - # Remove signature and normalize LINKEDIT before patching. - # Xcode 16+ ARM64 binaries have gaps in the __LINKEDIT segment that - # install_name_tool can't handle; strip -x rewrites LINKEDIT contiguously, - # and the signature must be removed first so strip/install_name_tool can write. + # Normalize LINKEDIT before patching. + # Xcode 16+ ARM64 bottles have gaps in __LINKEDIT that install_name_tool + # rejects. Ad-hoc signing compacts/reorganizes LINKEDIT as a side effect; + # removing the signature afterward leaves a clean, patchable binary. + codesign --force --sign - "$binary" 2>/dev/null || true codesign --remove-signature "$binary" 2>/dev/null || true - strip -x "$binary" 2>/dev/null || true # Set install name for dylibs if [[ "$is_lib" == "true" ]]; then @@ -248,7 +248,7 @@ done ### ================================ echo "" echo "โœ‚๏ธ Stripping symbols..." -find "$BUNDLE_DIR" -type f \( -name "*.dylib" -o -perm +111 \) | while read -r f; do +find "$BUNDLE_DIR" -type f \( -name "*.dylib" -o -perm /111 \) | while read -r f; do strip -x "$f" 2>/dev/null || true done From d448efe30b17fb39586df19ea64599c78cbdb107 Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Thu, 21 May 2026 22:12:09 -0700 Subject: [PATCH 10/13] tmp save --- packages/linux-tools-mac/assets/build-mac.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/linux-tools-mac/assets/build-mac.sh b/packages/linux-tools-mac/assets/build-mac.sh index 8bc2e246..d07ad608 100755 --- a/packages/linux-tools-mac/assets/build-mac.sh +++ b/packages/linux-tools-mac/assets/build-mac.sh @@ -181,7 +181,7 @@ done ### ================================ echo "" echo "๐Ÿ”“ Removing existing code signatures..." -find "$BUNDLE_DIR" -type f \( -name "*.dylib" -o -perm /111 \) | while read -r f; do +find "$BUNDLE_DIR" -type f | while read -r f; do codesign --remove-signature "$f" 2>/dev/null || true done @@ -248,7 +248,7 @@ done ### ================================ echo "" echo "โœ‚๏ธ Stripping symbols..." -find "$BUNDLE_DIR" -type f \( -name "*.dylib" -o -perm /111 \) | while read -r f; do +find "$BUNDLE_DIR" -type f | while read -r f; do strip -x "$f" 2>/dev/null || true done From fd60b715da7214bbf068618b3fd1cfc53b7c5753 Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Thu, 21 May 2026 22:30:25 -0700 Subject: [PATCH 11/13] xcode 16+ vs xcode 26.5 differences --- packages/linux-tools-mac/assets/build-mac.sh | 58 +++++++++++++++++--- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/packages/linux-tools-mac/assets/build-mac.sh b/packages/linux-tools-mac/assets/build-mac.sh index d07ad608..b89b61c9 100755 --- a/packages/linux-tools-mac/assets/build-mac.sh +++ b/packages/linux-tools-mac/assets/build-mac.sh @@ -185,6 +185,57 @@ find "$BUNDLE_DIR" -type f | while read -r f; do codesign --remove-signature "$f" 2>/dev/null || true done +### ================================ +### FIX LINKEDIT VMSIZE +### Homebrew bottles built with Xcode 16+ ld_prime have __LINKEDIT.vmsize +### larger than filesize. codesign --remove-signature does not shrink vmsize, +### leaving a gap that Xcode 16.4 install_name_tool rejects outright. +### Directly patch vmsize = page_aligned(filesize) before running install_name_tool. +### ================================ +echo "" +echo "๐Ÿ”ง Fixing __LINKEDIT vmsize..." +FIX_LINKEDIT_PY=' +import sys, struct + +def fix(path): + try: + data = open(path, "rb").read() + if len(data) < 32: + return + magic = struct.unpack_from(" len(ba): + break + cmd, sz = struct.unpack_from(e + "II", ba, off) + if sz == 0: + break + if cmd == 0x19 and off + 56 <= len(ba): + segname = ba[off + 8:off + 24].rstrip(b"\x00") + if segname == b"__LINKEDIT": + vmsize, = struct.unpack_from(e + "Q", ba, off + 32) + filesize, = struct.unpack_from(e + "Q", ba, off + 48) + if vmsize > filesize: + struct.pack_into(e + "Q", ba, off + 32, (filesize + 4095) & ~4095) + changed = True + off += sz + if changed: + open(path, "wb").write(ba) + except Exception: + pass + +for p in sys.argv[1:]: + fix(p) +' +find "$BUNDLE_DIR" -type f -print0 | xargs -0 python3 -c "$FIX_LINKEDIT_PY" + ### ================================ ### PATCH DYLIB REFERENCES ### ================================ @@ -197,13 +248,6 @@ patch_binary() { is_macho "$binary" || return 0 - # Normalize LINKEDIT before patching. - # Xcode 16+ ARM64 bottles have gaps in __LINKEDIT that install_name_tool - # rejects. Ad-hoc signing compacts/reorganizes LINKEDIT as a side effect; - # removing the signature afterward leaves a clean, patchable binary. - codesign --force --sign - "$binary" 2>/dev/null || true - codesign --remove-signature "$binary" 2>/dev/null || true - # Set install name for dylibs if [[ "$is_lib" == "true" ]]; then local lib_name From 77f24639e8618c057c4e8fcc3109296f43e5741f Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Thu, 21 May 2026 22:46:05 -0700 Subject: [PATCH 12/13] use macos-26 runners for latest xcode --- .github/workflows/build-linux-tools-mac.yaml | 2 +- packages/linux-tools-mac/assets/build-mac.sh | 71 +++----------------- 2 files changed, 11 insertions(+), 62 deletions(-) diff --git a/.github/workflows/build-linux-tools-mac.yaml b/.github/workflows/build-linux-tools-mac.yaml index fe7d78a8..4fcfbd63 100644 --- a/.github/workflows/build-linux-tools-mac.yaml +++ b/.github/workflows/build-linux-tools-mac.yaml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - runner: [macos-15-intel, macos-15] + runner: [macos-26-intel, macos-26] steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 diff --git a/packages/linux-tools-mac/assets/build-mac.sh b/packages/linux-tools-mac/assets/build-mac.sh index b89b61c9..821bf18b 100755 --- a/packages/linux-tools-mac/assets/build-mac.sh +++ b/packages/linux-tools-mac/assets/build-mac.sh @@ -6,6 +6,15 @@ if [[ "$(uname -s)" != "Darwin" ]]; then exit 1 fi +XCODE_VER="$(xcodebuild -version 2>/dev/null | awk '/^Xcode /{print $2}')" +XCODE_MAJOR="${XCODE_VER%%.*}" +if [[ -z "$XCODE_MAJOR" || "$XCODE_MAJOR" -lt 26 ]]; then + echo "โŒ Xcode 26+ required (found: ${XCODE_VER:-none})" + echo " install_name_tool in Xcode < 26 rejects Homebrew ld_prime binaries." + echo " Use a macos-26 runner." + exit 1 +fi + ### ================================ ### ARGS ### ================================ @@ -54,6 +63,7 @@ FORMULA_BINS=( echo "๐Ÿ› ๏ธ linux-tools-mac macOS bundle" echo " Arch: $ARCH" +echo " Xcode: $XCODE_VER" echo " Output: $OUTPUT_DIR" ### ================================ @@ -175,67 +185,6 @@ find "$LIB_DIR" -name "*.dylib" -type f | while read -r lib; do collect_deps "$lib" done -### ================================ -### REMOVE EXISTING SIGNATURES -### Must happen BEFORE any install_name_tool changes -### ================================ -echo "" -echo "๐Ÿ”“ Removing existing code signatures..." -find "$BUNDLE_DIR" -type f | while read -r f; do - codesign --remove-signature "$f" 2>/dev/null || true -done - -### ================================ -### FIX LINKEDIT VMSIZE -### Homebrew bottles built with Xcode 16+ ld_prime have __LINKEDIT.vmsize -### larger than filesize. codesign --remove-signature does not shrink vmsize, -### leaving a gap that Xcode 16.4 install_name_tool rejects outright. -### Directly patch vmsize = page_aligned(filesize) before running install_name_tool. -### ================================ -echo "" -echo "๐Ÿ”ง Fixing __LINKEDIT vmsize..." -FIX_LINKEDIT_PY=' -import sys, struct - -def fix(path): - try: - data = open(path, "rb").read() - if len(data) < 32: - return - magic = struct.unpack_from(" len(ba): - break - cmd, sz = struct.unpack_from(e + "II", ba, off) - if sz == 0: - break - if cmd == 0x19 and off + 56 <= len(ba): - segname = ba[off + 8:off + 24].rstrip(b"\x00") - if segname == b"__LINKEDIT": - vmsize, = struct.unpack_from(e + "Q", ba, off + 32) - filesize, = struct.unpack_from(e + "Q", ba, off + 48) - if vmsize > filesize: - struct.pack_into(e + "Q", ba, off + 32, (filesize + 4095) & ~4095) - changed = True - off += sz - if changed: - open(path, "wb").write(ba) - except Exception: - pass - -for p in sys.argv[1:]: - fix(p) -' -find "$BUNDLE_DIR" -type f -print0 | xargs -0 python3 -c "$FIX_LINKEDIT_PY" - ### ================================ ### PATCH DYLIB REFERENCES ### ================================ From fcaf1288dae41053344c3dd8825b4ae298757c6c Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Thu, 21 May 2026 23:14:50 -0700 Subject: [PATCH 13/13] address PR comments --- packages/linux-tools-mac/assets/build-mac.sh | 3 +++ packages/linux-tools-mac/package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/linux-tools-mac/assets/build-mac.sh b/packages/linux-tools-mac/assets/build-mac.sh index 821bf18b..51d78b62 100755 --- a/packages/linux-tools-mac/assets/build-mac.sh +++ b/packages/linux-tools-mac/assets/build-mac.sh @@ -48,6 +48,9 @@ BUNDLE_DIR="${TMP_DIR}/linux-tools-mac" BIN_DIR="${BUNDLE_DIR}/bin" LIB_DIR="${BUNDLE_DIR}/lib" +cleanup() { rm -rf "$TMP_DIR"; } +trap cleanup EXIT INT TERM + # Binaries to collect per formula โ€” "formula:bin1 bin2 bin3" entries FORMULA_BINS=( "gnu-tar:gtar" diff --git a/packages/linux-tools-mac/package.json b/packages/linux-tools-mac/package.json index 2f39f150..d0ca445f 100644 --- a/packages/linux-tools-mac/package.json +++ b/packages/linux-tools-mac/package.json @@ -1,5 +1,5 @@ { "name": "linux-tools-mac", "version": "0.0.0", - "description": "Portable macOS bundle of GNU/Linux-compatible tools (gtar, lzip, glib, gettext, libgsf, libtool, pcre, makedepend) used by electron-builder when packaging Linux targets on macOS." + "description": "Portable macOS bundle of GNU/Linux-compatible tools (gtar, lzip, glib, gettext, libgsf, libtool, pcre, makedepend, binutils) used by electron-builder when packaging Linux targets on macOS." }