I'm working on a project and decided to port it to CMake. While I was configuring I used the placeholder build name build; no problems were encountered. But after I switched the name to S&W, CMake wouldn't let me build. It produced the error message:
[cmake] CMake Error at CMakeLists.txt:28 (add_executable):
[cmake] The target name "S&W" is reserved or not valid for certain CMake features,
[cmake] such as generator expressions, and may result in undefined behavior.
I'm pretty sure the name S&W is not reserved by CMake.
So Why is this happening? Can I do anything to fix this error?
cmake_minimum_required(VERSION 3.14)
project(S&W)
include_directories("source")
include_directories("dependencies/include")
file(GLOB_RECURSE SRC "source/*.cpp")
LINK_DIRECTORIES("dependencies/lib/x86")
if ( MINGW )
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")
LINK_LIBRARIES("mingw32")
endif ( MINGW )
LINK_LIBRARIES("libSDL2main.a")
LINK_LIBRARIES("libSDL2.dll.a")
LINK_LIBRARIES("libSDL2_mixer.dll.a")
add_executable(${PROJECT_NAME} ${SRC})
The error tells that you cannot declare a target which contains some special symbols (& in your case).
However, this doesn't mean that you cannot generate the file with given name. Just create "normal" executable target, but assign OUTPUT_NAME property for it:
add_executable(SW ${SRC})
set_target_properties(SW PROPERTIES OUTPUT_NAME "S&W")
You have forbidden character in target name, namely &.
Take a look at documentation about cmake target naming, this paragraph in particular:
Target names may contain upper and lower case letters, numbers, the underscore
character (_), dot(.), plus(+) and minus(-). As a special case, ALIAS targets
and IMPORTED targets may contain two consequtive colons.
Related
Using CMake version 3.20.1, I'm doing something that I consider to be relatively simple: verify that the value of cfg pass via -DCMAKE_BUILD_TYPE=<cfg> is a valid configuration.
Referencing the CMake generator expressions docs, it appears that $<CONFIG:cfgs> is a specialized case of $<IN_LIST:string,list> where the string is ${CMAKE_CONFIGURATION_TYPES}. The CMake runtime-error I'm getting reinforces this, as well as telling me that I'm missing something.
What I'm expecting to work is this:
set( CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Build Configurations" FORCE )
if( NOT $<CONFIG:${CMAKE_CONFIGURATION_TYPES}> )
# Handle_the_bad_value()
endif()
when called from the command line as cmake .. -DCMAKE_BUILD_TYPE=Debug, but I get the following error:
CMake Error at CMakeLists.txt:45 (if):
if given arguments:
"NOT" "\$<CONFIG:Debug" "Release>"
Unknown arguments specified
Replacing
$<CONFIG:<cfg_list>
with either:
$<IN_LIST:${CMAKE_BUILD_TYPE},${CMAKE_CONFIGURATION_TYPES}>
or
$<IN_LIST:"Debug","Debug;Release">
gives me the following:
CMake Error at CMakeLists.txt:43 (if):
if given arguments:
"NOT" "\$<IN_LIST:Debug,Debug" "Release>"
Unknown arguments specified
I've played around with various changes to the original syntax before experimenting with $<IN_LIST:str,list>, but being that I cannot get the IN_LIST syntax to work, I feel there is something fundamental that I am getting wrong that I'm also failing to grasp from the documentation.
Subsequent experimentation, using:
if( NOT ${CMAKE_BUILD_TYPE} IN_LIST ${CMAKE_CONFIGURATION_TYPES} )
yields:
CMake Error at CMakeLists.txt:43 (if):
if given arguments:
"NOT" "Debug" "IN_LIST" "Debug" "Release"
Unknown arguments specified
So I tried brute force:
if( NOT ( "Debug" IN_LIST "Debug;Release" ) )
which evaluates, but incorrectly, because it does not find "Debug" in the list, so it enters the code within the if-statement. Final try:
if( NOT ( CMAKE_BUILD_TYPE IN_LIST CMAKE_CONFIGURATION_TYPES ) )
Does work as expected. Noted: NOT should apply to an expression that is within parenthesis. But it still doesn't help me understand the correct syntax for the CONFIG generator expression.
You cannot use generator expressions in if command, because generator expressions are expanded only at the end of configuration process, but if condition needs to be evaluated immediately.
Expression $<IN_LIST:<$CONFIG>,${CMAKE_CONFIGURATION_TYPES}> is valid
in BOOL context of other generator expression. But it could be used only for produce another string, not for emit an error.
If you want to check correctness of the build type, specified by a user, following things should be taken into account:
On multi-configuration generators (like Visual Studio) a build type is given by --config option at build stage. CMake automatically checks such build type for match against CMAKE_CONFIGURATION_TYPES, so there is no needs in manual checks.
On single-configiration generators (like Makefile) a build type is specified via CMAKE_BUILD_TYPE variable. So there is no needs in a generator expression to refer a build type.
Variable CMAKE_CONFIGURATION_TYPES should be specified only for multi-configuration generators. Setting it for single-configuration generators could confuse some scripts (e.g. FindXXX.cmake).
So, you could check a build type in the following way (based on the code in that answer)
# Use custom variable for avoid setting CMAKE_CONFIGURATION_TYPES
# for single-configuration generators.
set(MY_CONFIGURATION_TYPES "Debug;Release")
# Check whether generator is single-configuration or multi-configuration
get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(isMultiConfig)
# Multi-configuration generator
# Set possible values. CMake automatically checks correctness.
set(CMAKE_CONFIGURATION_TYPES "${MY_CONFIGURATION_TYPES}" CACHE STRING "" FORCE)
else()
# Single-configuration generator
if(NOT CMAKE_BUILD_TYPE)
# If not set, set the build type to default.
message("Defaulting to release build.")
set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
else()
# Check correctness of the build type entered by user
# Note that the right operand for IN_LIST should be **variable**,
# not just a list of values
if (NOT (CMAKE_BUILD_TYPE IN_LIST MY_CONFIGURATION_TYPES))
message(FATAL_ERROR "Incorrect build type")
endif()
endif()
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY HELPSTRING "Choose the type of build")
# set the suggested values for cmake-gui drop-down list
# Note, that CMake allows a user to input other values.
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${MY_CONFIGURATION_TYPES}")
endif()
Using CMake consists of two stages:
configure stage cmake -S . -B _build or cd _build && cmake .. etc.
build stage cmake --build _build or cd _build && make etc.
Generator expressions $<suff> are expanded when building the project. They are not expanded inside CMake scripts. For CMake script, when configuring the project, it's just text.
To compare it in CMake, compare it in CMake. I guess:
if (CMAKE_BUILD_TYPE IN_LIST CMAKE_CONFIGURATION_TYPES)
I want to set compiler path (for example:icc) automatically in cmake, so my program can compile at any computer as long as it have installed icc, and we do not need to care about where the icc is installed.
At First, I using the follow command to set compiler. Everything is OK.
set(Intel_C_COMPILER "/opt/intel/compilers_and_libraries_2019.0.117/linux/bin/intel64/icc")
set(Intel_CXX_COMPILER "/opt/intel/compilers_and_libraries_2019.0.117/linux/bin/intel64/icpc")
set(CMAKE_C_COMPILER ${Intel_C_COMPILER} )
set(CMAKE_CXX_COMPILER ${Intel_CXX_COMPILER})
project(MyProject)
....
Then, I want to set compiler path automatically, I know that the follow command can find compiler path
which icc
So I write the follow command try to set compiler automatically by cmake.
execute_process(COMMAND which icc OUTPUT_VARIABLE Intel_C_COMPILER)
execute_process(COMMAND which icpc OUTPUT_VARIABLE Intel_CXX_COMPILER)
message(Intel_CXX_COMPILER: ${Intel_C_COMPILER})
message(Intel_CXX_COMPILER: ${Intel_CXX_COMPILER})
set(CMAKE_C_COMPILER ${Intel_C_COMPILER} )
set(CMAKE_CXX_COMPILER ${Intel_CXX_COMPILER})
project(MyProject)
....
At these case, something strange happens, cmake shows that:
Intel_CXX_COMPILER:/opt/intel/compilers_and_libraries_2019.0.117/linux/bin/intel64/icpc
-- The C compiler identification is unknown
-- The CXX compiler identification is unknown
CMake Error at CMakeLists.txt:27 (project): The CMAKE_C_COMPILER:
/opt/intel/compilers_and_libraries_2019.0.117/linux/bin/intel64/icc
is not a full path to an existing compiler tool.
Tell CMake where to find the compiler by setting either the
environment variable "CC" or the CMake cache entry CMAKE_C_COMPILER
to the full path to the compiler, or to the compiler name if it is
in the PATH.
CMake Error at CMakeLists.txt:27 (project): The CMAKE_CXX_COMPILER:
/opt/intel/compilers_and_libraries_2019.0.117/linux/bin/intel64/icpc
is not a full path to an existing compiler tool.
Tell CMake where to find the compiler by setting either the
environment variable "CXX" or the CMake cache entry
CMAKE_CXX_COMPILER to the full path to the compiler, or to the
compiler name if it is in the PATH.
-- Configuring incomplete, errors occurred!
CMake said that the path is not a full path to an existing compiler, but as shown in the message, it is just where the compiler located!
I know there are other techniques that we can set compiler, for example export some environment variables to help cmake find the path, but I want to know why my method dose not work?
Is there any better way can handle this problem?
Thanks in advance.
Variables Intel_C_COMPILER and Intel_CXX_COMPILER have trailing newline. Way for removing that newline are described in that question and its answers: How to strip trailing whitespace in CMake variable?
E.g., you may run execute_process with OUTPUT_STRIP_TRAILING_WHITESPACE option, so it will behave similar to the shell's backtick operator (`which icc`).
Detailed description
Most of shell utilities output single- (or even multi-) line information with trailing newline. And utility which is not an exception. With trailing newline an output looks nice when one run these utilities in the terminal.
But when run such utility in the script and grab its output programmatically, one need to care about such newline.
Generally speaking, it is not possible to set the variables CMAKE_C_COMPILER and CMAKE_CXX_COMPILER from within a project.
Since the compiler detection happens with the project() call, the compiler has to be set early on while configuring the project.
I suggest you try the following:
export CC=/opt/intel/compilers_and_libraries_2019.0.117/linux/bin/intel64/icc
export CXX=/opt/intel/compilers_and_libraries_2019.0.117/linux/bin/intel64/icpc
cd /path/to/build
cmake /path/to/src
or you could also pass the variables CMAKE_C_COMPILER and CMAKE_CXX_COMPILER:
export CC=/opt/intel/compilers_and_libraries_2019.0.117/linux/bin/intel64/icc
export CXX=/opt/intel/compilers_and_libraries_2019.0.117/linux/bin/intel64/icpc
cd /path/to/build
cmake \
-DCMAKE_C_COMPILER:FILEPATH=/opt/intel/compilers_and_libraries_2019.0.117/linux/bin/intel64/icc \
-DCMAKE_CXX_COMPILER:FILEPATH=/opt/intel/compilers_and_libraries_2019.0.117/linux/bin/intel64/icpc \
/path/to/src
Important: When trying these commands, make sure to configure the project in an empty build directory.
It seems that cmake is not able to detect targets that do not exist. Honestly I was surprised to found that out.
target_link_libraries(Foo
PRIVATE
Bar
Car
)
If the library Car doesn't exist, I would expect that cmake returns an error at configuration time. Unfortunately this is not the case. Or is the behavior voluntary?
I use cmake version 3.12.1
Only the first argument to target_link_libraries required to be a target, other argument may be a target, but may be other things as well. Possible types of non-first arguments are described in the CMake documentation:
A library target name
A full path to a library file
A plain library name
A link flag
(keywords are omitted in that list).
In you case CMake interprets Car as a plain library name, so it will pass option -lCar to the linker.
Note, that CMake doesn't try to interpret the linker's flags.
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.
This question is related to Instruct Cmake to use CXX and CXXFLAGS when driving link? In the former question, we are trying to instruct CMake to use CXXFLAGS when it invokes the linker.
add_compile_options
We found that the following code
if (CMAKE_VERSION VERSION_LESS 2.8.12)
add_definitions(-foo)
else()
add_compile_options(-foo)
endif()
message(STATUS, "CXXFLAGS: ${CMAKE_CXX_FLAGS}")
produces the output
CXXFLAGS:
SET CMAKE_CXX_FLAGS
We found that the following code
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -foo" )
message(STATUS, "CXXFLAGS: ${CMAKE_CXX_FLAGS}")
produces the output
CXXFLAGS: -foo
Questions
We found CMake would create object files using -foo in both cases. So -foo is definitely making its way into CXXFLAGS.
What is the difference between the first set of CMake code and the second set of CMake code?
Why is CMAKE_CXX_FLAGS unset in one instance, and set in the other instance?
CMAKE_CXX_FLAGS is used to add flags for all C++ targets. That's handy to pass general arguments like warning levels or to selected required C++ standards. It has no effect on C or Fortran targets and the user might pass additional flags.
add_compile_options adds the options to all targets within the directory and its sub-directories. This is handy if you have a library in a directory and you want to add options to all the targets related to the library, but unrelated to all other targets. Additionally, add_compile_options can handle arguments with generator expressions. The documentation explicitly states, that
This command can be used to add any options, but alternative commands
exist to add preprocessor definitions (target_compile_definitions()
and add_definitions()) or include directories
(target_include_directories() and include_directories()).
add_definitions is intended to pass pre-processor values of the type -DFOO -DBAR=32 (/D on Windows) which defines and sets pre-processor variables. You could pass any flag, but the flags of the above form are detected and added to [COMPILE_DEFINITIONS][2] property, which you can later read and change. Here, you can use generator expressions, too. The documentation mentions scopes for directories, targets and source files.
For a given target, CMake will collect all flags from CMAKE_CXX_FLAGS, the target's and directory's COMPILE_DEFINITIONS and from all add_compile_options which affect the target.
CMAKE_CXX_FLAGS are not altered by the other commands or vice versa. This would violate the scope of these commands.