Una de las ventajas más prácticas de Go es la compilación cruzada: con un solo comando puedes generar un binario para Linux, Windows, macOS, ARM o cualquier combinación desde tu propia máquina, sin necesitar un entorno de construcción separado para cada plataforma. Los build tags, ldflags y CGO_ENABLED completan el control sobre qué entra en cada build.
Cross-compilation con GOOS y GOARCH
// Variables de entorno que controlan la plataforma destino // GOOS: sistema operativo (linux, windows, darwin, freebsd, android...) // GOARCH: arquitectura (amd64, arm64, arm, 386, mips, wasm...) // Compilar para Linux AMD64 desde cualquier plataforma GOOS=linux GOARCH=amd64 go build -o bin/app-linux ./cmd/app // Compilar para Windows desde Linux o macOS GOOS=windows GOARCH=amd64 go build -o bin/app.exe ./cmd/app // Compilar para macOS Apple Silicon (ARM64) GOOS=darwin GOARCH=arm64 go build -o bin/app-mac-m1 ./cmd/app // Compilar para Raspberry Pi (ARM 32-bit) GOOS=linux GOARCH=arm GOARM=7 go build -o bin/app-rpi ./cmd/app // WebAssembly GOOS=js GOARCH=wasm go build -o app.wasm ./cmd/app
Script para compilar para múltiples plataformas
#!/bin/bash
# build-all.sh
VERSION=$(git describe --tags --always)
LDFLAGS="-ldflags=-X main.Version=${VERSION}"
platforms=(
"linux/amd64"
"linux/arm64"
"darwin/amd64"
"darwin/arm64"
"windows/amd64"
)
for platform in "${platforms[@]}"; do
GOOS=${platform%/*}
GOARCH=${platform#*/}
output="bin/app-${GOOS}-${GOARCH}"
if [ "$GOOS" = "windows" ]; then
output="${output}.exe"
fi
GOOS=$GOOS GOARCH=$GOARCH go build $LDFLAGS -o "$output" ./cmd/app
echo "Compilado: $output"
done
ldflags: inyectar valores en tiempo de compilación
-ldflags permite sobreescribir variables del paquete sin tocar el código fuente. El patrón más habitual es inyectar la versión desde el tag de git:
// main.go
package main
import "fmt"
var (
Version = "dev"
BuildTime = "unknown"
GitCommit = "none"
)
func main() {
fmt.Printf("Versión: %snCommit: %snFecha: %sn", Version, GitCommit, BuildTime)
}
// Compilar inyectando los valores
go build
-ldflags="-X main.Version=$(git describe --tags)
-X main.GitCommit=$(git rev-parse --short HEAD)
-X main.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
-o app ./cmd/app
CGO_ENABLED=0: binarios estáticos
Por defecto, algunos paquetes de Go (como net y os/user) usan CGO y se enlazan dinámicamente con la libc del sistema. Con CGO_ENABLED=0 Go usa implementaciones puras en Go y genera un binario completamente estático:
// Binario estático (sin dependencias de libc) CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app ./cmd/app // Verificar que el binario es estático file app // app: ELF 64-bit LSB executable, x86-64, statically linked // Para imágenes Docker mínimas (scratch o distroless): FROM scratch COPY app /app ENTRYPOINT ["/app"]
Build tags: código condicional por plataforma
Los build tags (desde Go 1.17 con sintaxis //go:build) incluyen o excluyen ficheros según la plataforma, versión de Go u otras condiciones:
// fichero_linux.go se compila solo en Linux
//go:build linux
package main
import "fmt"
func infoPlataforma() string {
return fmt.Sprintf("Linux: /proc/self/status disponible")
}
// fichero_windows.go se compila solo en Windows
//go:build windows
package main
func infoPlataforma() string {
return "Windows: usando WinAPI"
}
// Tags más complejos con operadores lógicos //go:build linux && amd64 //go:build !cgo //go:build go1.21 // requiere Go 1.21 o superior //go:build integration // tag personalizado: go test -tags=integration
Tags personalizados para tests de integración
// integration_test.go
//go:build integration
package main_test
import "testing"
func TestIntegracionBD(t *testing.T) {
// Solo se ejecuta con: go test -tags=integration ./...
db := conectarBDReal()
defer db.Close()
// ...
}
