Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the right approach to encapsulate platform specific code in Go?

I want to develop a small Go application, which shows the used key stroke shortcuts to the audience of a presentation.

To hook into the keyboard events I will have to use some platform specific code. What is the Go way to encapsulate platform specific code? I've been searching for keywords like compiler switch or platform modules, but I couldn't really find something about.

like image 994
Robin Avatar asked Feb 16 '16 09:02

Robin


2 Answers

The solution to platform specific code is the build constraints.

Note: Prior to Go 1.17 the syntax was a comment line starting with // +build, but Go 1.17 introduced the //go:build pragma which is now the preferred way.

A build constraint, also known as a build tag, is a line comment that begins

//go:build

that lists the conditions under which a file should be included in the package. Constraints may appear in any kind of source file (not just Go), but they must appear near the top of the file, preceded only by blank lines and other line comments. These rules mean that in Go files a build constraint must appear before the package clause.

So basically each platform specific Go code should go into different files, and you can mark each of these Go files with the target they are intended for.

For example if a file contains Linux specific code (e.g. syscalls), start it with:

//go:build linux

If a file contains Windows specific syscalls, start it with:

//go:build windows

A lot more "options" are available, read the linked docs. For example you can specify constraints to the OS, Architecture, Go version, the compiler being used. You can also specify multiple constraints that will be interpreted with logical OR or AND, or you can use negation (e.g. this code is for every target platform except linux).

You can even mark a .go file to be ignored with the following constraint:

//go:build ignore

Note that build constraints are compiler specific. If a specific compiler does not recognize a build constraint, the compiler will ignore the file. As an example the Go AppEngine SDK comes with a built-in, modified Go compiler which additionally recognizes the

//go:build appengine

constraint, which means the source file is intended for the Google App Engine Platform only. "Regular" Go compilers will ignore the file, giving you the possibility to not have a bunch of compiler errors if someone tries to build the code without the Go AppEngine SDK.

like image 145
icza Avatar answered Nov 15 '22 20:11

icza


While @icza's answer is definitely the proper authoritative answer to OP's question about condtional compilation, in this specific case a simpler approach may be worthwhile.

Go is a language designed to compile the same (with the same compiler) on every platform, to encourage cross-platform code and library reuse. Go is not C/C++, for which time must be spent to write explicitly cross-platform code.

Of course, the platform agnosticity only goes as far as Go's runtime, and you're trying to capture keypresses, for which there's no real single platform-agnostic solution.

Thus, my simpler suggestion for this use case resembles the following:

package main

import (
    "runtime"
    kp "kplib"
)

func main () {
    switch runtime.GOOS {
    case "windows":
        kp.WinKey()
    case "darwin":
        kp.MacKey()
    case "linux":
        kp.UnixKey()
    default:
        kp.TryKey()
    }
}

With the implicit assurance the hypothetical kplib will compile everywhere (just be sure to call the right methods on the given platform!).

like image 23
cat Avatar answered Nov 15 '22 19:11

cat