Skip to content

Commit cf8e0e8

Browse files
committed
feat: Transform to PublishAotCompressed.macOS for cross-compilation
Major changes: - Rename all files with .macOS suffix for clear branding - Remove Linux/Windows UPX tools, keep only macOS ARM64 version - Add smart compression: compress Windows/Linux, skip macOS targets - Add short parameter support: /p:Upx=true|false - Add comprehensive bilingual documentation (English + Chinese) - Update CI/CD workflow for macOS-only builds - Integrate with PublishAotCross.macOS ecosystem Features: ✅ Automatic UPX compression after Native AOT compilation ✅ Cross-compilation support for Windows and Linux from macOS ✅ 60%+ typical compression rates ✅ Smart detection to skip macOS targets automatically ✅ Optional LZMA for better compression Closes MichalStrehovsky#1
1 parent 5129381 commit cf8e0e8

19 files changed

Lines changed: 840 additions & 1120 deletions

.github/workflows/main.yml

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,37 +12,47 @@ on:
1212

1313
jobs:
1414
build_and_test:
15-
strategy:
16-
matrix:
17-
include:
18-
- vm: ubuntu-latest
19-
# - vm: windows-latest
20-
runs-on: ${{ matrix.vm }}
21-
name: Build and test ${{ matrix.vm }}
15+
runs-on: macos-latest
16+
name: Build and test on macOS
2217
steps:
2318
- name: Checkout repo
24-
uses: actions/checkout@v3
25-
- name: Setup dotnet
26-
uses: actions/setup-dotnet@v2
19+
uses: actions/checkout@v4
20+
21+
- name: Setup .NET
22+
uses: actions/setup-dotnet@v4
2723
with:
28-
dotnet-version: '7.0.x'
24+
dotnet-version: '9.0.x'
2925
include-prerelease: true
30-
- name: Build (CI)
26+
27+
- name: Install UPX
28+
run: brew install upx
29+
30+
- name: Build Package (CI)
3131
if: ${{ github.event.inputs.version == '' }}
3232
run: |
33-
dotnet build -t:Pack src/PublishAotCompressed.nuproj
34-
dotnet publish -r linux-x64 -c Release test/Hello.csproj
35-
- name: Build (CD)
33+
dotnet build -t:Pack src/PublishAotCompressed.macOS.nuproj
34+
35+
- name: Test - Build for macOS (should skip UPX)
36+
if: ${{ github.event.inputs.version == '' }}
37+
run: |
38+
dotnet publish -r osx-arm64 -c Release test/Hello.csproj
39+
ls -lh test/bin/Release/net9.0/osx-arm64/publish/
40+
41+
- name: Build Package (CD)
3642
if: ${{ github.event.inputs.version != '' }}
37-
run: dotnet build -t:Pack src/PublishAotCompressed.nuproj -p:Version=${{ github.event.inputs.version }}
38-
- name: Archive NuGet
43+
run: dotnet build -t:Pack src/PublishAotCompressed.macOS.nuproj -p:Version=${{ github.event.inputs.version }}
44+
45+
- name: Archive NuGet Package
3946
if: ${{ github.event.inputs.version != '' }}
4047
uses: actions/upload-artifact@v4
4148
with:
42-
name: PublishAotCompressed.${{ github.event.inputs.version }}.nupkg
43-
path: src/bin/Debug/PublishAotCompressed.${{ github.event.inputs.version }}.nupkg
44-
- name: Create tag
45-
if: ${{ github.event.inputs.version != '' && github.actor == 'MichalStrehovsky' }}
49+
name: PublishAotCompressed.macOS.${{ github.event.inputs.version }}.nupkg
50+
path: src/bin/Debug/PublishAotCompressed.macOS.${{ github.event.inputs.version }}.nupkg
51+
52+
- name: Create Release Tag
53+
if: ${{ github.event.inputs.version != '' }}
4654
run: |
55+
git config user.name github-actions
56+
git config user.email github-actions@github.com
4757
git tag v${{ github.event.inputs.version }}
4858
git push origin v${{ github.event.inputs.version }}

.gitignore

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Build results
2+
[Bb]in/
3+
[Oo]bj/
4+
[Ll]og/
5+
[Ll]ogs/
6+
7+
# .NET Core
8+
project.lock.json
9+
project.fragment.lock.json
10+
artifacts/
11+
12+
# NuGet Packages
13+
*.nupkg
14+
# NuGet Symbol Packages
15+
*.snupkg
16+
# The packages folder can be ignored because of Package Restore
17+
**/[Pp]ackages/*
18+
19+
# macOS
20+
.DS_Store
21+
.AppleDouble
22+
.LSOverride
23+
24+
# Visual Studio Code
25+
.vscode/
26+
*.code-workspace
27+
28+
# Rider
29+
.idea/
30+
31+
# User-specific files
32+
*.rsuser
33+
*.suo
34+
*.user
35+
*.userosscache
36+
*.sln.docstates
37+
38+
# Windows
39+
Thumbs.db
40+
ehthumbs.db
41+
Desktop.ini
42+

README.md

Lines changed: 224 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,227 @@
1-
# PublishAotCompressed
1+
# PublishAotCompressed.macOS
22

3-
This is a NuGet package with an MSBuild target to compress results of [PublishAot](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/) with [UPX](https://upx.github.io/). Simply add a reference to this package and publish with `PublishAot` as usual. The result of AOT compilation will be compressed. UPX typically achieves 60% or more size savings. To achieve even more compression at the cost of startup time, specify `<PublishLzmaCompressed>true</PublishLzmaCompressed>` property as well.
3+
MSBuild targets to automatically compress Native AOT binaries with UPX on macOS. Designed to work seamlessly with cross-compilation tools like [PublishAotCross.macOS](https://github.com/interface95/PublishAotCross.macOS).
44

5-
UPX will in-memory decompress the program at launch. This is typically not observable.
5+
## Features
66

7-
A Hello World style program with `<UseSystemResourceKeys>true</UseSystemResourceKeys>` and `<InvariantGlobalization>true</InvariantGlobalization>` (two [documented](https://docs.microsoft.com/en-us/dotnet/core/deploying/trimming/trimming-options?pivots=dotnet-6-0#trimming-framework-library-features) size savings options that pretty much everyone should enable) compressed with UPX is around 830 kB in size, fully self-contained. (On Linux, don't forget to also set `<StripSymbols>true</StripSymbols>`, the [documented](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/) switch to place debugging symbols into a separate file.)
7+
-**Automatic UPX compression** after Native AOT compilation
8+
-**Cross-compilation support** - compress binaries for Windows and Linux from macOS
9+
-**macOS-only** - streamlined for macOS development workflow
10+
-**60%+ size reduction** typical compression rates
11+
-**Optional LZMA** for even better compression
12+
13+
## Prerequisites
14+
15+
- **macOS** (Apple Silicon or Intel)
16+
- **.NET 9.0 SDK** or later
17+
- **UPX** installed via Homebrew:
18+
```bash
19+
brew install upx
20+
```
21+
22+
## Quick Start
23+
24+
### 1. Install Cross-Compilation Tools
25+
26+
For cross-compiling to Windows/Linux, install [PublishAotCross.macOS](https://github.com/interface95/PublishAotCross.macOS):
27+
28+
```xml
29+
<PackageReference Include="PublishAotCross.macOS" Version="1.0.2-preview" />
30+
<PackageReference Include="PublishAotCompressed.macOS" Version="1.0.0-dev" />
31+
```
32+
33+
### 2. Configure Your Project
34+
35+
```xml
36+
<Project Sdk="Microsoft.NET.Sdk">
37+
<PropertyGroup>
38+
<OutputType>Exe</OutputType>
39+
<TargetFramework>net9.0</TargetFramework>
40+
<PublishAot>true</PublishAot>
41+
42+
<!-- Recommended size optimizations -->
43+
<UseSystemResourceKeys>true</UseSystemResourceKeys>
44+
<InvariantGlobalization>true</InvariantGlobalization>
45+
</PropertyGroup>
46+
47+
<ItemGroup>
48+
<PackageReference Include="PublishAotCross.macOS" Version="1.0.2-preview" />
49+
<PackageReference Include="PublishAotCompressed.macOS" Version="1.0.0-dev" />
50+
</ItemGroup>
51+
</Project>
52+
```
53+
54+
### 3. Publish with Compression
55+
56+
#### For Windows (via lld-link + xwin):
57+
58+
```bash
59+
# Make sure lld-link is in PATH
60+
export PATH="$(brew --prefix lld)/bin:$PATH"
61+
62+
# Publish and compress
63+
dotnet publish -r win-x64 -c Release
64+
dotnet publish -r win-arm64 -c Release
65+
```
66+
67+
#### For Linux (via Zig):
68+
69+
```bash
70+
# Publish and compress
71+
dotnet publish -r linux-x64 -c Release /p:StripSymbols=false
72+
dotnet publish -r linux-arm64 -c Release /p:StripSymbols=false
73+
74+
# For Alpine Linux (musl)
75+
dotnet publish -r linux-musl-x64 -c Release /p:StripSymbols=false
76+
dotnet publish -r linux-musl-arm64 -c Release /p:StripSymbols=false
77+
```
78+
79+
#### For macOS (native):
80+
81+
```bash
82+
dotnet publish -r osx-arm64 -c Release
83+
dotnet publish -r osx-x64 -c Release
84+
```
85+
86+
> **Note**: macOS targets automatically skip UPX compression. The package will display a message and produce uncompressed binaries that run normally. This is by design since UPX-compressed macOS binaries cannot run due to security restrictions.
87+
88+
## Configuration
89+
90+
### Compression Settings
91+
92+
You can use either the full property name or the short alias:
93+
94+
```xml
95+
<PropertyGroup>
96+
<!-- Short property name (recommended) -->
97+
<Upx>false</Upx>
98+
99+
<!-- Or use the full property name -->
100+
<PublishAotCompressed>false</PublishAotCompressed>
101+
102+
<!-- Use best compression (default: true) -->
103+
<CompressBest>true</CompressBest>
104+
105+
<!-- Use LZMA for maximum compression (slower startup) -->
106+
<PublishLzmaCompressed>true</PublishLzmaCompressed>
107+
</PropertyGroup>
108+
```
109+
110+
**Command line usage:**
111+
112+
```bash
113+
# Enable compression (short form) ✅ Recommended
114+
dotnet publish -r win-x64 -c Release /p:Upx=true
115+
116+
# Disable compression (short form) ✅ Recommended
117+
dotnet publish -r win-x64 -c Release /p:Upx=false
118+
119+
# Or use the full property name
120+
dotnet publish -r win-x64 -c Release /p:PublishAotCompressed=false
121+
```
122+
123+
### Typical Results
124+
125+
For a Hello World program with size optimizations enabled:
126+
127+
| Target | Uncompressed | Compressed | Ratio |
128+
|--------|--------------|------------|-------|
129+
| Windows x64 | ~1.2 MB | ~500 KB | 58% |
130+
| Linux x64 | ~1.3 MB | ~520 KB | 60% |
131+
| macOS ARM64 | ~1.2 MB | N/A (skipped) | - |
132+
133+
## How It Works
134+
135+
This package hooks into the Native AOT build process (`AfterTargets="LinkNative"`):
136+
137+
1. **Detects platform**: Identifies target OS (Windows/Linux/macOS) from RuntimeIdentifier
138+
2. **Selects UPX**: Uses macOS ARM64 UPX tool from the package
139+
3. **Compresses binary**: Runs UPX with appropriate flags for the target platform
140+
4. **Validates**: Ensures running on macOS (cross-compilation host)
141+
142+
**Build Flow:**
143+
144+
```
145+
.NET AOT Compiler → Native Binary → UPX Compression → Compressed Executable
146+
(on macOS) (.exe/.elf) (macOS tool) (for target OS)
147+
```
148+
149+
## Cross-Compilation Matrix
150+
151+
| Source (Build Host) | Target Platform | UPX Support | Tool Required |
152+
|---------------------|----------------|-------------|---------------|
153+
| macOS ARM64 | win-x64/arm64 | ✅ Compresses | PublishAotCross.macOS |
154+
| macOS ARM64 | linux-x64/arm64 | ✅ Compresses | PublishAotCross.macOS |
155+
| macOS ARM64 | osx-arm64/x64 | ⏭️ Skipped (auto) | Native AOT |
156+
157+
## Important Notes
158+
159+
### macOS Target Binaries
160+
161+
This package **automatically skips UPX compression** for macOS targets (osx-arm64, osx-x64) because:
162+
- UPX-compressed macOS binaries cannot run due to System Integrity Protection (SIP)
163+
- Code signing is incompatible with UPX compression
164+
- Gatekeeper blocks execution of compressed binaries
165+
166+
When you build for macOS, you'll see this message:
167+
```
168+
Skipping UPX compression for macOS target (osx-arm64). This package is designed
169+
for cross-compilation to Windows/Linux only.
170+
```
171+
172+
**The produced macOS binary will be uncompressed and run normally.** No action needed!
173+
174+
### Linux Deployment
175+
176+
Native AOT Linux binaries require **ICU library** on target system:
177+
178+
```bash
179+
# Ubuntu/Debian
180+
sudo apt-get install -y libicu-dev
181+
182+
# Alpine Linux
183+
apk add --no-cache icu-libs
184+
```
185+
186+
Or disable ICU dependency:
187+
```xml
188+
<InvariantGlobalization>true</InvariantGlobalization>
189+
```
190+
191+
## Troubleshooting
192+
193+
### UPX not found
194+
195+
```bash
196+
brew install upx
197+
```
198+
199+
### "PublishAotCompressed.macOS can only be used on macOS"
200+
201+
This package only works on macOS. For other platforms, use:
202+
- [PublishAotCompressed](https://github.com/MichalStrehovsky/PublishAotCompressed) (original, multi-platform)
203+
204+
### Cross-compilation fails
205+
206+
Make sure you've installed the cross-compilation tools:
207+
- For Windows: See [PublishAotCross.macOS Windows setup](https://github.com/interface95/PublishAotCross.macOS#for-windows-cross-compilation)
208+
- For Linux: See [PublishAotCross.macOS Linux setup](https://github.com/interface95/PublishAotCross.macOS#for-linux-cross-compilation)
209+
210+
## Example Project
211+
212+
See the `test/` directory for a minimal example.
213+
214+
## Related Projects
215+
216+
- **[PublishAotCross.macOS](https://github.com/interface95/PublishAotCross.macOS)** - Cross-compilation from macOS to Windows/Linux
217+
- **[PublishAotCompressed](https://github.com/MichalStrehovsky/PublishAotCompressed)** - Original multi-platform UPX compression package
218+
219+
## License
220+
221+
MIT License - see LICENSE.TXT for details.
222+
223+
## Credits
224+
225+
- Based on [PublishAotCompressed](https://github.com/MichalStrehovsky/PublishAotCompressed) by Michal Strehovsky
226+
- UPX by [UPX Team](https://upx.github.io/)
227+
- Designed to work with [PublishAotCross.macOS](https://github.com/interface95/PublishAotCross.macOS)

0 commit comments

Comments
 (0)