Use Build Tags in Mixed Cases
Use build tags and suffixes in mixed cases, i.e. when some platforms have their unique implementations, but some share code.
Let's extend the previous example by the following requirement: the app should also support macOS, darwin in Go's vocabulary, and the implementation of the platform-specific part is exactly the same as it is for Linux. This is a pretty common situation for macOS. Some parts work close or similar to the linux platform, some are similar to those in freebsd, while some are unique to darwin itself.
In the case of our example, we have two options:
- implement the new requirement separately for
darwin, hence in its own file,something_darwin.go - or re-use the existing implementation which, for the sake of exercise, is assumed to be the same.
The former option has a rather weak advantage of not bothering with conditional compilation, and just relies on the automatic choice the compiler makes. A big disadvantage though is unjustified duplication, which clearly can and should be avoided. And this is exactly what the second option is about.
In order to re-use the existing implementation for darwin, and continue supporting linux, we need to make a few changes:
- first off, stop relying on the file suffix, as there is no file suffix that would include both
linuxanddarwin, and exclude all other platforms at the same time. This means, we shall rename the file - secondly, we need to specify a proper build tag to tell the compiler when it should include the file.
How to choose a new name for the something_linux.go file? We can't simply append _darwin at the end, as this file becomes suffixed with darwin, and will be only included for this platform. The name should not be a valid word from the list of operating systems, platforms and any other words supported by Go. Here are a few examples:
something_darlin.gosomething_lindar.gosomething_linuxdarwin.go.
However, this particular case is very often. What's common between Linux, macOS, and many other Unix and Unix-like operating systems? Right, they are, though to varying degrees, compliant with POSIX. So a pretty common thing to see in the standard library and other cross-platform projects is the use of the word posix as a suffix for cases when Linux and other operating systems from the Unix family share same code. Going further, you can also often run into cases when you have something common among all or many Unices, but different for Linux. In such case, for example, code that is the same for FreeBSD, OpenBSD, NetBSD and macOS can be put into a file with a name suffixed by _unix.go. Keep this in mind, and don't reinvent the wheel.
Alright, the name problem is solved. The existing file needs to be renamed from something_linux.go to something_posix.go. The name does not tell anymore the compiler when to include and exclude the file, but a tag will do. The code in the file should be built for Linux or macOS. That or is really important, as it's a condition. It does not make sense to express it as and, since this is an impossible combination (an operating system cannot be Linux and macOS at the same time). Here is what the resulting tag should look like:
// +build linux darwin
Also, keep in mind the rules about the placement of a build tag.
This is what our example was like before the changes:
- directory listing:
.
├── bin
├── cmd
│ └── myapp
│ └── main.go
└── something
├── something.go
├── something_linux.go
└── something_windows.go
- and the content of the
something_linux.gofile:
package something
const (
Feature2 = "this is a version supported by a number of unix-like systems"
)
This is what it looks like after the changes:
- directory listing:
.
├── bin
├── cmd
│ └── myapp
│ └── main.go
└── something
├── something.go
├── something_posix.go
└── something_windows.go
- and the content of the
something_posix.gofile:
// +build linux darwin
package something
const (
Feature2 = "this is a version supported by a number of unix-like systems"
)
- building:
# On/For Mac.
$ GOOS=darwin go build -o bin/myapp-darwin ./cmd/myapp
# Running.
$ ./bin/myapp-darwin
common feature for all platforms
this is a version supported by a number of unix-like systems