Importing (RTEMS ) libraries in CMake - cmake

I am trying to import a library in CMake the modern way like shown in this thread:
How do I add a library path in cmake?
The goal is to build a RTEMS test program. I'm building on a Ubuntu 20.04 machine, and I am cross compiling for an ARM target with the arm/stm32h7 BSP.
The libraries are located inside an external lib folder.
I almost got the build process working, however CMake appears to do something which breaks the linking process. I propably did the mistake but I have problems figuring it out.
This is the basic setup of my CMake file, after I set up everything for cross compilation of RTEMS binaries:
...
# Here comes application stuff again
add_executable(${CMAKE_PROJECT_NAME} init.c led.c stm32h7xx_nucleo.c)
set(RTEMS_LIB_NAME "rtems_${RTEMS_ARCH_NAME}_${RTEMS_BSP_NAME}")
add_library(${RTEMS_LIB_NAME} SHARED IMPORTED)
set_target_properties(${RTEMS_LIB_NAME} PROPERTIES
IMPORTED_LOCATION ${RTEMS_BSP_LIB_PATH}
INTERFACE_INCLUDE_DIRECTORIES ${RTEMS_BSP_INC_PATH}
)
#target_link_directories(${RTEMS_LIB_NAME} INTERFACE
# ${RTEMS_BSP_LIB_PATH}
#)
#target_include_directories(${RTEMS_LIB_NAME} INTERFACE
# ${RTEMS_BSP_INC_PATH}
#)
target_link_options(${RTEMS_LIB_NAME} INTERFACE
# -I${RTEMS_BSP_INC_PATH}
# -B${RTEMS_BSP_LIB_PATH}
-Wl,--gc-sections
-Wl,-Bstatic
-Wl,-Bdynamic
-qrtems
)
target_link_libraries(${CMAKE_PROJECT_NAME} ${RTEMS_LIB_NAME})
Building the individual source files appears to work fine.
The raw link command attempted by CMake will be the following:
/home/rmueller/Documents/RTEMS/toolchain/rtems/6/bin/arm-rtems6-gcc
-mthumb -mcpu=cortex-m7 -mfpu=fpv5-d16 -mfloat-abi=hard
-Wl,--gc-sections -Wl,-Bstatic -Wl,-Bdynamic
-qrtems CMakeFiles/blinky.dir/init.c.o CMakeFiles/blinky.dir/led.c.o CMakeFiles/blinky.dir/stm32h7xx_nucleo.c.o
-o blinky -Wl,-rpath,/home/rmueller/Documents/RTEMS/toolchain/rtems
/6/arm-rtems6/stm32h7 /home/rmueller/Documents/RTEMS/toolchain/rtems/6/arm-rtems6/stm32h7/lib
And I get the error:
./../../../arm-rtems6/bin/ld: cannot open linker script file linkcmds: No such file or directory
This is propably because the libraries are not in the search path somehow.
I then found out that the following command links the binary properly:
/home/rmueller/Documents/RTEMS/toolchain/rtems/6/bin/arm-rtems6-gcc
-mthumb -mcpu=cortex-m7 -mfpu=fpv5-d16 -mfloat-abi=hard
-Wl,--gc-sections -Wl,-Bstatic -Wl,-Bdynamic
-qrtems CMakeFiles/blinky.dir/init.c.o CMakeFiles/blinky.dir/led.c.o
CMakeFiles/blinky.dir/stm32h7xx_nucleo.c.o -o blinky
-L/home/rmueller/Documents/RTEMS/toolchain/rtems/6/arm-rtems6/stm32h7/lib
Is the way to import the library wrong? I could just add the -L flag manually to my build target using commands like target_link_options , but I was thinking it would be nice if the search path could just be an interface requirement when linking the RTEMS library.
UPDATE: I think I found one error: I imported the library path as a SHARED library and it propably has to be imported as STATIC.
THe command now looks like this:
/home/rmueller/Documents/RTEMS/toolchain/rtems/6/bin/arm-rtems6-gcc -mthumb
-mcpu=cortex-m7 -mfpu=fpv5-d16 -mfloat-abi=hard -Wl,--gc-sections -Wl,-Bstatic -Wl,-Bdynamic -qrtems
CMakeFiles/blinky.dir/init.c.o CMakeFiles/blinky.dir/led.c.o CMakeFiles/blinky.dir/stm32h7xx_nucleo.c.o
-o blinky /home/rmueller/Documents/RTEMS/toolchain/rtems/6/arm-rtems6/stm32h7/lib
UPDATE2:
I solved the problem. There was still a little syntax error, I think the quotes were missing. The command to set the library properties looks like this now :
set_target_properties(${RTEMS_LIB_NAME} PROPERTIES
IMPORTED_LOCATION "${RTEMS_BSP_LIB_PATH}"
INTERFACE_INCLUDE_DIRECTORIES "${RTEMS_BSP_INC_PATH}"
)
And the binary is linked properly :-)
UPDATE3:
And it has stopped working again. This is really weird. The -L flag appears to be missing..
Kind Regards
Robin

