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.cfiles and create.olist.
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:→ Ensuresbuild/andbin/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
.cand.hfile insrc/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