================= Make Build System ================= Currently, NuttX supports both CMake and Make build systems. This guide explains the NuttX ``make``-based build system. Due to *requirements, constraints, and the complexity of the build process*, NuttX divides this work into multiple files, each handling specific parts of the build process. As stated in :doc:`/introduction/inviolables`, multiple platforms should be supported: - :ref:`win_mk`: handles windows platform support. - :ref:`unix_mk`: handles unix-like platforms support. NuttX supports multiple build modes. See :doc:`/guides/protected_build`: - :ref:`flatlibs_mk`: Kernel and user-space built into a single ``blob``. - :ref:`protectedlibs_mk`: Kernel and user-space built as two separate ``blobs``. - :ref:`kernelibs_mk`: Kernel built into single ``blob``. User apps must be loaded into memory for execution. NuttX targets multiple libs, or ``silos``, each handling its own compilation: .. note:: Gregory Nutt has a nice presentation about `NuttX architecture `_ There the ``silo`` concept is explained. Only the ``silos`` there are listed below as libs. The build mode influences the needed libs. .. code-block:: console $ ls -l staging/ drwxr-xr-x 2 xxx xxx 4096 Oct 6 16:02 . drwxr-xr-x 27 xxx xxx 4096 Oct 6 16:02 .. -rw-r--r-- 1 xxx xxx 323640 Oct 6 16:02 libapps.a -rw-r--r-- 1 xxx xxx 384352 Oct 6 16:02 libarch.a -rw-r--r-- 1 xxx xxx 62182 Oct 6 16:02 libbinfmt.a -rw-r--r-- 1 xxx xxx 6468 Oct 6 16:01 libboards.a -rw-r--r-- 1 xxx xxx 2820054 Oct 6 16:02 libc.a -rw-r--r-- 1 xxx xxx 161486 Oct 6 16:01 libdrivers.a -rw-r--r-- 1 xxx xxx 981638 Oct 6 16:02 libfs.a -rw-r--r-- 1 xxx xxx 224446 Oct 6 16:02 libmm.a -rw-r--r-- 1 xxx xxx 2435746 Oct 6 16:01 libsched.a -rw-r--r-- 1 xxx xxx 51768 Oct 6 16:02 libxx.a .. _verbosity: Verbosity --------- The ``V`` variable can be passed to ``make`` to control the build verbosity. - **Quiet (Default):** The build output is minimal. - **Verbose (`V=1 V=2`):** Shows the full compiler commands *(enables command echo)*. - **Verbose Tools (`V=2`):** Enables verbose output for tools and scripts. .. code-block:: console # V=1,2: Enable echo of commands $ make V=1 # V=2: Enable bug/verbose options in tools and scripts $ make V=2 Build Process ------------- .. note:: Keep the configuration step and board folder layout short. Separate docs should be created. The ``Make`` based build process starts with the NuttX tree configuration. This is done by running ``tools/configure.sh`` script. The configuration step, prepares the NuttX kernel tree, setting the board specific arch, chip and board files. The ``Make`` build system refers the needed subsystems using *generic* naming: - The *current* architecture is refered as ``arch`` - The *current* chip is refered as ``chip`` - The *current* board is refered as ``board`` These *generic* names are mapped to the *actual* names by symlinks: - The *current* chip directory gets symlinked to ``nutt/include/arch/chip``. - The *current* board directory gets symlinked to ``nutt/include/arch/board``. - The *current* arch directory gets symlinked to ``nutt/include/arch``. The board config is stored as ``defconfig`` file, which is a minimal config, storing only the configs that differs from default config values. Due to NuttX's particularity of strict dependance to the ``app`` directory, the ``.config`` file is not generated by either ``kconfiglib`` or ``kconfig-frontends``, but rather by the an in-tree ``tools/process_config.sh`` script. This script takes a "base" input file (the boards ``defconfig`` file), additional include paths (the most relevant being the ``apps`` top directory), and generate an output file (the ``$(TOPDIR)/.config`` file). .. code-block:: console # part of configure.sh shell script, starting at line 240 # # src_config=${configpath}/defconfig # dest_config="${TOPDIR}/.config" # original_config="${TOPDIR}/.config.orig" # backup_config="${TOPDIR}/defconfig" $ ln -sf ${src_makedefs} ${dest_makedefs} || \ { echo "Failed to symlink ${src_makedefs}" ; exit 8 ; } $ ${TOPDIR}/tools/process_config.sh -I ${configpath}/../../common/configs \ -I ${configpath}/../common \ -I ${configpath} -I ${TOPDIR}/../apps \ -I ${TOPDIR}/../nuttx-apps \ -o ${dest_config} ${src_config} Starting the Build ^^^^^^^^^^^^^^^^^^ The root **Makefile** is the build process entrypoint. Its main job is to check for a ``.config`` file and include the appropriate **host-specific Makefile**. The "actual" first steps of the build process are handled by the host-specific **Makefile** (either ``Win.mk`` or ``Unix.mk``). Early on, during parsing, both host-specific **Makefile** will also include - board's ``Make.defs`` file mentioned above. This will include also - the main ``.config`` file. - the tools ``config.mk`` file. - the arch ``toolchain.defs`` file. - based on the build mode, one of the following files: - :ref:`flatlibs_mk` - :ref:`protectedlibs_mk` - :ref:`kernelibs_mk` - :ref:`directories_mk` Built-in dependency mechanism ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. note:: The documentation for :ref:`mkdeps` should be extended. Lots of details are missing. Part of the documentation here should be moved. NuttX implements a built-in dependency mechanism. See :ref:`mkdeps`. This mechanism uses ``gcc`` to generate a ``*.d`` like dependency files that can be included by the **Makefile** to track file dependencies. This mechanism is kick-started by the ``depend`` target, and targets specific directories in the NuttX tree based on the build mode. It is *required* by both ``pass1dep`` and ``pass2dep``. .. code-block:: makefile pass1dep: context tools/mkdeps$(HOSTEXEEXT) tools/cnvwindeps$(HOSTEXEEXT) $(Q) for dir in $(USERDEPDIRS) ; do \ $(MAKE) -C $$dir depend || exit; \ done pass2dep: context tools/mkdeps$(HOSTEXEEXT) tools/cnvwindeps$(HOSTEXEEXT) $(Q) for dir in $(KERNDEPDIRS) ; do \ $(MAKE) -C $$dir EXTRAFLAGS="$(KDEFINE) $(EXTRAFLAGS)" depend || exit; \ done Both ``pass1dep`` and ``pass2dep`` sets different directories and orders in which the ``depend`` target is ran. See :ref:`directories_mk`. Building NuttX libs ^^^^^^^^^^^^^^^^^^^ The host-specific **Makefile** will not build the required NuttX libs. It will defer work by "recursively" calling make in each of the directories listed in the ``$(USERLIBS)`` and ``$(NUTTXLIBS)`` variables. pass1 & pass2 ^^^^^^^^^^^^^ The NuttX binary is always generated by running both ``pass1`` and ``pass2`` targets. The actual dependencies of the mentioned targets may vary depending on the build mode. Different NuttX build modes will not influence the "execution" of the ``pass1`` and ``pass2`` targets, but rather will influence the dependencies pulled by those targets. - ``pass1`` target depends on the ``$(USERLIBS)``. - ``pass2`` target depends on the ``$(NUTTXLIBS)``. .. code-block:: makefile all: pass1 pass2 pass1: $(USERLIBS) pass2: $(NUTTXLIBS) The content of the ``$(USERLIBS)`` and ``$(NUTTXLIBS)`` variables is defined in each build mode makefile. See :ref:`build_modes` above. Staging the libs ^^^^^^^^^^^^^^^^ After compiling libraries defined by the ``$(USERLIBS)`` and ``$(NUTTXLIBS)`` at the previous step, the ``make`` build system will ``install`` them at a special ``staging/`` directory, residing at the root of the NuttX tree. These libraries are passed to the the final target rule (``$(BIN)``). .. code-block:: makefile # Unix.mk : line 536 $(BIN): pass1 pass2 # ... $(Q) $(MAKE) -C $(ARCH_SRC) EXTRA_OBJS="$(EXTRA_OBJS)" LINKLIBS="$(LINKLIBS)" APPDIR="$(APPDIR)" EXTRAFLAGS="$(KDEFINE) $(EXTRAFLAGS)" $(BIN) # ... .. _win_mk: Win.mk ------ Although targeting different platforms, both **Win.mk** and **Unix.mk** aim to produce the same output. The need for independent files is due to the differences in the platform's approaches. Forward vs Back slashes ^^^^^^^^^^^^^^^^^^^^^^^ One of the main differences is the use of forward slashes (``/``) on unix-like platforms versus backslashes (``\``) on windows ${HOSTEXEEXT} ${HOSTDYNEXT} ^^^^^^^^^^^^^^^^^^^^^^^^^^^ These variables are used by the build system to configure the executable suffix required by the used platform. They are defined in :ref:`config_mk`. For windows platform: - ``${HOSTEXEEXT}`` is set to ``.exe``. - ``${HOSTDYNEXT}`` is set to ``.dll``. Symbolic Linking ^^^^^^^^^^^^^^^^ For the windows platform, the build system handles symbolic links differently. .. _unix_mk: Unix.mk ------- Versioning ^^^^^^^^^^ The build system will impact versioning if NuttX is cloned as a repo. See :ref:`versioning`. config.h, .config, mkconfig ^^^^^^^^^^^^^^^^^^^^^^^^^^^ NuttX's build system defers the ``config.h`` generation to a separate tool called ``mkconfig``. See :ref:`makefile_host`. .. code-block:: makefile tools/mkconfig$(HOSTEXEEXT): prebuild $(Q) $(MAKE) -C tools -f Makefile.host mkconfig$(HOSTEXEEXT) The ``include/nuttx/config.h`` recipe calls the ``mkconfig`` executable generated by the rule above to create the ``config.h`` file from the current ``.config`` file. Symlinks & dirlinks ^^^^^^^^^^^^^^^^^^^ Dirlinks are symbolic links that allow the build system to use generic paths while pointing to architecture-specific, chip-specific, or board-specific directories. This enables a single build system workflow across many different hardware configurations. - Symlink ``arch//include`` to ``include/arch`` - Symlink ``boards////include`` to ``include/arch/board`` - Symlink ``arch//include/`` to ``include/arch/chip`` - Symlink ``boards///`` to ``arch//src/board/`` .. note:: Some boards make use of a ``common`` directory. In that case: - ``boards///common`` is symlinked to ``arch//src/board`` - ``boards///`` is symlinked to ``arch//src/board/`` - Symlink ``arch//src/`` to ``arch//src/chip`` The ``.dirlinks`` file itself is just a timestamp marker that indicates all dirlinks have been created. Dummies ^^^^^^^ The main reason for the use of dummies is to handle some specific scenarios, such as external code bases, custom chips and boards or to overcome tooling limitations. If any of the features below are not used, the build system will fallback to a dummy. - **${EXTERNALDIR}** Possible values for ``$(EXTERNALDIR)`` are ``external`` or ``dummy``. NuttX code base can be extended by using ``$(TOPDIR)/external/`` directory. The build system searches for a ``Kconfig`` file in that directory. If found, the build system defines the ``EXTERNALDIR`` variable to ``external`` and also appends another lib (``libexternal``) to the build process. .. code-block:: makefile # External code support # If external/ contains a Kconfig, we define the EXTERNALDIR variable to 'external' # so that main Kconfig can find it. Otherwise, we redirect it to a dummy Kconfig # This is due to kconfig inability to do conditional inclusion. EXTERNALDIR := $(shell if [ -r $(TOPDIR)/external/Kconfig ]; then echo 'external'; else echo 'dummy'; fi) - **dummy/Kconfig** The ``dummy/Kconfig`` is used to handle custom chips and boards. If in-tree chip/board is used, the build system will resolve to dummy_kconfig files. - ``$(CHIP_KCONFIG)`` is set to ``$(TOPDIR)$(DELIM)arch$(DELIM)dummy$(DELIM)dummy_kconfig`` - ``$(BOARD_KCONFIG)`` is set to ``$(TOPDIR)$(DELIM)boards$(DELIM)dummy$(DELIM)dummy_kconfig`` If custom chip/board is used, the build system will resolve to their custom paths. .. code-block:: makefile # Copy $(CHIP_KCONFIG) to arch/dummy/Kconfig arch/dummy/Kconfig: @echo "CP: $@ to $(CHIP_KCONFIG)" $(Q) cp -f $(CHIP_KCONFIG) $@ # Copy $(BOARD_KCONFIG) to boards/dummy/Kconfig boards/dummy/Kconfig: @echo "CP: $@ to $(BOARD_KCONFIG)" $(Q) cp -f $(BOARD_KCONFIG) $@ - **boards/dummy.c** A special ``boards/dummy.c`` file is used by the build system to generate a useless object. The purpose of the useless object is to assure that libboards.a/lib is created. Some archivers (ZDS-II, SDCC) require a non-empty library or they will generate errors. .. _build_modes: Build Modes ^^^^^^^^^^^ As specified above, NuttX supports multiple build modes. The build mode is selected based on specific ``Kconfig`` options. .. code-block:: makefile # Library build selections # # NUTTXLIBS is the list of NuttX libraries that is passed to the # processor-specific Makefile to build the final NuttX target. # USERLIBS is the list of libraries used to build the final user-space # application # EXPORTLIBS is the list of libraries that should be exported by # 'make export' is ifeq ($(CONFIG_BUILD_PROTECTED),y) include tools/ProtectedLibs.mk else ifeq ($(CONFIG_BUILD_KERNEL),y) include tools/KernelLibs.mk else include tools/FlatLibs.mk endif The content of each file referenced above is documented in its own section. - tools/ProtectedLibs.mk :ref:`protectedlibs_mk` - tools/FlatLibs.mk :ref:`flatlibs_mk` - tools/KernelLibs.mk :ref:`kernelibs_mk` .. _config_mk: Config.mk --------- ``Config.mk`` contains common definitions used by many configuration files. * It defines the logic behind the :ref:`verbosity` * It resolves the platform specific particularities, such as * build *tools* ``e.g. mkdeps, cnvwindeps, link.sh/.bat, unlink.sh/.bat`` * file extensions ``e.g. .exe, .dll for windows, .o, .a for unix`` * delim based on the host platform. ``e.g. \ for windows, / for unix`` * Resolve custom bard/chip/arch * Defines the rules for the dependency mechanism .. code-block:: makefile OBJPATH ?= . %.dds: %.S $(Q) $(MKDEP) --obj-path $(OBJPATH) --obj-suffix $(OBJEXT) $(DEPPATH) "$(CC)" -- $(CFLAGS) -- $< > $@ %.ddc: %.c $(Q) $(MKDEP) --obj-path $(OBJPATH) --obj-suffix $(OBJEXT) $(DEPPATH) "$(CC)" -- $(CFLAGS) -- $< > $@ %.ddp: %.cpp $(Q) $(MKDEP) --obj-path $(OBJPATH) --obj-suffix $(OBJEXT) $(DEPPATH) "$(CXX)" -- $(CXXFLAGS) -- $< > $@ %.ddx: %.cxx $(Q) $(MKDEP) --obj-path $(OBJPATH) --obj-suffix $(OBJEXT) $(DEPPATH) "$(CXX)" -- $(CXXFLAGS) -- $< > $@ %.ddh: %.c $(Q) $(MKDEP) --obj-path $(OBJPATH) --obj-suffix $(OBJEXT) $(DEPPATH) "$(CC)" -- $(HOSTCFLAGS) -- $< > $@ * Defines the include flag prefix based on compiler * Defines the common functions used to compile, assemble, archive files, etc. This file (along with **/.config) must be included at the top of each configuration-specific Make.defs file like .. code-block:: makefile include $(TOPDIR)/.config include $(TOPDIR)/tools/Config.mk Subsequent logic within the configuration-specific Make.defs file may then override these default definitions as necessary. .. _flatlibs_mk: FlatLibs.mk ----------- This file defines the library sets for the **flat build mode**. In this mode, the NuttX kernel and all user-space applications are compiled and linked into a single, monolithic binary. Its primary responsibilities are: - **Populating ``NUTTXLIBS``:** The file systematically appends all required libraries to the ``NUTTXLIBS`` variable. This includes core OS libraries (``libsched``, ``libmm``, ``libc``), architecture and board support libraries, and device drivers. - **Conditional Library Inclusion:** It uses ``ifeq ($(CONFIG_XXX),y)`` checks to conditionally include libraries for optional features based on the system's Kconfig. For example, ``staging/libnet.a`` is added only if ``CONFIG_NET=y``, and ``staging/libcrypto.a`` is added if ``CONFIG_CRYPTO=y``. - **Defining ``USERLIBS``:** In the flat build, there is no separate user-space binary, so the ``USERLIBS`` variable is initialized but remains empty. - **Setting ``EXPORTLIBS``:** It assigns the complete list of ``NUTTXLIBS`` to the ``EXPORTLIBS`` variable. This ensures that the ``make export`` command packages all the compiled libraries required to link the final binary. .. _protectedlibs_mk: ProtectedLibs.mk ---------------- This file defines the library sets for the **protected build mode**. In this mode, the NuttX kernel and user-space applications are built as two distinct binaries, enabling memory protection and a more robust system architecture. Its primary responsibilities are: - **Separate Library Lists:** Unlike the flat build, this file populates both the ``NUTTXLIBS`` variable (for the kernel binary) and the ``USERLIBS`` variable (for user-space applications). - **Kernel vs. User Library Variants:** It explicitly differentiates between kernel and user variants of core libraries. For instance: - Kernel-specific libraries (e.g., ``libkc.a``, ``libkmm.a``, ``libkarch.a``) are added to ``NUTTXLIBS``. - User-space counterparts (e.g., ``libc.a``, ``libmm.a``, ``libarch.a``) are added to ``USERLIBS``. - **System Call Mechanism:** It includes essential components for the system call interface: - ``staging/libstubs.a`` (system call stubs) is added to ``NUTTXLIBS``. - ``staging/libproxies.a`` (system call proxies) is added to ``USERLIBS``. These facilitate safe communication between user applications and the kernel. - **Conditional Library Inclusion:** Similar to other build modes, it uses ``ifeq ($(CONFIG_XXX),y)`` checks to conditionally include libraries for optional features in both kernel and user-space, based on the Kconfig settings. - **Exporting User Libraries:** A key distinction is that ``EXPORTLIBS`` is set to ``$(USERLIBS)``. This means that the ``make export`` command will primarily package the user-space libraries, which are then used by external build systems or for linking user applications. .. _kernelibs_mk: KernelLibs.mk ------------- This file defines the library sets for the **kernel build mode**. In this mode, only the NuttX kernel is compiled and linked into a single binary. User applications are expected to be loaded and executed separately, typically in a protected user-space environment. Its primary responsibilities are: - **Kernel-Only ``NUTTXLIBS``:** The file populates the ``NUTTXLIBS`` variable with all the necessary kernel-space libraries. This includes core OS components (e.g., ``libsched``, ``libdrivers``), kernel variants of common libraries (e.g., ``libkc.a``, ``libkmm.a``, ``libkarch.a``), and board support. - **Empty ``USERLIBS``:** The ``USERLIBS`` variable is explicitly initialized but remains empty, as this build mode does not produce a user-space binary. - **System Call Stubs:** It includes ``staging/libstubs.a`` in ``NUTTXLIBS``, providing the kernel-side implementation for system calls. User-space system call proxies (``libproxies.a``) are not included, consistent with the absence of a user-space build. - **Conditional Library Inclusion:** It uses ``ifeq ($(CONFIG_XXX),y)`` checks to conditionally include kernel-specific libraries for optional features based on the system's Kconfig. - **Exporting User Libraries (Empty):** ``EXPORTLIBS`` is set to ``$(USERLIBS)``. Consequently, in this kernel-only build mode, ``EXPORTLIBS`` will be empty, reflecting that no user-space libraries are produced for external packaging. .. _directories_mk: Directories.mk -------------- This file defines the directories used throughout the NuttX build process. These directory lists are not static but are **dynamically constructed** based on the active Kconfig configuration. The file begins by defining a ``BASEDIRS`` variable, which includes core directories that are always part of the build. Subsequently, it uses conditional logic (``ifeq ($(CONFIG_XXX),y)``) to append additional directories to various master lists, such as ``ALLDIRS`` and ``CLEANDIRS``, only if their corresponding Kconfig options are enabled. For example: - The ``net/`` directory is added to the build lists only if ``CONFIG_NET=y``. - The ``graphics/`` directory is included if ``CONFIG_GRAPHICS=y``. - The ``crypto/`` directory is included if ``CONFIG_CRYPTO=y``. The file defines key variables that hold these dynamically generated directory lists: - ``KERNDEPDIRS``: Directories containing kernel files for dependency generation. - ``USERDEPDIRS``: Directories containing user-space files for dependency generation. - ``CCLEANDIRS``: Directories where the ``clean_context`` target will execute. - ``CLEANDIRS``: All known directories where the ``clean`` target will execute. - ``CONTEXTDIRS``: Directories with special pre-build requirements, such as auto-generation of files or symbolic link creation. .. _libtargets_mk: LibTargets.mk ------------- The ``LibTargets.mk`` file defines all the targets needed to build the NuttX libraries from their respective source directories and then install the resulting library file into the ``staging/`` directory. For many core libraries (e.g., ``libc``, ``libm``, ``libmm``, ``libbuiltin``, ``libnx``, ``libarch``), ``LibTargets.mk`` defines distinct targets for **kernel** and **user** variants. - **Kernel libraries** are typically prefixed with ``libk`` (e.g., ``libkc.a`` for kernel C library, ``libkmm.a`` for kernel memory management). These are compiled with specific ``EXTRAFLAGS`` that include ``$(KDEFINE)``, which sets kernel-specific preprocessor definitions (e.g., ``-D__KERNEL__``). This ensures they are built with the necessary context and APIs for the kernel environment. - **User libraries** retain their standard names (e.g., ``libc.a``, ``libmm.a``). These are compiled without the ``$(KDEFINE)`` flag, making them suitable for user-space applications. The build process for user-mode libraries often depends on ``pass1dep``, while kernel-mode libraries depend on ``pass2dep``, reflecting the two-pass build strategy in protected and kernel build modes. For each variant of a library, two main targets are defined: 1. **A build target:** This target recursively calls ``make`` within the library's source directory (e.g., ``libs/libc/``, ``mm/``) to compile the sources and create the library archive (``.a``) file. It passes the appropriate ``EXTRAFLAGS`` depending on whether it's a kernel-mode or user-mode build. The dependency for these targets is either ``pass1dep`` or ``pass2dep``, ensuring dependencies are checked before building. 2. **A staging target:** This target depends on the successful creation of the library archive from the previous step. Its recipe calls the ``INSTALL_LIB`` macro (defined in ``Unix.mk`` or ``Win.mk``) to copy the newly created library to the top-level ``staging/`` directory. The file contains conditional logic, primarily based on ``CONFIG_BUILD_FLAT``, to adjust dependencies (``pass1dep`` vs. ``pass2dep``) for libraries that can be part of either the user or kernel space, ensuring they are built in the correct pass according to the selected build mode.