Advertisement

One of “Go”’s strengths is its capability to build your “Go” project cross-platform for all kinds of operating systems and architectures, e. g. windows/amd64 (Windows), linux/386 (Linux), darwin/amd64 (MacOS). But setting up the environment for this is not that easy. This article is a small “HOWTO” listing all the steps required to set up a cross-platform “Go” environment.

To make it easier for readers to follow along without any operating system issues, I use a “Docker” image as a working environment. As “Docker” is quite common these days, this article expects readers to be comfortable with it. But the instructions given below shall work on most Linux distributions as well.

Prepare build environment

First, let’s pull down the official “Docker” image of the “Alpine Linux” project. This is a very small “Docker” image which can be easily extended with additional packages.

docker pull alpine
# => Using default tag: latest
# => latest: Pulling from library/alpine
# => 88286f41530e: Pull complete
# => Digest: sha256:1072e499f3f655a032e88542330cf75b02e7bdf673278f701d7ba61629ee3ebe
# => Status: Downloaded newer image for alpine:latest

After the download has finished run the image and start a shell in the container context.

docker run -it --rm --name go-build-1 alpine sh
# => / #

Subsequently we need to update apk’s package index. Otherwise it does not find any package we’re looking for.

apk update
# => fetch http://dl-cdn.alpinelinux.org/alpine/v3.6/main/x86_64/APKINDEX.tar.gz
# => fetch http://dl-cdn.alpinelinux.org/alpine/v3.6/community/x86_64/APKINDEX.tar.gz
# => v3.6.2-41-g11187df7f2 [http://dl-cdn.alpinelinux.org/alpine/v3.6/main]
# => v3.6.2-32-g6f53cfcccd [http://dl-cdn.alpinelinux.org/alpine/v3.6/community]
# => OK: 8436 distinct packages available

Install pre-build go

To start building our very own “Go”, we need to install a pre-build “Go” first. We chose to use the “Go” package coming with “Alpine Linux” for this.

apk add go
# [...]
# => OK: 266 MiB in 24 packages

Setup packages

We need some more tools and libraries installed along with the pre-build “Go” package to run the build script. So, let’s add them to the container.

apk add make gcc git libc-dev bash
# [...]
# => OK: 298 MiB in 33 packages

Run the shell

Nowadays most Linux distributions use some kind of an advanced shell – e.g. “Bash”, “Zsh” – as the default one. To “simulate” a normal Linux distribution, we install the “Bash” shell first.

apk add bash
# => OK: 306 MiB in 38 packages

Next, we run it.

bash
# => bash-4.3#

Clone the repository

After we cloned the repository using the git command, we check out the version of “Go” we want to build.

  1. Assign the install path to a variable to make building the project easier:

    go_lang_install_path=~/.local/share/go-source
    
  2. Clone the “Git” repository of the “Go” project:

    git clone https://github.com/golang/go $go_lang_install_path
    # => Cloning into '/root/.local/share/go-source'...
    # [...]
    
    cd $go_lang_install_path
    
  3. Find the “Go” version to checkout:

    git tag | grep 1.8
    # => v1.8.3
    
  4. Check out the source code tagged with version tag:

    git checkout go1.8.3
    # => Note: checking out 'go1.8.3'.
    # [...]
    
  5. Switch to src directory:

    cd src
    

