Exercise 1: Compiling Go Without Changes

In this exercise, you’ll learn how to build the Go toolchain from the source code without making any modifications. This is an essential skill before we start making changes to the language!

Learning Objectives

By the end of this exercise, you will:

Step 1: Understanding the Bootstrap Process

Go is written in Go itself! This creates a “chicken and egg” problem - how do you compile Go without having Go? The solution is bootstrapping:

  1. The Go team provides pre-compiled binaries
  2. These binaries compile the current Go source code
  3. The newly compiled version can then be used for development

Let’s check if you have Go installed (needed for bootstrapping):

go version
# Must show version 1.24 or newer

⚠️ Critical: You must have Go 1.24 or newer installed to build Go 1.26.1. If you don’t have Go installed or your version is too old, install the latest version from https://golang.org/dl/

Step 2: Navigate to the Go Source Directory

cd go/src
pwd
# Should show: /path/to/workshop/go/src

# Verify you're on the correct Go version
git describe --tags
# Should show: go1.26.1

Step 3: Start the Build Process

Go provides different scripts for building. Let’s start with make.bash which builds the toolchain, then explore the source code while it’s running!

On Unix-like systems (Linux, macOS):

./make.bash

On Windows:

make.bat

This script will:

  1. Build the Go toolchain (compiler, linker, runtime, standard library)
  2. The first build compiles everything from scratch, so it will take approximately 2-10 minutes depending on your system
  3. Subsequent builds will be much faster, since only the modified files and their dependencies need to be recompiled

What about all.bash and run.bash?

You might wonder about other scripts in the src/ directory:

For this workshop, make.bash is perfect because:

Step 4: Explore Source Code While Building

While the build is running, open a new terminal or IDE and let’s explore the Go source code structure! This is a great time to understand what we’re building.

In your new terminal:

cd /path/to/workshop/go  # Navigate to your Go source directory
ls -la

Repository Structure

Key directories you should see:

Examine the Go Compiler Structure

Let’s take a closer look at src/cmd/compile/:

cd src/cmd/compile
ls -la

Key files and directories:

Step 5: Understanding the Build Output

Switch back to your original terminal where the build is running. As the build progresses, you should see output like:

Building Go cmd/dist using /usr/local/go. (go1.26.1 darwin/amd64)
Building Go toolchain1 using /usr/local/go.
Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.
Building Go toolchain2 using go_bootstrap and Go toolchain1.
Building Go toolchain3 using go_bootstrap and Go toolchain2.
Building packages and commands for darwin/amd64.

Let’s break down what each line means:

  1. Building Go cmd/dist using /usr/local/go: First, it builds dist, a small helper tool that manages the rest of the build process. It uses your system Go (/usr/local/go) to compile it.

  2. Building Go toolchain1 using /usr/local/go: Your system Go compiles the Go 1.26.1 compiler source code, producing toolchain1 — a first version of the new compiler, but built by an older Go version.

  3. Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1: Using toolchain1, it builds go_bootstrap, a minimal version of the go command needed to manage the next build steps.

  4. Building Go toolchain2 using go_bootstrap and Go toolchain1: Now toolchain1 compiles itself — the Go 1.26.1 compiler source is compiled again, but this time using the new compiler instead of your system Go. The result is toolchain2.

  5. Building Go toolchain3 using go_bootstrap and Go toolchain2: toolchain2 compiles the same source one more time to produce toolchain3. Since both toolchain2 and toolchain3 were built from the same source by equivalent compilers, they should produce identical binaries — this verifies the build is reproducible.

  6. Building packages and commands for darwin/amd64: Finally, it uses the verified toolchain to compile the standard library and all the Go tools (go, gofmt, etc.) for your platform.

Step 6: Locate Your Compiled Go Binary

After successful compilation, your new Go binary will be in:

ls -la /path/to/workshop/go/bin

You should see:

Step 7: Test Your Custom Go Build

Let’s test your newly compiled Go:

# Check version of your compiled Go
../bin/go version

Create a hello.go in the a temporary directory, for example /tmp.

package main

import "fmt"

func main() {
    fmt.Println("Hello from my custom Go build!")
}
# Compile and run with your custom Go
/path/to/workshop/go/bin/go run /tmp/hello.go

⚠️ Troubleshooting

GOROOT Interference

If running ../bin/go run /tmp/hello.go (or the full path to the binary) gives unexpected results or uses the system Go instead of your newly built one, you may need to unset the GOROOT environment variable first:

unset GOROOT
/path/to/workshop/go/bin/go run /tmp/hello.go

This happens because GOROOT may be set by your system Go installation, pointing the new binary to the wrong standard library and tools. Unsetting it lets the binary auto-detect its own root directory based on its location.

What We Learned

Next Steps

Congratulations! You now have a working Go toolchain built from source.

You can now proceed with any of the following exercises to learn about different parts of Go:

Or return to the main workshop to choose an exercise.