Setup#
C has no standard package manager, no “init a project” command,
and three competing build systems. The operator’s setup work is
picking a compiler, a build system, and a debugger, then writing
a Makefile or CMakeLists.txt once and reusing it.
Install#
1. Compiler.
gcc, the GNU Compiler Collection. The Linux default.
clang, the LLVM frontend. Better error messages and diagnostics; the default on macOS and increasingly on Linux.
$ sudo apt install build-essential gdb clang # Debian / Ubuntu
$ sudo dnf groupinstall "Development Tools" -y # Fedora / RHEL
$ xcode-select --install # macOS
2. Build system.
make for small and embedded work.
CMake for anything cross-platform or with dependencies.
Meson + Ninja for newer projects (faster than CMake).
3. Debugger.
gdb on Linux.
lldb on macOS (and increasingly Linux).
4. Static analyzer and linter.
clang-tidyclang --analyze
5. Verify.
$ gcc --version
$ clang --version
$ gdb --version
$ make --version
$ cmake --version
Setup project#
1. Lay out the source.
my-tool/
├── Makefile # or CMakeLists.txt
├── README.md
├── include/
│ └── my_tool.h
├── src/
│ ├── my_tool.c
│ └── main.c
└── tests/
└── test_my_tool.c
2. Minimal Makefile.
CC ?= cc
CFLAGS ?= -std=c11 -Wall -Wextra -Wpedantic -Werror -O2 -g
LDFLAGS ?=
INCLUDES = -Iinclude
SRC = $(wildcard src/*.c)
OBJ = $(SRC:.c=.o)
BIN = my-tool
$(BIN): $(OBJ)
^I$(CC) $(LDFLAGS) -o $@ $^
%.o: %.c
^I$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
clean:
^Irm -f $(OBJ) $(BIN)
.PHONY: clean
(Make uses literal tabs; the ^I markers above are stand-ins.)
3. Minimal CMakeLists.txt.
cmake_minimum_required(VERSION 3.20)
project(my_tool C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_compile_options(-Wall -Wextra -Wpedantic -O2 -g)
add_executable(my-tool src/main.c src/my_tool.c)
target_include_directories(my-tool PUBLIC include)
4. Build.
$ make # Make path
$ cmake -S . -B build && cmake --build build # CMake path
Strict#
Compile flags you want on every C project.
Flag |
Effect |
|---|---|
|
All routine warnings. |
|
Treat warnings as errors. |
|
Pin the standard. |
|
Force-multi-defined globals into errors (default in C23). |
|
libc-level overflow checks at runtime. |
|
Stack canaries. |
|
Address and UB sanitisers for the operator’s debug build. |
Common Tasks#
Generate ``compile_commands.json`` for IDE / lint tooling.
$ cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
$ ln -s build/compile_commands.json .
Run AddressSanitizer.
$ clang -fsanitize=address,undefined -g -O1 -o tool src/*.c
$ ./tool
Run static analysis.
$ cppcheck --enable=all --inconclusive src/
$ clang-tidy src/*.c -- -Iinclude
$ clang --analyze src/*.c
Open a core dump in gdb.
$ gdb --batch -ex 'thread apply all bt' tool core.1234
Cross-compile to ARM.
$ sudo apt install gcc-aarch64-linux-gnu
$ aarch64-linux-gnu-gcc -static -o tool-arm64 src/*.c
References#
Tooling for
gdb,valgrind,perf, sanitisers.man gcc,man clang,man make,man gdb.Cppreference, the community C and C++ reference.
The C Programming Language, Kernighan and Ritchie (K&R).