Multi-Platform Builds with go build

Introduction

The Go build command in Go is indispensable for compiling your code along with its dependencies into an executable file. Its primary objective is to convert Go source code into a functional program. This command offers flexibility, enabling you to compile either a single package or an entire application along with all its dependencies.

How to use

Using Golang build is straightforward. Simply run the command in the directory containing your Go source files. This will compile the package located in the current directory. If the build is successful, an executable file will be created in the same directory.

go build

Compiling a packages

All packages

You can use the ./… pattern to compile all packages within a directory and its subdirectories.

go build ./...

Specific Package:

You can choose a specific package to build by specifying it

go build path/to/package

Building Go binaries:

Creating Go binaries is an essential aspect of Go development. The process involves using the Go build command to transform Go source code into executable files. Let’s walk through the steps of building Go binaries:

To reach your project directory

open your terminal or command prompt and transition to the directory where your Go source code resides.

cd /path/to/your/project

Build the Binary

Run the Golang build command to compile your Go source code into an executable binary. By default, the binary will be named after the directory containing the main package.

go build -o output_filename

Cross compilation

Cross-compiling Go binaries enable you to create executables for various operating systems and architectures using just one machine. You can specify the target operating system (GOOS) and architecture (GOARCH) for the binary you intend to build.

GOOS

The target operating system. Examples include Linux, Windows, and Darwin (macOS).

GOARCH

The target architecture. Examples include amd64 (64-bit), 386 (32-bit), arm (32-bit ARM), and arm64 (64-bit ARM).

Go facilitates cross-compilation across a diverse spectrum of operating systems and architectures. Presented below is a compilation of platforms compatible with Go binaries, accompanied by their respective GOOS (operating system) and GOARCH (architecture) values.
The following command lists all supported platforms (operating system and architecture combinations) for Go build.

go tool dist list

Supported Operating Systems (GOOS)

    • aix
    • android
    • darwin (macOS)
    • dragonfly
    • freebsd
    • hurd
    • illumos
    • js (WebAssembly)
    • linux
    • netbsd
    • openbsd
    • plan9
    • solaris
    • windows
    • Zos

Supported Architectures (GOARCH)

    • 386 (32-bit x86)
    • amd64 (64-bit x86)
    • amd64p32 (x86-64 with 32-bit pointers; used on Plan 9 only)
    • arm (32-bit ARM)
    • arm64 (64-bit ARM)
    • loong64 (64-bit LoongArch)
    • mips (MIPS, big-endian)
    • mipsle (MIPS, little-endian)
    • mips64 (MIPS, 64-bit, big-endian)
    • mips64le (MIPS, 64-bit, little-endian)
    • ppc64 (64-bit PowerPC, big-endian)
    • ppc64le (64-bit PowerPC, little-endian)
    • riscv64 (64-bit RISC-V)
    • s390x (IBM System z)
    • wasm (WebAssembly)

Go module

Go modules, first introduced in Go 1.11, simplify dependency management for developers, ensuring consistent builds and efficient dependency handling. A module, in essence, is a grouping of interconnected Go packages that are versioned simultaneously.

To start integrating Go modules for dependency management in your project, initiate a module by running the following command:

go mod init <module-name>

The above command creates a go.mod file in your project directory, which tracks the module path and its dependencies.

  • go.mod: This file lists the module’s dependencies, specifying the required versions. It gets automatically updated when you add or remove dependencies.
  • go.sum: This file contains the checksums of the module’s dependencies, ensuring the integrity and authenticity of the packages. It helps verify that the dependencies have not been tampered with.

Dependency Management

In the go.mod file, modules list their dependencies along with the required version or version range. Go modules then handle the download and management of these dependencies automatically, ensuring the correct versions are used.

Adding Dependencies

When you import a package, it’s downloaded and managed automatically, and you can also add dependencies explicitly using the go get command.

go get -u module_path

Upgrading Dependencies

You can upgrade dependencies to their latest compatible versions using:

go get module_path@version

Cleaning up dependencies

To eliminate any unused dependencies and ensure the accuracy of go.mod and go.sum files in reflecting the packages your project utilises, execute the following command.

go mod tidy

Build tags

Build tags in Go provide a way to include or exclude files from the compilation process based on specific conditions. They are particularly useful for conditional compilation, platform-specific code, and enabling/disabling features.

Build tags in Go are specified as comments at the top of source files, usually before the package declaration. They’re written using a specific format.

Syntax

// build <tags>

Example

// +build linux 
package main 
import "fmt" 
func main() { 
    fmt.Println("Hello, Linux!") 
}

In this example, the comment // +build linux indicates that the following code should be included only when building for Linux. If you try to build this code on a non-Linux platform, the code inside main() will be excluded from the compilation process.

