Build Constraints in Golang

Add to bookmarks

Wed Nov 13 2019

Build constraints in go

Ever had to write go code that works on one platform and another piece of code that works on another? or ever had to painstakingly assist a colleague in setting up his engine to run CGO related code? Just me? Ok, but sooner or later you would come across/need to use golang's build constraints

What are they?

Build constraints in golang allows us (the developer) control the files that are included during compile time in our go program, based on a certain condition (constraints) that are set in those files.

How do they work

You set a build constraint for a file in golang by placing a comment (space included) in the format // +build {tags} at the start of the file, followed by an empty line beneath the comment. The go compiler interprets anything after the +build as the constraints that need to be fulfilled before that file is included in the build.

If we had a file in our project with the following contents:

// +build darwin

package main

func init(){
    operatingSystem = "darwin"
}

This file would only be compiled if the OS being built for was set as darwin, which is set using the GOOS variable.

Building with constraints

You can set the constraints to be used in building your go program by setting the -tags build flag when running go build or any other go command that accepts build flags e.g go build -o output -tags ci . This would build the current go module with the constraints ci set and save the executable in the output binary. You can even set more than one constraint using the build flag e.g go build -tags ci, another,third tag

Available build constraints

While custom build tags are set using the tags build flag, golang automatically sets a few tags based on environment variables and other factors. Here is a list of the available tags

GOOS and GOARCH Environment Values

You can set constraints in your source code to only run files if a certain GOOS or GOARCH is used. e.g

// +build darwin,amd64

package utils

You can view the complete list of GOOS and GOARCH values here

GO Version constraint

You can also constrain the inclusion of a file to the go version being used in building the entire module. EX to only build the file if the go version being used is 1.12 and above you would use // +build go1.12. This would include the file if the go version is 1.12 or 1.13 (latest as of writing this)

CGO

Files can be set to be included in the build process if cgo is enabled. For instance, excluding test of CGO code in a build pipeline that does not have the required C libraries. To set a constraint for cgo simply use

// +build cgo

Compiler being used

You can include files based on the go compiler being used, the available compiler values are either gc or gccgo

Build constraint syntax

You can combine constraints in the same way as you would any other conditional statement in programming i.e AND, OR, NOT

To set a constraint to negate the ! should be used e.g

// +build !cgo

this will only include the file in the build if CGO is NOT enabled.

You can also combine multiple constraints. e.g

// +build cgo darwin

will only include the file in the build if CGO is enabled and the GOOS is set to darwin.

OR combinations

// +build darwin,linux

Finally, you can go crazy and combine them all e.g

// +build linux,386 darwin,!cgo

Results to (linux AND 386) OR (darwin AND (NOT cgo)).

Conclusion

With build constraints, you can easily write different sections of your code for different conditions instead of having to deal with nasty IF statements and having to rewrite entire chunks of code for different platforms.

ENJOY!