Multi-Binary Makefile for Multiple Executables

Create a Makefile that builds multiple executables from separate source directories, using foreach and call functions for DRY target generation.

Build Patterns

Detailed Explanation

Building Multiple Binaries

Projects that produce multiple executables (e.g., a server and a CLI client) need a Makefile that avoids duplicating build rules for each binary.

Using foreach

BINARIES = server client worker
BIN_DIR  = bin

all: $(addprefix $(BIN_DIR)/,$(BINARIES))

$(BIN_DIR)/%: cmd/%/main.go
	@mkdir -p $(BIN_DIR)
	go build -o $@ ./cmd/$*/...

The addprefix function prepends bin/ to each binary name. The % pattern matches any binary name, and $* gives the matched stem. This single rule builds all three binaries from their respective cmd/ directories.

Using define and call (Advanced)

define BUILD_BINARY
$(BIN_DIR)/$(1): $$(wildcard cmd/$(1)/*.go)
	@mkdir -p $(BIN_DIR)
	go build -o $$@ ./cmd/$(1)/...
endef

$(foreach bin,$(BINARIES),$(eval $(call BUILD_BINARY,$(bin))))

The define block creates a template. The call function substitutes $(1) with each binary name. The eval function treats the result as Makefile syntax. Note the $$ escaping — variables inside define that should be expanded during rule execution (not during eval) need double-dollar escaping.

Separate Build Configurations

server: CFLAGS += -DSERVER_MODE
client: CFLAGS += -DCLIENT_MODE

server client: %: src/%.c src/common.c
	$(CC) $(CFLAGS) -o $(BIN_DIR)/$@ $^

Target-specific variable assignments let you add flags for individual binaries without affecting others. The static pattern rule %: src/%.c applies only to the listed targets.

Key Techniques

  • addprefix and addsuffix manipulate lists of file names
  • Static pattern rules (targets: pattern: prereqs) restrict pattern matching
  • Target-specific variables provide per-target configuration
  • The foreach/eval/call pattern generates rules dynamically

Use Case

Building a microservices monorepo where multiple Go or C binaries share common source code but need separate executables with different build configurations.

Try It — Makefile Generator

Open full tool