Skip to content

Make Rodos installable with CMake

Patrick Kappl requested to merge PatrickKappl/rodos:cmake_install into master

This MR adds install rules to the CMakeLists files of Rodos for those users who, for whatever reason, do not want to add Rodos as a submodule/folder. The installation procedure is customizable to hopefully cover the most common scenarios. I will first explain the intended usage and then some implementation details.

Intended usage

As an example the following instructions show how to install and use Rodos when targeting a host machine suitable for the linux-x86 port and when cross-compiling with the discovery_f429 toolchain file.

Install Rodos for the host machine

Runnning

cmake --toolchain cmake/port/linux-x86.cmake -S . -B build
cmake --build build
sudo cmake --install build

in the Rodos root directory configures, builds and installs Rodos to the default install location on the host machine. On Linux this means that the header files are found in /usr/local/include/rodos and the path to the static library is /urs/local/lib/rodos/librodos.a. The CMakeLists.txt of the user application only needs to add the following two lines:

find_package(rodos REQUIRED 0.1.0)
target_link_libraries(<target> PRIVATE rodos::rodos)

The version number (0.1.0) is optional.

Install Rodos for cross-compilation targets

In this case we usually create a folder for the target environment and we want to install Rodos (and all other libraries) there. We do this by specifying an install prefix.

cmake --toolchain cmake/port/discovery_f429.cmake -S . -B build
cmake --build build
sudo cmake --install build --prefix <prefix>

The header files are now in <prefix>/include/rodos and the path to the library is <prefix>/lib/rodos/librodos.a. The install prefix could, e.g., be /usr/local/stm32. The CMakeLists.txt of the application looks exactly the same as before but we must tell CMake that the find_*() commands should search in a different directory now so we specify the CMAKE_FIND_ROOT_PATH.

cmake --toolchain <prefix>/src/rodos/cmake/port/discovery_f429.cmake -DCMAKE_FIND_ROOT_PATH=<prefix> -S . -B build

To install different versions of Rodos even in the same target environment (= with the same install prefix) the option USE_PORT_SUFFIX can be used.

cmake --toolchain cmake/port/discovery_f429.cmake -DUSE_PORT_SUFFIX=ON -S . -B build
cmake --build build
sudo cmake --install build --prefix <prefix>

It appends the name of the toolchain file (without the extension) to the name of the installed package. This means that the path to the Rodos library is now <prefix>/lib/rodos_discovery_f429/librodos.a. The location of the header files does not change allowing them to be shared between different installations. The find_package() call in the CMakeLists.txt of the application must now exactly match with the port of the rodos library that should be used.

find_package(rodos_discovery_f429 REQUIRED 0.1.0)
# or, e.g.,
# find_package(rodos_linux-x86 REQUIRED 0.1.0)
target_link_libraries(<target> PRIVATE rodos::rodos)

Uninstalling

Unfortunately there is no built-in support for uninstalling targets with CMake so this whole thing is a bit of a hack. There are two custom build targets to uninstall Rodos: uninstall and uninstall-all. The first one uses install_manifest.txt which is automatically generated by make, located in the build folder and contains a list of all installed files. Running

cmake --build build --target uninstall

deletes all the files in that list. Directories created by the installation are, however, not deleted. Running

cmake --build build --target uninstall-all

deletes the folders <prefix>/lib/rodos[_<port_suffix>], <prefix>/include/rodos and <prefix>/src/rodos and everything therein (rm -r).

Whichever target was chosen for uninstalling remember that the header files are shared between Rodos ports with the same install prefix. If multiple ports were installed with the same prefix and one is uninstalled, the other ports have to be installed again to get the header files back. This is not nice but It Works™.

Implementation details

Most of the implementation is in new files, but I also changed some existing CMake code. Since at first I did not consider creating an MR there are some "unnecessary" changes due to personal preference and style in this implementation. I am happy to adapt or even revert those. The only change of existing code that is really necessary is that the target include directories must be explicitly specified only for the BUILD_INTERFACE since full paths are used for these and they are different in the build and install tree. The rest is optional although two more changes would be particularly nice to have/keep:

  1. The directories_to_include set in some of the ports are now relative to the root directory. This makes specifying the include directories in the install tree much easier.

  2. The library target was renamed from rodos to rodos_rodos and the alias rodos::rodos was added. I know that this is a breaking change but I did this because it is a best practice for CMake. The double colon prevents CMake from interpreting an incorrectly spelled target name as a library name that could be linked with -l<library_name>. Thus typos are found easier and earlier. Since the exported target is also called rodos::rodos the user does not need to care if Rodos is added via find_package() or add_subdirectory(). They can always write target_link_libraries(<target> PRIVATE rodos::rodos).

    I can revert the target name but maybe an additional alias called rodos is enough for backwards compatibility.

Merge request reports