Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/fernandodanielmaqueda/gcc-bison-flex-GNUmakefile/llms.txt

Use this file to discover all available pages before exploring further.

The framework drives the complete transformation from raw source files to runnable programs or linkable libraries. Each stage of the pipeline is handled by a pattern rule defined in making.mk, and each stage can be made the final stopping point by setting an option variable — letting you inspect preprocessed output, assembly code, or object files without changing the recipe logic.

Source file types

The framework recognises three source file extensions:
ExtensionLanguageTool
.cC sourceCC (gcc by default)
.yYacc/Bison grammarYACC (bison by default)
.lLex/Flex scannerLEX (flex by default)
Each program, static library, or shared library lists its sources in a <name>_SOURCES variable. The framework infers which pipeline stages to run from the file extensions in that list.

Yacc/Bison pipeline

A .y grammar file is run through YACC before any C compilation happens:
$(OBJDIR)%.tab.c \
$(OBJDIR)%.tab.h \
$(OBJDIR)%.output: \
  %.y \
  | $$(call mkfwk_dirname,$$@)
	$(YACC) $(YFLAGS) $($<_YFLAGS) \
 -d -v -o'$(OBJDIR)$*.tab.c' '$<'
One invocation of bison produces three files in $(OBJDIR):
  • %.tab.c — the parser implementation in C
  • %.tab.h — token definitions, included by scanner code and user headers
  • %.output — a human-readable report of the parser states (with --report=state --report=itemset --report=lookahead by default)
The .tab.c file then feeds into the normal C compilation stages below.

Lex/Flex pipeline

A .l scanner file is processed by LEX:
$(OBJDIR)%.lex.yy.c: \
  %.l \
  | $$(call mkfwk_dirname,$$@)
	$(LEX) $(LFLAGS) $($<_LFLAGS) \
 -o'$@' '$<'
The output %.lex.yy.c in $(OBJDIR) also feeds into the C compilation stages. When .y files are also present, making.mk arranges for the corresponding .tab.h file to be a prerequisite of the .lex.yy.c compilation step via MKFWK_YDEFS, ensuring the token header exists before the scanner is compiled.

C compilation stages

After code generation, every .c file (including generated .tab.c and .lex.yy.c) passes through up to four sub-stages. Three option variables control where the pipeline stops:
1

Preprocessing (.c → .i)

Enabled by setting GENERATE_PREPROCESSING_OUTPUT=X. The compiler is called with -E to expand macros and #include directives, writing a .i file to $(OBJDIR):
$(CC) $(CPPFLAGS) $(CFLAGS) \
 -E -o'$(OBJDIR)$*.i' '$<' ...
When this is the final stage, dependency information is captured in the same invocation via -MMD -MF.
2

Compilation (.i or .c → .s)

Enabled by setting GENERATE_COMPILING_OUTPUT=X. The compiler is called with -S to emit assembly code, writing a .s file to $(OBJDIR):
$(CC) $(CFLAGS) \
 -S -o'$(OBJDIR)$*.s' '$<' ...
3

Assembly (.s or .c → .o)

Enabled by default (GENERATE_ASSEMBLING_OUTPUT=X). The compiler is called with -c to produce an object file in $(OBJDIR):
$(CC) $(CPPFLAGS) $(CFLAGS) $(ASFLAGS) \
 -c -o'$(OBJDIR)$*.o' '$<' ...
Dependency tracking happens here too: -MMD -MF appended to the same command writes $(DEPDIR)*.d and then $(DEPDIR)*.d.timestamp is touched to record that the .d file is up to date.
4

Linking (.o → program or library)

Object files are linked by a per-target explicit rule generated from mkfwk_rule_for_program, mkfwk_rule_for_static_library, or mkfwk_rule_for_shared_library.Programs (BIN_PROGRAMS) land in $(BINDIR) with $(EXEEXT) appended (.exe on Windows, empty elsewhere):
$(CC) $(CPPFLAGS) $(CFLAGS) $(ASFLAGS) $(LDFLAGS) \
 -o'$@' \
 $(patsubst %.c,$(OBJDIR)%.o,...) \
 $(LDLIBS) $(-ly if .y present) $(-lfl if .l present)
Static libraries (BIN_LIBRARIES) are archived with AR into $(BINDIR)libname.a:
$(AR) $(ARFLAGS) '$@' '$(filter %.o,$?)'
$(RANLIB) '$@'   # if ranlib is available
Shared libraries (BIN_SOLIBRARIES) are linked with -shared -Wl,-soname,name into $(BINDIR)libname.so. The -fpic flag is automatically added to all compilation steps for shared library sources.

Intermediate file handling

GNU Make normally deletes files it treats as intermediates. The framework prevents unwanted deletion in two ways depending on the REGENERATE_INTERMEDIATES option:
REGENERATE_INTERMEDIATESBehaviour
Set (default X)all explicitly depends on every intermediate file. Make keeps them and rebuilds them if they are removed.
UnsetIntermediate files are listed under .SECONDARY. Make never deletes them automatically, but does not rebuild them unless a final binary needs them.
The .DELETE_ON_ERROR special target is always active (set in base.mk), so a partially-written target file is removed if its recipe exits with a non-zero status.

Output directories

All generated files land in three directories, each configurable:
VariableDefaultContents
BINDIRbin/Programs (.exe on Windows) and libraries (.a, .so)
OBJDIRobj/Intermediate compiled files (.tab.c, .lex.yy.c, .i, .s, .o)
DEPDIR.deps/Dependency files (.d, .d.timestamp)
The framework automatically computes the full set of required subdirectories from the source paths and creates them with mkdir before any build step that needs them, using order-only prerequisites (| dir) on every rule.

The all target

all is always the .DEFAULT_GOAL. Its exact dependency list is built dynamically in central.mk:
# With REGENERATE_INTERMEDIATES set:
all: \
  $(MKFWK_INTERMEDIATE_TARGETS) \
  $(BIN_LIBRARIES) $(BIN_SOLIBRARIES) \
  $(BIN_PROGRAMS) ;

# Without REGENERATE_INTERMEDIATES:
all: \
  $(BIN_LIBRARIES) $(BIN_SOLIBRARIES) \
  $(BIN_PROGRAMS) ;
.SECONDARY: $(MKFWK_INTERMEDIATE_TARGETS) $(TARGET_DIRS)
MKFWK_INTERMEDIATE_TARGETS is computed by a sed script that maps each source path to its expected intermediate outputs (.tab.c/.tab.h/.output for .y, .lex.yy.c for .l, and .i/.s/.o for .c) based on which output options are currently enabled.

Pipeline summary

.y  ──YACC──▶  obj/%.tab.c  ──┐
               obj/%.tab.h    │
               obj/%.output   │

.c  ──────────────────────▶  obj/%.i  ──▶  obj/%.s  ──▶  obj/%.o  ──┐

.l  ──LEX───▶  obj/%.lex.yy.c  ──────────────────────────────────────┤

                                      ┌───────────────────────────────┘

                              bin/program          (programs)
                              bin/libname.a        (static libraries)
                              bin/libname.so       (shared libraries)
When GENERATE_PREPROCESSING_OUTPUT, GENERATE_COMPILING_OUTPUT, and GENERATE_ASSEMBLING_OUTPUT are all unset at once, the framework bypasses the intermediate .i/.s/.o files and passes source files directly to the linker in a single compiler invocation. In practice, the default configuration (GENERATE_ASSEMBLING_OUTPUT=X) always produces .o files.

Build docs developers (and LLMs) love