Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions MacUtilGUI/MacUtilGUI.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.automation.apple-events</key>
<true/>
</dict>
Expand Down
8 changes: 6 additions & 2 deletions MacUtilGUI/MacUtilGUI.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@
<SelfContained>true</SelfContained>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<!-- Include native libs in the bundle directory, NOT extracted at runtime to ~/.net/
This prevents hardened runtime from rejecting dylibs with mismatched Team IDs.
IncludeAllContentForSelfExtract must also be false when
IncludeNativeLibrariesForSelfExtract is false (NETSDK1143). -->
<IncludeNativeLibrariesForSelfExtract>false</IncludeNativeLibrariesForSelfExtract>
<IncludeAllContentForSelfExtract>false</IncludeAllContentForSelfExtract>
<PublishReadyToRun>true</PublishReadyToRun>
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
<DebugType>embedded</DebugType>

<!-- macOS specific properties -->
Expand Down
55 changes: 53 additions & 2 deletions MacUtilGUI/build_universal.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,42 @@ if [ $? -ne 0 ]; then
exit 1
fi

# Copy dependencies
rsync -a --exclude="$APP_NAME" "./bin/Release/net9.0/publish/osx-x64/" "$universal_app/Contents/MacOS/" 2>/dev/null
# Copy non-binary dependencies from x64 build
print_status "Copying dependencies..."
rsync -a --exclude="$APP_NAME" --exclude="*.dylib" "./bin/Release/net9.0/publish/osx-x64/" "$universal_app/Contents/MacOS/" 2>/dev/null

# Merge native dylibs with lipo (so they work on both architectures)
print_status "Creating universal native libraries..."
x64_dir="./bin/Release/net9.0/publish/osx-x64"
arm64_dir="./bin/Release/net9.0/publish/osx-arm64"
merged_count=0
skipped_count=0

for dylib in "$x64_dir"/*.dylib; do
[ -e "$dylib" ] || continue
dylib_name=$(basename "$dylib")
if [ -f "$arm64_dir/$dylib_name" ]; then
# Both architectures exist — merge into universal
lipo -create "$x64_dir/$dylib_name" "$arm64_dir/$dylib_name" \
-output "$universal_app/Contents/MacOS/$dylib_name" 2>/dev/null
if [ $? -eq 0 ]; then
print_success "Merged: $dylib_name"
merged_count=$((merged_count + 1))
else
# lipo failed — copy x64 version as fallback
cp "$dylib" "$universal_app/Contents/MacOS/$dylib_name"
print_warning "Could not merge $dylib_name (copied x64 version)"
skipped_count=$((skipped_count + 1))
fi
else
# Only exists in x64 build — copy as-is
cp "$dylib" "$universal_app/Contents/MacOS/$dylib_name"
print_status "Copied x64-only: $dylib_name"
skipped_count=$((skipped_count + 1))
fi
done

print_status "Merged $merged_count universal libraries, copied $skipped_count single-arch"

# Create Info.plist
cat > "$universal_app/Contents/Info.plist" << EOF
Expand Down Expand Up @@ -130,6 +164,23 @@ fi
# Make executable
chmod +x "$universal_app/Contents/MacOS/$APP_NAME"

# Ad-hoc sign native dylibs (prevents hardened runtime rejection)
# This is a minimal sign for unsigned builds. For distribution, use sign_macos.sh
print_status "Ad-hoc signing native libraries..."
for dylib in "$universal_app/Contents/MacOS/"*.dylib; do
[ -e "$dylib" ] || continue
codesign --force --sign - "$dylib" 2>/dev/null && print_success "Signed: $(basename "$dylib")"
done

# Ad-hoc sign the bundle itself (no hardened runtime for dev builds)
print_status "Ad-hoc signing the app bundle..."
codesign --force --deep --sign - --options=0x0 "$universal_app" 2>/dev/null
if [ $? -eq 0 ]; then
print_success "Ad-hoc signing complete (no hardened runtime)"
else
print_warning "Ad-hoc signing failed — run sign_macos.sh for proper signing"
fi

# Show results
universal_size=$(du -sh "$universal_app" | cut -f1)
print_success "Universal app created: $universal_app"
Expand Down
16 changes: 16 additions & 0 deletions MacUtilGUI/sign_macos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,22 @@ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 0
fi

# Sign native libraries first (required for .NET single-file apps)
# .NET self-contained bundles extract native dylibs to a temp directory at runtime.
# These dylibs must be individually signed before the bundle, otherwise macOS
# hardened runtime rejects them with "different Team IDs" even when
# disable-library-validation is set.
print_status "Signing native libraries inside the bundle..."
NATIVE_LIBS=$(find "$APP_BUNDLE_PATH/Contents/MacOS" -name "*.dylib" -type f 2>/dev/null)
for dylib in $NATIVE_LIBS; do
codesign --force --sign "$DEVELOPER_ID" "$dylib" 2>/dev/null
if [ $? -eq 0 ]; then
print_success "Signed: $(basename "$dylib")"
else
print_warning "Could not sign: $(basename "$dylib") (may not exist in single-file bundles)"
fi
done

# Sign the app bundle with deep signing (signs all nested components)
print_status "Signing the app bundle and all nested components..."
codesign --force --deep --timestamp --options=runtime --entitlements "$ENTITLEMENTS_FILE" --sign "$DEVELOPER_ID" "$APP_BUNDLE_PATH"
Expand Down