Declaring programs, libraries, and per-binary settings
Define which executables and libraries to build, auto-discover sources, set per-binary compiler flags, and configure run arguments and working directories.
Use this file to discover all available pages before exploring further.
The framework uses a declarative pattern: you list output target names in BIN_PROGRAMS, BIN_LIBRARIES, or BIN_SOLIBRARIES, and for each name you configure a set of $(name)_* variables that control how that particular binary is found, compiled, and run. The GNUmakefile template ships with a fully annotated example for a single program that you can extend directly.
The BIN prefix comes from the default BINARY_PREFIXES+=BIN in main.mk. The framework derives the output directory from $(prefix)DIR, so BIN_* targets are placed in $(BINDIR).
The template below is the actual GNUmakefile shipped with the project. Each section is explained in detail after the listing.
# Filename: GNUmakefile / v2024.03.25-001, part of gcc-bison-flex-GNUmakefile# Includes the main.mk makefile of the GNU Make framework. If couldn't be found, it shall fail$(if $(MKFWK_MAIN_MAKEFILE),$(eval include $(MKFWK_MAIN_MAKEFILE)),$(eval include mkframework/main.mk))# Prevents GNU Make from even considering to remake this very same makefile, as that isn't necessary, thus optimizing startup time.PHONY: $(MKFWK_LAST_INCLUDING_DIR)GNUmakefile## This is an example for a single program set to be made and placed into $(BINDIR)# Basename for the programprogram1:=myprogram# Space-separated basenames of the programs to be made and placed into BINDIRBIN_PROGRAMS+=$(program1)# Executing the program with this makefile.# The arguments to pass to the program.$(program1)_ARGS:=# The working directory for the program. Alternatively, it can be left empty to use the current directory.$(program1)_CWD:=$(BINDIR)# Subdirectory to search for source files for the program. Alternatively, it can be left empty to use the current directory. By default: src/$(program1)_SRCDIR:=src/# Checks that the set directory above exists$(call mkfwk_make_check_set_directory_existence,$(program1)_SRCDIR)# Finds source files under the set directory above$(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?^\./??' ;)# Note: By default, the *find* command does recursive searchs. If you want a max depth of 1, you may add: ! -path '.' -prune# Sets the found source files as the program's source files$(program1)_SOURCES:=$($(program1)_FIND_SOURCES)# Sets the program's linking order. You may add the -l and -L linking flags here$(program1)_LDADD:=$($(program1)_SOURCES)# Add here the options to be passed to CC for the preprocessing phase for $(program1)$(program1)_CPPFLAGS=# Add here the options to be passed to CC for $(program1)$(program1)_CFLAGS=# Add here the options to be passed to CC for the assembling phase for $(program1)$(program1)_ASFLAGS=# Add here the options to be passed to CC for the linking phase for $(program1)$(program1)_LDFLAGS=# Subdirectory to search for *.h header files.# Alternatively, it can be left empty to use the current directory. By default: $($(program1)_SRCDIR)$(program1)_INCLUDEDIR:=$($(program1)_SRCDIR)# Checks that the set directory above exists$(call mkfwk_make_check_set_directory_existence,$(program1)_INCLUDEDIR)# Finds *.h header files and produces -I'dir' options correspondingly$(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'.'?" ;)) \$(if $(filter %.y,$($(program1)_SOURCES)),$(shell $(PRINTF) '%s\n' '$(subst $(SPACE),'$(SPACE)',$(patsubst %.y,$(OBJDIR)%,$(filter %.y,$($(program1)_SOURCES))))' | $(SED) -e 's?[^/]*$$??' -e 's?/$$??' -e "s?^?-I'?" -e "s?\$$?'?" -e "s?^-I''\$$?-I'.'?" ;))# Adds the produced -I'dir' options to be passed to CC for the preprocessing phase for $(program1)$(program1)_CPPFLAGS+=$($(program1)_FIND_-I_FLAGS)# Subdirectory to search for library files (lib*.a and lib*.so files).# Alternatively, it can be left empty to use the current directory. By default: $($(program1)_SRCDIR)$(program1)_LIBDIR:=$($(program1)_SRCDIR)# Checks that the set directory above exists$(call mkfwk_make_check_set_directory_existence,$(program1)_LIBDIR)# Finds library files and produces -L'dir' options correspondingly$(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'.'?" ;))# Adds the produced -L'dir' options to be passed to CC for the linking phase for $(program1)$(program1)_LDFLAGS+=$($(program1)_FIND_-L_FLAGS)### Parses the unexpanded canned directives of the MKFWK_FOOTER variable defined by the GNU Make framework$(eval $(value MKFWK_FOOTER))
The first non-comment line includes main.mk from the framework directory. The conditional form checks whether MKFWK_MAIN_MAKEFILE is already set (for nested makefiles); if not it falls back to the conventional mkframework/main.mk path.
2
Declare the local name variable
program1:=myprogram
Using a local variable (:= for immediate assignment) for the basename keeps the rest of the block readable and makes renaming a one-line change.
3
Register the target
BIN_PROGRAMS+=$(program1)
Append to BIN_PROGRAMS (or BIN_LIBRARIES / BIN_SOLIBRARIES) rather than assigning, so that multiple programs in the same file can coexist.
4
Set run arguments and working directory
$(program1)_ARGS:=$(program1)_CWD:=$(BINDIR)
These are used by make run-<name>. _ARGS is passed verbatim as shell arguments. _CWD is the working directory; it defaults to $(BINDIR) so the program runs from beside its binary.
_SRCDIR is the root used for source discovery. The mkfwk_make_check_set_directory_existence call is a parse-time check — if the directory does not exist, Make aborts immediately with an error instead of silently building an empty binary.
The find invocation recursively locates all *.c (excluding generated *.tab.c and *.lex.yy.c), *.y, and *.l files under _SRCDIR. The result is stored in _SOURCES. _LDADD defaults to _SOURCES which controls both what is compiled and the link order; you can append -l and -L flags here if needed.
These per-binary flag variables are layered on top of the global flags (CPPFLAGS, CFLAGS, etc.). Use += to extend rather than replace the global values.
The framework finds all *.h files under _INCLUDEDIR, extracts their directory paths, and produces -I'dir' flags that are appended to the per-binary _CPPFLAGS. If any .y source files are present, the corresponding $(OBJDIR) subdirectory (where Bison writes its .tab.h) is also added automatically.
The framework finds all lib*.a and lib*.so files under _LIBDIR, extracts their directory paths, and produces -L'dir' flags appended to the per-binary _LDFLAGS.
10
Evaluate MKFWK_FOOTER
$(eval $(value MKFWK_FOOTER))
This must be the last line of the file. It expands the canned footer directives defined by main.mk, which include all the footer makefiles that define the actual build, clean, run, and debug targets.
The same $(name)_* pattern applies to every entry in BIN_PROGRAMS, BIN_LIBRARIES, and BIN_SOLIBRARIES. Replace $(program1) with your chosen local variable holding the basename.
$(name)_SOURCES — source file list
A space-separated list of source files (*.c, *.y, *.l) that belong to this binary. Normally populated by the _FIND_SOURCES auto-discovery pattern, but you can assign it manually:
Controls the order in which compiled objects and libraries are passed to the linker. You can append -l and -L flags here. Defaults to $($(name)_SOURCES).
# Link against an extra library not found by auto-discovery$(program1)_LDADD:=$($(program1)_SOURCES) -lsqlite3
_LDADD is required for BIN_PROGRAMS and BIN_SOLIBRARIES. It is not used for BIN_LIBRARIES (static archives link all members unconditionally).
$(name)_SRCDIR — source search directory
Default:src/Root directory for auto-discovery of source files. Leave empty to search the top-level makefile directory. Must exist at parse time or Make aborts.
$(program1)_SRCDIR:=src/calculator/
$(name)_INCLUDEDIR — header search directory
Default:$($(name)_SRCDIR)Directory searched for *.h files to produce -I flags. Must exist at parse time. When .y sources are present, the corresponding $(OBJDIR) path for Bison’s generated header is also added automatically.
$(program1)_INCLUDEDIR:=include/
$(name)_LIBDIR — library search directory
Default:$($(name)_SRCDIR)Directory searched for lib*.a and lib*.so files to produce -L flags. Must exist at parse time.
Defaults: empty (before auto-discovered -I / -L flags are appended)Applied on top of the global and per-source-type flags for this binary only. Use += to extend, or = to set an additional value that stacks with the auto-discovered flags.
$(program1)_CPPFLAGS= # filled in by -I auto-discovery below$(program1)_CFLAGS=-Wno-unused-variable$(program1)_LDFLAGS= # filled in by -L auto-discovery below
$(name)_ARGS — run arguments
Default: emptyArguments passed to the program when running make run-<name>. The value is appended verbatim after the binary path in the shell invocation.
$(program1)_ARGS:=input.txt --verbose
$(name)_CWD — run working directory
Default:$(BINDIR)Working directory used when running make run-<name>, make gdb-<name>, and make valgrind-*-<name>. Leave empty to use the directory where make was invoked.
Keep each program’s block self-contained by using a local variable (:=) for the basename. This way you can copy and paste the entire block to add a second program, change the one local variable, and have everything work correctly.