Dependency Parables
- Two opening stories illustrate the concept of “dependencies” before any code appears.
- Yak-shaving fable
- Goal: wash car ➔ need hose ➔ toll bridge ➔ neighbour’s toll-pass ➔ must return neighbour’s cushion ➔ cushion stuffed with yak hair ➔ end up shaving a yak at the zoo.
- Moral: ask whether every dependency (step) is necessary; sometimes you should “just pay the toll.”
- “There’s a Hole in My Bucket” (Henry & Liza)
- Sequential complaints and fixes:
- Hole in bucket ➔ fix with straw
- Straw too long ➔ cut with axe
- Axe too blunt ➔ sharpen with stone
- Stone too dry ➔ wet with water
- Demonstrates cyclic or chained dependencies.
- Connection to software: every #include, module import or library link is a dependency.
- Excessive or circular dependencies create needless work, delay and complexity.
What Make Is For
- make = Unix utility that automates compilation by analysing file timestamps and declared dependencies.
- Mandatory tooling for C/C++ projects at Adelaide Uni; foundational to more modern build systems (e.g. CMake, Meson, Bazel, Ninja).
- Two reasons to recompile a file:
- The file itself changed (newer timestamp).
- A prerequisite it relies on changed.
- Without make:
- Naïve manual compile:
- Example:
gcc file1.c file2.c … file20.c -o My_Exe
- Risks: forget files (file14), typos (fi1e17.c) → broken builds.
- Lazy wildcard compile:
gcc *.c -o My_Exe
- Works only if every *.c truly belongs to this executable.
- Fails when multiple programs share directories or files.
- Full rebuilds waste time. Even projects with 20 files begin to exhibit noticeable delays; million-line codebases can take hours or days to build from scratch.
Core Mechanics of Make
- Operates through a text file named exactly
Makefile
(no extension) in the working directory.- Alternative: invoke with
make -f CustomFile
to use a differently named file.
- Command
make
without arguments executes the first target in the Makefile (the “default”). - Each rule has three parts:
- Target (red in slides) – the file to build.
- Prerequisites / Dependencies (blue) – files the target relies on.
- Recipe / Command (green) – shell commands to produce the target.
- Whitespace matters: the recipe line must start with a real , not spaces.
Minimal Example
my_exe: my_code.c my_code.h
gcc my_code.c -o my_exe
- Meaning:
- Re-build
my_exe
if it does not exist OR if my_code.c
or my_code.h
is newer. - Invoked by
make
(if it’s the first rule) or make my_exe
.
Utility Targets
- Recipes can run any shell commands; they need not produce the file named by the target.
- Conventional example:
clean:
rm *.o my_exe
- Used to delete intermediates; invoked with
make clean
.
Layered / Recursive Dependencies
Illustrative Makefile:
clean:
rm *.o my_exe
my_exe: driver.o quack.o
gcc driver.o quack.o -o my_exe
driver.o: driver.cpp
gcc -c driver.cpp
quack.o: quack.cpp quack.h
gcc -c quack.cpp
Process from scratch:
make my_exe
finds driver.o
& quack.o
missing ⇒ descends into their rules.gcc -c
compiles each source into object files (*.o
).- Final link step produces
my_exe
.
Rebuild scenario:
- Change only
quack.h
➔ quack.o
is out-of-date ➔ only quack.o
and my_exe
are rebuilt; driver.o
is untouched. Saves compilation time.
Visual Dependency Trees
- Hierarchical graph: leaf nodes are headers / source; roots are executables.
- Altering one file triggers rebuilds only along paths that reference it.
- Shared dependencies (a header used in two sub-trees) cause multiple branches to rebuild.
- Upgrading a standard header like
stdio.h
would cascade everywhere, exemplifying large rebuild costs.
Custom Makefile Names
- Syntax:
make -f <my_weird_make_file> <targets>
.- Example:
make -f QuackMake my_exe
.
Built-in Shortcuts & Symbols
- Provide terse recipes but can obscure clarity.
- @ → the target name.
- ^ → the full list of prerequisites.
- < → the first prerequisite.
*
→ shell-style wildcard in prerequisite list.%
→ pattern wildcard inside target name.
Examples:
my_exe: file1.c file2.c file3.c
gcc $^ -o $@
%.o: %.c
gcc -c $<
- First rule links three object files into an executable using @/^.
- Second rule is a template: any
.o
depends on a same-named .c
; compiles with only the first prereq via <.
Multiple targets, same rule:
alpha beta: common.h
@echo Building $@
- Recipe runs twice, once for
alpha
, once for beta
.
Macros / Variables
- Define once, reuse everywhere.
CC = gcc
CFLAGS = -Wall -O2
%.o: %.c
$(CC) $(CFLAGS) -c $<
- Later switching to
clang
requires editing only the variable. - Syntax $(NAME) or ${NAME} (both legal, mildly confusing).
Practical Guidance & Caveats
- Make’s historical evolution = “hack-on-hack,” originally written by a student intern; still standard but replaced in industry by higher-level generators.
- IDEs & build systems auto-write Makefiles; understanding them lets you debug when auto-generation fails.
- Danger zones:
- Over-relying on wildcards (
*
, %
) may inadvertently compile or link unwanted files. - Incorrect timestamps (e.g. extracted archives, version-control checkout with old dates) can trick make into skipping necessary rebuilds.
- Cyclic dependencies can cause infinite rebuild loops or ambiguous rules.
Philosophical / Ethical Reflection
- Yak-shaving metaphor warns against needless work created by poor planning of dependencies—applies to life and software.
- Always weigh cost vs. benefit: sometimes re-architecting (paying the toll) is cheaper than stringing more abstractions.
- Clean, minimal dependency graphs aid maintainability, shorten build times, and reduce energy consumption—an ecological plus.
Numerical & Statistical Nuggets
- Small student programs (
- Header change causing global rebuild demonstrates highly nonlinear compile cost: O(N) files touched vs. O(1) for isolated modules.
Commands & Flags To Memorise
make
– build default (first) target.make <target>
– build specific target.make -f File
– use alternative makefile.gcc -c file.c
– compile to object without linking.gcc a.o b.o -o exe
– link objects into executable.
Study Checklist
- Explain difference between target, prerequisite, recipe.
- Demonstrate writing a clean target.
- Show how make decides whether to rebuild.
- Use @, ^, < correctly in context.
- Recognise pitfalls of wildcards and auto-generated rules.
- Describe object files and linking.
- Recount yak-shaving story as a mnemonic for dependency minimisation.
- Compare make to modern tools (CMake, Ninja).