In modern software development, efficient build automation is crucial for a project's success. Makefiles, a popular build automation tool, provides developers of all stacks with a concise and powerful way to manage complex build processes.

By integrating and interoperating Makefiles with Go, you can automate repetitive tasks, streamline build processes and enhance productivity.

Understanding Makefiles

the popular makefile logo

Understanding how Makefiles work is crucial for efficiently organizing and automating your project builds.

Makefiles follow a specific structure and syntax for defining rules for building and maintaining projects. At the core, a Makefile consists of rules specifying targets, dependencies, and commands. Each rule begins with a target, followed by its dependencies and the commands needed to build or execute the target.

Targets represent the desired output files or actions for the Makefile. Targets can be filenames or labels that describe the task. For example, a target named clean may remove unnecessary files generated during the build process.

Dependencies are files or tasks required for building a target. If any dependencies are modified, the Makefile will rebuild associated targets to ensure that necessary parts of the project function. You’ll specify dependencies after the target, separated by spaces.

Commands are actions or shell commands that need execution to build or perform specific targets. Commands are usually written in a shell scripting language of the operating system.

Each command must begin with a tab character for recognition.

        build: main.go utils.go
    go build -o myapp main.go utils.go

clean:
    rm myapp

In the above Makefile, there are two targets: build and clean.

The build target depends on the main.go and utils.go files, and the associated command runs on the Go compiler to build an executable named myapp.

On the other hand, the clean target removes the myapp executable.

To compile your Go code, navigate to the working directory and run the make build command.

        make build

The Make tool will handle the compilation process, allowing you to focus on writing code.

Setting Up Makefiles for Your Go Projects

You can use Makefiles to build and compile your programs, perform code tests and quality checks, and for continuous integration and deployment.

Run this command to create a Makefile for your project in the root directory. The Makefile will serve as the entry point for defining your build tasks.

        touch Makefile

Your Makefile shouldn’t have any extensions.

After creating the Makefile, you can write commands, dependencies, and targets to the file for your project’s operations.

Here’s an example Makefile that serves as a build automation tool for your project:

        # Makefile

# Variables
GOCMD=go
GOBUILD=$(GOCMD) build
GOTEST=$(GOCMD) test
GOCLEAN=$(GOCMD) clean

# Targets
build:
    $(GOBUILD) -o app .

test:
    $(GOTEST) ./...

clean:
    $(GOCLEAN)
    rm -f app

You can customize your Makefile according to your project’s specific needs.

Here’s a modified version of the Makefile for passing additional flags and environment variables during your project’s test or build process:

        # Makefile

# Variables
GOCMD=go
GOBUILD=$(GOCMD) build
GOTEST=$(GOCMD) test
GOCLEAN=$(GOCMD) clean
BINARY_NAME=app
TEST_FLAGS=-v

# Targets
build:
    $(GOBUILD) -o $(BINARY_NAME) .

test:
    $(GOTEST) $(TEST_FLAGS) ./...

clean:
    $(GOCLEAN)
    rm -f $(BINARY_NAME)

In this Makefile, there are two new variables named BINARY_NAME and TEST_FLAGS. The BINARY_NAME variable specifies the name of the generated executable file.

The TEST_FLAGS variable allows you to specify additional flags when running tests (in this case, the -v flag enables verbose output during test execution).

Makefiles also provides an easy way to configure environment variables and flags for your project.

You can add these lines to your Makefile if you need to set the GOOS and GOARCH variables to build your project for a specific operating system or architecture.

        # Makefile

# Variables
GOCMD=go
GOBUILD=$(GOCMD) build
GOTEST=$(GOCMD) test
GOCLEAN=$(GOCMD) clean
BINARY_NAME=app
TEST_FLAGS=-v
GOOS=linux
GOARCH=amd64

# Targets
build:
    $(GOBUILD) -o $(BINARY_NAME) .

test:
    $(GOTEST) $(TEST_FLAGS) ./...

clean:
    $(GOCLEAN)
    rm -f $(BINARY_NAME)

In the updated Makefile, there are two new variables for the GOOS and GOARCH environment variables that allow you to specify the target operating system and architecture for your build (in this case, the GOOS variable is set to linux and GOARCH to amd64 to build the project for Linux on the x86-64 architecture).

Remember to adapt these variables based on your project’s specific requirements.

You can refer to the Go documentation for the list of supported values for GOOS and GOARCH if you need to build for a different platform.

Build Automation With Makefiles

Makefiles are also handy for compiling code, generating documentation, and managing dependencies.

With Makefiles, you can automate compilation time, saving you time and effort.

Here’s a Makefile that compiles a Go program:

        # Define variables
GOCMD = go
GOBUILD = $(GOCMD) build
BINARY_NAME = myprogram

# Default target
all: build

# Build target
build:
    $(GOBUILD) -o $(BINARY_NAME)

# Clean target
clean:
    rm -f $(BINARY_NAME)

The Makefile defines variables like the GOCMD (the Go command) and GOBUILD (the build command). The build target invokes the go build command to compile our Go program and produce the binary with the specified name (myprogram in this case). The clean target removes the generated binary from the project.

makefile in action for building programs

You can use Makefiles to automate dependency installation and keep your project’s external dependencies updated.

        # Define variables
GOCMD = go
GOBUILD = $(GOCMD) build
GOGET = $(GOCMD) get
BINARY_NAME = myprogram

# Default target
all: build

# Build target
build:
    $(GOBUILD) -o $(BINARY_NAME)

# Install dependencies
deps:
    $(GOGET) -u

# Clean target
clean:
    rm -f $(BINARY_NAME)

The Makefile has a deps target that uses the go get command to install or update project dependencies. You can run make deps to install your project’s dependencies.

Makefiles can automate documentation generation and improve your docs as code process.

Here’s how you can auto-generate documentation for your Go projects with the godoc tool and Makefile:

        # Define variables
GODOC = godoc
DOCS_DIR = docs

# Default target
all: docs

# Generate documentation
docs:
    $(GODOC) -html -dir . > $(DOCS_DIR)/index.html

# Clean target
clean:
    rm -rf $(DOCS_DIR)

In the Makefile, the docs target invokes the godoc command with the -html flag to generate HTML documentation for your go code and saves the documentation to the docs directory.

You’ll run the make docs command to generate up-to-date documentation for your Go project.

Makefiles Are Handy for Working With Unix-Based Operating Systems

By leveraging the flexibility of Makefiles, you can define custom build commands and execute complex tasks within minutes. Makefile saves time and ensures consistency and reproducibility across build environments.

Another advantage of Makefiles is its compatibility with Unix-based systems. Make is a widely supported utility on Unix-based operating systems, including Linux and macOS, making it a portable and reliable tool for developers.