I have 2 libs and 1 executable in my CMakeList.txt. I would like everything linked into the executable.
cmake_minimum_required( VERSION 2.8 )
# Mark the language as C so that CMake doesn't try to test the C++
# cross-compiler's ability to compile a simple program because that will fail
project( jsos C ASM )
set( CMAKE_EXECUTABLE_OUTPUT_PATH "./build/" )
# We had to adjust the CMAKE_C_FLAGS variable in the toolchain file to make sure
# the compiler would work with CMake's simple program compilation test. So unset
# it explicitly before re-setting it correctly for our system
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0" )
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g" )
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostartfiles" )
# Set the linker flags so that we use our "custom" linker script
set( CMAKE_EXE_LINKER_FLAGS "-Wl,-T,${PROJECT_SOURCE_DIR}/etc/linker.ld" )
add_library(duktape STATIC
src/libs/duktape/duktape.c
)
add_library(fdlibm STATIC
src/libs/fdlibm/e_acos.c
src/libs/fdlibm/e_acosh.c
src/libs/fdlibm/e_asin.c
MORE FILES
)
add_executable(kernel
src/start.S
src/kernel.c
src/cstartup.c
src/cstubs.c
src/rpi-gpio.c
src/rpi-interrupts.c
src/rpi-armtimer.c
src/rpi-systimer.c
)
add_dependencies(kernel fdlibm duktape)
target_link_libraries(kernel fdlibm duktape)
add_custom_command(
TARGET kernel POST_BUILD
COMMAND ${CMAKE_OBJCOPY} ./kernel${CMAKE_EXECUTABLE_SUFFIX} -O binary ./kernel.img
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Convert the ELF output file to a binary image"
)
At the moment when I link these together I get a bunch of errors like:
[100%] Linking C executable kernel
libduktape.a(duktape.c.obj): In function `duk_double_trunc_towards_zero':
src/libs/duktape/duktape.c:12102: undefined reference to `fabs'
src/libs/duktape/duktape.c:12102: undefined reference to `floor'
But fabs and floor are in fdlibm. duk_double_trunc_towards_zero is in the duktape library so that seemed to link OK. What am I doing wrong?
In your statement:
target_link_libraries(kernel fdlibm duktape)
the external referece symbols to be resolved are searched from the ordered list of libs provided (fdlibm duktape in this case) in this way:
starting from the library being linked;
searching the symbols on the libraries on the right of it;
In your case, when resolving external symbols of duktape (which some are in fdlibm), fdlibm is not even used for this search, and the symbols duktape requires are not found. Just putting fdlibm after duktape will let the symbols to be found.
For example, if you had a fdlibm depending on some symbols defined in duktape, and also the other way around, you should use:
target_link_libraries(kernel fdlibm duktape fdlibm)
so that symbols are always resolved.
Related
I am using the arm-linux-androideabi-g++ compiler. When I try to compile a simple "Hello, World!" program it compiles fine. When I test it by adding a simple exception handling in that code it works too (after adding -fexceptions .. I guess it is disabled by default).
This is for an Android device, and I only want to use CMake, not ndk-build.
For example - first.cpp
#include <iostream>
using namespace std;
int main()
{
try
{
}
catch (...)
{
}
return 0;
}
./arm-linux-androideadi-g++ -o first-test first.cpp -fexceptions
It works with no problem...
The problem ... I am trying to compile the file with a CMake file.
I want to add the -fexceptions as a flag. I tried with
set (CMAKE_EXE_LINKER_FLAGS -fexceptions ) or set (CMAKE_EXE_LINKER_FLAGS "fexceptions" )
and
set ( CMAKE_C_FLAGS "fexceptions")
It still displays an error.
Note: Given CMake evolution since this was answer was written in 2012, most of the suggestions here are now outdated/deprecated and have better alternatives.
Suppose you want to add those flags (better to declare them in a constant):
SET(GCC_COVERAGE_COMPILE_FLAGS "-fprofile-arcs -ftest-coverage")
SET(GCC_COVERAGE_LINK_FLAGS "-lgcov")
There are several ways to add them:
The easiest one (not clean, but easy and convenient, and works only for compile flags, C & C++ at once):
add_definitions(${GCC_COVERAGE_COMPILE_FLAGS})
Appending to corresponding CMake variables:
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
Using target properties, cf. doc CMake compile flag target property and need to know the target name.
get_target_property(TEMP ${THE_TARGET} COMPILE_FLAGS)
if(TEMP STREQUAL "TEMP-NOTFOUND")
SET(TEMP "") # Set to empty string
else()
SET(TEMP "${TEMP} ") # A space to cleanly separate from existing content
endif()
# Append our values
SET(TEMP "${TEMP}${GCC_COVERAGE_COMPILE_FLAGS}" )
set_target_properties(${THE_TARGET} PROPERTIES COMPILE_FLAGS ${TEMP} )
Right now I use method 2.
In newer versions of CMake you can set compiler and linker flags for a single target with target_compile_options and target_link_libraries respectively (yes, the latter sets linker options too):
target_compile_options(first-test PRIVATE -fexceptions)
The advantage of this method is that you can control propagation of options to other targets that depend on this one via PUBLIC and PRIVATE.
As of CMake 3.13 you can also use target_link_options to add linker options which makes the intent more clear.
Try setting the variable CMAKE_CXX_FLAGS instead of CMAKE_C_FLAGS:
set (CMAKE_CXX_FLAGS "-fexceptions")
The variable CMAKE_C_FLAGS only affects the C compiler, but you are compiling C++ code.
Adding the flag to CMAKE_EXE_LINKER_FLAGS is redundant.
The preferred way to specify toolchain-specific options is using CMake's toolchain facility. This ensures that there is a clean division between:
instructions on how to organise source files into targets -- expressed in CMakeLists.txt files, entirely toolchain-agnostic; and
details of how certain toolchains should be configured -- separated into CMake script files, extensible by future users of your project, scalable.
Ideally, there should be no compiler/linker flags in your CMakeLists.txt files -- even within if/endif blocks. And your program should build for the native platform with the default toolchain (e.g. GCC on GNU/Linux or MSVC on Windows) without any additional flags.
Steps to add a toolchain:
Create a file, e.g. arm-linux-androideadi-gcc.cmake with global toolchain settings:
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
set(CMAKE_CXX_FLAGS_INIT "-fexceptions")
(You can find an example Linux cross-compiling toolchain file here.)
When you want to generate a build system with this toolchain, specify the CMAKE_TOOLCHAIN_FILE parameter on the command line:
mkdir android-arm-build && cd android-arm-build
cmake -DCMAKE_TOOLCHAIN_FILE=$(pwd)/../arm-linux-androideadi-gcc.cmake ..
(Note: you cannot use a relative path.)
Build as normal:
cmake --build .
Toolchain files make cross-compilation easier, but they have other uses:
Hardened diagnostics for your unit tests.
set(CMAKE_CXX_FLAGS_INIT "-Werror -Wall -Wextra -Wpedantic")
Tricky-to-configure development tools.
# toolchain file for use with gcov
set(CMAKE_CXX_FLAGS_INIT "--coverage -fno-exceptions -g")
Enhanced safety checks.
# toolchain file for use with gdb
set(CMAKE_CXX_FLAGS_DEBUG_INIT "-fsanitize=address,undefined -fsanitize-undefined-trap-on-error")
set(CMAKE_EXE_LINKER_FLAGS_INIT "-fsanitize=address,undefined -static-libasan")
You can also add linker flags to a specific target using the LINK_FLAGS property:
set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS " ${flag}")
If you want to propagate this change to other targets, you can create a dummy target to link to.
This worked for me when I needed a precompile definition named "NO_DEBUG":
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -DNO_DEBUG")
Then from code
#ifdef NO_DEBUG
.....
With CMake 3.4+, APPEND can be used with the string command to add flags.
string(APPEND CMAKE_EXE_LINKER_FLAGS " -fexceptions")
I am using the arm-linux-androideabi-g++ compiler. When I try to compile a simple "Hello, World!" program it compiles fine. When I test it by adding a simple exception handling in that code it works too (after adding -fexceptions .. I guess it is disabled by default).
This is for an Android device, and I only want to use CMake, not ndk-build.
For example - first.cpp
#include <iostream>
using namespace std;
int main()
{
try
{
}
catch (...)
{
}
return 0;
}
./arm-linux-androideadi-g++ -o first-test first.cpp -fexceptions
It works with no problem...
The problem ... I am trying to compile the file with a CMake file.
I want to add the -fexceptions as a flag. I tried with
set (CMAKE_EXE_LINKER_FLAGS -fexceptions ) or set (CMAKE_EXE_LINKER_FLAGS "fexceptions" )
and
set ( CMAKE_C_FLAGS "fexceptions")
It still displays an error.
Note: Given CMake evolution since this was answer was written in 2012, most of the suggestions here are now outdated/deprecated and have better alternatives.
Suppose you want to add those flags (better to declare them in a constant):
SET(GCC_COVERAGE_COMPILE_FLAGS "-fprofile-arcs -ftest-coverage")
SET(GCC_COVERAGE_LINK_FLAGS "-lgcov")
There are several ways to add them:
The easiest one (not clean, but easy and convenient, and works only for compile flags, C & C++ at once):
add_definitions(${GCC_COVERAGE_COMPILE_FLAGS})
Appending to corresponding CMake variables:
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
Using target properties, cf. doc CMake compile flag target property and need to know the target name.
get_target_property(TEMP ${THE_TARGET} COMPILE_FLAGS)
if(TEMP STREQUAL "TEMP-NOTFOUND")
SET(TEMP "") # Set to empty string
else()
SET(TEMP "${TEMP} ") # A space to cleanly separate from existing content
endif()
# Append our values
SET(TEMP "${TEMP}${GCC_COVERAGE_COMPILE_FLAGS}" )
set_target_properties(${THE_TARGET} PROPERTIES COMPILE_FLAGS ${TEMP} )
Right now I use method 2.
In newer versions of CMake you can set compiler and linker flags for a single target with target_compile_options and target_link_libraries respectively (yes, the latter sets linker options too):
target_compile_options(first-test PRIVATE -fexceptions)
The advantage of this method is that you can control propagation of options to other targets that depend on this one via PUBLIC and PRIVATE.
As of CMake 3.13 you can also use target_link_options to add linker options which makes the intent more clear.
Try setting the variable CMAKE_CXX_FLAGS instead of CMAKE_C_FLAGS:
set (CMAKE_CXX_FLAGS "-fexceptions")
The variable CMAKE_C_FLAGS only affects the C compiler, but you are compiling C++ code.
Adding the flag to CMAKE_EXE_LINKER_FLAGS is redundant.
The preferred way to specify toolchain-specific options is using CMake's toolchain facility. This ensures that there is a clean division between:
instructions on how to organise source files into targets -- expressed in CMakeLists.txt files, entirely toolchain-agnostic; and
details of how certain toolchains should be configured -- separated into CMake script files, extensible by future users of your project, scalable.
Ideally, there should be no compiler/linker flags in your CMakeLists.txt files -- even within if/endif blocks. And your program should build for the native platform with the default toolchain (e.g. GCC on GNU/Linux or MSVC on Windows) without any additional flags.
Steps to add a toolchain:
Create a file, e.g. arm-linux-androideadi-gcc.cmake with global toolchain settings:
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
set(CMAKE_CXX_FLAGS_INIT "-fexceptions")
(You can find an example Linux cross-compiling toolchain file here.)
When you want to generate a build system with this toolchain, specify the CMAKE_TOOLCHAIN_FILE parameter on the command line:
mkdir android-arm-build && cd android-arm-build
cmake -DCMAKE_TOOLCHAIN_FILE=$(pwd)/../arm-linux-androideadi-gcc.cmake ..
(Note: you cannot use a relative path.)
Build as normal:
cmake --build .
Toolchain files make cross-compilation easier, but they have other uses:
Hardened diagnostics for your unit tests.
set(CMAKE_CXX_FLAGS_INIT "-Werror -Wall -Wextra -Wpedantic")
Tricky-to-configure development tools.
# toolchain file for use with gcov
set(CMAKE_CXX_FLAGS_INIT "--coverage -fno-exceptions -g")
Enhanced safety checks.
# toolchain file for use with gdb
set(CMAKE_CXX_FLAGS_DEBUG_INIT "-fsanitize=address,undefined -fsanitize-undefined-trap-on-error")
set(CMAKE_EXE_LINKER_FLAGS_INIT "-fsanitize=address,undefined -static-libasan")
You can also add linker flags to a specific target using the LINK_FLAGS property:
set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS " ${flag}")
If you want to propagate this change to other targets, you can create a dummy target to link to.
This worked for me when I needed a precompile definition named "NO_DEBUG":
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -DNO_DEBUG")
Then from code
#ifdef NO_DEBUG
.....
With CMake 3.4+, APPEND can be used with the string command to add flags.
string(APPEND CMAKE_EXE_LINKER_FLAGS " -fexceptions")
I have this CMakeLists.txt file:
cmake_minimum_required(VERSION 3.8)
include(${CMAKE_CURRENT_SOURCE_DIR}/src/Something.cmake)
add_executable(execute main.cpp)
add_dependencies(somethingInterface Something)
add_dependencies(execute somethingInterface)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_compile_options(execute
PRIVATE
-std=c++11
-g
)
add_library(library SHARED IMPORTED)
set_target_properties(library PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/library.so)
target_link_libraries(execute
PRIVATE
library
)
The library shared imported will be created in file Something.cmake, but tt must be built first.
It was a add_custom_command(TARGET POST_BUILD...) in file Something.cmake.
I don't have any problem in using CMake builds here, but when I am using Ninja there an error.
ninja: error: 'library.so', needed by 'execute', missing and no known rule to make it
Or do you have any suggestion on how can you do this?
I think Ninja has a requirement that "library.so" must exist first, but CMake it is different. It checks whether the library is there at runtime.
There is indeed a divergence between the way Make and Ninja handle imported targets. What works with Make, may sometimes not work with Ninja.
In particular, the following lines of code work with Make, but not with Ninja:
ExternalProject_Add(extProject
GIT_REPOSITORY <GIT_URL>
CMAKE_CACHE_ARGS "-
DCMAKE_INSTALL_PREFIX:STRING=${CMAKE_INSTALL_PREFIX}"
)
add_library(extLib SHARED IMPORTED)
add_dependencies(extLib extProject)
set_target_properties(extLib
PROPERTIES IMPORTED_LOCATION ${CMAKE_INSTALL_PREFIX}/lib/libext.so
)
target_link_libraries(project extLib)
The CMake configure step will work fine, but at build time Ninja will complain:
ninja: error: '/path/to/libext.so', needed by 'project', missing and no known rule to make it
But this will work fine with Make.
You need to specify the library as a byproduct of the ExternalProject_Add comment as mentioned by Tsyvarev, as ExternalProject runs at build time.
The following works fine for me:
ExternalProject_Add(extProject
GIT_REPOSITORY <GIT_URL>
CMAKE_CACHE_ARGS "-
DCMAKE_INSTALL_PREFIX:STRING=${CMAKE_INSTALL_PREFIX}"
BUILD_BYPRODUCTS ${CMAKE_INSTALL_PREFIX}/lib/libext.so
)
add_library(extLib SHARED IMPORTED)
add_dependencies(extLib extProject)
set_target_properties(extLib
PROPERTIES IMPORTED_LOCATION ${CMAKE_INSTALL_PREFIX}/lib/libext.so
)
target_link_libraries(project extLib)
Elsewhere the question has been asked, "How do I turn off optimizations on one file?" The answer is usually something like this:
cmake_minimum_required( VERSION 3.8 )
project( Hello )
add_executable( hello hello.c foo.c bar.c )
set( CMAKE_C_FLAGS_RELEASE "" )
set( CMAKE_CXX_FLAGS_RELEASE "" )
set_source_files_properties( hello.c
PROPERTIES
COMPILE_FLAGS -O0 )
This works unless you invoke cmake like this:
cmake -GNinja -DCMAKE_BUILD_TYPE=Release ../hello
And you get this in your build.ninja
FLAGS = -O3 -DNDEBUG -O0
Checking the documentation on COMPILE_FLAGS
Additional flags to be added when compiling this source file.
This makes sense, it is added to the list of COMPILE_FLAGS, it does not override existing compiler flags.
So, within CMake how can you override the optimisation level on a single file and being able to compile the rest of the project in Release? Otherwise you can force the compile to CMAKE_BUILD_TYPE="" which is the default behavior, but that somewhat defeats a selling point of Cmake.
You can't overwrite compiler options with the makefile CMake generators on source file level. Options are always appended (see my answer at Is Cmake set variable recursive? for the complete formula).
This is - as far as I know - only supported with the Visual Studio solution/project generators. These generators have flag tables to identify flags that are in the same group/that does overwrite a previous defined flag.
So yours is more like a feature request to also add compiler option tables to CMake's makefile generators.
Alternatives
I just wanted to add some crazy CMake magic I came up with as a workaround. Add the following to your main CMakeLists.txt after the project() command:
if (CMAKE_BUILD_TYPE)
define_property(
SOURCE
PROPERTY COMPILE_FLAGS
INHERITED
BRIEF_DOCS "brief-doc"
FULL_DOCS "full-doc"
)
string(TOUPPER ${CMAKE_BUILD_TYPE} _build_type)
set_directory_properties(PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS_${_build_type}}")
set(CMAKE_CXX_FLAGS_${_build_type} "")
endif()
This example moves the CMAKE_CXX_FLAGS_<build type> content into an new COMPILE_FLAGS directory property that is then linked to COMPILE_FLAGS source file property via define_property(... INHERITED ...).
Now the build type specific flags are only defined in COMPILE_FLAGS for each source file and you can overwrite/change them e.g. with the code snippet from your example:
set_source_files_properties(
hello.c
PROPERTIES
COMPILE_FLAGS -O0
)
References
Directory properties and subdirectories
CMake: How do I change properties on subdirectory project targets?
My project has a dependency library in ~/SimGrid/lib directory.
How can I correctly add it to my CMakeLists.txt?
Now I try link_directories(/home/whoami/SimGrid/lib), but it doesn't help and gives:
[ 5%] Linking CXX executable CSim2Sim
/usr/bin/ld: cannot find -lsimgrid
collect2: error: ld returned 1 exit status
My full CMakeLists.txt is here:
project(CSim2Sim)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g -O0")
set(SOURCE_FILES src/main.cpp)
add_executable(CSim2Sim ${SOURCE_FILES})
include_directories(/home/whoami/SimGrid/include)
link_directories(/home/whoami/SimGrid/lib)
target_link_libraries(CSim2Sim simgrid)
On Linux, linker expects library filename to be prefixed with "lib":
/home/whoami/SimGrid/lib/libsimgrid.so
If your library file has no such prefix, it cannot be found by the linker using plain library name.
As opposite, specifying full library filename in target_link_libraries call works always:
# Assume filename of the library is 'simgrid.so'
target_link_libraries(CSim2Sim /home/whoami/SimGrid/lib/simgrid.so)
In that case link_directories() call isn't needed.
link_directories only tells CMake what directories to look inside for the libraries you want to link to, not what libraries you want to link. My suggestion would be to create a file named SimGrid.cmake and add the following:
# -*- cmake -*-
set(SIMGRID_INCLUDE_DIRS
${CMAKE_SOURCE_DIR}/SimGrid/include
)
set(SIMGRID_LIBRARIES
simgrid.a
simgrid2.a # etc for the names of the actual libs you want to link
)
In your CMakeLists.txt add include(SimGrid) to pull the new file in.
Then in your CmakeLists.txt use the new variables and target_link_libraries which is the function you use to tell the linker what libraries you want to link:
include_directories(${SIMGRID_INCLUDE_DIRS})
target_link_libraries(CSim2Sim ${SIMGRID_LIBRARIES})
Of course, you could just add it all to your single CMakeLists.txt, but dividing it up is good practice to keep things manageable.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g -O0")
set(SOURCE_FILES src/main.cpp)
add_executable(CSim2Sim ${SOURCE_FILES})
link_directories(${CMAKE_SOURCE_DIR}/SimGrid/lib)
include_directories(${CMAKE_SOURCE_DIR}/SimGrid/include)
set(SIMGRID_LIBRARIES
simgrid.a
simgrid2.a
)
target_link_libraries(CSim2Sim ${SIMGRID_LIBRARIES})
My recommendation is to find the libs your project depends on and link it to the target. Thus any absolut or relative path are not in the cmake file but detected while run of cmake.
See How can I find a library name of .so file?