Skip to content

Manual Builds That Expose Every Layer

Manual commands are valuable because they reveal inputs and artifacts. They are not a recommendation to maintain a hundred-file application with shell history. Build tools automate the same graph introduced here.

Compile translation units separately

For a project with src/main.cpp, src/report.cpp, and include/report/report.hpp:

sh
c++ -std=c++20 -Iinclude -Wall -Wextra -Wpedantic -g -c src/report.cpp -o report.o
c++ -std=c++20 -Iinclude -Wall -Wextra -Wpedantic -g -c src/main.cpp -o main.o
c++ main.o report.o -o report-cli

-Iinclude changes header search during compilation. It does not link a library. -Ldirectory changes library search during linking; -lname requests a platform-named library such as libname.a or libname.so.

Quote includes normally search relative/project paths according to driver rules; angle includes normally represent configured/system include paths. Do not encode machine-specific absolute include directories in source.

Use c++ for a C++ final link so it supplies the correct C++ runtime and standard library. The order of archive libraries matters on common one-pass Unix-style linkers:

sh
c++ main.o -Lvendor/lib -lreport -o report-cli

The object that references a symbol appears before the archive expected to satisfy it. Build systems abstract this while preserving dependency order.

MSVC expresses analogous inputs differently:

powershell
cl /std:c++20 /W4 /EHsc /I include /c src\report.cpp
cl /std:c++20 /W4 /EHsc /I include /c src\main.cpp
link main.obj report.obj /OUT:report-cli.exe

Ordinarily cl can coordinate the final link as well.

Archives collect objects

sh
ar rcs libreport.a report.o
c++ main.o -L. -lreport -o report-cli

An archive index lets the linker select members satisfying unresolved references. ar packages objects; it does not compile them. MSVC uses lib.exe for static libraries.

Shared libraries are platform-specific products

On a typical ELF platform:

sh
c++ -std=c++20 -fPIC -Iinclude -c src/report.cpp -o report.o
c++ -shared report.o -o libreport.so

macOS uses dynamic-library flags and install names; Windows DLLs require exported symbols and commonly produce an import library. Copying this ELF recipe across platforms is not portability.

The consumer needs compile-time headers, link-time library metadata, and runtime access to the shared binary. RPATH/install-name policies, application bundles, or installers should make discovery intentional.

Macros configure translation

sh
c++ -DREPORT_TRACE=1 -DREPORT_LIMIT=100 ...

Every translation unit that depends on an ABI-affecting macro must agree. Conditional fields or calling annotations in public headers can silently make objects incompatible. Prefer generated configuration headers and target-level build definitions over ad hoc command differences.

Dependency files enable correct incremental builds

An object depends on its source and every included header. GCC-compatible drivers can emit Make-compatible dependency data:

sh
c++ -MMD -MP -c src/report.cpp -o report.o

This commonly writes report.d. A handwritten Makefile that tracks only .cpp timestamps will fail to rebuild after header changes.

Flags belong to purposes

  • Language contract: -std=c++20
  • Diagnostics: -Wall -Wextra -Wpedantic
  • Debug information: -g
  • Optimization: -O0, -O2, -O3, or size-oriented choices
  • Instrumentation: sanitizer and coverage flags
  • Code generation: architecture, visibility, PIC, runtime options
  • Preprocessor: -D, -I
  • Linker inputs/options: libraries and driver-forwarded linker flags

Do not copy an internet “ultimate flags” list. Flags differ by compiler and target; some change ABI or floating semantics.

Inspect artifacts

Use file to identify formats, nm to inspect symbols, objdump/readelf for ELF details, otool for Mach-O dependencies, and dumpbin for Windows objects. Ask focused questions: which architecture, is a symbol defined, which shared library is required, is debug information present?

Optional prompts

Debug: Adding -Ivendor/include does not fix an undefined reference. Why?

Answer: Include paths affect compilation and declarations. An undefined reference is a link problem requiring a matching object or library with a compatible symbol.

Explain: Why should a C++ final link normally use c++/g++/clang++?

Answer: The C++ driver supplies the standard library and runtime link defaults required by C++ objects.

Further reference