Build binaries

  1. Find out the path where your pre-build “Go” libraries are stored:

    We use the apk command for this as it knows the contents of all installed packages. As we can see, the relevant parts of the pre-build “Go” can be found in /usr/lib/go.

    apk info -L go | head
    # => usr/bin/gofmt
    # => usr/bin/go
    # => usr/lib/go/bin/gofmt
    # => usr/lib/go/bin/go
    # => usr/lib/go/lib/time/zoneinfo.zip
    # => usr/lib/go/lib/time/update.bash
    # [...]
    
  2. Bootstrap “Go” for all needed platforms:

    We use GOROOT_BOOTSTRAP to set the path where the libraries of the pre-build go binary can be found. You need to configure the operating system using the GOOS-variable. The architecture is controlled by the GOARCH-variable. In the context of this article a platform is a combination of an operating system and an architecture. You can find a full list of supported platforms at [1].

    GOOS=windows GOARCH=amd64 GOROOT_BOOTSTRAP=/usr/lib/go ./make.bash
    # [...]
    # => Installed Go for windows/amd64 in /root/.local/share/go-source
    # => Installed commands in /root/.local/share/go-source/bin
    
    GOOS=darwin GOARCH=amd64 GOROOT_BOOTSTRAP=/usr/lib/go ./make.bash
    # [...]
    # => Installed Go for darwin/amd64 in /root/.local/share/go-source
    # => Installed commands in /root/.local/share/go-source/bin
    
    GOOS=linux GOARCH=amd64 GOROOT_BOOTSTRAP=/usr/lib/go ./make.bash
    # [...]
    # => Installed Go for linux/amd64 in /root/.local/share/go-source
    # => Installed commands in /root/.local/share/go-source/bin
    

Setup shell environment for use of go

Next, we need to set up our shell environment. In this article we choose ~/.local/share/go-packages as GOPATH to contain all installed “Go”-packages - use whatever directory suites your needs best. Additionally we modifed PATH to make the shell look for go and binaries from installed “Go” packages in the configured paths first. Given you’re using the “Bash” shell, you can use the following snippet to configure it.

echo "export GOPATH=~/.local/share/go-packages" >> ~/.bashrc
echo "export PATH=~/.local/share/go-source/bin:$PATH" >> ~/.bashrc
echo "export PATH=~/.local/share/go-packages/bin:$PATH" >> ~/.bashrc

After that source the .bashrc to activate the configuration in your current session.

source ~/.bashrc

Test your installation

After you have built your own “Go” and set up your shell environment, it’s time to test the installation. We use a small “helloworld” project for this to build the sources for three different platforms: linux/amd64 (Linux), darwin/amd64 (MacOS) and windows/amd64 (Windows).

  1. Create a directory for the project in $GOPATH:

    package_dir=~/.local/share/go-packages/src/github.com/fedux-org/hello-world
    mkdir -p $package_dir
    cd $package_dir
    
  2. Create a file named helloworld.go in $package_dir:

    First start the “Vi” editor.

    vi helloworld.go
    

    After that, press i, paste this code snippet into the editor and stop the “insert” mode with <Esc>. Save and close the file using :wq.

    package main
    
    import "fmt"
    
    func main() {
        fmt.Printf("hello, world\n")
    }
    
  3. Build the tool for each platform:

    GOOS=windows GOARCH=amd64 go build -o helloworld.windows.exe helloworld.go
    GOOS=darwin  GOARCH=amd64 go build -o helloworld.mac helloworld.go
    GOOS=linux   GOARCH=amd64 go build -o hellworld.linux helloworld.go
    
  4. Check the built binaries:

    First of all we need to add the file tool to our container. Again we use apk for this.

    apk add file
    # [...]
    # => OK: 311 MiB in 40 packages
    

    Afterwards we try to find out, if all binaries are built for the correct platform using the file command. As we can see, everything is fine. Hooray!

    file helloworld.windows.exe
    # => helloworld.windows.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows
    
    file helloworld.mac
    # => helloworld.mac: Mach-O 64-bit x86_64 executable, flags:<NOUNDEFS>
    
    file hellworld.linux
    # => hellworld.linux: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, not stripped, with debug_info
    

Conclusion

Given you have “Go” bootstrapped for all required platforms, it’s that easy to build projects cross-platform. I hope you enjoyed reading this article and I see you again next time.

References

Discussion

If you found a mistake in this article or would like to contribute some content to it, please file an issue in this Git Repository

Disclaimer

The contents of this article are put together to the best of the authors' knowledge, but it cannot be guaranteed that it's always accurate in any environment. It is up to the reader to make sure that all information found in this article, does not do any damage to the reader's working environment or wherever this information is applied to. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, arising from, out of or in connection with this article. Please also note the information given on the Licences' page.