Use multiple tags

You can specify multiple tags using logical AND and OR operations.

AND

Separate tags with spaces.

// +build linux amd64

OR

Separate tags with commas.

// +build linux,windows

NOT

Prefix a tag with !

// +build !windows

Combining Tags

You can combine multiple conditions using space (AND) or comma (OR).

// +build linux,!cgo 
package main 
import "fmt" 
func main() { 
fmt.Println("This will build on Linux if CGO is disabled") 
}

Custom Build Tags

You can define your own custom tags and use them to conditionally include or exclude files.

// +build mycustomtag 
package main 
import "fmt" 
func main() { 
fmt.Println("This will build if the 'mycustomtag' tag is specified")
 }

To include or exclude files based on custom tags, you can specify the tags when running commands like Golang build, Go test, or Go run using the -tags flag.

go build -tags=myCustomTag 
go test -tags=myCustomTag  
go run -tags=myCustomTag 

Go run Vs Go build vs Go install

1.Go run

Compile and run Go programs quickly without generating a binary.

Syntax

go run filename.go

go run filename.go

go run file1.go file2.go

When to Use: Ideal for testing and debugging code snippets or small programs, offering convenience for rapid iteration without the need to manage compiled binaries.
How It Works:

  1. Compiles the source code into a temporary binary.
  2. Run the binary immediately.
  3. Deletes the binary after execution.

2.Go build

Compiles the Go source code into an executable binary but does not install it into the bin directory.

Syntax

go build

You can also build specific files or packages:

go build filename.go 
go build ./path/to/package

When to Use: When you want to compile your code into a binary for testing, deployment, or sharing but do not need it installed into your Go workspace.
How It Works:

  • Compiles the source code into a binary executable.
  • Place the binary in the current directory by default (or a specified directory).

3. Go install

Compiles the Go source code and instals the resulting binary into the bin directory of your Go workspace ($GOPATH/bin).

Syntax

go install

This command instals the current package. You can specify a package path:

go install ./path/to/package 
go install github.com/username/projectname@latest

When to Use: When you want to build and install the binary to make it easily accessible from anywhere in your system. It’s particularly useful for installing command-line tools or deploying binaries.

How It Works:

  • Compiles the source code into a binary executable.
  • Place the binary in the $GOPATH/bin directory.
  • If the package being installed contains a main package, the resulting binary is named after the directory containing the main package.

Golang build Error handling

Handling errors in Go during the build process involves recognizing common errors, figuring out their causes, and fixing them. Here are some common build errors and how to deal with them:

1.Missing Dependencies

cannot find package "example.com/some/package" in any of: /usr/local/go/src/example.com/some/package (from $GOROOT) /home/user/go/src/example.com/some/package (from $GOPATH)

Ensure that all dependencies are fetched. Use go get to download the missing package:

go get example.com/some/package

2. Incorrect Import Paths

Verify the import paths in your source files. Ensure they are correct and align with the structure of your project and modules.

3.Compilation error

Ensure all functions and variables are defined. Check for typos or incorrect cases in function names. Ensure that all necessary files are included in the build.

4. Version mismatched

Verify that the version you are trying to use exists. Update your go.mod file to specify

5.Environment Variable Issues

Ensure that your GOPATH and other environment variables are set correctly. Set them to absolute paths in your shell configuration file (.bashrc, .zshrc, etc.)

go: GOPATH entry is relative; must be absolute path

Understanding Go Build Cache

Go’s build cache makes builds faster by reusing existing files.

  • How Go Build Cache Works
    1. Hashing: When you run go build, go install, or other related commands, the Go toolchain calculates a hash for each package based on its source files, dependencies, and build options.
    2. Storage: Compiled package results are stored in the cache directory. By default, this is located in $GOCACHE, which is usually set to ~/.cache/go-build on Unix-like systems and %LOCALAPPDATA%\go-build on Windows.
    3. Reusing Cache: On subsequent builds, the Go toolchain checks the cache for an existing entry with a matching hash. If found, it reuses the cached result instead of recompiling the package from scratch.

How to deploy Go build on the server

Deploying a Go application on a server has multiple ways. Following are the ways listed below for deploying go build on the server:

  1. Copy Binary: Once you’ve built your Go program, send the file to the server using either scp or rsync.
  2. Docker: Turn your Go program into a Docker image, upload it to a Docker hub, and then run it on the server using Docker.
  3. Systemd Service: Put your Go program on the server, and set up a systemd service file to control it like any other service.
  4. Kubernetes: Wrap your Go program in a Docker container, and then use Kubernetes to launch and manage it within a cluster.
  5. CI/CD Pipeline: Set up an automated pipeline (like Jenkins, GitHub Actions, or GitLab CI) to build and deploy your Go app to the server automatically.