# 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`:

```text
my-package/
├── .aux4                  # root package + a default/fallback implementation
├── LICENSE
├── README.md
├── RELEASE.md
└── dist/
    └── linux/
        └── .aux4           # { "platform": "linux", ... }
```

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

```json
{
  "scope": "myorg",
  "name": "texttools",
  "version": "1.0.0",
  "description": "Text file utilities",
  "license": "MIT",
  "profiles": [
    {
      "name": "main",
      "commands": [
        {
          "name": "trim",
          "execute": [
            "aux4 trim-impl run --file ${file}"
          ],
          "help": {
            "text": "Remove trailing whitespace from every line of a file, in place",
            "variables": [
              {
                "name": "file",
                "text": "The file to clean",
                "arg": true
              }
            ]
          }
        },
        {
          "name": "trim-impl",
          "private": true,
          "execute": ["profile:trim-impl"],
          "help": {
            "text": "Internal platform-specific implementation. Not for direct use."
          }
        }
      ]
    },
    {
      "name": "trim-impl",
      "commands": [
        {
          "name": "run",
          "private": true,
          "execute": [
            "sed -i '' -E 's/[[:space:]]+$//' ${file}"
          ],
          "help": {
            "text": "BSD sed variant (default, used on macOS). The linux dist override below replaces this with the GNU sed form.",
            "variables": [
              {
                "name": "file",
                "text": "The file to clean in place"
              }
            ]
          }
        }
      ]
    }
  ]
}
```

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

```json
{
  "platform": "linux",
  "profiles": [
    {
      "name": "trim-impl",
      "commands": [
        {
          "name": "run",
          "private": true,
          "execute": [
            "sed -i -E 's/[[:space:]]+$//' ${file}"
          ],
          "help": {
            "text": "GNU sed variant (Linux) — in-place editing doesn't need the empty backup-suffix argument BSD sed requires.",
            "variables": [
              {
                "name": "file",
                "text": "The file to clean in place"
              }
            ]
          }
        }
      ]
    }
  ]
}
```

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:

```text
my-package/
├── .aux4
├── LICENSE
├── README.md
├── RELEASE.md
└── dist/
    ├── darwin/
    │   ├── amd64/
    │   │   └── my-binary
    │   └── arm64/
    │       └── my-binary
    ├── linux/
    │   ├── amd64/
    │   │   └── my-binary
    │   ├── arm64/
    │   │   └── my-binary
    │   └── 386/
    │       └── my-binary
    └── windows/
        ├── amd64/
        │   └── my-binary.exe
        ├── arm64/
        │   └── my-binary.exe
        └── 386/
            └── my-binary.exe
```

### Supported Platforms

| OS | Architectures |
|---|---|
| darwin (macOS) | amd64, arm64 |
| linux | amd64, arm64, 386 |
| windows | amd64, 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

```go
// main.go
package main

import (
    "fmt"
    "os"
)

func main() {
    name := "World"
    if len(os.Args) > 1 {
        name = os.Args[1]
    }
    fmt.Printf("Hello, %s!\n", name)
}
```

#### Build for all platforms

```bash
# Create dist directories
mkdir -p dist/darwin/{amd64,arm64}
mkdir -p dist/linux/{amd64,arm64,386}
mkdir -p dist/windows/{amd64,arm64,386}

# Build for macOS
GOOS=darwin GOARCH=amd64 go build -o dist/darwin/amd64/my-greeter .
GOOS=darwin GOARCH=arm64 go build -o dist/darwin/arm64/my-greeter .

# Build for Linux
GOOS=linux GOARCH=amd64 go build -o dist/linux/amd64/my-greeter .
GOOS=linux GOARCH=arm64 go build -o dist/linux/arm64/my-greeter .
GOOS=linux GOARCH=386 go build -o dist/linux/386/my-greeter .

# Build for Windows
GOOS=windows GOARCH=amd64 go build -o dist/windows/amd64/my-greeter.exe .
GOOS=windows GOARCH=arm64 go build -o dist/windows/arm64/my-greeter.exe .
GOOS=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:

```json
{
  "scope": "myorg",
  "name": "greeter",
  "version": "1.0.0",
  "description": "Cross-platform greeting tool",
  "license": "MIT",
  "profiles": [
    {
      "name": "main",
      "commands": [
        {
          "name": "greet",
          "execute": [
            "${packageDir}/my-greeter ${name}"
          ],
          "help": {
            "text": "Greet someone",
            "variables": [
              {
                "name": "name",
                "text": "Name to greet",
                "default": "World",
                "arg": true
              }
            ]
          }
        }
      ]
    }
  ]
}
```

### Step 3: Create Supporting Files

#### LICENSE

```bash
cat > LICENSE << 'EOF'
MIT License
...
EOF
```

**Tip:** You can also use [aux4/license](https://hub.aux4.io/r/public/packages/aux4/license) to generate your LICENSE file:

```bash
aux4 license use \
  --name mit \
  --owner "MyOrg" \
  --year 2024 \
  --project "greeter"
