Build System Reference

yotta defines the way that software builds in order to make it easier for
separate modules to work together. It has both a simple automatic build system
that will build modules based on the source files discovered in the source
directory, and the capability to support building modules of any complexity
using CMake.

yotta makes some useful information available to the modules being compiled, which
can be embedded in the built binary or otherwise used at compile time. The
majority of this information is defined by yotta’s configuration
system, but some other information is also available.

Information Always Available

The name of the library being built by the current module is available as
YOTTA_MODULE_NAME, as if it were defined:

#define YOTTA_MODULE_NAME modulename-unquoted

No header needs to be included for this definition to be available.

Use the preprocessor stringification
trick to get the
module name as a string, if desired. Note that this definition is not
currently available when compiling tests, and there are other circumstances
where using custom CMake can make it unavailable.

Information Available in the Build Info Header

If the yotta build information header is included, then you can also access
other information. Note that this header changes with every build (as it
includes a build timestamp and unique ID), so do not include it unnecessarily.

yotta will automatically build the contents of the source and test
subdirectories of a software module. If you wan to exclude files from being
picked up by this build system then you can add them to a
.yotta_ignore file placed at the top of the module.

Any files in the source directory, and any of its subdirectories, will be
compiled into a single static library (for normal modules), or into the
application (for an executable module).

Any source files at the top-level of the test directory will be compiled into
separate test executables, and the (recursive) contents of any subdirectories
will each be compiled into a single test executable. You can use the
yotta test subcommand to build and run these tests.

Files in any other directories are normally ignored by yotta. By convention,
public header files that a module exposes are placed in a subdirectory with the
same name as the module, and then should be included as:

To override yotta’s default build rules for the source and test
directories, place your own CMakeLists.txt file in these directories. yotta
will also ensure that any CMakeLists.txt file in any other top-level
subdirectory of your module is included in the build. The testing with yotta
guide explains how to make yotta aware of any tests you add manually so that
yotta test can run them.

yotta will also respect a CMakeLists.txt file at the top-level of your module.
If this file is detected, then yotta will not automatically generate any build
rules. This is useful if you’re adding yotta support to a module with an
existing build system, especially if the build system already uses CMake.

To ensure that yotta can automatically link your module to other modules, make
sure you define exactly one library with the same name as your module. A custom
build system may also define other build artifacts. In this case take care to
ensure that you name them in a way that minimizes the likelihood of name
collisions with other modules.

./source/<anything>.cmake: any .cmake files found in the source
directory will be included at the end of the yotta-generated build rules
for the source directory. If you want to make a very simple modification
(such as definining a new preprocessor macro that your module needs), then
this is the best way to do it.

./test/CMakeLists.txt: defining a CMakeLists.txt file in the
test directory replaces yotta’s default build rules for your tests. yotta
will build your library or executable from the contents of the source
directory as normal.

./<anything>/CMakeLists.txt: Any subdirectory with a CMakeLists.txt
file will be included in the build (unless it is ignored in the
.yotta_ignore file). There aren’t very many good reasons to do this.

If you have a script ./scripts/munge.py <input> <output> that you want to run on an input file
./resources/input.data to generate an output C file to include in the build,
you can use a custom CMake file like this:

./source/CMakeLists.txt

# construct the output path for our generated file (in the build directory, so# that it gets removed by `yotta clean`):set(MYMODULE_GENERATED_FILES ${CMAKE_BINARY_DIR}/generated/mymodule)# ensure that the directory for the generated file exists:file(MAKE_DIRECTORY "${MYMODULE_GENERATED_FILES}")# save the paths to the script, input and output files, for convenience:set(MYMODULE_MUNGE_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/../scripts/munge.py")set(MYMODULE_MUNGE_INPUT "${CMAKE_CURRENT_LIST_DIR}/../resources/input.data")set(MYMODULE_MUNGE_OUTPUT "${MYMODULE_GENERATED_FILES}/generated.c")# define the command to generate this fileadd_custom_command(
OUTPUT "${MYMODULE_GENERATED_FILES}/generated.c"
DEPENDS "${MYMODULE_MUNGE_SCRIPT}""${MYMODULE_MUNGE_INPUT}"
COMMAND python "${MYMODULE_MUNGE_SCRIPT}""${MYMODULE_MUNGE_INPUT}""${MYMODULE_MUNGE_OUTPUT}"
COMMENT "Munging input into generated.c")# define the library for this module, using the generated file:add_library(mymodule # your module must create a library with its own name
sourcefile1.c
sourcefile2.c
"${MYMODULE_GENERATED_FILES}/generated.c")# link against the module's dependenciestarget_link_libraries(mymodule
mydependency
myotherdependency
)

Note that as we’re replacing the yotta-generated CMakeLists for the source
directory, you need to make sure you’re still linking against all of your
module’s dependencies

You can use a .cmake file to change the link flags of an existing target
without having to redefine the automatically generated build rules. For
example, if your module is called mymodule, you could add this:

./source/override_flags.cmake:

# add -funroll loops to the compile commands used for the sources in this
# module... loops deserve some fun too!
set_target_properties(mymodule COMPILE_FLAGS "-funroll-loops")

Note that here “mymodule” is the name of the static library that your module is
generating (the CMake “target” that it defines – nothing to do with the yotta
target). By convention all yotta modules produce a static library with the same
name as the module

For documentation on the other things that you can set with
set_target_properties, including preprocessor definitions, see the CMake
docs.

yotta makes all the definitions which are available to the preprocessor
available to CMake scripts (including, for example, the path to the build
information header, and the definitions derived from the config information).

In addition, several other definitions are available, including:

YOTTA_CONFIG_MERGED_JSON_FILE: This expands to the full path of a file
where the current yotta config information has been written. If you want to
use the config information for advanced pre-build steps (such as including
portions of it in the executable in a parseable form), then this is the file
you should read the config information from.

Everything that yotta generates during the build is created within the build
subdirectory. Within this directory build products are further divided by the
name of the target being built. This makes it safe to
switch between building for different targets without cleaning.