Okay, I finally solved the issue. The above option is used to explicitely include libraries. In the RTEMS case, simply adding the library path and using -qrtems is sufficient.
The resulting and working CMakeLists.txt file can be found here: https://github.com/rmspacefish/rtems-demo/blob/master/applications/stm32/blinky/CMakeLists.txt

Related

generating debug info with emscripten / ninja / cmake on complex project

I am trying to debug a port of some c and c++ code to WASM. I worked out how to source level debug in the browser with a simple 10 line .c program but now I want to make that work with a non trivial code base. (mixed c and c++). The wasm code works in a simple app but not in my more complex use case, hence the need to debug it
I use CMake to generate ninja build files
Here is where I am setting flags in my CMakeLists.txt
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \
-s MODULARIZE=1 \
-s SINGLE_FILE=1 \
-s EXPORT_NAME=aubio \
-g --bind")
this was basically copied from the original codebase (the -g instead of -Oz is mine), I am not a CMake nor ninja nor emscripten wizard. When I build this I can see that the CXX flags are not passed to the emc++ compile passes, only to the 'linker' phase
I am not even sure where the -g (perhaps with source-map) needs to be for a multi-file project 'linked' into a single file. Should it be on the compile passes or the link pass , or maybe both. But certainly at the moment I do not get any symbols anywhere, no 'map' file(s) (embedded DWARF?, since the browser plugin claims to support it)
set(CMAKE_BUILD_TYPE Debug)
seems to embed DWARF to WASM file.
I don't know why set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") nor target_compile_options(foo PUBLIC -g) doesn't work in Release build.

C++20 modules with cmake + ninja [duplicate]

