CS210 · Block 1 · Lesson 7

Make and the Build Process

Multi-file projects · #include with quotes · The four phases of compilation · Makefiles · Targets, dependencies, recipes · Variables · Incremental rebuilds
At a glance: This is your first multi-file project. You learn how a C program built from several source files turns into one executable, why you would write a Makefile to automate the process, and how to write one. The hands-on practice happens through the Makefile walkthrough and the error-matching self-check in Section 5; the lab applies it to a small project you extend with a new function.
Contents
  1. Lesson Objectives
  2. Assigned Readings
  3. Pre-Class Work
  4. Lesson Materials and Overview
  5. In-Class Work
  6. After Class
1. Lesson Objectives

By the end of this lesson, cadets will be able to:

  1. Describe the four phases of C compilation (preprocess, compile, assemble, link). (Outcome 2)
  2. Use #include with a project header file to access declarations from another translation unit. (Outcome 2)
  3. Write a basic Makefile with targets, dependencies, and recipes. (Outcome 7)
  4. Use Make variables to manage compiler flags and source files. (Outcome 7)
  5. Use make to build a multi-file C program from provided source files. (Outcome 7)
2. Assigned Readings

Complete before class. The MTBE sections introduce Makefile syntax; you will write one in the walkthrough.

Source Sections Why
MTBE Getting Started through Variables The syntax of targets, dependencies, recipes, and variables
Beej-C Chapter 4 review (Functions, prototypes) Optional. Refresh prototypes; they appear in this lesson's headers.
3. Pre-Class Work
Do this first
Plan on 30 to 45 minutes. The reading is short; the bigger pre-class job is taking Quiz 2 in Blackboard, which covers Lessons 3 through 6 and is due before class today.

Pre-Class Checklist

1
Read the MTBE sections (Getting Started through Variables)
Focus on the vocabulary; you will write Makefile rules during class. Pay attention to:
  • Targets, prerequisites, and recipes (the three parts of a rule)
  • Why recipe lines must begin with a literal TAB character
  • How make uses file timestamps to decide what to rebuild
  • Variables like CC and CFLAGS
2
Take Quiz 2 in Blackboard
Covers Lessons 3 through 6: operators, standard libraries, conditionals, loops, and functions. Due before class. This is separate from the Lesson 7 reflection you will take after class.
3
Verify your toolchain still works
Compile and run any small C file you already have. If gcc complains or your terminal does not behave, fix it before class. The lab today builds a project; toolchain issues will block you.
Run:
$ which gcc && gcc --version | head -1
$ which make && make --version | head -1
If either is missing or shows an error, schedule EI before class.
4. Lesson Materials and Overview

This lesson sits on top of everything from Lessons 1 through 6, but the topic is new: how a real C project is built. The reference material below covers the concepts you will see in lecture and use in the Makefile walkthrough.

Jump to
Slides Multi-file programs The four phases Makefile syntax Variables Incremental rebuilds Code style and tooling

Slides

The Lesson 7 deck walks through multi-file programs, the four phases of compilation, Makefile syntax, variables, and the incremental rebuild story. The walkthrough in Section 5 is where you practice writing one.

Open Lesson 7 slides →

Multi-file C programs

Up to now you have written one .c file at a time. Real C projects are not one file. They split into many. The reasons are practical: a file with thousands of lines is hard to read; functions that belong together belong in their own file; teams of cadets (or engineers) cannot all edit the same file at once.

When code is split across files, three pieces of vocabulary appear.

Piece What it does
.c file (source) Contains function definitions — the actual code that runs.
.h file (header) Contains function declarations (prototypes) so other files can call those functions.
.o file (object) The compiled output of one .c file. Not yet a runnable program; the linker glues several of them together.

A typical file pair looks like this. greet.h declares what greet.c provides:

// greet.h — the declaration
const char *make_greeting(const char *name);
// greet.c — the definition
#include "greet.h"

const char *make_greeting(const char *name) {
  return "Hello, friend!";
}

When another file wants to call make_greeting, it includes the header:

