Lesson  in  Having fun with the Go source code

Compiling Go Without Changes

Compiling Go Without Changes

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:

  • Understand the Go build process and bootstrap concept
  • Successfully compile Go from source
  • Know how to explore Go source code structure
  • Know how to test your custom Go build

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. Take approximately 2-10 minutes depending on your system

Note: The first time you run this, it will take longer as it needs to compile everything from scratch.

What about all.bash and run.bash?

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

  • make.bash: Builds the Go toolchain only (what we're using)
  • run.bash: Runs the comprehensive test suite (requires Go to be built first)
  • all.bash: Convenience script that runs make.bash + run.bash + prints build info

For this workshop, make.bash is perfect because:

  • Faster build time means less waiting
  • We just need a working Go build for our experiments
  • We can run tests later if needed with run.bash

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:

  • src/: Contains the Go source code
    • src/cmd/: Command-line tools (go, gofmt, etc.)
    • src/runtime/: Go runtime system
    • src/go/: Go language packages (parser, AST, etc.)
  • test/: Test files for the Go language
  • api/: API compatibility data
  • doc/: Documentation

Examine the Go Compiler Structure

The Go compiler is located in src/cmd/compile/. Let's explore it:

cd src/cmd/compile
ls -la

Key files and directories:

  • main.go: Entry point of the compiler
  • internal/: Internal compiler packages
    • internal/syntax/: Lexer/parser (scanner, parser)
    • internal/types2/: Type checker
    • internal/ir/: Intermediate representation
    • internal/gc/: Code generation

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.

This shows the multi-stage bootstrap process:

  • The compiler is build with the go version installed in your system (toolchain1)
  • Then the compiler is built again using the toolchain1 to produce the toolchain2
  • Finally the toolchain3 is generated using the toolchain2.
  • The toolchain3 and toolchain2 should be identical

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:

  • go - The main Go command
  • gofmt - Go formatter
  • Other Go tools

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

  • Bootstrap Process: Go compiles itself using an existing Go installation
  • Go Source Structure: Well-organized codebase with clear separation (cmd/, runtime/, etc.)
  • Build Process: ./make.bash builds everything

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.

Previous lesson
Introduction and Setup
Next lesson
Adding the