Clang and MSVC already supports Modules TS from unfinished C++20 standard.
Can I build my modules based project with CMake or other build system and how?
I tried build2, it supports modules and it works very well, but i have a question about it's dependency management (UPD: question is closed).
CMake currently does not support C++20 modules.
See also the relevant issue in the CMake issue tracker. Note that supporting modules requires far more support from the build system than inserting a new compiler option. It fundamentally changes how dependencies between source files have to be handled during the build: In a pre-modules world all cpp source files can be built independently in any order. With modules that is no longer true, which has implications not only for CMake itself, but also for the downstream build system.
Take a look at the CMake Fortran modules paper for the gory details. From a build system's point of view, Fortran's modules behave very similar to the C++20 modules.
Update: CMake 3.20 introduces experimental support for Modules with the Ninja Generator (and only for Ninja). Details can be found in the respective pull request. At this stage, this feature is still highly experimental and not intended for production use. If you intend to play around with this anyway, you really should be reading both the Fortran modules paper and the dependency format paper to understand what you're getting into.
This works on Linux Manjaro (same as Arch), but should work on any Unix OS. Of course, you need to build with new clang (tested with clang-10).
helloworld.cpp:
export module helloworld;
import <cstdio>;
export void hello() { puts("Hello world!"); }
main.cpp:
import helloworld; // import declaration
int main() {
hello();
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(main)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(PREBUILT_MODULE_PATH ${CMAKE_BINARY_DIR}/modules)
function(add_module name)
file(MAKE_DIRECTORY ${PREBUILT_MODULE_PATH})
add_custom_target(${name}.pcm
COMMAND
${CMAKE_CXX_COMPILER}
-std=c++20
-stdlib=libc++
-fmodules
-c
${CMAKE_CURRENT_SOURCE_DIR}/${ARGN}
-Xclang -emit-module-interface
-o ${PREBUILT_MODULE_PATH}/${name}.pcm
)
endfunction()
add_compile_options(-fmodules)
add_compile_options(-stdlib=libc++)
add_compile_options(-fbuiltin-module-map)
add_compile_options(-fimplicit-module-maps)
add_compile_options(-fprebuilt-module-path=${PREBUILT_MODULE_PATH})
add_module(helloworld helloworld.cpp)
add_executable(main
main.cpp
helloworld.cpp
)
add_dependencies(main helloworld.pcm)
Assuming that you're using gcc 11 with a Makefile generator, the following code should work even without CMake support for C++20:
cmake_minimum_required(VERSION 3.19) # Lower versions should also be supported
project(cpp20-modules)
# Add target to build iostream module
add_custom_target(std_modules ALL
COMMAND ${CMAKE_COMMAND} -E echo "Building standard library modules"
COMMAND g++ -fmodules-ts -std=c++20 -c -x c++-system-header iostream
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
# Function to set up modules in GCC
function (prepare_for_module TGT)
target_compile_options(${TGT} PUBLIC -fmodules-ts)
set_property(TARGET ${TGT} PROPERTY CXX_STANDARD 20)
set_property(TARGET ${TGT} PROPERTY CXX_EXTENSIONS OFF)
add_dependencies(${TGT} std_modules)
endfunction()
# Program name and sources
set (TARGET prog)
set (SOURCES main.cpp)
set (MODULES mymod.cpp)
# Setup program modules object library
set (MODULE_TARGET prog-modules)
add_library(${MODULE_TARGET} OBJECT ${MODULES})
prepare_for_module(${MODULE_TARGET})
# Setup executable
add_executable(${TARGET} ${SOURCES})
prepare_for_module(${TARGET})
# Add modules to application using object library
target_link_libraries(${TARGET} PRIVATE ${MODULE_TARGET})
Some explanation:
A custom target is added to build the standard library modules, in case you want to include standard library header units (search for "Standard Library Header Units" here). For simplicity, I just added iostream here.
Next, a function is added to conveniently enable C++20 and Modules TS for targets
We first create an object library to build the user modules
Finally, we create our executable and link it to the object library created in the previous step.
Not consider the following main.cpp:
import mymod;
int main() {
helloModule();
}
and mymod.cpp:
module;
export module mymod;
import <iostream>;
export void helloModule() {
std::cout << "Hello module!\n";
}
Using the above CMakeLists.txt, your example should compile fine (successfully tested in Ubuntu WSL with gcc 1.11.0).
Update:
Sometimes when changing the CMakeLists.txt and recompiling, you may encounter an error
error: import "/usr/include/c++/11/iostream" has CRC mismatch
Probably the reason is that every new module will attempt to build the standard library modules, but I'm not sure. Unfortunately I didn't find a proper solution to this (avoiding rebuild if the gcm.cache directory already exists is bad if you want to add new standard modules, and doing it per-module is a maintenance nightmare). My Q&D solution is to delete ${CMAKE_BINARY_DIR}/gcm.cache and rebuild the modules. I'm happy for better suggestions though.
CMake ships with experimental support for C++20 modules:
https://gitlab.kitware.com/cmake/cmake/-/blob/master/Help/dev/experimental.rst
This is tracked in this issue:
https://gitlab.kitware.com/cmake/cmake/-/issues/18355
There is also a CMakeCXXModules repository that adds support for modules to CMake.
https://github.com/NTSFka/CMakeCxxModules
While waiting for proper C++20 modules support in CMake, I've found that if using MSVC Windows, for right now you can make-believe it's there by hacking around the build instead of around CMakeLists.txt: continously generate with latest VS generator, and open/build the .sln with VS2020. The IFC dependency chain gets taken care of automatically (import <iostream>; just works). Haven't tried Windows clang or cross-compiling. It's not ideal but for now at least another decently workable alternative today, so far.
Important afterthought: use .cppm and .ixx extensions.
CMake does not currently support C++20 modules like the others have stated. However, module support for Fortran is very similar, and perhaps this could be easily changed to support modules in C++20.
http://fortranwiki.org/fortran/show/Build+tools
Now, perhaps there i an easy way to modify this to support C++20 directly. Not sure. It is worth exploring and doing a pull request should you resolve it.
Add MSVC version (revised from #warchantua 's answer):
cmake_minimum_required(VERSION 3.16)
project(Cpp20)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(PREBUILT_MODULE_DIR ${CMAKE_BINARY_DIR}/modules)
set(STD_MODULES_DIR "D:/MSVC/VC/Tools/MSVC/14.29.30133/ifc/x64") # macro "$(VC_IFCPath)" in MSVC
function(add_module name)
file(MAKE_DIRECTORY ${PREBUILT_MODULE_DIR})
add_custom_target(${name}.ifc
COMMAND
${CMAKE_CXX_COMPILER}
/std:c++latest
/stdIfcDir ${STD_MODULES_DIR}
/experimental:module
/c
/EHsc
/MD
${CMAKE_CURRENT_SOURCE_DIR}/${ARGN}
/module:export
/ifcOutput
${PREBUILT_MODULE_DIR}/${name}.ifc
/Fo${PREBUILT_MODULE_DIR}/${name}.obj
)
endfunction()
set(CUSTOM_MODULES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/modules)
add_module(my_module ${CUSTOM_MODULES_DIR}/my_module.ixx)
add_executable(test
test.cpp
)
target_compile_options(test
BEFORE
PRIVATE
/std:c++latest
/experimental:module
/stdIfcDir ${STD_MODULES_DIR}
/ifcSearchDir ${PREBUILT_MODULE_DIR}
/reference my_module=${PREBUILT_MODULE_DIR}/my_module.ifc
/EHsc
/MD
)
target_link_libraries(test ${PREBUILT_MODULE_DIR}/my_module.obj)
add_dependencies(test my_module.ifc)
I was not able to find Cmake support for modules. Here is an example how to use modules using clang. I am using Mac and this example works ok on my system. It took me quite a while to figure this out so unsure how general this is across linux or Windows.
Source code in file driver.cxx
import hello;
int main() { say_hello("Modules"); }
Source code in file hello.cxx
#include <iostream>
module hello;
void say_hello(const char *n) {
std::cout << "Hello, " << n << "!" << std::endl;
}
Source code in file hello.mxx
export module hello;
export void say_hello (const char* name);
And to compile the code with above source files, here are command lines on terminal
clang++ \
-std=c++2a \
-fmodules-ts \
--precompile \
-x c++-module \
-Xclang -fmodules-embed-all-files \
-Xclang -fmodules-codegen \
-Xclang -fmodules-debuginfo \
-o hello.pcm hello.mxx
clang++ -std=c++2a -fmodules-ts -o hello.pcm.o -c hello.pcm
clang++ -std=c++2a -fmodules-ts -x c++ -o hello.o \
-fmodule-file=hello.pcm -c hello.cxx
clang++ -std=c++2a -fmodules-ts -x c++ -o driver.o \
-fmodule-file=hello=hello.pcm -c driver.cxx
clang++ -o hello hello.pcm.o driver.o hello.o
and to get clean start on next compile
rm -f *.o
rm -f hello
rm -f hello.pcm
expected output
./hello
Hello, Modules!
Hope this helps, all the best.
With C++20 Modules the file compilation order matters, which is totally new. That's why the implementation is complicated and still experimental in 2023. Please read the authors blogpost

Link libc statically

I am trying to make a static executable with CMake 3.15. I am building on Alpine Linux (hence with musl), and currently, my executable's ldd output is:
# ldd my_executable
/lib/ld-musl-x86_64.so.1 (0x7fc6f7977000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x7fc6f65b3000)
libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7fc6f7977000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x7fc6f659f000)
I can set target_link_options(my_executable PRIVATE -static-libgcc -static-libstdc++), and they got linked statically:
# ldd my_executable
/lib/ld-musl-x86_64.so.1 (0x7fc6f7977000)
libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7fc6f7977000)
But I can't manage to get musl to link statically. I tried (with clean builds, to make sure that the configure step starts from zero):
Adding -static to target_link_options above.
set(CMAKE_EXE_LINKER_FLAGS "-static") before the call to add_executable(my_executable ...)
Adding -static to target_link_libraries(my_executable lib1 lib2 -static)
When I run CMake in VERBOSE=1 mode, it always ends the linking command with:
... -Wl,-Bdynamic -ldl -lrt -lm -lpthread
I believe that this is my issue: I want to get rid of that -Bdynamic. What am I missing? Is this -Bdynamic coming from one of my dependencies? I build them all from sources as static (.a) libraries, so how could they be linking libc dynamically? Or would I need to patch them all to add -static when I build them?
As hinted by KamilCuk's comment, the answer here seems to have the solution. Still, I'm not doing exactly the same, therefore I'll keep this answer, too.
For the target executable that I want statically linked:
add_executable(my_executable main.cpp)
I had to set the following properties/options:
set_target_properties(my_executable PROPERTIES LINK_SEARCH_START_STATIC ON)
set_target_properties(my_executable PROPERTIES LINK_SEARCH_END_STATIC ON)
target_link_options(my_executable PRIVATE -static-libgcc -static-libstdc++ -static)
Some notes:
LINK_SEARCH_*_STATIC were useful to remove -Bdynamic from the linking command.
I never managed to remove -ldl from the linking command, but it seems like dl did not get link eventually (presumably because it is not used).
ldd was not enough to verify that my_executable is statically linked. readelf -l my_executable showed that it does not have an INTERP header, and there is currently no such thing as a dynamic binary without it (using musl).
It turns out that checking whether a binary is statically linked or not is not so straightforward :-).

