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?
Related
In the build process, I set directories where I gather the build output of different sub-projects. The directories are set as :
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_LIST_DIR}/../build/bin/debug" )
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_LIST_DIR}/../build/bin/release" )
Now, I'd like to copy some files (a directory of qt plugins) to that directory dependent on the configuration which it is built for.
I tried:
# copy qt plugins
add_custom_command( TARGET mytarget POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${QT_DIR}/../../../plugins"
"${$<UPPER_CASE:CMAKE_RUNTIME_OUTPUT_DIRECTORY_$<CONFIG> >}/plugins"
COMMAND_EXPAND_LISTS)
thus, I try to build a string that equals the variable name and then try to expand that as described here: CMake interpret string as variable. In other words: I would like to have a generator expression that evaluates to the content of CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG or CMAKE_RUNTIME_OUTPUT_DIRECTOR_RELEASE dependent on the current build configuration.
However running cmake with the statement above results in an error:
"CMakeLists.txt:112: error: Syntax error in cmake code at [..] when parsing string ${$<UPPER_CASE:CMAKE_RUNTIME_OUTPUT_DIRECTORY_$<CONFIG> >}/plugins Invalid character ('<') in a variable name: '$'
So my question is, how can I use a generator-expression to access the corresponding variable? (Bonus question: is there another/better way to achieve the same goal?)
So my question is, how can I use a generator-expression to access the corresponding variable?
You cannot. There is currently (CMake <=3.23) no way to expand a variable whose name is determined by the value of a generator expression.
Bonus question: is there another/better way to achieve the same goal?
Yes, and you are almost there! You can use $<TARGET_FILE_DIR:...>:
add_custom_command(
TARGET mytarget POST_BUILD
COMMAND
${CMAKE_COMMAND} -E copy_directory
"${QT_DIR}/../../../plugins"
"$<TARGET_FILE_DIR:mytarget>/plugins"
VERBATIM
)
This works because TARGET_FILE_DIR evaluates to the actual directory containing the executable or library file for mytarget, no matter the active configuration, property values, etc.
Docs: https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#genex:TARGET_FILE_DIR
CMAKE_RUNTIME_OUTPUT_DIRECTORY_<CONFIG> is already relative to the binary directory so you should not try to compute the binary directory in its definition. Also, it supports generator expressions. Thus, the following will be much more robust:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "bin/$<LOWER_CASE:$<CONFIG>>"
CACHE STRING "Common output directory for runtime artifacts")
This has a bunch of concrete benefits:
No need to set CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG or CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE
This will work for MinSizeRel and RelWithDebInfo, plus any custom configurations one might add down the line.
Since it's defined as a cache variable, it can be overridden for debugging / working around name clashes, etc.
A bit more context for (3): most CMAKE_* variables are intended to be either read-only or user-configurable (i.e. at the command line, from the GUI, etc.). Overriding their defaults via set(CACHE) is a polite compromise. A notable exception to this rule is the collection of Qt codegen flags (CMAKE_AUTO{MOC,RCC,UIC}). These must typically be set for the build to produce usable binaries.
I want to collect object files (.o) in a particular directory with CMake
I have tried the below snippet from How to collect object files (.o) in a particular directory with CMake?
add_custom_command(TARGET ${LIBNAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${OBJ_DIR}
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_OBJECTS:${LIBNAME_OBJ}> ${OBJ_DIR})
The command copy_if_different works well when the library contains less number of sources and fails when it's more.
How to collect objects of a project which contains huge number of sources ?
For add_custom_command to execute its COMMAND arguments, something has to create a process environment for the command to execute in. Whether that's done by a command shell or directly using exec(), the size limit for that environment is defined by the limits.h macro ARG_MAX. On POSIX systems, getconf ARG_MAX will show the compile-time value of that configuration. On my Fedora 35 system, the value is 2097152 which seems almost impossible to exceed, but maybe you're dealing with smaller limits.
(Also, the ARG_MAX limit represents the entire environment, not just command arguments. So if there are a lot of environment variables defined, that could eat into your max length.)
If you truly are exceeding that, you have two choices:
Option 1: Install the objects in the install stage, as intended
If I'm understanding the linked q&a correctly, the only reason you need the $<TARGET_OBJECTS> paths is so that you can copy them somewhere else in the build tree, in order to then install them from there?
If you just want to install them, a simpler solution is to write custom install code, e.g.
# necessary because config variables aren't present
# in the install context.
install(CODE "set(OBJECT_DEST \"${CMAKE_INSTALL_LIBDIR}\")"
install(CODE [[
file(INSTALL
DESTINATION "${CMAKE_INSTALL_PREFIX}/${OBJECT_DEST}"
FILES
$<TARGET_OBJECTS:_actual_object_name_>
)]])
Messing around with keeping the object name in a variable is a mistake when using custom install code, IMHO, as the evaluation contexts get too confusing. Instead of ${LIBNAME_OBJ}, just use the actual target name in the generator expression.1
Since the generator-expression expansion is handled by CMake when generating the cmake_install.cmake script, there shouldn't be any limit on the length it can have. (Not beyond whatever limits it has internally, if any.)
Option 2: Break up your targets
If you really want to use add_custom_command with the objects, you'll need smaller targets. Why do all of your sources have to be defined on a single object library? Just break them up:
set(SOURCES1 a.c b.c c.c d.c)
set(SOURCES2 w.c x.c y.c z.c)
add_library(s1_obj OBJECT ${SOURCES1})
add_library(s2_obj OBJECT ${SOURCES2})
add_executable(foo
$<TARGET_OBJECTS:s1_obj>
$<TARGET_OBJECTS:s2_obj>
)
add_custom_target(copy_all_objs
COMMAND ${CMAKE_COMMAND} -E make_directory ${OBJ_ROOT_DIR}/myobjects/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_OBJECTS:s1_obj>
${OBJ_ROOT_DIR}/myobjects/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_OBJECTS:s2_obj>
${OBJ_ROOT_DIR}/myobjects/
COMMAND_EXPAND_LISTS)
Notes
Actually, I typically recommend using real target names whenever possible in generator expressions.
With some targets — at least SHARED, STATIC, MODULE and ARCHIVE libraries, plus executables — the target name defines the default output file name. So, I get using a variable for the name. (Though, I did say default — it can be overridden with the OUTPUT_NAME target property, and often that's a better way to make filenames configurable. And it lets you keep static target names.)
But for targets like INTERFACE, OBJECT, and ALIAS libraries, the target name is meaningless — it's only used internally in the CMake code. There's no reason not to just hardcode it.
If you're doing this in a macro/function and the target name has to be in a variable, your best bet is probably to write the install() code as a quoted argument rather than a bracket argument, so that variables get evaluated immediately. Which means lots of fun escaping, for the things you don't want evaluated immediately. (Though you can lose the OBJECT_DEST variable, at least.):
install(CODE "
file(INSTALL
DESTINATION
\"\$\{CMAKE_INSTALL_PREFIX\}/${CMAKE_INSTALL_LIBDIR}\"
FILES
$<TARGET_OBJECTS:${LIBNAME_OBJ}>
)")
I would like to have a command or option to list all the modified cache variables of the current build configuration. While cmake -L[AH] is nice, it is also quite overwhelming and doesn't show which are non-default values.
There seems to be a variable property MODIFIED that sounds exactly like what I'm looking for - but the documentation is not very reassuring:
Internal management property. Do not set or get.
This is an internal cache entry property managed by CMake to track interactive user modification of entries. Ignore it.
This question also didn't help: CMAKE: Print out all accessible variables in a script
There are so many ways you could change or initialize variables in CMake (command line, environment variables, script files, etc.) that you won't be able to cover them all.
I just came up with the following script that covers the command line switches. Put the following file in your CMake project's root folder and you get the modified variables printed:
PreLoad.cmake
set(_file "${CMAKE_BINARY_DIR}/UserModifiedVars.txt")
get_directory_property(_vars CACHE_VARIABLES)
list(FIND _vars "CMAKE_BACKWARDS_COMPATIBILITY" _idx)
if (_idx EQUAL -1)
list(REMOVE_ITEM _vars "CMAKE_COMMAND" "CMAKE_CPACK_COMMAND" "CMAKE_CTEST_COMMAND" "CMAKE_ROOT")
file(WRITE "${_file}" "${_vars}")
else()
file(READ "${_file}" _vars)
endif()
foreach(_var IN LISTS _vars)
message(STATUS "User modified ${_var} = ${${_var}}")
endforeach()
This will load before anything else and therefore can relatively easily identify the user modified variables and store them into a file for later reference.
The CMAKE_BACKWARDS_COMPATIBILITY is a cached variable set by CMake at the end of a configuration run and therefor is used here to identify an already configured CMake project.
Reference
CMake: In which Order are Files parsed (Cache, Toolchain, …)?
In CMake, the flags for the C++ compiler can be influenced in various ways: setting CMAKE_CXX_FLAGS manually, using add_definitions(), forcing a certain C++ standard, and so forth.
In order to compile a target in the same project with different rules (a precompiled header, in my case), I need to reproduce the exact command that is used to compile files added by a command like add_executable() in this directory.
Reading CMAKE_CXX_FLAGS only returns the value set to it explicitly, CMAKE_CXX_FLAGS_DEBUG and siblings only list default Debug/Release options. There is a special functions to retrieve the flags from add_definitions() and add_compiler_options(), but none seem to be able to return the final command line.
How can I get all flags passed to the compiler into a CMake variable?
To answer my own question: It seems like the only way of getting all compiler flags is to reconstruct them from the various sources. The code I'm working with now is the following (for GCC):
macro (GET_COMPILER_FLAGS TARGET VAR)
if (CMAKE_COMPILER_IS_GNUCXX)
set(COMPILER_FLAGS "")
# Get flags form add_definitions, re-escape quotes
get_target_property(TARGET_DEFS ${TARGET} COMPILE_DEFINITIONS)
get_directory_property(DIRECTORY_DEFS COMPILE_DEFINITIONS)
foreach (DEF ${TARGET_DEFS} ${DIRECTORY_DEFS})
if (DEF)
string(REPLACE "\"" "\\\"" DEF "${DEF}")
list(APPEND COMPILER_FLAGS "-D${DEF}")
endif ()
endforeach ()
# Get flags form include_directories()
get_target_property(TARGET_INCLUDEDIRS ${TARGET} INCLUDE_DIRECTORIES)
foreach (DIR ${TARGET_INCLUDEDIRS})
if (DIR)
list(APPEND COMPILER_FLAGS "-I${DIR}")
endif ()
endforeach ()
# Get build-type specific flags
string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE_SUFFIX)
separate_arguments(GLOBAL_FLAGS UNIX_COMMAND
"${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BUILD_TYPE_SUFFIX}}")
list(APPEND COMPILER_FLAGS ${GLOBAL_FLAGS})
# Add -std= flag if appropriate
get_target_property(STANDARD ${TARGET} CXX_STANDARD)
if ((NOT "${STANDARD}" STREQUAL NOTFOUND) AND (NOT "${STANDARD}" STREQUAL ""))
list(APPEND COMPILER_FLAGS "-std=gnu++${STANDARD}")
endif ()
endif ()
set(${VAR} "${COMPILER_FLAGS}")
endmacro ()
This could be extended to also include options induced by add_compiler_options() and more.
Easiest way is to use make VERBOSE=1 when compiling.
cd my-build-dir
cmake path-to-my-sources
make VERBOSE=1
This will do a single-threaded build, and make will print every shell command it runs just before it runs it. So you'll see output like:
[ 0%] Building CXX object Whatever.cpp.o
<huge scary build command it used to build Whatever.cpp>
There actually is a fairly clean way to do this at compile time using CXX_COMPILER_LAUNCHER:
If you have a script print_args.py
#!/usr/bin/env python
import sys
import argparse
print(" ".join(sys.argv[1:]))
# we need to produce an output file so that the link step does not fail
p = argparse.ArgumentParser()
p.add_argument("-o")
args, _ = p.parse_known_args()
with open(args.o, "w") as f:
f.write("")
You can set the target's properties as follows:
add_library(${TARGET_NAME} ${SOURCES})
set_target_properties(${TARGET_NAME} PROPERTIES
CXX_COMPILER_LAUNCHER
${CMAKE_CURRENT_SOURCE_DIR}/print_args.py
)
# this tells the linker to not actually link. Which would fail because output file is empty
set_target_properties(${TARGET_NAME} PROPERTIES
LINK_FLAGS
-E
)
This will print the exact compilation command at compile time.
Short answer
It's not possible to assign final value of compiler command line to variable in CMake script, working in all use cases.
Long answer
Unfortunately, even solution accepted as answer still not gets all compiler flags. As gets noted in comments, there are Transitive Usage Requirements. It's a modern and proper way to write CMake files, getting more and more popular. Also you may have some compile options defined using generator expressions (they look like variable references but will not expand when needed).
Consider having following example:
add_executable(myexe ...);
target_compile_definitions(myexe PRIVATE "PLATFORM_$<PLATFORM_ID>");
add_library(mylib ...);
target_compile_definitions(mylib INTERFACE USING_MY_LIB);
target_link_libraries(myexe PUBLIC mylib);
If you try to call proposed GET_COMPILER_FLAGS macro with myexe target, you will get resulting output -DPLATFORM_$<PLATFORM_ID> instead of expected -DPLATFORM_Linux -DUSING_MY_LIB.
This is because there are two stages between invoking CMake and getting build system generated:
Processing. At this stage CMake reads and executes commands from cmake script(s), particularly, variable values getting evaluated and assigned. At this moment CMake just collecting all required info and being prepared to generate build system (makefiles).
Generating. CMake uses values of special variables and properties, being left at end of processed scripts to finally decide and form generated output. This is where it constructs final command line for compiler according to its internal algorithm, not avaliable for scripting.
Target properties which might be retrieved at processing stage with get_target_property(...) or get_property(... TARGET ...) aren't complete (even when invoked at the end of script). At generating stage CMake walks through each target dependency tree (recursively) and appends properties values according to transitive usage requirements (PUBLIC and INTERFACE tagged values gets propagated).
Although, there are workarounds, depending on what final result you aiming to achieve. This is possible by applying generator expressions, which allows use final values of properties of any target (defined at processing stage)... but later!
Two general possibilites are avaliable:
Generate any output file based on template, which content contains variable references and/or generator expressions, and defined as either string variable value, or input file. It's not flexible due to very limited support of conditional logic (i.e. you cannot use complex concatenations available only with nested foreach() loops), but has advantages, that no further actions required and content described in platform-independent way. Use file(GENERATE ...) command variant. Note, that it behaves differently from file (WRITE ...) variant.
Add custom target (and/or custom command) which implements further usage of expanded value. It's platform dependent and requires user to additionally invoke make (either with some special target, or include to all target), but has advantage, that it's flexible enough because you may implement shell script (but without executable bit).
Example demonstrating solution with combining these options:
set(target_name "myexe")
file(GENERATE OUTPUT script.sh CONTENT "#!/bin/sh\n echo \"${target_name} compile definitions: $<TARGET_PROPERTY:${target_name},COMPILE_DEFINITIONS>\"")
add_custom_target(mycustomtarget
COMMAND echo "\"Platform: $<PLATFORM_ID>\""
COMMAND /bin/sh -s < script.sh
)
After calling CMake build directory will contain file script.sh and invoking make mycustomtarget will print to console:
Platform: Linux
myexe compile definitions: PLATFORM_Linux USING_MY_LIB
Use
set(CMAKE_EXPORT_COMPILE_COMMANDS true)
and get compile_commands.json
I am using CMake to build a simple C++ project, hereafter named P. The structure of P is quite simple:
P/src/
P/src/package1
P/src/packege2
P/src/...
P/src/main-app
I would like to collect the libraries in package1, package2, ... in a variable called P_LIBS.
In a first attempt, I tried to collect the libraries available in package1, package2, ... in the variable called P_LIBS initially set in the src/CMakeLists.txt file. However, the updates to P_LIBS made in the CMakeLists.txt of the subfolders are not propagated to the parent folder.
I would rather not write a list of libraries in the main CMakeLists.txt file. I would rather modify such variable while moving in the directory tree.
After a search on the internet I could not find any valid suggestion. If I look at the various Find files, I only see long listings of libraries in their main CMakeLists.txt file.
Is there a way to do what (I hope) I explained above?
Thanks to sakra's link I was able to 'propagate' names up to the parent folder. However, the names I add to the P_LIBS variable are later interpreted as 'library' names, not as reference to CMake targets. In other words, if
P_LIBS = {a, b}
the 'a' and 'b' are interpreted as the library names, i.e. CMake generates:
gcc [...] -l a -o exe
instead of
gcc [...] /path/to/a.o -o exe
(.o or other extensions)
You are propably constructing the targets list as a string, try to make them a list instead. For example:
# in package1/CMakeLists.txt
set(P_LIBS ${P_LIBS} a b PARENT_SCOPE)