```

#### README.md

````bash
cat > README.md << 'EOF'
# Greeter

A cross-platform greeting tool.

## Supported Platforms

- macOS (Intel and Apple Silicon)
- Linux (x64, ARM64, x86)
- Windows (x64, ARM64, x86)

## Usage

```bash
aux4 greet John
```
EOF
````

### Step 4: Build the Package

```bash
aux4 aux4 pkger build ./out .
```

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

```text
out/
├── myorg_greeter_1.0.0.zip              # Global package (contains all platforms)
├── darwin_amd64_myorg_greeter_1.0.0.zip
├── darwin_arm64_myorg_greeter_1.0.0.zip
├── linux_386_myorg_greeter_1.0.0.zip
├── linux_amd64_myorg_greeter_1.0.0.zip
├── linux_arm64_myorg_greeter_1.0.0.zip
├── windows_386_myorg_greeter_1.0.0.zip
├── windows_amd64_myorg_greeter_1.0.0.zip
└── 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:

```bash
# On macOS ARM64 (Apple Silicon)
aux4 aux4 pkger install --fromFile ./out/darwin_arm64_myorg_greeter_1.0.0.zip

# On macOS Intel
aux4 aux4 pkger install --fromFile ./out/darwin_amd64_myorg_greeter_1.0.0.zip

# On Linux x64
aux4 aux4 pkger install --fromFile ./out/linux_amd64_myorg_greeter_1.0.0.zip

# On Windows x64
aux4 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):

```bash
aux4 aux4 login
aux4 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:

```bash
aux4 aux4 pkger install myorg/greeter
```

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

### Step 8: Use the Package

```bash
aux4 greet John
```

```text
Hello, 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:

```json
{
  "profiles": [
    {
      "name": "main",
      "commands": [
        {
          "name": "build",
          "execute": [
            "aux4 builder all",
            "aux4 aux4 pkger build ./out ./package"
          ],
          "help": {
            "text": "Build binaries for all platforms and create package"
          }
        },
        {
          "name": "builder",
          "execute": [
            "profile:builder"
          ],
          "help": {
            "text": "Platform-specific build commands"
          }
        }
      ]
    },
    {
      "name": "builder",
      "commands": [
        {
          "name": "darwin",
          "execute": [
            "GOOS=darwin GOARCH=amd64 go build -o package/dist/darwin/amd64/my-greeter .",
            "GOOS=darwin GOARCH=arm64 go build -o package/dist/darwin/arm64/my-greeter ."
          ],
          "help": {
            "text": "Build for macOS"
          }
        },
        {
          "name": "linux",
          "execute": [
            "GOOS=linux GOARCH=amd64 go build -o package/dist/linux/amd64/my-greeter .",
            "GOOS=linux GOARCH=arm64 go build -o package/dist/linux/arm64/my-greeter .",
            "GOOS=linux GOARCH=386 go build -o package/dist/linux/386/my-greeter ."
          ],
          "help": {
            "text": "Build for Linux"
          }
        },
        {
          "name": "windows",
          "execute": [
            "GOOS=windows GOARCH=amd64 go build -o package/dist/windows/amd64/my-greeter.exe .",
            "GOOS=windows GOARCH=arm64 go build -o package/dist/windows/arm64/my-greeter.exe .",
            "GOOS=windows GOARCH=386 go build -o package/dist/windows/386/my-greeter.exe ."
          ],
          "help": {
            "text": "Build for Windows"
          }
        },
        {
          "name": "all",
          "execute": [
            "aux4 builder darwin",
            "aux4 builder linux",
            "aux4 builder windows"
          ],
          "help": {
            "text": "Build for all platforms"
          }
        }
      ]
    }
  ]
}
```

Then build everything with:

```bash
aux4 build
```

Or build for a specific platform:

```bash
aux4 builder darwin
```

---

## See Also

- [Simple Package](/packaging/simple)
- [Package with Dependencies](/packaging/dependencies)
- [Package with System Dependencies](/packaging/system-dependencies)