How to add the option `llvm-config --cxxflags --ldflags --libs` in CMake?

Does anyone know how to add the option `llvm-config --cxxflags --ldflags --libs` into CMake? The tricky part for me is the backtick `.
I need to config my CMake files to obtain a compilation command like:
g++ test.cpp -lclangBasic -I/usr/lib/llvm-6.0/include
-Wall `llvm-config --cxxflags --ldflags --libs`
I tried to use the following options, but they don't work:
add_compile_options(`llvm-config --cxxflags --ldflags --libs`)
# or
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} `llvm-config --cxxflags --ldflags --libs`")
Thank you in advance.
===============
Update 1.
Using the output from `llvm-config --cxxflags --ldflags --libs`, I can compile successfully with the following command:
g++ test.cpp -lclangBasic -I/usr/lib/llvm-6.0/include
-Wall -I/usr/lib/llvm-6.0/include -L/usr/lib/llvm-6.0/lib -lLLVM-6.0
I can pass -I/usr/lib/llvm-6.0/include by using include_directories(usr/lib/llvm-6.0/include).
But still, I don't know how to pass the part -L/usr/lib/llvm-6.0/lib -lLLVM-6.0 to CMake. Using link_directories and target_link_libraries like the following doesn't work for me:
link_directories(/usr/lib/llvm-6.0/lib)
target_link_libraries(test PUBLIC "LLVM-6.0")
Does anyone know to make them work in CMake?
===============
Update 2.
I have to add the following code into the file CMakeLists.txt to make CMake work.
add_library(LLVM-6.0 SHARED IMPORTED) # or STATIC instead of SHARED
set_target_properties(LLVM-6.0 PROPERTIES
IMPORTED_LOCATION "/usr/lib/llvm-6.0/lib/libLLVM-6.0.so"
INTERFACE_INCLUDE_DIRECTORIES "/usr/lib/llvm-6.0/include"
)
add_library(clangBasic SHARED IMPORTED) # or STATIC instead of SHARED
set_target_properties(clangBasic PROPERTIES
IMPORTED_LOCATION "/usr/lib/llvm-6.0/lib/libclangBasic.a"
INTERFACE_INCLUDE_DIRECTORIES "/usr/lib/llvm-6.0/include"
)
target_link_libraries(solidity PUBLIC "LLVM-6.0;clangBasic")
However, this looks manually, and I'm still looking for better solutions...
LLVM/Clang use CMake and provide a config module, so any project trying to use LLVM/Clang just need a find_package(Clang...) to bring the exported targets into a scope (and don't need to use any external tools (like llvm-config or pkg-config blah-blah)). Meaning that after Clang has found, you can use target_link_libraries(blah Clang::clangBasic ...) and no need to do any "manual import" or whatever ppl suggest in the comments...
As a quick check one may use the following command:
$ cmake --find-package -DNAME=Clang -DCOMPILER_ID=GNU -DLANGUAGE=CXX -DMODE=EXIST
Clang found.
$ cmake --find-package -DNAME=Clang -DCOMPILER_ID=GNU -DLANGUAGE=CXX -DMODE=COMPILE
-I/usr/lib/llvm-6.0/include
I used Clang 6.0 installed from the deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-6.0 main repo.

Set multiple paths to CMAKE_Fortran_MODULE_DIRECTORY

So I am trying to include an installed Fortran library to a CMake project. I know the directory where the module (.mod) file is located, but my project can't seem to find it unless I set CMAKE_Fortran_MODULE_DIRECTORY.
SET(LIB_INCLUDE_DIR /usr/local/include)
SET(EX_FILES main.f90 file1.f90 file2.f90)
INCLUDE_DIRECTORIES(${LIB_INCLUDE_DIR})
ADD_EXECUTABLE(test ${EX_FILES})
TARGET_LINK_LIBRARIES(test lib1)
Where the error is
use lib1
1
Fatal Error: Can't open module file 'lib1.mod' for reading at (1): No such file or directory
unless I include the line
SET(CMAKE_Fortran_MODULE_DIRECTORY ${LIB_INCLUDE_DIR})
And then it can find the file just fine. But I'm running into a little problem. By setting CMAKE_Fortran_MODULE_DIRECTORY, CMake tries to write all generated modules to this directory rather than the CMAKE_BINARY_DIR (where I would like it).
From the documentation, I know that CMAKE_Fortan_MODULE_DIRECTORY is meant to be set to specify where to write generated module files, but some compilers look to that directory to find modules. Is there any way to set multiple directories so that if it can't find in/write to one directory, it searches the second? When I try to set CMAKE_Fortran_MODULE_DIRECTORY to be multiple directories, it only looks at the first directory.
If it helps I am on a Ubuntu 14.04 LTS system using gfortran 4.8.4
EDIT:
So by Alexander Vogt's suggestion I ran with VERBOSE=1 and got
cd /home/user/Repos/build/src && gfortran -I/home/user/Repos/build/src -isystem /usr/local/include -c /home/user/Repos/test_project/src/main.f90 -o CMakeFiles/test.dir/main.f90.o
when I did NOT set CMAKE_Fortran_MODULE_DIRECTORY and
cd /home/user/Repos/build/src && gfortran -J/usr/local/include -I/home/user/Repos/build/src -isystem /usr/local/include -c /home/user/Repos/test_project/src/main.f90 -o CMakeFiles/test.dir/main.f90.o
when I did.
It seems the only difference is the -J flag, which sets exactly what CMAKE_Fortran_MODULE_DIRECTORY sets. Is there some flag that sets just where to look for compiled modules, and not where to put them?