An Advanced Example

Here is another question: What if we need to support a third feature which is unique to the Mac, and is unavailable for all other platforms? In this case, we should use the full power available at our disposal, and also avoid exposing the difference to main. How can this be accomplished?

We can use our example to illustrate the approach. Let's assume there should be a feature that does something on darwin but does not exist/has no effect on other platforms. There are two approaches available:

  • mock the behaviour on the platforms that don't have the feature, so it can be called in a platform-independent way, i.e. make it no-op
  • or encapsulate the platform-dependent call such that it's not exposed to the platform-independent code, hence is transparent.

The latter is available and really helpful in real life when implementing unique parts nested within a higher-level code that is called by code that is unaware of different platforms. For our example we'll follow the former way, but the second is preferred whenever it's possible.

Also note, that a simple if runtime.GOOS == "darwin" { // unique code } won't do the trick. Why? Because it will be attempted to be included for all platforms during compilation, and conditioned only at runtime.

Let's introduce a third feature which should work only on the Mac, while all existing conditions must remain. In order to make this happen, we:

  • implement the new feature for darwin
  • mock it for windows and linix
  • the second step implies that we have to add a Linux-specific file. We'll use the suffix for that.

We also don't want to make a special case for the existing code shared between Linux and macOS. The fact that we're now going to support all three platforms, does not mean we should have a separate platform-specific implementation, just for the sake of separation. As one of the guiding principles says, the more code can be shared and/or abstracted, the better. And if the second feature introduced earlier remains the same for the two platforms, there is absolutely no point in splitting it.

Let's have a look at the project after the necessary have been made.

  • directory listing:
.
├── bin
├── cmd
│   └── myapp
│       └── main.go
└── something
    ├── something.go
    ├── something_darwin.go
    ├── something_linux.go
    ├── something_posix.go
    └── something_windows.go

Notice the two new files, something_darwin.go and something_linux.go.

  • the main.go file:
package main

import (
	"fmt"

	"../../something"
)

func main() {
	fmt.Println(something.Feature1)
	fmt.Println(something.Feature2)

	something.DoFeature3()
}

Notice how the code does not depend on platform here.

  • something.go
package something

const (
	Feature1 = "common feature for all platforms"
)

This piece of code is common among all supported platforms.

  • something_darwin.go
package something

import (
	"fmt"
)

func DoFeature3() {
	fmt.Println("this is a unique feature for the mac")
}

This code is compiled for and executed only when running on the Mac.

  • something_linux.go
package something

func DoFeature3() {}

Here we mock the missing functionality for Linux.

  • something_posix.go
// +build linux darwin

package something

const (
	Feature2 = "this is a version supported by a number of unix-like systems"
)

This code remains the same, as it works nicely for the two platforms, hence no change is needed. This file is included for both platforms.

  • something_windows.go
package something

const (
	Feature2 = "this is a windows specific version"
)

func DoFeature3() {}

Finally, for Windows, the new feature is mocked in this existing file.

Here are a few results of building and running:

# Build for darwin.
GOOS=darwin go build -o bin/myapp-darwin ./cmd/myapp

# Run on darwin.
$ ./bin/myapp-darwin
common feature for all platforms
this is a version supported by a number of unix-like systems
this is a unique feature for the mac


# Build for linux.
$ GOOS=linux go build -o bin/myapp-linux ./cmd/myapp

# Run on linux.
$ docker run -it --rm -v $(pwd)/bin:/root/bin/ alpine:3.13 ash
% /root/bin/myapp-linux
common feature for all platforms
this is a version supported by a number of unix-like systems

As you see, the feature works as expected on macOS, and is non-existent on Linux. Quod Erat Demonstrandum.