3.10.25

Exercise 28: Intermediate Makefiles in C


Hi! Today I’m gonna show you how to make a solid C project skeleton using Makefiles. Think of Makefiles like cooking recipes for your code—they list the ingredients (source files), the steps to prepare the dish (compiling and linking), and even how to clean up the kitchen (removing build junk). In this post, we’ll go step by step through Exercise 28: Intermediate Makefiles from Learn C the Hard Way. By the end, you’ll have a reusable skeleton that keeps your projects neat, organized, and super easy to build.

Project Skeleton Structure

what your project will look like?


c-skeleton/
├── LICENSE          # License for your project
├── README.md        # Project description (Markdown)
├── Makefile         # The magic recipe
├── bin/             # Where executables go
├── build/           # Where build artifacts go
├── src/             # Your source code (.c, .h)
│   └── dbg.h        # Debugging header (from Ex19)
└── tests/           # Automated tests

  • LICENSE → Defines usage rights.

  • README.md → Basic project instructions.

  • Makefile → The brain of the build system.

  • bin/ → Holds compiled programs.

  • build/ → Holds libraries/object files.

  • src/ → Your C source files.

  • tests/ → Automated test programs.

The Makefile Explanation 

a reference Makefile used in this project:

CFLAGS=-g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(OPTFLAGS)
LIBS=-ldl $(OPTLIBS)
PREFIX?=/usr/local

SOURCES=$(wildcard src/**/*.c src/*.c)
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))

TEST_SRC=$(wildcard tests/*_tests.c)
TESTS=$(patsubst %.c,%,$(TEST_SRC))

TARGET=build/libYOUR_LIBRARY.a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))

all: $(TARGET) $(SO_TARGET) tests

dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all

$(TARGET): CFLAGS += -fPIC
$(TARGET): build $(OBJECTS)
	ar rcs $@ $(OBJECTS)
	ranlib $@

$(SO_TARGET): $(TARGET) $(OBJECTS)
	$(CC) -shared -o $@ $(OBJECTS)

build:
	@mkdir -p build
	@mkdir -p bin

.PHONY: tests
tests: CFLAGS += $(TARGET)
tests: $(TESTS)
	sh ./tests/runtests.sh

clean:
	rm -rf build $(OBJECTS) $(TESTS)
	rm -f tests/tests.log
	find . -name "*.gc*" -exec rm {} \;
	rm -rf `find . -name "*.dSYM" -print`

install: all
	install -d $(DESTDIR)/$(PREFIX)/lib/
	install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/

check:
	@echo Files with potentially dangerous functions.
	@egrep '[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)
	|stpn?cpy|a?sn?printf|byte_)' $(SOURCES) || true

Sections in the Makefile

1. Header Section

  • CFLAGS → Compiler options (debugging, warnings, optimizations).

  • LIBS → Linking libraries.

  • PREFIX → Default install path (/usr/local).

  • SOURCES & OBJECTS → Automatically detect .c files and create .o list.

2. Build Targets

  • all → Default target (builds everything).

  • dev → Developer build with extra warnings.

  • $(TARGET) → Builds static library .a.

  • $(SO_TARGET) → Builds shared library .so.

3. Directories

  • build: → Ensures build/ and bin/ exist.

4. Unit Tests

  • tests: → Compiles and runs unit tests.

  • Needs a helper script runtests.sh.

5. Cleaner

  • clean: → Removes build files, test logs, and compiler junk.

6. Install

  • install: → Copies library to system directory.

7. Checker

  • check: → Warns if dangerous C functions (strcpy, etc.) are used.

Unit Test Script

Create a file tests/runtests.sh:



#!/bin/sh

echo "Running unit tests:"

for i in tests/*_tests
do
    if test -f $i
    then
        if $VALGRIND ./$i 2>> tests/tests.log
        then
            echo $i PASS
        else
            echo "ERROR in test $i: see tests/tests.log"
            tail tests/tests.log
            exit 1
        fi
    fi
done

echo ""

This script runs all tests and reports if something fails.

Commands You Can Try

  • Clean build files:

make clean
  • Run security checks:

make check
  • Build everything:

make
  • Install library (needs root):

sudo make install

Extra Credit

  • Add a .c and .h file in src/ to test building.

  • Explore the regex in check: to learn about unsafe C functions.

  • Read more about automated unit testing in C.


No comments:

Post a Comment

rating System

Loading...

A Friendship Story of Learning Data Structures with C

Sr. No. DSU BLOGS CHAPTERS 1 Array Operations in C, The Group of Friendship (Create, Insert, Delete ...