I have this particular call to target_link_libraries that I am trying to get working.
target_link_libraries(
Array
$<$<CXX_COMPILER_ID:Clang>: c++abi>
$<$<CXX_COMPILER_ID:GNU>: c++ c++abi c gcc_s gcc>
)
It is outputting a build command like this:
clang++ CMakeFiles/Array.dir/driver.cpp.o CMakeFiles/Array.dir/Array.cpp.o -o Array $<1: -lc++abi> $<0: -lc++ -lc++abi -lc -lgcc_s -lgcc>
Am I writing the generator expressions correctly or is the call to target_link_libraries wrong?
So I fixed it my putting the the generators in quotes and replacing spaces with semi-colons.
target_link_libraries(
Array
"$<$<CXX_COMPILER_ID:Clang>:c++abi>"
"$<$<CXX_COMPILER_ID:GNU>:c++;c++abi;c;gcc_s;gcc>"
)
Related
I have several executables:
add_executable(exe1 ${DRIVERS_DIR}/exe1.cpp)
add_executable(exe2 ${DRIVERS_DIR}/exe2.cpp)
add_executable(exe3 ${DRIVERS_DIR}/exe3.cpp)
And I need to add a link library to all of them:
target_link_libraries(exe1 ${LIB_NAME})
target_link_libraries(exe2 ${LIB_NAME})
target_link_libraries(exe3 ${LIB_NAME})
How can I replace three target_link_libraries with a single one with generator expression for exe1, exe2, exe3 ?
with generator expression for exe1, exe2, exe3?
You cannot use a generator expression in the target argument of target_link_libraries, period. It simply is impossible.
How can I replace three target_link_libraries with a single one[?]
You can use a loop:
set(exes exe1 exe2 exe3)
foreach (exe IN LISTS exes)
add_executable("${exe}" "${DRIVERS_DIR}/${exe}.cpp")
target_link_libraries("${exe}" PRIVATE "${LIB_NAME}")
endforeach ()
This looks pretty clean to me.
You cannot use generator expressions here.
It's pretty simple to create a function for creating the executable and linking it though:
#[===[
Usage:
my_create_linked_exe(target source...)
]===]
function(my_create_linked_exe TARGET SRC)
add_executable(${TARGET} ${SRC} ${ARGN})
target_link_libraries(${TARGET} PRIVATE ${LIB_NAME})
endfunction()
my_create_linked_exe(exe1 ${DRIVERS_DIR}/exe1.cpp)
my_create_linked_exe(exe2 ${DRIVERS_DIR}/exe2.cpp)
# helper.cpp only added to demonstrate you could pass more than a single parameter
my_create_linked_exe(exe3 ${DRIVERS_DIR}/exe3.cpp ${DRIVERS_DIR}/helper.cpp)
I have the generator expression GEN_EXPR_INCLUDE_PATHS that provides a list of the directories to include.
The goal is to pass this list directly as compiler option via add_custom_command:
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/${HEADER_FILE_NAME}.gch
DEPENDS ${HEADER_FILE} IMPLICIT_DEPENDS CXX ${HEADER_FILE}
COMMAND ${CMAKE_CXX_COMPILER}
${FLAGS}
${HEADER_INCLUDE_PATHS}
${gccGarbageCollectorOpts}
-c ${HEADER_FILE} -o
${CMAKE_BINARY_DIR}/${HEADER_FILE_NAME}.gch
)
Where HEADER_INCLUDE_PATHS is following generator expression:
$<1:-I$<JOIN:$<TARGET_PROPERTY:Some_Target,INTERFACE_INCLUDE_DIRECTORIES>, -I>>
Just because the INTERFACE_INCLUDE_DIRECTORIES of Some_Target is received as generator expression.
But cmake adds escape chars before spaces, and gcc fails due to these extra chars.
Is it possible to do something with it?
I'll be appriciate for any support.
add_custom_command accepts COMMAND parameters as a list. In CMake a list is a string which elements are separated by semicolon, so it is semicolon which should be used for joining, not a space.
Also, for correctly split semicolon-containing strings, add_custom_command needs COMMAND_EXPAND_LISTS keyword.
set(HEADER_INCLUDE_PATHS -I$<JOIN:$<TARGET_PROPERTY:mylib,INTERFACE_INCLUDE_DIRECTORIES>,;-I>)
add_custom_command(
...
COMMAND ${CMAKE_CXX_COMPILER}
...
"${HEADER_INCLUDE_PATHS}" # Double quotes are important!
...
COMMAND_EXPAND_LISTS # This option is important too
)
Actually, an example with a space-separated join of include directories
-I$<JOIN:$<TARGET_PROPERTY:INCLUDE_DIRECTORIES>, -I>
is provided in CMake documentation itself. Not sure about their intention for this example.
I'm using a customized clang/llvm to build my project. The customization is basically the addition of optimization passes. To pass options to my passes when compiling with clang I'm using:
clang [..] -mllvm -MyOption [..]
Now it happens that I need to pass multiple options this way:
clang [..] -mllvm -MyOption -mllvm -MyOption2=value [..]
This in combination with CMake's target_compile_options() stops working, CMake removes the second -mllvm because it seems to think it is duplicated.
target_compile_options(vslib INTERFACE -mllvm -MyOption)
target_compile_options(vslib INTERFACE -mllvm -MyOption2=val)
I tried putting " around both options, doesn't work.
Is there a way to achieve this with CMake?
https://cmake.org/cmake/help/v3.12/command/target_compile_options.html:
The set of options is de-duplicated to avoid repetition. While beneficial for individual options, the de-duplication step can break up option groups. For example, -D A -D B becomes -D A B. One may specify a group of options using shell-like quoting along with a SHELL: prefix. The SHELL: prefix is dropped and the rest of the option string is parsed using the separate_arguments() UNIX_COMMAND mode. For example, "SHELL:-D A" "SHELL:-D B" becomes -D A -D B.
So in your case that would be:
target_compile_options(vslib INTERFACE "SHELL:-mllvm -MyOption" "SHELL:-mllvm -MyOption2=val")
Try:
get_property(tmp TARGET vslib PROPERTY INTERFACE_COMPILE_OPTIONS)
list(APPEND tmp -mllvm)
list(APPEND tmp -MyOption)
list(APPEND tmp -mllvm)
list(APPEND tmp -MyOption2=value)
set_property(TARGET vslib PROPERTY INTERFACE_COMPILE_OPTIONS "${tmp}")
or maybe just:
set_property(TARGET vslib APPEND PROPERTY INTERFACE_COMPILE_OPTIONS -mllvm -MyOption)
set_property(TARGET vslib APPEND PROPERTY INTERFACE_COMPILE_OPTIONS -mllvm -MyOption2=value)
How "${PROJECT_BINARY_DIR}/CMakeFiles/project.dir/", the place object files resulted from compilation will be placed on, can be un-hardcoded?
Going straightly to the problem, we have some tests that check objects resulted from compilation on harfbuzz cmake and we use a hardcoded string there but that doesn't seem right and I hope some ${} or $<> exist for that.
I'm afraid you're out of luck here. CMake keeps this as an internal implementation detail, by design.
I'd say it's unlikely to change, but if you want to be absolutely future-proof, you could use a workaround of creating a static library out of the object files and then manually unpacking it back into object files (using the appropriate archiver/librarian) as part of the test. If the object files are also used by another target, and linking to that static library wouldn't work for that target, you can make an object library out of the files and then use that in both the original target and the for-test static library.
Here's an example of how you might achieve this workaround:
add_library(MyObjectLib OBJECT src/a.cpp src/b.cpp)
add_executable(Main src/main.cpp $<TARGET_OBJECTS:MyObjectLib>)
add_library(LibForTesting STATIC $<TARGET_OBJECTS:MyObjectLib>)
add_test(
NAME check-static-inits.sh
COMMAND ${PROJECT_SOURCE_DIR}/src/prepare-and-check-static-inits.sh $<TARGET_FILE:LibForTesting>
)
And here's what the script prepare-and-check-static-inits.sh would look like (pseudo-code):
ar -x $1 -o some_dir
./check-static-inits.sh some_dir
Turning my comment into an answer
There is at the moment no variable or generator expression to get the list of object files used for linking a archive or library target.
But you could append compiler/archiver/linker calls with any program/script and utilize CMake's expansion rules inside those calls.
Note: That will only work CMake's Command-Line Build Tool Generators. And the list(APPEND ...) calls only have to be there once in your CMake code after your project() call.
Examples
Generate a symbolic link to <OBJECT_DIR> with <TARGET_NAME>
project(MyLib)
list(
APPEND CMAKE_CXX_ARCHIVE_FINISH
"\"${CMAKE_COMMAND}\" -E create_symlink \"<OBJECT_DIR>\" \"<TARGET_NAME>\""
)
[...]
add_library(MyLib STATIC src/a.cpp src/b.cpp)
Call some program to do something with the <OBJECTS> list (e.g. echo or write to a file)
project(MyExe)
list(
APPEND CMAKE_CXX_LINK_EXECUTABLE
"\"${CMAKE_COMMAND}\" -E echo \"<OBJECTS>\""
)
[...]
add_executable(MyExe main.cpp)
Directly do something after each object file is generated. In your case where you want to call objdump for each object file it would e.g. be:
list(
APPEND CMAKE_CXX_COMPILE_OBJECT
"${CMAKE_OBJDUMP} -t \"<OBJECT>\" > $(notdir <OBJECT>.dump)"
)
Unfortunately there is no expansion rule for "output file name" hence the platform/make specific trick with $(notdir ...).
References
CMAKE_<LANG>_ARCHIVE_FINISH
CMAKE_<LANG>_LINK_EXECUTABLE
CMAKE_<LANG>_COMPILE_OBJECT
How to get path to object files with CMake for both multiconfiguration generator and makefile based ones?
In my CMake scripts, I run other CMake instances using exec_program(${CMAKE_COMMAND} ...). I want to make the ${CMAKE_MODULES_PATH} of the parent environment available to the child environments. Therefore, I tried to pass the variable as argument:
exec_program(${CMAKE_COMMAND} ... ARGS ...
-DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} ...)
This messes up the other parameters and I get a CMake Error: The source directory <first-module-path> does not appear to contain CMakeLists.txt.. Therefore, I tried escaping the variable:
exec_program(${CMAKE_COMMAND} ... ARGS ...
-DCMAKE_MODULE_PATH="${CMAKE_MODULE_PATH}" ...)
When I print the ${CMAKE_MODULE_PATH} form within the child environment, all the paths get printed, separated by a space each. However, CMake doesn't find scripts inside those paths. I guess it has something to do with the list being passed as string rather than a semi-color separated list.
How can I pass a CMake variable holding a list of strings to another CMake command?
In my CMake scripts, I run other CMake instances using exec_program(${CMAKE_COMMAND} ...)
According to documentation this command is deprecated:
Deprecated. Use the execute_process() command instead.
Example with the list variable, CMakeLists.txt:
execute_process(
COMMAND "${CMAKE_COMMAND}" "-DVAR=A;B;C" -P script.cmake
OUTPUT_VARIABLE output
)
script.cmake:
message("VAR: ${VAR}")
foreach(x ${VAR})
message("x: ${x}")
endforeach()
output:
VAR: A;B;C
x: A
x: B
x: C