Build tags, cross-compilation y ldflags en Go: compilar para cualquier plataforma

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()
    // ...
}

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP