Build Constraints in Golang
Add to bookmarksWed 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!