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.

For the common case of a C project with no parser or scanner generation, the framework requires only a few declarations: the program name, its source directory, and the closing MKFWK_FOOTER expansion. Everything else — object files, dependency tracking, the bin/ and obj/ output directories — is handled automatically.

When to use this approach

Use a pure-C setup when your project contains only .c and .h files. There is no need to involve Bison or Flex variables unless .y or .l source files are present. The framework will discover your .c files, compile each to obj/, and link the final binary into bin/.

Project layout

After running make all for the first time, the framework creates the directories it needs. A typical project looks like this:
myproject/
├── GNUmakefile
├── mkframework/        # the framework directory
├── src/
│   ├── main.c
│   └── util.c
├── bin/                # created automatically
│   └── myprogram
└── obj/                # created automatically
    ├── src/
    │   ├── main.o
    │   └── util.o
    └── ...
The bin/ and obj/ directories are set by BINDIR and OBJDIR in main.mk. You can override them before the MKFWK_FOOTER expansion if you need a different layout.

Setting up the GNUmakefile

1

Copy the framework into your project

Place the mkframework/ directory at the root of your project, next to your GNUmakefile:
cp -r /path/to/gcc-bison-flex-GNUmakefile/mkframework ./
2

Include main.mk

The first line of your GNUmakefile includes the framework header. The conditional handles the case where the framework path is overridden via the MKFWK_MAIN_MAKEFILE environment variable:
$(if $(MKFWK_MAIN_MAKEFILE),$(eval include $(MKFWK_MAIN_MAKEFILE)),$(eval include mkframework/main.mk))
This line must come before any program declarations.
3

Declare your program

Use a local variable to hold the program basename, then add it to BIN_PROGRAMS:
program1 := myprogram
BIN_PROGRAMS += $(program1)
BIN_PROGRAMS is a space-separated list. You can add more programs later by appending additional basenames.
4

Set the source directory and discover sources

Point the program’s _SRCDIR to the directory containing your .c files, then use the framework’s FIND command to discover them:
$(program1)_SRCDIR := src/
$(call mkfwk_make_check_set_directory_existence,$(program1)_SRCDIR)

$(program1)_FIND_SOURCES := $(shell $(FIND) \
    $(if $($(program1)_SRCDIR),'$($(program1)_SRCDIR)',.) \
    -type f \( \( -name '*.c' ! -name '*.tab.c' ! -name '*.lex.yy.c' \) \
              -o \( -name '*.y' -o -name '*.l' \) \) \
    -print | $(SED) -e 's?^\./??' ;)

$(program1)_SOURCES := $($(program1)_FIND_SOURCES)
$(program1)_LDADD   := $($(program1)_SOURCES)
_SOURCES is the list of source files to compile. _LDADD controls the linking order — for a simple program it mirrors _SOURCES, but you can reorder or add library flags here.
By default the FIND command searches recursively. To limit to a single directory level, append ! -path '.' -prune to the expression.
5

Configure include and library paths

The framework can auto-discover -I and -L flags from your source tree. For a pure-C project the header search usually points at the same directory as your sources:
$(program1)_INCLUDEDIR := $($(program1)_SRCDIR)
$(call mkfwk_make_check_set_directory_existence,$(program1)_INCLUDEDIR)

$(program1)_FIND_-I_FLAGS := $(sort \
    $(shell $(FIND) \
        $(if $($(program1)_SRCDIR),'$($(program1)_INCLUDEDIR)',.) \
        -type f -name '*.h' -print \
      | $(SED) -e 's?^\./??' -e 's?[^/]*$$??' -e 's?/$$??' \
               -e "s?^?-I'?" -e "s?$$?'?" \
               -e "s?^-I''$$?-I'.'?" ;))

$(program1)_CPPFLAGS += $($(program1)_FIND_-I_FLAGS)
This produces -I'src/' (or whichever subdirectories contain .h files) and appends them to the per-program _CPPFLAGS.
6

Set per-program flags (optional)

You can set preprocessor, compiler, assembler, and linker flags per program. These are merged with the global CPPFLAGS, CFLAGS, ASFLAGS, and LDFLAGS when the binary is built:
$(program1)_CPPFLAGS =    # additional -D / -U flags
$(program1)_CFLAGS   =    # additional compiler flags
$(program1)_ASFLAGS  =    # additional assembler flags
$(program1)_LDFLAGS  =    # additional linker flags
7

Set run arguments and working directory (optional)

The _ARGS variable passes command-line arguments when you use make run-myprogram. _CWD sets the working directory the program is launched from:
$(program1)_ARGS :=
$(program1)_CWD  := $(BINDIR)
8

Expand MKFWK_FOOTER

The final line of your GNUmakefile must always be:
$(eval $(value MKFWK_FOOTER))
This parses all the framework’s build, run, and debug rules. Nothing after this line is processed by the framework.

Complete annotated GNUmakefile

