CMake - compile natively and crosscompile the same code - build

We're writing an application for an embedded ARM/Linux device. Development is performed on a Windows PC, using a crosscompiler, Eclipse and Ninja. CMake currently can create the build scripts that work well for the intended purpose.
We have unit tests that run on the embedded device attached to the net, once the project is pushed (over Git) to the server.
We're trying to implement unit tests, that would run on the PC, before we try them on the device. That means building natively, using MinGW GCC - of course we can't launch the ARM Linux executables on the PC.
Even if we switch the toolchain, launching CMake to rebuild the ruleset for Ninja, or create two build directories, one for PC, one for ARM, the problem remains that CMake will try to run a test executable, and later during build, unit tests will be attempted on the ARM build.
How can we configure the builds (through CMake) to create both - and not attempt to run the crosscompiled ones on the PC?

I have a similar setup in my projects (building from the same sources a simulator, unit tests, and the target binary), and you can check for CMAKE_CROSSCOMPILING to differentiate your two use cases. Just putting
if (NOT CMAKE_CROSSCOMPILING)
....
endif()
around the particular commands should do the trick.
And you need to have two binary output directories. CMake does not allow to mix toolchains in one directory.
But you don't need to have two IDE projects. In my projects:
I've added all sources - incl. the "cross-compile only" files - into the library/executable targets
I'm marking them as "excluded from build" for the PC only variants
So all sources will show-up in one IDE project (e.g. for searching through the code)
I've added the cross-compiling call as a custom target
I've removed it from the default build, but you can explicitly start it from your IDE
In my case it's an external script, but you can also pass the necessary calls directly in COMMAND parameters
You could even use another ExternalProject_Add() to include your own project for cross-compiling
Here are some code snippets for this kind of approach:
if (NOT CMAKE_CROSSCOMPILING)
set(PC "ON" CACHE INTERNAL "hw-platform PC")
unset(MCU CACHE)
else()
set(MCU "ON" CACHE INTERNAL "hw-platform MCU")
unset(PC CACHE)
endif()
...
if (PC)
# Exclude files that only compile/work on MCU
set_source_files_properties(... PROPERTIES HEADER_FILE_ONLY 1)
endif()
...
if (PC)
add_test(...)
endif()
...
if (PC AND EXISTS "${CMAKE_SOURCE_DIR}/buildMcu.cmd")
add_custom_target(
BUILD_MCU
COMMAND buildMcu.cmd
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
set_target_properties(BUILD_MCU PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD 1)
endif()
References
How to instruct CMake to use the build architecture compiler

Related

Unit testing by CTest of Cross-compiled Project

I am developing hobby OS which is cross-compiled to other architecture than my development PC and is run in QEMU emulator.
I am trying to introduce unit testing of the source files I develop for my kernel but to let them run on my development machine rather than in QEMU on my target.
In order to cross-compile my sources, I use the toolchain file which uses my custom GCC toolchain. As coming from CMake principle of toolchain file usage, the toolchain file is set up prior to project(...) definition in my root CMakeLists.txt file.
My question is how to build my test executables for my dev machine (x86_64) using the built-in GCC while cross-compiling my kernel binary for target platform... I do not know how to set CMAKE_CXX_COMPILER / CMAKE_C_COMPILER cmake variables specifically for each use case (testing, target build)
To have a look at my particular project, please feel free to navigate here:https://gitlab.com/revolta/revolta
I would like to add test/ to my project root including selected sources from source/... and manage it somehow from my root CMakeLists.txt
Thanks in advance for any concept ideas and help! Cheers Martin
The rule is - there is one compiler per configuration. Do not try to make to use two compilers per configuration. Instead run cmake two times and configure it twice,. separately for x86 testing and separately for releasing to target build.
So write a small script (you have configure.sh anyway) (I usually write a makefile with all PHONY targets) that would run and build the project twice for two configurations:
# ./compile_and_test_your_project.sh
# build for target host
cmake -DCMAKE_TOOLCHAIN_FILE=the_toolchain -S. -B_build/crosscompiled
cmake --build _build/crosscompiled --target the_main_project_target
( cd _build/crosscompiled && ctest )
# build for native host
cmake -DCMAKE_C_FLAGS="-fsanitize=undefined -ggdb3 -O0" -S. -B_build/native
cmake --build _build/native --target only_testing_targets
( cd _build/native && ctest )
Do not set CMAKE_CROSSCOMPILING_EMULATOR inside cmake config. I advise to try to keep cmake configuration platform agnostic as much as you can and pass platform specific parts using arguments to cofiguration cmake. Such way is scalable - you may use a different toolchain and different environment with ease, or try different compiler options. Inside cmake you can see if you are crosscompiling with just if (CMAKE_CROSSCOMPILING). Also see CMAKE_CROSSCOMPILING_EMULATOR.
ps. My makefile from one of my projects that sets different CMAKE_CROSSCOMPILING_EMULATOR depending on make target.

CMake: Compile unit test natively and cross compile code for embedded target

We are developing an application for an embedded MCU (stm32) and we are trying to set up unit testing with googletest. Is it possible to compile a target using the embedded compiler (gcc-arm-none and some compile options) and compile another target (the unit tests) using the native compiler with CMake? We would abstract and mock the hardware dependencies. This would allow us to run the tests on the host computer and during CI.
I am open to another approach if this is not the way to go or if this approach have issues.
Yes, it is possible. For cross-compilation, you use a toolchain file. When building tests, simply do not specify the toolchain file, to use the native toolchain.
You can also set a flag that indicates that tests are being built and use that inside CmakeLists.txt:
if(ENABLE_TESTING)
enable_testing()
add_subdirectory(test)
else()
# Build embedded app
endif()

How to change some CMAKE_C_FLAGS for just one executable in CMake?

I have a CMake project that supports multiple platform combination (arm, powerpc ,x86-64). Native platform is x86-64.
I have toolchain.cmake file for each cross-platform build(for arm and powerpc).
When I give toolcahin.cmake files for cross-platform build to cmake everything is working as expected except one thing.
In the project I want to build a particular executable with native build support(that is on x86-64). Since I gave toolchain.cmake file explicitly, so all CMAKE_C_FLAGS are global for all executable in project.
So I want to give toolchain.cmake(say for arm) to cmake and want to build the whole project except one executable on native(x86-64) in one go.
Is there any way where I can change the complete CMAKE_C_FLAGS and can provide the native one for that particular executable ?
I tried few things as per How to change a compiler flag for just one executable in CMake? but can't get the solution to this problem

How can I use CMake to both build wxwidgets on-demand and link with it

I have the following situation:
I'm working on an application that depends on a number of third party libs, among them wxwidgets
I build the application for multiple target configurations (x86, arm, Linux, Windows) using Linux as my build host system
Due to the above mentioned multiple target configurations, I have chosen to build those third-party libs from source, using CMake's ExternalProject_Add function.
The third-party libs are built 'on-demand' at a location separate from my application's CMAKE_BINARY_DIR so that I can wipe the build tree for my application without having to rebuild the third-party libs (takes a looooong time).
The location of the third-party libs is different depending on what target configuration I build them for (obviously).
I'm quite new to CMake and the problem I currently face is this:
The source files in my application can't find the wx include files and I need to set the correct linker flags to be able to link my application against wxwidgets.
This seems to be handled by a utility 'wx-config' that provides exactly that info as output when run with either the --cppflags or --libs flag. I can not however, figure out how to catch that output and append it to the include dirs and linked libraries I setup from my CMakeLists.txt files.
So basically what I want is.
Build wxwidgets (if it doesn't exist) for the current target configuration
Run wx-config --cppflags and --libs to find out the correct include dirs and linker flags for the current target configuration
Use the info from step 2 when building targets that are my own application
So far I've tried something like this:
# Set a target-configuration-specific location
set(wxwidgetsTop ${MYPROJECT_EXTERNAL_DIR}/wxwidgets/wxwidgets_${MYPROJECT_CURRENT_TARGET_CFG})
# Build the project
ExternalProject_Add( wxWidgetsExternal
PREFIX ${wxwidgetsTop}
URL ${MYPROJECT_EXTERNAL_DIR}/tarballs/wxWidgets-3.0.2.tar.bz2
SOURCE_DIR ${wxwidgetsTop}/src/wxwidgets
CONFIGURE_COMMAND ${configure_cmdline}
BUILD_COMMAND make -j${MYPROJECT_NCPU}
INSTALL_COMMAND make install
)
# Create a wxwidgets target to be used as a dependency from other code
add_library(wxWidgets IMPORTED STATIC GLOBAL)
add_dependencies(wxWidgets wxWidgetsExternal)
# (non-working) attempt to get the correct include dirs and linker
# flags for wxwidgets
add_custom_command(TARGET wxWidgetsExternal
POST_BUILD
COMMAND ${INSTALL_DIR}/bin/wx-config ARGS --cppflags
COMMENT "Running wx-config"
)
but the above does not provide a way to actually use the result from the custom command to append the cppflags and linker options when building the targets that make up my application.
What is a good way to achieve what I want?
I see three different ways of doing this:
Method 1: use find_package
Use wxWidgets as a standalone requirement for your project, and expect the devs to install it before building your project. In your CMakeLists.txt you will need to call find_package(wxWidgets), like this:
find_package(wxWidgets COMPONENTS net gl core base)
if(wxWidgets_FOUND)
include(${wxWidgets_USE_FILE})
# and for each of your dependent executable/library targets:
target_link_libraries(<YourTarget> ${wxWidgets_LIBRARIES})
endif()
This has the advantage of not rebuilding the lib if you rebuild your project, however it requires some work for your user (they need to handle the installation of wxWidgets by hand) and for you (you need to setup include paths / compile definitions / ... by hand).
Method 2: embed wxWidgets
The second option is to bundle wxWidgets in your repo (svn external or git submodule) and usually (re)write the CMakeLists.txt of this lib to be target-oriented. Then, in your top-most CMakeLists.txt, you can do the following:
# for example, if you just need core and net:
target_link_librairies(my_app PUBLIC wxWidgetsCore wxWidgetsNet)
# No need to manually setup include dirs, etc...
To make a CMakeLists.txt target-oriented, you define include directories and other compilation properties for a target, not a directory. Example:
# When defining wxWidgetsCore, for example
add_library(wxWidgetsCore ...)
target_include_directories(wxWidgetsCore PUBLIC someDir)
target_compile_definitions(wxWidgetsCore PUBLIC -pedantic)
target_link_libraries(wxWidgetsCore PUBLIC someLib)
The drawback of this approach is that rebuilding your project will trigger a rebuild of wxWidgets. However, it is possible to trick this by not using "rebuild" but "clean just my app, then build". Here is some insight on how to achieve this.
Method 3: some sort of hybrid
The big drawback of method 2 leads to the third approach: don't put wxWidgets in your project, but create a CMakeLists.txt that will "import" the lib. The idea: you ask your user for the directory where wxWidgets is installed, then this script will setup everything for your project. First, put the CMakeLists.txt here:
/your-project-root
/thirdparty
/wxWidgets
CMakeLists.txt
/dir-where-wxwidgets-is-installed
...
Now, you define an imported target:
# When defining wxWidgetsCore, for example
set(WX_INCLUDE_DIR ${USER_SPECIFIED_WX_ROOT}/include)
add_library(wxWidgetsCore IMPORTED GLOBAL)
set_property(TARGET wxWidgetsCore APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES ${WX_INCLUDE_DIR})
See INTERFACE_INCLUDE_DIRECTORIES and INTERFACE_LINK_LIBRARIES. You need your user to have build wxWidgets somewhere in his system, but from your point of view you just do target_link_libraries(your_app PUBLIC wxWidgets...), as in method 2. The advantage is that this approach is interchangeable with method 2 transparently, and you don't put the whole dependency in your project.
Setting cppflags and linker flags has to be done at CMake time, but you are trying to run wx-config at build time and you are not capturing its output anyway, so your add_custom_command() isn't doing anything useful other than printing things to the build tool's output.
Ideally, you would use the FindwxWidgets module CMake already provides. It requires wxWidgets to already be built (but see further below). Have a look at the CMake documentation for it and see if that at least sounds like what you are trying to achieve manually by using wx-config. If you can get FindwxWidgets to do the job for you, that would be a much cleaner approach.
Getting something to build at configure time so you can use it later on in your CMakeLists.txt file is a bit more tricky. ExternalProject_Add() downloads and builds things at build time, but you need wxWidgets to be built earlier at configure time. I wrote an article recently for how to do at least the downloading part at configure time and you should be able to adapt it to do the whole build at configure time instead. The article uses Google Test as its example and can be found here:
https://crascit.com/2015/07/25/cmake-gtest/
It would be trivial to make it put the wxWidgets build wherever you like, not just in the CMAKE_BINARY_DIR area. That would allow you to have different wxWidgets builds for each build configuration and to be able to wipe out your application's build tree independently of the wxWidgets builds.
Hope that points you in the right direction.
The solution I use checks for wxWidgets installation in the system using find_package, if it's not found, then the script downloads wxWidgets from github and links the program against downloaded library. The lib is installed in the build directory, so only the first build is slow - subsequent builds do not even check wxWidgets sources timestamps, so the process is as fast as building using preinstalled wxWidgets library.
Here's how my script does it:
It quietly checks for wxWidgets installation using find_package(wxWidgets QUIET),
If it's found, the script adds a dummy library wxWidgets_external,
If it's not, then it creates an ExternalProject named wxWidgets_external which downloads, builds and installs the library in the build dir, setting wxWidgets_ROOT_DIR to point to the wxWidgets installation dir,
Then we add another ExternalProject pointing to a folder with the main program's source files and CMakeLists.txt build script. This external projects depends on wxWidgets_external which is either a dummy library in case wxWidgets is preinstalled in the system, or an external project set up to download the library from github,
In the aforementioned CMakeLists.txt we again call find_package, this time with REQUIRED parameter and use the library the standard way (https://docs.wxwidgets.org/trunk/overview_cmake.html). Because we set up the dependencies and variables correctly, this call will use either preinstalled wxWidgets (if it's available) or the one downloaded from github.
There are more quirks to it, but that's the gist of it. The full sample code (tested on Linux, Windows and Mac) is available on github (https://github.com/lszl84/wx_cmake_template).
Also see full blog post which explains this in more detail: https://justdevtutorials.medium.com/wxwidgets-cmake-multiplatform-superbuild-4ea86c4e6eda

Framework for building and managing third-party libraries

I am working on a cross-platform project which uses a large number of third party libraries (currently 22 and counting, and I expect this number to increase significantly). My project is CMake-based, and keeps the ThirdParty/ directory organized like so:
ThirdParty/$libname/include/
ThirdParty/$libname/lib/$platform/$buildtype/
My CMakeLists.txt has logic to determine the appropriate values for $platform (mac-i386, mac-ia64, win32-i386, and so on) and $buildtype (debug/release).
The difficulty arises in keeping these libraries up-to-date for each platform. Currently I am doing this manually - when I update one library, I go and rebuild it for each platform. But this is a nightmare, and adding a new platform is a two day affair.
This would be easy to automate if the third party libraries were themselves CMake-based, but they use everything from CMake to autoconf to custom solutions to hand-rolled Makefiles. There is no consistency between them, and all require various hoops to be jumped through with regards to build platform (especially with regards to 32- vs. 64-bit builds).
Are there any tools (or CMake extensions) which would make this easier to manage? Is there even any reasonable common ground that can be reached between CMake and autoconf, for example?
The ideal solution would give me a single command to build everything that needs rebuilding for a given platform (cross-compilation is not necessary, as I have access to all necessary platforms), but anything that incrementally makes my life easier would be appreciated.
You can probably use ExternalProject for this.
Create custom targets to build projects in external trees.
The 'ExternalProject_Add' function creates a custom target to drive download, update/patch, configure, build, install and test steps of an external project.
If you already have the source in your project's file hierarchy, then you can use something like this (for zlib):
include(ExternalProject)
ExternalProject_Add(zlib URL ${CMAKE_CURRENT_SOURCE_DIR}/zlib-1.2.4/
CONFIGURE_COMMAND cd <SOURCE_DIR> && ./configure --prefix=${CMAKE_CURRENT_BINARY_DIR}/zlib-build
BUILD_IN_SOURCE 1
BUILD_COMMAND make)
That will copy the zlib source code from your source tree into the build tree, run the configure step (after cd'ing into the copied directory), then run make on the configured directory. Finally the built version of zlib is installed into the current build directory, in a sub-directory called zlib-build.
You can tweak the setup, configure, and build steps however you like - zlib 1.2.4 for example doesn't like to have "configure" run out-of-source.
For a custom setup, you can skip the CONFIGURE step and just run the build command (for example). This requires a recent version of CMake to work (2.8+).