I am using CMake to build a project for an embedded system, but my unit testing is done on an x86 host. Therefore, I need to use a completely different compiler to build the tests from the one used for the cross-compiled binary.
I have a main CMakeLists.txt file at the top level of my project, and then another one in my testing folder which gets added with add_subdirectory at the top level. Should I just keep them completely separate or is there a better way to accomplish this?
Running my main build without the cross compiler will cause it to fail, so it really needs to be a separate process for the tests.
Should I just keep them completely separate
There's no need to. Keep proper minimum dependencies between targets, add EXCLUDE_FROM_ALL to tests targets and manage targets with good grouping. Or explicitly compile only targets that you want to compile.
or is there a better way to accomplish this?
The known limitation:
You may have one compiler per one cmake configuration.
You can't (easily ;) "switch" between compilers and use different compiler for different target.
To use a different compiler, you have to reconfigure cmake.
Solution:
Configure cmake differently for unit testing and for normal compilation.
Typically use an external tool/script/whatever to manage cmake configuration and execute actions you want.
By properly labeling unit tests and using EXCLUDE_FROM_ALL on unused target, compile only what you want.
I typically use a Makefile, only because shell does autodetection of autocompletion for me:
all: normal_build
normal_build:
cmake -S . -B _build/$# \
-DCMAKE_TOOLCHAIN_FILE=the_target_toolchain_file.camle
cmake --build _build/$# --target the_firmware
unit_tests_on_pc:
camke -S . -B _build/$# \
-DCMAKE_C_FLAGS="-fsanitize -Wall -Wextra -pedantic etc...."
cmake --build _build/$# --target unit_tests # note - compile only unit tests
cd _build/$# && ctest -LE "on_target"
# tests with set CMAKE_CROSSCOMPILING_EMULATOR in toolchain file
unit_tests_on_simulator:
cmake -S . -B _build/$# \
-DCMAKE_TOOLCHAIN_FILE=the_toolchain_file_for_simulator.camke
cmake --build _build/$# --target unit_tests
cd _build/$# && ctest -E "on_target"
# tests with set CMAKE_CROSSCOMPILING_EMULATOR to a script
# that flashes some connected target with the firmware and get's output from it
unit_tests_on_target:
cmake -S . -B _build/$# \
-DCMAKE_TOOLCHAIN_FILE=the_toolchain_file_for_unit_tests.camke
# special targets compiled here - only integration tests for real hardware!
cmake --build _build/$# --target integration_tests
cd _build/$# && ctest -E "on_target"
# etc..
Related
I tried to install google benchmark(https://github.com/google/benchmark) in my ubuntu machine by :
Remember I am using windows subsystem for linux.
# Check out the library.
$ git clone https://github.com/google/benchmark.git
# Go to the library root directory
$ cd benchmark
# Make a build directory to place the build output.
$ cmake -E make_directory "build"
# Generate build system files with cmake, and download any dependencies.
$ cmake -E chdir "build" cmake -DBENCHMARK_DOWNLOAD_DEPENDENCIES=on -DCMAKE_BUILD_TYPE=Release ../
# or, starting with CMake 3.13, use a simpler form:
# cmake -DCMAKE_BUILD_TYPE=Release -S . -B "build"
# Build the library.
$ cmake --build "build" --config Release
$ sudo cmake --build "build" --config Release --target install
and my CMakeLists.txt :
cmake_minimum_required(VERSION 3.16)
set (CMAKE_CXX_STANDARD 17)
set (CMAKE_CXX_FLAGS "-O3 -lbenchmark -pthread")
project(proj)
find_package(benchmark REQUIRED)
set(SOURCES main.cpp)
add_executable(proj ${SOURCES})
target_link_libraries(proj benchmark::benchmark)
main.cpp
#include <benchmark/benchmark.h>
static void BM_StringCreation(benchmark::State& state) {
for (auto _ : state)
std::string empty_string;
}
// Register the function as a benchmark
BENCHMARK(BM_StringCreation);
BENCHMARK_MAIN();
and when I build it, I got this :
make[2]: *** No rule to make target '/tmp/build/80754af9/snappy_1649923748780/_build_env/x86_64-conda-linux-gnu/sysroot/usr/lib/librt.so', needed by 'proj'. Stop.
make[1]: *** [CMakeFiles/Makefile2:76: CMakeFiles/proj.dir/all] Error 2
make: *** [Makefile:84: all] Error 2
Instead of all the cmake -E <cmd> stuff, try simply running cmake to configure the build instead. I don't think you're using most of those commands correctly, probably due to copy-pasting wrong (just guessing).
Try this, explanations are in the comments:
# Check out the library.
$ git clone https://github.com/google/benchmark.git
# Go to the library root directory
$ cd benchmark
# configure current source dir ('.') and binary dir 'build'
$ cmake . -B build -DBENCHMARK_DOWNLOAD_DEPENDENCIES=on -DCMAKE_BUILD_TYPE=Release
# build
$ cmake --build "build" --config Release
The commands in your post should be split across multiple lines, so for example, cmake -E chdir build should be cmake -E and then chdir build instead, etc.
Something probably went wrong when you were copy-pasting the commands. Either way, the version I posted above will (as far as I can see) do what you want.
I'm wanting to use this library in my cmake project, however it comes with a configure file instead of CMakeLists.txt
#! /bin/sh
# waf configure wrapper
# based on http://code.google.com/p/waf/source/browse/configure
CUR_DIR=$PWD
#possible relative path
WORKINGDIR=`dirname $0`
cd $WORKINGDIR
#abs path
WORKINGDIR=`pwd`
cd $CUR_DIR
WAF=$WORKINGDIR/tools/waf
# Generates a Makefile. Requires that $WAF is set.
#
generateMakefile()
{
cat > Makefile << EOF
#!/usr/bin/make -f
# Waf Makefile wrapper
WAF_HOME=$CUR_DIR
all:
#$WAF build
all-debug:
#$WAF -v build
all-progress:
#$WAF -p build
install:
$WAF install --yes;
uninstall:
$WAF uninstall
clean:
#$WAF clean
distclean:
#$WAF distclean
#-rm -rf build
#-rm -f Makefile
check:
#$WAF check
dist:
#$WAF dist
.PHONY: clean dist distclean check uninstall install all
EOF
}
generateMakefile
"${WAF}" configure $*
exit $?
Are there automated tools for the conversion? Does CMake supoprt the use of configure files? Is there a rule of thumb for conversion - i.e. replace ... with add_library?
You don't need to convert an upstream library to cmake to be able to use it in cmake projects. As long as you're able to install and/or link to that library, you can configure your cmake project to use it.
A common pattern to consume third-party libraries with cmake is to use cmake's find_package() function by supplying your own special-purpose cmake module files to find and configure the library.
Say, you're hoping to load libfoo. Here are the steps:
create a directory within your project tree to store your custom cmake modules (say, ./cmake/modules)
in that directory create a text file named FindFoo.cmake.
Within FindFoo.cmake add checks to determine if foo is actually present in the system. If it is then set the relevant variables. Otherwise, throw an error. The ideal thing is to create a build target for that library, which you can simply add as dependencies to other cmake targets.
Configure your cmake project to use your local modules by adding set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/"),
Finally, configure your cmake project to include your Foo library by calling find_package(Foo REQUIRED).
The way to do the conversion is to read the configure script and understand what it does and how/why. Then write a CMakeLists.txt file that achieves the same.
There are no shortcuts.
I'm trying to build this
https://github.com/patrikhuber/eos
but I'm having troubles.
The instructions are pretty simple, as it says on gitHub
To build:
git clone --recursive https://github.com/patrikhuber/eos.git
mkdir build && cd build # creates a build directory next to the 'eos' folder
cmake -G "<your favourite generator>" ../eos -DCMAKE_INSTALL_PREFIX=../install/
make && make install # or open the project file and build in an IDE like Visual Studio
I'm using "Ninja" as generator and it looks like the cmake part goes through successfully as I get
-- Configuring done
-- Generating done
-- Build files have been written to: /home/francesco/eos/build
That's where things stop "working" for me, or where I fail to understand what's next. Following the instructions, I type
make && make install
and I get this message
make: *** No targets specified and no makefile found. Stop.
I looked around for solutions but I don't really understand what I am supposed to do: I tried
./configure
but I'm getting
bash: ./configure: No such file or directory
Anyone can please help?
Thanks
It always depends on your CMake "Generator". The 'make' is linux/mingw tool/command. For VisualStudio you can use nmake or sln/proj generated stuff.
More reliable could be utilize CMake for building i.e. for "NMake Makefiles" generator:
cmake --build <build folder> --target install
or
cmake --build <build folder> --config release --target install
for VisualStudio generator
I had the same problem, and solved by tinkering with locations for cmake and make. Here's what I used:
cmake -DCMAKE_INSTALL_PREFIX=/usr/local
make
I believe /usr/local is the default location (see here)
I run cmake in command prompt with:
mkdir build && cd build
.. cmake
But now, I have problem constructing the command to build realease static.
I tried:
C:\Users\Kuba\Downloads\rabbitmq-c>cmake --build build --BUILD_STATIC_LIBS=ON
Which yields the error:
Unknown argument --BUILD_STATIC_LIBS=ON
How to correct this? Thanks !
You should define the variable using the -D option:
cmake --build build -DBUILD_STATIC_LIBS=ON
Please read the documentation for more information.
Configuring the build is a separate step from building it.
From the source directory create a binary directory:
mkdir build && cd build
Then configure the build (this is where you can add other build-flags):
cmake -DBUILD_STATIC_LIBS=ON ..
then build it:
cmake --build .
I have the following CMake structure:
CMakelists.txt
toolchain.cmake
folder1
----CMakelists.txt
folder2
----CMakelists.txt
etc..
My first-level CMakelists.txt file includes the other subdirectories. Now I want to build my code for a different target.
The manual way is:
1.
mkdir hostBuild
cd hostBuild
cmake ..
make
2.
mkdir crossBuild
cd crossBuild
cmake .. --DCMAKE_TOOLCHAIN_FILE=toolchain.cmake
make
Is it possible that this process can run automatically?
For example, I just have to run cmake . at my first level.
Is something like this is possible?
No. The recommendation would be to just put your manual steps into a shell script.
CMake can only handle one compiler/make environment at a time (if you switch the compiler you need a different binary output directory).
Especially a toolchain file that does contain SET(CMAKE_SYSTEM_NAME ...) does change the whole outcome of the configuration/generation process.
For details on what CMake does see: CMake: In which Order are Files parsed (Cache, Toolchain, …)?
And you could make use of some CMake command line options in your shell script:
if [ ! -d hostBuild ]; then
cmake -E make_directory hostBuild
cmake -E chdir hostBuild cmake ..
fi
cmake --build hostBuild
...