Here is the full template for a single-program pure-C project, based directly on the upstream GNUmakefile:
# Include the framework header
$(if $(MKFWK_MAIN_MAKEFILE),$(eval include $(MKFWK_MAIN_MAKEFILE)),$(eval include mkframework/main.mk))

# Prevent Make from trying to remake this file
.PHONY: $(MKFWK_LAST_INCLUDING_DIR)GNUmakefile

# ── Program declaration ──────────────────────────────────────────────────────

program1 := myprogram
BIN_PROGRAMS += $(program1)

# Run-time settings
$(program1)_ARGS :=
$(program1)_CWD  := $(BINDIR)

# ── Sources ──────────────────────────────────────────────────────────────────

$(program1)_SRCDIR := src/
$(call mkfwk_make_check_set_directory_existence,$(program1)_SRCDIR)

$(program1)_FIND_SOURCES := $(shell $(FIND) \
    $(if $($(program1)_SRCDIR),'$($(program1)_SRCDIR)',.) \
    -type f \( \( -name '*.c' ! -name '*.tab.c' ! -name '*.lex.yy.c' \) \
              -o \( -name '*.y' -o -name '*.l' \) \) \
    -print | $(SED) -e 's?^\./??' ;)

$(program1)_SOURCES := $($(program1)_FIND_SOURCES)
$(program1)_LDADD   := $($(program1)_SOURCES)

# ── Flags ────────────────────────────────────────────────────────────────────

$(program1)_CPPFLAGS =
$(program1)_CFLAGS   =
$(program1)_ASFLAGS  =
$(program1)_LDFLAGS  =

# ── Include path auto-discovery ──────────────────────────────────────────────

$(program1)_INCLUDEDIR := $($(program1)_SRCDIR)
$(call mkfwk_make_check_set_directory_existence,$(program1)_INCLUDEDIR)

$(program1)_FIND_-I_FLAGS := $(sort \
    $(shell $(FIND) \
        $(if $($(program1)_SRCDIR),'$($(program1)_INCLUDEDIR)',.) \
        -type f -name '*.h' -print \
      | $(SED) -e 's?^\./??' -e 's?[^/]*$$??' -e 's?/$$??' \
               -e "s?^?-I'?" -e "s?$$?'?" \
               -e "s?^-I''$$?-I'.'?" ;))

$(program1)_CPPFLAGS += $($(program1)_FIND_-I_FLAGS)

# ── Library path auto-discovery ──────────────────────────────────────────────

$(program1)_LIBDIR := $($(program1)_SRCDIR)
$(call mkfwk_make_check_set_directory_existence,$(program1)_LIBDIR)

$(program1)_FIND_-L_FLAGS := $(sort \
    $(shell $(FIND) \
        $(if $($(program1)_SRCDIR),'$($(program1)_LIBDIR)',.) \
        -type f \( -name 'lib*.a' -o -name 'lib*.so' \) -print \
      | $(SED) -e 's?^\./??' -e 's?[^/]*$$??' -e 's?/$$??' \
               -e "s?^?-L'?" -e "s?$$?'?" \
               -e "s?^-L''$$?-L'.'?" ;))

$(program1)_LDFLAGS += $($(program1)_FIND_-L_FLAGS)

# ── Footer (must be last) ────────────────────────────────────────────────────

$(eval $(value MKFWK_FOOTER))

Minimal src/main.c example

#include <stdio.h>

int main(void) {
    printf("Hello from myprogram\n");
    return 0;
}

Building and running

# Build all declared programs
make all

# Run the program (launches from BINDIR by default)
make run-myprogram

What VERBOSE output looks like

With VERBOSE=X (the default), each build step is surrounded by angle-bracket markers:
<<< CC: Regenerating the program: "bin/myprogram" >>>
<<< Done >>>
Each individual compilation step is similarly wrapped:
<<< CC: Regenerating the .o file: "obj/src/main.o" >>>
<<< Done >>>
To suppress these messages, override VERBOSE on the command line:
make all VERBOSE=

Common build adjustments

Disable debug symbols, enable optimization

CC_DEBUG=X is on by default, which passes -g3 and -DDEBUG=1 to the compiler. Turn it off and add -O2:
make all CC_DEBUG= CFLAGS+="-O2"
Or set CFLAGS permanently in your GNUmakefile after the include line:
CFLAGS += -O2
CC_DEBUG =

Preview what would be rebuilt

Set MUST_MAKE=X to print which targets are out of date without actually building them:
make all MUST_MAKE=X
The output lists each target that would be remade along with its newer dependencies.

Adding -I include paths manually

The _FIND_-I_FLAGS mechanism auto-generates paths from header locations, but you can also add paths directly:
$(program1)_CPPFLAGS += -I'include/'
Add this line after the _FIND_-I_FLAGS assignment so it is appended rather than overwriting the auto-discovered flags.

Next steps

Bison and Flex project

Add a parser and scanner to your project.

Libraries

Build and link static or shared libraries.

Multi-program project

Declare multiple programs in a single GNUmakefile.

Build targets reference

All available make targets for building.

Build docs developers (and LLMs) love