Skip to content

Multiplatform Builds

Why Build for Multiple Platforms?

There are a number of use cases for building code for different platforms. Common examples include:

  • Building Docker images for both ARM64 (Apple Silicon, AWS Graviton) and AMD64 architectures
  • Creating binaries for Windows, macOS, and Linux
  • Supporting different CPU architectures for embedded systems
  • Ensuring your application works across cloud providers with different infrastructure

Grog provides several approaches to for handling multi-platform builds.

Approaches to Multi-platform Building

There are two primary strategies for building for multiple platforms:

  1. Cross-compilation: Create a single build target that outputs artifacts for multiple platforms
  2. Platform-specific targets: Create separate build targets for each platform and run them on appropriate hosts

Grog supports both approaches, with platform selectors providing special support for the second strategy.

Cross-compilation Approach

Cross-compilation involves building for a different platform than the one you’re running on. Many languages and build tools support this out-of-the-box.

Example: Go Cross-compilation

Go has excellent built-in cross-compilation support:

targets:
- name: build_multi_platform
command: |
# Build for Linux AMD64
GOOS=linux GOARCH=amd64 go build -o dist/myapp-linux-amd64 ./cmd/myapp
# Build for macOS ARM64
GOOS=darwin GOARCH=arm64 go build -o dist/myapp-darwin-arm64 ./cmd/myapp
# Build for Windows AMD64
GOOS=windows GOARCH=amd64 go build -o dist/myapp-windows-amd64.exe ./cmd/myapp
inputs:
- cmd/**/*.go
- internal/**/*.go
- go.mod
- go.sum
outputs:
- dist/myapp-linux-amd64
- dist/myapp-darwin-arm64
- dist/myapp-windows-amd64.exe

Example: Multi-platform Docker Images

For Docker images, you can use Docker’s BuildX feature:

targets:
- name: build_multi_arch_image
command: |
# Set up Docker BuildX builder with multi-platform support
docker buildx create --name multiplatform-builder --use
# Build and push multi-platform image
docker buildx build --platform linux/amd64,linux/arm64 \
-t registry.example.com/myapp:latest \
--push .
inputs:
- Dockerfile
- src/**/*
outputs:
- docker::registry.example.com/myapp:latest

Platform-specific Targets with Platform Selectors

Grog’s platform selectors allow you to define targets that only run on specific platforms. When a target doesn’t match the current host’s platform, Grog will skip it automatically.

Defining Platform Selectors

You can specify platform requirements using the platform field:

targets:
- name: build_linux_amd64
command: go build -o dist/myapp-linux-amd64 ./cmd/myapp
platform:
os:
- linux
arch:
- amd64
inputs:
- cmd/**/*.go
- go.mod
outputs:
- dist/myapp-linux-amd64
- name: build_darwin_arm64
command: go build -o dist/myapp-darwin-arm64 ./cmd/myapp
platform:
os:
- darwin
arch:
- arm64
inputs:
- cmd/**/*.go
- go.mod
outputs:
- dist/myapp-darwin-arm64

In this example:

  • build_linux_amd64 will only run on Linux AMD64 hosts
  • build_darwin_arm64 will only run on macOS ARM64 hosts (like Apple Silicon Macs)

Platform Selector Behavior

When you run grog build, Grog will:

  1. Check each target’s platform selector against the os and arch the grog binary was built for
  2. Skip targets that don’t match the current platform
  3. Build targets that do match the platform

This allows you to define all platform variants in a single build file, but only build the ones appropriate for the current environment.

Handling Dependencies with Platform Selectors

When a target depends on a platform-specific target, Grog enforces platform compatibility:

targets:
- name: linux_binary
command: go build -o dist/myapp-linux ./cmd/myapp
platform:
os:
- linux
outputs:
- dist/myapp-linux
- name: package_linux
dependencies:
- :linux_binary
command: tar -czf dist/myapp-linux.tar.gz dist/myapp-linux
platform:
os:
- linux
outputs:
- dist/myapp-linux.tar.gz

If you try to build package_linux on a non-Linux platform, Grog will fail with an error because the dependency linux_binary cannot be built on the current platform.

Platform-agnostic Dependencies

Sometimes you need to create platform-agnostic targets that depend on platform-specific ones. For these cases, you can use conditional logic in your build commands:

targets:
- name: test_all_platforms
dependencies:
- :build_linux_amd64 # Will be skipped on non-Linux/AMD64 platforms
- :build_darwin_arm64 # Will be skipped on non-macOS/ARM64 platforms
command: |
# Run tests for whatever platform we're on
if [ "$(uname)" == "Darwin" ]; then
./test_darwin.sh
elif [ "$(uname)" == "Linux" ]; then
./test_linux.sh
else
echo "Unsupported platform for testing"
exit 1
fi