aux4
Documentation
HomeGetting StartedInstallationIDEsVariablesExecutorsFunctionsConfigurationCommandsHooksSecurityRenderTestingPackage ManagerPackagingGitHub ActionsDocker

Multi-Platform Package

This guide shows two ways to make a package behave correctly across different operating systems: overriding a command's implementation per platform (same command, different execute logic), and shipping a compiled binary per OS/architecture.

Platform-Specific Commands (Same Command, Different Implementation)

Sometimes the difference between platforms isn't a compiled binary — it's that the same shell tool behaves differently on each OS. A common example is sed: GNU sed (Linux) edits a file in place with -i, while BSD sed (macOS) requires an explicit (empty) backup suffix, -i ''.

Put a .aux4 file directly under dist/<os>/, with a "platform" field, alongside your root .aux4:

1my-package/ 2├── .aux4 # root package + a default/fallback implementation 3├── LICENSE 4├── README.md 5├── RELEASE.md 6└── dist/ 7 └── linux/ 8 └── .aux4 # { "platform": "linux", ... }

Root .aux4 — the default implementation (BSD sed, works out of the box on macOS):

1{ 2 "scope": "myorg", 3 "name": "texttools", 4 "version": "1.0.0", 5 "description": "Text file utilities", 6 "license": "MIT", 7 "profiles": [ 8 { 9 "name": "main", 10 "commands": [ 11 { 12 "name": "trim", 13 "execute": [ 14 "aux4 trim-impl run --file ${file}" 15 ], 16 "help": { 17 "text": "Remove trailing whitespace from every line of a file, in place", 18 "variables": [ 19 { 20 "name": "file", 21 "text": "The file to clean", 22 "arg": true 23 } 24 ] 25 } 26 }, 27 { 28 "name": "trim-impl", 29 "private": true, 30 "execute": ["profile:trim-impl"], 31 "help": { 32 "text": "Internal platform-specific implementation. Not for direct use." 33 } 34 } 35 ] 36 }, 37 { 38 "name": "trim-impl", 39 "commands": [ 40 { 41 "name": "run", 42 "private": true, 43 "execute": [ 44 "sed -i '' -E 's/[[:space:]]+$//' ${file}" 45 ], 46 "help": { 47 "text": "BSD sed variant (default, used on macOS). The linux dist override below replaces this with the GNU sed form.", 48 "variables": [ 49 { 50 "name": "file", 51 "text": "The file to clean in place" 52 } 53 ] 54 } 55 } 56 ] 57 } 58 ] 59}

dist/linux/.aux4 — overrides just the trim-impl profile with the GNU sed form:

1{ 2 "platform": "linux", 3 "profiles": [ 4 { 5 "name": "trim-impl", 6 "commands": [ 7 { 8 "name": "run", 9 "private": true, 10 "execute": [ 11 "sed -i -E 's/[[:space:]]+$//' ${file}" 12 ], 13 "help": { 14 "text": "GNU sed variant (Linux) — in-place editing doesn't need the empty backup-suffix argument BSD sed requires.", 15 "variables": [ 16 { 17 "name": "file", 18 "text": "The file to clean in place" 19 } 20 ] 21 } 22 } 23 ] 24 } 25 ] 26}

The public trim command is identical on every platform — it always calls aux4 trim-impl run --file ${file}. Only the private trim-impl profile differs, and only on Linux. Isolating the platform-sensitive logic into its own private profile (rather than inlining it into trim itself) is what makes a clean override possible — see the next paragraph for why.