// main.c
#include <stdio.h>   // library header — angle brackets
#include "greet.h" // project header — quotes

int main(void) {
  printf("%s\n", make_greeting("cadet"));
  return 0;
}

The include-form rule. Quotes for project headers (search the source directory first), angle brackets for library headers (search system include paths). Mixing these up gives you No such file or directory errors.

Lesson 20 will revisit multi-file design properly: include guards, what belongs in which file, the static keyword for file scope. For now the goal is to use a multi-file project well enough to build it with Make.

The four phases of C compilation

When you type gcc hello.c -o hello, gcc actually runs four steps under the hood. Knowing the names helps you read error messages.

Phase Input → Output What happens
Preprocess .c → .i #include directives are replaced with the contents of header files. Macros expand.
Compile .i → .s C source becomes assembly language for your CPU. Most compile errors happen here (syntax, type mismatches).
Assemble .s → .o Assembly becomes machine code in an object file. Object files cannot run on their own; they are puzzle pieces.
Link .o files → exe One or more object files plus standard libraries get combined into an executable. undefined reference errors happen here.

Why this matters for reading errors. If gcc says expected ';' before something, that is a compile error. If gcc says undefined reference to 'foo', that is a linker error. They have different causes and different fixes. The error-matching self-check in Section 5 trains you to tell them apart.

The -c flag tells gcc to stop after the assemble phase, producing a .o file. Without -c, gcc does the whole pipeline including linking, which is why a single gcc hello.c can produce a runnable program directly.

Makefile syntax

A Makefile is a file named exactly Makefile (capital M, no extension) that lives in your project directory. It contains rules. Each rule has three parts.

target: prerequisite1 prerequisite2
→TAB←recipe to build the target
  • Target — the file you want to build (e.g., greet.o or program).
  • Prerequisites (also called dependencies) — the files the target depends on. If any of these are newer than the target, the recipe runs.
  • Recipe — the shell command(s) that build the target. Recipe lines must start with a literal TAB character. Spaces will not work. This is the most famous Makefile gotcha.

A complete (tiny) Makefile that builds program from hello.c:

program: hello.c
gcc -Wall -Werror hello.c -o program

Run make with no arguments and it builds the first target listed. Run make clean to invoke a target named clean, which conventionally removes generated files.

Variables: CC and CFLAGS

Two variables appear in almost every C Makefile. They are conventional, not built into Make itself, so you have to define them yourself.

CC = gcc
CFLAGS = -Wall -Werror

program: hello.c
$(CC) $(CFLAGS) hello.c -o program

CC is the C compiler to use. CFLAGS is the list of flags to pass to it. To use the variable inside a recipe, write $(CC) or $(CFLAGS) (the dollar sign and parentheses are required).

Course standard
In CS210, every CFLAGS must include -Wall and -Werror. -Wall turns on warnings; -Werror promotes warnings to errors so they cannot be ignored. The lab autograder checks for both.

Why Make exists: incremental rebuilds

A project with 6 source files needs at least 7 gcc commands to build from scratch (one compile per .c, one link). Doing that by hand every time you change one line is tedious and error-prone. Worse, most of the work is wasted: only the file you changed actually needs to recompile.

make uses file timestamps to figure out the minimum work. For each rule, if any prerequisite is newer than the target, the recipe runs. Otherwise it is skipped. Walk the rules in dependency order and you get the shortest possible rebuild.

