scripts/rules.mk
¶
This is the main file of the build system, and it contains the generic rules and macros used by all the Makefiles. The behaviour of this file is modified by the macros:
TOOL
: Selects the toolchain used for building.
SYS
: Selects the target system for building.
ARCH
: Selects the target architecture for building.
KBUILD_DEFCONFIG
: Selects the Kconfig file used for configuration.
All the Makefiles are able to include this file based in the macro PROJECTDIR
,
as it is the first thing defined in any Makefile:
PROJECTDIR = ../
include $(PROJECTDIR)/scripts/rules.mk
rules.mk
defines the common locations used by all the Makefiles:
INCDIR
: Directory for include files.
BINDIR
: Directory for binary executables.
LIBDIR
: Directory for libraries.
SCRIPTDIR
: Directory for shared scripts.
These locations are used in several places, for example, in the generic rules for the -I options. Every rule can be customized by the following macros:
PROJ_CPPFLAGS
: Flags used by the preprocessor.
PROJ_CFLAGS
: Flags used by the C compiler.
PROJ_LDFLAGS
: Flags used by the linker.
PROJ_ASFLAGS
: Flags used by the assembler.
PROJ_ARFLAGS
: Flags used by the library archiver.
PROJ_RLFLAGS
: Flags used by the library randomizer.
PROJ_LDLIBS
: Flags used by the linker to include libraries.
All these macros are built based on four categories:
MORE_XXXX
: Flags added by specific Makefiles.
SYS_XXXX
: Flags defined for the target system (such as Linux or Nuttx).
TOOL_XXXX
: Flags defined for the toolchain (such as gnu or clang).User flags: Flags controlled by the user to customize the build:
CFLAGS
: For the compiler command.
LDFLAGS
: For the linker command.
LIBS
: For the additional libraries added to the linker command.
ARFLAGS
: For the library archiver command.
RLFLAGS
: For the library index builder command.
ASFLAGS
: For the assembler command.
For example, a Makefile can do:
PROJECTDIR = ../
include $(PROJECTDIR)/scripts/rules.mk
MORE_CPPFLAGS = -I./local_include
and all the objects compiled by that Makefile will include that option.
In the same way, a user can run the make with:
make CFLAGS=-g
and all the objects will be compiled adding the option -g
to the C compiler flags.
rules.mk
also defines the rules to maintain the recursive structure.
The macro DIRS
must be defined by the Makefile before including rules.mk
and then make will apply automatically the recursive targets to them.
For example:
PROJECTDIR = ../
DIRS = libevp-agent libevp-app-sdk
include $(PROJECTDIR)/scripts/rules.mk
all: libevp-agent libevp-app-sdk
defines libevp-agent
and libevp-app-sdk
as recursive targets and
when the target all is required to build then
make will move unconditionally to them and
it will build the target all on them.
In the same way,
when the special targets clean
and distclean
are invoked,
the build system iterates unconditionally to the directories specified in DIRS
,
invoking the target on each directory,
before invoking the target in the current directory.
rules.mk
contains the more common clean
and distclean
actions that
usually are required by the Makefiles,
and the Makefiles don't have to define them
unless they need special actions. For example:
clean:
rm -f MQTT-C/*.o
will remove all the artifacts generated in the current directory and it will remove all the objects in the MQTT-C directory.
Toolchains and cross compilation¶
The toolchain is selected by the Make macro TOOL
and it defines all the macros required for a specific toolchain.
The file scripts/rules.mk
defines the default values of all the tools using:
CXX = $(CROSS_COMPILE)$(COMPXX)
CC = $(CROSS_COMPILE)$(COMP)
AS = $(CROSS_COMPILE)$(ASM)
LD = $(CROSS_COMPILE)$(LINKER)
AR = $(CROSS_COMPILE)$(ARCHIVE)
CPP = $(CROSS_COMPILE)$(PRECOMP)
NM = $(CROSS_COMPILE)$(NAMES)
RANLIB = $(CROSS_COMPILE)$(RLIB)
OBJCOPY = $(CROSS_COMPILE)$(OCOPY)
OBJDUMP = $(CROSS_COMPILE)$(ODUMP)
All the tools are defined prepending the macro CROSS_COMPILE
,
which is used for cross compilation.
For example,
if the build is for linux aarch32 and musl libc using a PC
which has installed a GNU arm toolchain for
that configuration it can cross compile using something like:
make config
make TOOL=gnu CROSS_COMPILE=arm-linux-musleabi- ARCH=armel
which will use the cross compiler and will also select the desired
target architecture for Toolchain for WASM modules.
The first make
execution with the target config
configures the build (see config target for the config target
and Architecture definition for the definition of the ARCH
macro).
Every toolchain is expected to define the following macros:
COMPXX
: C++ compiler.
COMP
: C compiler.
ASM
: Assembler program.
LINKER
: Linker program.
ARCHIVE
: Program used to create library archives.
PRECOMP
: C preprocessor.
NAMES
: Nm compatible program.
RLIB
: Ranlib compatible program.
OCOPY
: Objcopy compatible program.
ODUMP
: Objdump compatible program.
Optionally, toolchains can define the following macros
TOOL_CFLAGS
: Flags added by the toolchain to compile C files.
TOOL_CXXFLAGS
: Flags added by the toolchain to compile C++ files.
TOOL_LDFLAGS
: Flags added by the toolchain to to link programs.
TOOL_ASFLAGS
: Flags added by the toolchain to assembly files.
TOOL_ARFLAGS
: Flags added by the toolchain to create libraries.
TOOL_RLFLAGS
: Flags added by the toolchain to create library indexes.
TOOL_LDLIBS
: Libraries added by the toolchain to link programs.
TOOL
can take one of the following values:
gnu
: It is the default value and defines all the values for the GNU toolchain.
clang
: Defines all the values for the clang toolchain.
wasi
: Defines all the values for the Toolchain for WASM modules used to compile to wasm.
cppcheck
: Toolchain that extends the GNU toolchain to perform static analysis.
For example, if static analysis using cppcheck is required, it is possible to run:
make config
make TOOL=cppcheck analysis
The gnu
and clang
toolchains can be customized using two macros:
SANITIZER
: Setting it toENABLED
enables the sanitizer options.
COVERAGE
: It can select a coverage tool for the build process.
ccov
: Coverage using clang options.
gcov
: Coverage using gcc options.
While ccov
is tied to clang,
gcov
can be used with both toolchains in some systems.
Both coverage tools add a coverage target
that generates the coverage information.
For example:
make config
make TOOL=clang SANITIZER=ENABLED COVERAGE=ccov test
make TOOL=clang COVERAGE=ccov coverage
will compile and execute the tests (see test target).
with the sanitizer options and
with the clang coverage instrumentation
and the last make
execution with the coverage
target
generates a directory called coverage
that contains all the html and coverage information.
The ccov
coverage tool is also used in the ci
and contains a special target coverage-ci
that
filters the coverage information to cover only the libevp-agent
library.
Architecture definition¶
ARCH
is not usually required,
because it is usually derived from the output of uname
,
but it cannot be derived in that way for cross compilation
or in systems that lack uname
.
In that case it is required and
the list of accepted values for ARCH
is:
x86_64
: System V AMD64 ABI.
i386
: System V Intel386 ABI.
aarch64
: System V Arm 64 bit ABI.
armel
: System V Arm 32 bit ABI with hardware floating point.
armhf
: System V Arm 32 bit ABI with software floating point.
xtensa
: Xtensa ESP32 ABI.
System definition¶
As there are some build flags that depend of the target system
the build system can be customized using the macro SYS
.
This macro is usually sets by default using uname
,
but it can be required in some systems lacking that tool or
when cross compilation is used.
The target build systems supported are:
posix
: It defines the required options for fully POSIX complaint systems.
nuttx
: It defines the required options for NuttX.
wasm
: It defines the required options compiling for WASM target (used by modules).
Default rules¶
There is a set of rules
that are shared between all the Makefiles
and rules.mk
contains the common definition for all of them.
FORCE:
.PHONY: FORCE
The target FORCE
can be used in any rule to force a build of the target.
It brings the same behavior of the common extension rule .PHONY
,
but it is pure POSIX without needing the GNU extension.
.s.o:
$(AS) $(PROJ_ASFLAGS) $< -o $@
This rule generates an object file from an assembly file without applying the C preprocessor, while the rule
.S.o:
$(CPP) $(PROJ_CPPFLAGS) $< | $(AS) $(PROJ_ASFLAGS) -o $@
uses the C preprocessor before assembling the file.
It contains rules to generate an object file from a C or C++ file:
.c.o:
$(CC) $(PROJ_CFLAGS) -o $@ -c $<
.cpp.o:
$(CC) $(PROJ_CXXFLAGS) -o $@ -c $<
It has a rule that can be used to generate an executable elf file from an object of the same name
.o.elf:
$(CC) $(PROJ_LDFLAGS) -o $@ $< $(PROJ_LDLIBS)
that for example will generate hello
from hello.o
.
It also contains a special rule that compiles a C file into a wo
file
that is required
when native and wasm applications are required in the same directory:
.c.wo:
$(CC) $(PROJ_CFLAGS) -o $@ -c $<
.wo.wasm:
$(CC) $(PROJ_LDFLAGS) -o $@ $<
rules.mk
also contains a set of rules for debugging:
.c.s:
$(CC) $(PROJ_CFLAGS) -S -o $@ $<
.c.i:
$(CPP) $(PROJ_CPPFLAGS) -o $@ $<
.o.dump:
trap "rm -f $$$$.dump" EXIT QUIT INT TERM;\
$(OBJDUMP) -D $< > $$$$.dump && mv $$$$.dump $@
.elf.dump:
trap "rm -f $$$$.dump" EXIT QUIT INT TERM;\
$(OBJDUMP) -D $< > $$$$.dump && mv $$$$.dump $@
.o.lst:
trap "rm -f $$$$.lst" EXIT QUIT INT TERM;\
$(NM) $< > $$$$.lst && mv $$$$.lst $@
.elf.lst:
trap "rm -f $$$$.lst" EXIT QUIT INT TERM;\
$(NM) $< > $$$$.lst && mv $$$$.lst $@
.a.lst:
trap "rm -f $$$$.lst" EXIT QUIT INT TERM;\
$(NM) -A $< > $$$$.lst && mv $$$$.lst $@
Allowing such actions as:
Generating an assembly file from a C file.
Generating the output of the C preprocessor.
Generating a disassembly of an object file.
Generating a dissasembly of an elf file.
Generating a symbol list form an object.
Generating a symbol list form an elf file.
Generating a symbol list from a library archive.
It also defines rules for common targets,
for example clean
and distclean
,
which will have common command lines between
the different directories,
removing the generated artifacts by the previous commented rules.
They also consider the definition of the macro DIRS
and apply them in a recursive way.
It also contains a few rules that makes easier the integration with CMake:
# CMake rules
%/build/Makefile: %/CMakeLists.txt
CC=$(CC) \
CXX=$(CXX) \
SYS=$(SYS) \
ARCH=$(ARCH) \
CFLAGS="$(CFLAGS)" \
MBEDTLS_CFLAGS="$(MBEDTLS_CFLAGS)" \
$(SCRIPTDIR)/cmake-$* $(PWD)/$(PROJECTDIR)
%: %/build/Makefile FORCE
cd $@/build && $(MAKE) install
that enables actions such as:
# cmake dependencies
wasm-micro-runtime: wasm-micro-runtime/build/Makefile
flatcc: flatcc/build/Makefile
mbedtls: mbedtls/build/Makefile
that will generate a chain of dependencies
which will compile correctly a CMake project
containing a CMakeList.txt
file.
Personal configuration¶
The build system allows a personal configuration file,
useful in same cases where it can be very tedious to pass always all the parameters,
and for that reason
it tries to include the file config.mk
(which can be customized by the user) from the top level directory.
This file can include multiple definitions, for example:
WASI_PREFIX=/opt/wasi-sdk/bin/
CFLAGS=-g -Og
TOOL=clang
KBUILD_DEFCONFIG=configs/unit-test-all-hubs-wasm.config
defining the value of the macros
WASI_PREFIX
,
CFLAGS
,
TOOL
and KBUILD_DEFCONFIG
for all the Makefiles
(see config target and Toolchains and cross compilation for the meaning of these macros),
usable for debug purposes.