At build time, aux4 aux4 pkger build merges each dist/<platform>/.aux4 into the root .aux4. The merge works per profile, not per command: a profile in the dist file replaces a root profile of the same name in its entirety (or is added if the root doesn't define it). That's why the platform-specific run command lives alone in its own trim-impl profile — if it were bundled into a profile with other, platform-independent commands, the Linux override would have to repeat all of them just to replace the one that differs.

Installing via the hub downloads the zip already merged for the user's OS, so the rest of the package — and its users — never need to know which variant is actually installed.

This technique composes with the binary-per-platform approach below — a dist/<os>/<arch>/ binary and a dist/<os>/.aux4 override can both exist in the same package.

Platform-Specific Binaries

A multi-platform package uses the dist/ directory to organize binaries by OS and architecture:

1my-package/ 2├── .aux4 3├── LICENSE 4├── README.md 5├── RELEASE.md 6└── dist/ 7 ├── darwin/ 8 │ ├── amd64/ 9 │ │ └── my-binary 10 │ └── arm64/ 11 │ └── my-binary 12 ├── linux/ 13 │ ├── amd64/ 14 │ │ └── my-binary 15 │ ├── arm64/ 16 │ │ └── my-binary 17 │ └── 386/ 18 │ └── my-binary 19 └── windows/ 20 ├── amd64/ 21 │ └── my-binary.exe 22 ├── arm64/ 23 │ └── my-binary.exe 24 └── 386/ 25 └── my-binary.exe

Supported Platforms

OSArchitectures
darwin (macOS)amd64, arm64
linuxamd64, arm64, 386
windowsamd64, arm64, 386

Step 1: Create the Binary

This example uses Go to create a cross-platform binary. You can use any language that compiles to native executables.

Create the Go source

1// main.go 2package main 3 4import ( 5 "fmt" 6 "os" 7) 8 9func main() { 10 name := "World" 11 if len(os.Args) > 1 { 12 name = os.Args[1] 13 } 14 fmt.Printf("Hello, %s!\n", name) 15}

Build for all platforms

1# Create dist directories 2mkdir -p dist/darwin/{amd64,arm64} 3mkdir -p dist/linux/{amd64,arm64,386} 4mkdir -p dist/windows/{amd64,arm64,386} 5 6# Build for macOS 7GOOS=darwin GOARCH=amd64 go build -o dist/darwin/amd64/my-greeter . 8GOOS=darwin GOARCH=arm64 go build -o dist/darwin/arm64/my-greeter . 9 10# Build for Linux 11GOOS=linux GOARCH=amd64 go build -o dist/linux/amd64/my-greeter . 12GOOS=linux GOARCH=arm64 go build -o dist/linux/arm64/my-greeter . 13GOOS=linux GOARCH=386 go build -o dist/linux/386/my-greeter . 14 15# Build for Windows 16GOOS=windows GOARCH=amd64 go build -o dist/windows/amd64/my-greeter.exe . 17GOOS=windows GOARCH=arm64 go build -o dist/windows/arm64/my-greeter.exe . 18GOOS=windows GOARCH=386 go build -o dist/windows/386/my-greeter.exe .

Step 2: Create the .aux4 File

Reference the binary using ${packageDir}. When installed, aux4 extracts only the binary for the user's platform:

1{ 2 "scope": "myorg", 3 "name": "greeter", 4 "version": "1.0.0", 5 "description": "Cross-platform greeting tool", 6 "license": "MIT", 7 "profiles": [ 8 { 9 "name": "main", 10 "commands": [ 11 { 12 "name": "greet", 13 "execute": [ 14 "${packageDir}/my-greeter ${name}" 15 ], 16 "help": { 17 "text": "Greet someone", 18 "variables": [ 19 { 20 "name": "name", 21 "text": "Name to greet", 22 "default": "World", 23 "arg": true 24 } 25 ] 26 } 27 } 28 ] 29 } 30 ] 31}

Step 3: Create Supporting Files

LICENSE

1cat > LICENSE << 'EOF' 2MIT License 3... 4EOF

Tip: You can also use aux4/license to generate your LICENSE file:

1aux4 license use \ 2 --name mit \ 3 --owner "MyOrg" \ 4 --year 2024 \ 5 --project "greeter"

README.md

1cat > README.md << 'EOF' 2# Greeter 3 4A cross-platform greeting tool. 5 6## Supported Platforms 7 8- macOS (Intel and Apple Silicon) 9- Linux (x64, ARM64, x86) 10- Windows (x64, ARM64, x86) 11 12## Usage 13 14```bash 15aux4 greet John 16``` 17EOF

Step 4: Build the Package

1aux4 aux4 pkger build ./out .

For multi-platform packages, the build creates multiple zip files:

1out/ 2├── myorg_greeter_1.0.0.zip # Global package (contains all platforms) 3├── darwin_amd64_myorg_greeter_1.0.0.zip 4├── darwin_arm64_myorg_greeter_1.0.0.zip 5├── linux_386_myorg_greeter_1.0.0.zip 6├── linux_amd64_myorg_greeter_1.0.0.zip 7├── linux_arm64_myorg_greeter_1.0.0.zip 8├── windows_386_myorg_greeter_1.0.0.zip 9├── windows_amd64_myorg_greeter_1.0.0.zip 10└── windows_arm64_myorg_greeter_1.0.0.zip

Step 5: Install from File

When installing from file, you must use the platform-specific zip file, not the main zip:

1# On macOS ARM64 (Apple Silicon) 2aux4 aux4 pkger install --fromFile ./out/darwin_arm64_myorg_greeter_1.0.0.zip 3 4# On macOS Intel 5aux4 aux4 pkger install --fromFile ./out/darwin_amd64_myorg_greeter_1.0.0.zip 6 7# On Linux x64 8aux4 aux4 pkger install --fromFile ./out/linux_amd64_myorg_greeter_1.0.0.zip 9 10# On Windows x64 11aux4 aux4 pkger install --fromFile ./out/windows_amd64_myorg_greeter_1.0.0.zip

The main zip file (myorg_greeter_1.0.0.zip) cannot be installed directly with --fromFile because it contains all platforms. Use the platform-specific zip for local installation.

Step 6: Publish

Publish the main zip file (which contains all platforms):

1aux4 aux4 login 2aux4 aux4 pkger publish ./out/myorg_greeter_1.0.0.zip

Step 7: Install from aux4 Hub

When installing from the aux4 Hub, aux4 automatically downloads the correct platform-specific package:

1aux4 aux4 pkger install myorg/greeter

aux4 detects the user's OS and architecture and downloads the appropriate package.

Step 8: Use the Package

1aux4 greet John
1Hello, John!

Building with aux4

You can automate the build process using aux4 itself. We recommend creating a build command that builds your binaries to the dist/ folder.

For multi-platform packages, we suggest creating a builder profile with commands for each platform, then having your main build command call aux4 builder all.

Create a .aux4 file in your project root:

1{ 2 "profiles": [ 3 { 4 "name": "main", 5 "commands": [ 6 { 7 "name": "build", 8 "execute": [ 9 "aux4 builder all", 10 "aux4 aux4 pkger build ./out ./package" 11 ], 12 "help": { 13 "text": "Build binaries for all platforms and create package" 14 } 15 }, 16 { 17 "name": "builder", 18 "execute": [ 19 "profile:builder" 20 ], 21 "help": { 22 "text": "Platform-specific build commands" 23 } 24 } 25 ] 26 }, 27 { 28 "name": "builder", 29 "commands": [ 30 { 31 "name": "darwin", 32 "execute": [ 33 "GOOS=darwin GOARCH=amd64 go build -o package/dist/darwin/amd64/my-greeter .", 34 "GOOS=darwin GOARCH=arm64 go build -o package/dist/darwin/arm64/my-greeter ." 35 ], 36 "help": { 37 "text": "Build for macOS" 38 } 39 }, 40 { 41 "name": "linux", 42 "execute": [ 43 "GOOS=linux GOARCH=amd64 go build -o package/dist/linux/amd64/my-greeter .", 44 "GOOS=linux GOARCH=arm64 go build -o package/dist/linux/arm64/my-greeter .", 45 "GOOS=linux GOARCH=386 go build -o package/dist/linux/386/my-greeter ." 46 ], 47 "help": { 48 "text": "Build for Linux" 49 } 50 }, 51 { 52 "name": "windows", 53 "execute": [ 54 "GOOS=windows GOARCH=amd64 go build -o package/dist/windows/amd64/my-greeter.exe .", 55 "GOOS=windows GOARCH=arm64 go build -o package/dist/windows/arm64/my-greeter.exe .", 56 "GOOS=windows GOARCH=386 go build -o package/dist/windows/386/my-greeter.exe ." 57 ], 58 "help": { 59 "text": "Build for Windows" 60 } 61 }, 62 { 63 "name": "all", 64 "execute": [ 65 "aux4 builder darwin", 66 "aux4 builder linux", 67 "aux4 builder windows" 68 ], 69 "help": { 70 "text": "Build for all platforms" 71 } 72 } 73 ] 74 } 75 ] 76}

Then build everything with:

1aux4 build

Or build for a specific platform:

1aux4 builder darwin

See Also