From-scratch build of a 4-module project
5 commands fire: 4 compiles + 1 link.
Edit one .c file, run make again
2 commands fire: 1 compile + 1 link.
Edit a .h file used by 3 modules
4 commands fire: 3 compiles (everyone that #included the header) + 1 link.

Header dependencies matter. Make only knows about dependencies you list explicitly. If your rule says main.o: main.c but main.c #includes mathy.h, then changes to mathy.h will not trigger a rebuild of main.o. The result is a build that compiles fine but produces a broken executable, because main.o was generated from a stale view of mathy.h. List every header in every .o rule's prerequisites.

All of this becomes concrete in the Makefile walkthrough in Section 5.

Code style and tooling

The DFCS C Programming Standard is the course style guide. You have been following it implicitly since Lesson 1 (comment header block, line length, naming, brace placement). Starting this lesson, the formatting rules from Section 6 (Indentation, Delimiters, Whitespace) are enforced by tooling rather than by memory.

Two configuration files now ship with every CS210 lab template:

File What it does
.clang-format Encodes the Section 6 rules: 4-space indentation, open brace on the same line as the statement, one space between operators and operands, 100-character line cap. Your editor reformats your code on save.
.editorconfig Universal editor settings: spaces (not tabs) in .c and .h files, LF line endings, final newline at end of file. The Makefile gets tabs (Make needs them).

Turn on Format on Save in your editor and the formatter takes over. You will not lose points on Section 6 rules from this lesson forward.

The content rules in the standard still apply: variable and function names (Section 5), no magic numbers or global variables or single-line ifs (Section 7), defensive programming (Section 8). No formatter can check those for you. See the Resources section for the full DFCS C Programming Standard.

5. In-Class Work

Three things to do in class. Use the checklist to track your progress.

  • Work through the Makefile walkthrough below. Ten guided tasks: build a 4-module project by hand, write a Makefile, refactor with variables, see incremental rebuilds. About 25 minutes.
  • Take the error-matching self-check. Five scenarios. For each, read the source and the error and pick the cause. Score 4 of 5 to earn the passing completion code. About 10 minutes.
  • Complete Lab 07: Make a Multi-File Project. Add one new function to a provided 4-module codebase, then write a Makefile that builds it. Auto-graded through GitHub Classroom.

Makefile Walkthrough

Ten tasks across four sections. Start by compiling and linking a 4-module project by hand. Then write a Makefile that automates the build, refactor it with variables, and watch incremental rebuilds save work.

Before you begin

Enter your name. It will appear on the completion screen so you can include it in your screenshot for the Lesson 7 reflection.

CS210 Lesson 7 · Makefile Walkthrough

Build a multi-file C project by hand, then let Make do the work. Type one shell command or Makefile rule per turn. The shell on the left shows what Make does; the right pane shows your Makefile and the dependency graph.
Task 0 of 10
Section 1: Manual compilation
Current task
Loading...
Your Makefile will appear here as you build it.
$
Enter submits. Shift+Enter inserts a newline for multi-line Makefile rules (target line, then TAB and recipe).
How this works: The shell is simulated. It understands a small set of commands (gcc, make, touch, ls, cat, rm, ./program) and your Makefile rules. Only the expected input advances the walkthrough so the project state stays predictable. Mistype twice and the hint button will pulse. After the walkthrough completes, free mode unlocks so you can experiment.

Error-Matching Self-Check

Five scenarios. Each shows code or a Makefile snippet, the command that ran, and the error you got back. Pick the cause. Reading errors and matching them to causes is a learnable skill.

Before you begin

Enter your name. It will appear on the score screen so you can include it in your screenshot for the Lesson 7 reflection.

CS210 Lesson 7 · Error-Matching Self-Check

Five scenarios. Each shows code or a Makefile snippet, the command that ran it, and the error you got back. Pick the cause. The skill being practiced is reading the error and matching it to what actually went wrong.
How this works: One scenario per card. Pick the cause and click Submit. Answers are locked once submitted, so think before submitting. Score at least 4 out of 5 to earn the passing completion code.
6. After Class
  • Take the Lesson 7 reflection in Blackboard. Enter the two completion codes from the Makefile walkthrough and the error-matching self-check, then answer the reflection prompt. Attach a screenshot of the walkthrough completion screen.
  • Finish Lab 07 if you did not complete it in class. Submit through GitHub Classroom before Lesson 8.
  • Heads up: Lesson 8 is GR1. It covers Lessons 1 through 7. Review the in-class material and your lab Makefile; both are fair game.
  • If anything in the walkthrough or the lab is still fuzzy, schedule EI before GR1.
Need help? Schedule EI with your instructor. Bring specific code or specific screenshots, and the exact error message or output you do not understand. Specific questions get unstuck quickly.