Build tags e compilação condicional
1. Introdução às Build Tags
Build tags são diretivas de compilação que permitem incluir ou excluir arquivos do processo de build do Go com base em condições específicas. Elas são essenciais para lidar com código dependente de plataforma, funcionalidades experimentais, ou configurações de ambiente.
A sintaxe moderna utiliza o formato //go:build (introduzido no Go 1.17), substituindo o legado // +build. O compilador avalia essas tags antes de compilar cada arquivo, decidindo se ele deve ser processado ou ignorado.
//go:build linux
package main
func getOS() string {
return "Linux"
}
2. Sintaxe e Operadores Lógicos
As build tags suportam expressões booleanas completas com operadores lógicos:
Tags simples:
//go:build linux
//go:build amd64
//go:build !windows
Operadores combinados:
// Apenas Linux 64 bits ou macOS
//go:build (linux && amd64) || darwin
// Qualquer sistema exceto Windows
//go:build !windows
// Linux ou macOS com arquitetura ARM
//go:build (linux || darwin) && arm64
Parênteses são obrigatórios para agrupar expressões e garantir a precedência correta.
3. Arquivos com Build Tags no Nome
Go oferece uma convenção de nomenclatura que simplifica a separação por SO e arquitetura:
file_linux.go // Compila apenas no Linux
file_windows.go // Compila apenas no Windows
file_darwin.go // Compila apenas no macOS
file_amd64.go // Compila apenas em AMD64
file_linux_amd64.go // Compila apenas no Linux AMD64
Limitações: Essa convenção só funciona para sistema operacional (GOOS) e arquitetura (GOARCH). Para tags customizadas, é obrigatório usar a diretiva //go:build.
Quando usar cada abordagem:
- Use nome de arquivo para diferenças simples de SO/arquitetura
- Use tags explícitas para combinações complexas ou tags customizadas
4. Aplicações Práticas: Portabilidade entre SOs
Vamos criar um utilitário que obtém informações do sistema de forma portável:
// sysinfo.go - Interface comum
package sysinfo
type Info struct {
Hostname string
OS string
Uptime uint64
}
func Get() (*Info, error) {
return getSysInfo()
}
// sysinfo_linux.go
//go:build linux
package sysinfo
import "syscall"
func getSysInfo() (*Info, error) {
var info syscall.Sysinfo_t
if err := syscall.Sysinfo(&info); err != nil {
return nil, err
}
return &Info{
Hostname: getHostname(),
OS: "linux",
Uptime: info.Uptime,
}, nil
}
func getHostname() string {
name, _ := syscall.Gethostname()
return name
}
// sysinfo_windows.go
//go:build windows
package sysinfo
import (
"os"
"syscall"
)
func getSysInfo() (*Info, error) {
hostname, _ := os.Hostname()
return &Info{
Hostname: hostname,
OS: "windows",
Uptime: getUptimeWindows(),
}, nil
}
func getUptimeWindows() uint64 {
dll := syscall.NewLazyDLL("kernel32.dll")
proc := dll.NewProc("GetTickCount64")
ret, _, _ := proc.Call()
return uint64(ret) / 1000
}
5. Aplicações Práticas: Flags de Compilação Customizadas
Tags customizadas permitem ativar/desativar funcionalidades durante o build:
// logger.go
package logger
type Logger struct {
verbose bool
}
func New() *Logger {
return &Logger{}
}
func (l *Logger) Log(msg string) {
// Implementação padrão silenciosa
}
// logger_debug.go
//go:build debug
package logger
import "fmt"
func (l *Logger) Log(msg string) {
if l.verbose {
fmt.Println("[DEBUG]", msg)
}
}
func (l *Logger) SetVerbose(v bool) {
l.verbose = v
}
// main.go
package main
import "logger"
func main() {
log := logger.New()
log.Log("Aplicação iniciada")
// Com 'go build -tags debug', exibe a mensagem
}
Compilando com tags:
go build -tags "debug" -o app
go build -tags "debug,integration" -o app # Múltiplas tags
6. Testes Condicionais com Build Tags
Testes também podem ser condicionais, útil para testes de integração ou específicos de plataforma:
// database_test.go
package database
import "testing"
func TestLocalConnection(t *testing.T) {
// Teste básico que roda sempre
}
// database_integration_test.go
//go:build integration
package database
import "testing"
func TestDatabaseConnection(t *testing.T) {
// Teste que requer banco de dados real
// Só executa com: go test -tags integration
}
Executando testes específicos:
go test -tags integration ./...
go test -tags "integration,linux" ./...
7. Build Tags e o Arquivo go.mod
Build tags interagem com módulos de forma transparente — arquivos excluídos por tags não são compilados, mesmo que estejam no módulo. Isso é útil para:
- Excluir arquivos de dependências que não são compatíveis com sua plataforma
- Usar
go:generatecom tags para gerar código condicional:
//go:generate go run gen.go -tags "production"
//go:build production
package main
Problemas comuns: Conflitos de tags podem ocorrer quando múltiplos arquivos no mesmo pacote têm condições mutuamente exclusivas mas nenhuma cobre todos os casos. Sempre garanta que pelo menos um arquivo seja compilado para cada combinação possível.
8. Boas Práticas e Armadilhas Comuns
Evite duplicação excessiva: Use interfaces para abstrair funcionalidades e mantenha o máximo de código compartilhado:
// storage.go
type Storage interface {
Save(data []byte) error
Load() ([]byte, error)
}
Mantenha a legibilidade: Comentários claros sobre o propósito de cada tag:
//go:build (linux && amd64) || darwin
// Este arquivo implementa operações de E/S específicas
// para Linux AMD64 e macOS (Apple Silicon e Intel)
Teste todas as combinações em CI:
# Exemplo de matriz de build no GitHub Actions
matrix:
goos: [linux, windows, darwin]
goarch: [amd64, arm64]
Depuração com go list:
# Verifique quais arquivos serão compilados
go list -f '{{.GoFiles}}' -tags "linux,amd64"
Migração de // +build para //go:build:
# Use a ferramenta oficial para migração
gofmt -w *.go
Referências
- Documentação oficial de Build Constraints — Referência completa da sintaxe e comportamento das build tags no Go.
- Go Wiki: Go 1.17 Build Constraints — Guia de migração do formato
// +buildpara//go:build. - How to Use Build Tags in Go — Tutorial prático da DigitalOcean com exemplos reais de portabilidade entre SOs.
- Go Build Tags: Conditional Compilation — Artigo detalhado sobre compilação condicional com casos de uso avançados.
- Testing with Build Tags in Go — Guia específico sobre como usar build tags em testes condicionais e de integração.
- Go By Example: Build Tags — Exemplos concisos e diretos para iniciantes em build tags.