|
1 | | -# PublishAotCompressed |
| 1 | +# PublishAotCompressed.macOS |
2 | 2 |
|
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). |
4 | 4 |
|
5 | | -UPX will in-memory decompress the program at launch. This is typically not observable. |
| 5 | +## Features |
6 | 6 |
|
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