CMake how to get binary target complete directory and name on Windows - cmake

Is there a way in CMake to find a binary target complete name (mybin.exe) by inspecting target properties? Like
get_target_property(EXENAME targetname OUTPUT_NAME) (or RUNTIME_OUTPUT_NAME)
Or I have to use a custom command like in How to get library full-native name on cmake?
With get_target_property seems I'm only able to get the "logical" target name out of it (mybin), with no other information. Am I missing something?
Thank you

There's a reason this is not possible without generator expressions: There are multi configuration generators such as the Visual Studio generators that create a build system for multiple build configurations (Release, Debug, ...) in a single CMake configuration run (cmake -S ... -B ...). It's even the default to create binaries in a directory with a name matching the configuration built.
Depending on what you want to do with the information, there are some alternatives:
You may be able to use generator expressions, e.g. if you need the information as part of add_custom_command, add_custom_target, add_test or similar. Several target properties also allow for use of generator expressions.
You may be able to establish the desired directory structure during an install step, see the install() command.
You may be able to get the build system to generate the files in a specific location e.g. by setting the CMAKE_RUNTIME_OUTPUT_DIRECTORY variable in the toplevel CMakeLists.txt. Note that this will still result in configuration dependent subdirectories being created for multi configuration generators, unless the variable value contains a generator expression. (You could, simply be adding $<0:>, but this could easily result in binaries of different configurations overwriting one another.)
If you cannot specify this in the toplevel CMakeLists.txt, via command line or cmake presets, you could still use a path relative to CMAKE_BINARY_DIR; this should make the binaries easy to locate.

In the end that is a matter of doing:
add_custom_command(TARGET ${your_target}
POST_BUILD
WORKING_DIRECTORY ${your_dir}
COMMAND echo "$<TARGET_FILE:${your_target}>" > "exename.txt"
DEPENDS ${your_target} ${another_target}
VERBATIM
)
so that exename.txt contains the full path to the executable target
Didn't find a way to do st like:
set(EXENAME "$<TARGET_FILE:${your_target}>")
anyway...

Related

CMake: How to set dependency from executable to custom command [duplicate]

I'm trying to use add_custom_command to generate a file during the build. The command never seemed to be run, so I made this test file.
cmake_minimum_required( VERSION 2.6 )
add_custom_command(
OUTPUT hello.txt
COMMAND touch hello.txt
DEPENDS hello.txt
)
I tried running:
cmake .
make
And hello.txt was not generated. What have I done wrong?
The add_custom_target(run ALL ... solution will work for simple cases when you only have one target you're building, but breaks down when you have multiple top level targets, e.g. app and tests.
I ran into this same problem when I was trying to package up some test data files into an object file so my unit tests wouldn't depend on anything external. I solved it using add_custom_command and some additional dependency magic with set_property.
add_custom_command(
OUTPUT testData.cpp
COMMAND reswrap
ARGS testData.src > testData.cpp
DEPENDS testData.src
)
set_property(SOURCE unit-tests.cpp APPEND PROPERTY OBJECT_DEPENDS testData.cpp)
add_executable(app main.cpp)
add_executable(tests unit-tests.cpp)
So now testData.cpp will generated before unit-tests.cpp is compiled, and any time testData.src changes. If the command you're calling is really slow you get the added bonus that when you build just the app target you won't have to wait around for that command (which only the tests executable needs) to finish.
It's not shown above, but careful application of ${PROJECT_BINARY_DIR}, ${PROJECT_SOURCE_DIR} and include_directories() will keep your source tree clean of generated files.
Add the following:
add_custom_target(run ALL
DEPENDS hello.txt)
If you're familiar with makefiles, this means:
all: run
run: hello.txt
The problem with two existing answers is that they either make the dependency global (add_custom_target(name ALL ...)), or they assign it to a specific, single file (set_property(...)) which gets obnoxious if you have many files that need it as a dependency. Instead what we want is a target that we can make a dependency of another target.
The way to do this is to use add_custom_command to define the rule, and then add_custom_target to define a new target based on that rule. Then you can add that target as a dependency of another target via add_dependencies.
# this defines the build rule for some_file
add_custom_command(
OUTPUT some_file
COMMAND ...
)
# create a target that includes some_file, this gives us a name that we can use later
add_custom_target(
some_target
DEPENDS some_file
)
# then let's suppose we're creating a library
add_library(some_library some_other_file.c)
# we can add the target as a dependency, and it will affect only this library
add_dependencies(some_library some_target)
The advantages of this approach:
some_target is not a dependency for ALL, which means you only build it when it's required by a specific target. (Whereas add_custom_target(name ALL ...) would build it unconditionally for all targets.)
Because some_target is a dependency for the library as a whole, it will get built before all of the files in that library. That means that if there are many files in the library, we don't have to do set_property on every single one of them.
If we add DEPENDS to add_custom_command then it will only get rebuilt when its inputs change. (Compare this to the approach that uses add_custom_target(name ALL ...) where the command gets run on every build regardless of whether it needs to or not.)
For more information on why things work this way, see this blog post: https://samthursfield.wordpress.com/2015/11/21/cmake-dependencies-between-targets-and-files-and-custom-commands/
This question is pretty old, but even if I follow the suggested recommendations, it does not work for me (at least not every time).
I am using Android Studio and I need to call cMake to build C++ library. It works fine until I add the code to run my custom script (in fact, at the moment I try to run 'touch', as in the example above).
First of,
add_custom_command
does not work at all.
I tried
execute_process (
COMMAND touch hello.txt
)
it works, but not every time!
I tried to clean the project, remove the created file(s) manually, same thing.
Tried cMake versions:
3.10.2
3.18.1
3.22.1
when they work, they produce different results, depending on cMake version, one file or several. This is not that important as long as they work, but that's the issue.
Can somebody shed light on this mystery?

Creating a library in CMake depending on source files not available when generating build files

I have a CMake configuration file building two libraries:
a third-party library (here called ThirdPartyLib) containing a real-time OS / board support package from a supplier. It is built outside CMake using the autotools toolchain.
an extended version of the former library (here called ExtendedThirdPartyLib)
Unfortunately, some source code that I need (various tools) are not built in the ordinary build script for (1). Since I don't want to mess with the suppliers build script I want to add another library (2), building the missing files and thus extending the library from the supplier.
I want to able to do something like this in CMakeFiles.txt:
cmake_minimum_required(VERSION 3.2)
project(bsp)
include(ExternalProject)
ExternalProject_Add(
ThirdPartyLib
URL <http://some.url/bsp.tar.bz2
BUILD_COMMAND make -C ../external/ThirdPartyLib/src
)
set_target_properties(ThirdPartyLib PROPERTIES EXCLUDE_FROM_ALL TRUE)
add_library(ExtendedThirdPartyLib
${CMAKE_CURRENT_BINARY_DIR}/some/path/missing_file1.c
${CMAKE_CURRENT_BINARY_DIR}/some/path/missing_file2.c
)
add_dependencies(ExtendedThirdPartyLib ThirdPartyLib)
target_include_directories(ExtendedThirdPartyLib PUBLIC
${CMAKE_CURRENT_BINARY_DIR}/some/path/include
)
target_link_libraries(ExtendedThirdPartyLib ThirdPartyLib)
The problem here is that the path to missing_file1.c and missing_file2.c are not valid when CMake is generating the build files (they are extracted from the tarball from the supplier). CMake exits with an error output saying: "Cannot find source file".
Is there a neat way to make this work? I.e. is it possible to convince CMake that certain non-existant input files will exist when building of the library begins? Or is there any other recommended way to solve this issue?
(I have temporary made local copies of the files I need to build from the suppliers tarball, but that is of course not a good solution. If those files are changed in future versions of the suppliers package and I forget to overwrite my local copies it could be a horrible mess...
Another "solution" would be to create a small makefile outside CMake and use another ExternalProject_Add in the CMakeFiles.txt somehow. But that's not a good solution either, e.g. if compile and linker flags are modified I need to remember to change the makefile too.)
Personally, I dislike the ExternalProject_Add command, because it does way too many things for my taste, but I've digressed.
What if you do something like this, where bar is simulating your ExtendedThirdPartyLib target, since it depends on generated files
cmake_minimum_required(VERSION 3.11)
project(lol C)
set(SOURCES lol.c) # only this file exists
add_library(lol ${SOURCES})
set(FOO_FILES "foo1.c" "foo2.c")
add_custom_command(OUTPUT ${FOO_FILES}
COMMAND ${CMAKE_COMMAND} -E touch ${FOO_FILES}
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Creating ${FOO_FILES}"
VERBATIM)
add_custom_target(foo DEPENDS ${FOO_FILES})
add_library(bar ${FOO_FILES})
add_dependencies(bar foo)
target_link_libraries(lol bar)
The whole approach hinges on the fact that the method, where produced/generated files are procured, is explicitly defined via the custom command and associated custom target.
You should modify the custom command to extract the required files (e.g. could even call some external script) from the tarball (which might require downloading with curl or something similar).

Generator expression in configure_file

We are porting some rather old code and of course we want to use generator expressions now.
The by configure_file generated .pc files now contain -I$<INSTALL_INTERFACE:include>.
The only hint about how to resolve generator expressions I found was to use
file(GENERATE
Of course this is executed during the configure step so the above expression is resolved to an empty string.
Edit:
here is an example
CMakeLists.txt:
cmake_minimum_required(VERSION 3.11)
project(test CXX)
add_library(foo SHARED main.cpp)
target_include_directories(foo PUBLIC $<INSTALL_INTERFACE:include>)
# now later buried deep in some functions
get_property( _include_dirs TARGET foo PROPERTY INCLUDE_DIRECTORIES )
configure_file(config.in config.out #ONLY)
# content of config.out is "include = -I$<INSTALL_INTERFACE:include>"
file(GENERATE OUTPUT config.out2 INPUT ${CMAKE_CURRENT_BINARY_DIR}/config.out)
# content of config.out2 is "include = -I"
# most likely because the INSTALL_INTERFACE isn't used when the file is generated
config.in:
include = #_include_dirs#
and main.cpp is just empty.
As the CMake documentation states the file (GENERATE ...) command can use generator expressions which are evaluated by the generator.
Generate Files
You can let CMake generate files with custom code directly without the workaround of a configure_file command.
Single-Config Generators
For single-config generators like Makefiles you can use:
file (GENERATE
OUTPUT "config.out"
CONTENT "include = -I$<INSTALL_INTERFACE:include>"
)
As my CMake code has to work with both multi-config and single-config generators I did not test the code specifically.
Single- And Multi-Config Generators
In general for all generators, one can use the following signature:
file (GENERATE
OUTPUT "config_$<CONFIG>.out"
CONTENT "include = -I$<INSTALL_INTERFACE:include>"
)
Due to the nature of the command in relation to multi-config generators like Visual Studio this will generate multiple files in your build folder for each build type specified in this variable CMAKE_CONFIGURATION_TYPES:
config_Debug.out
config_Release.out
config_RelWithDebInfo.out
...
config_< BUILD_TYPE >.out
If you do not specify unique filenames and CMake tries to generate the files it will stop the execution with an error.
Use Generated Files
To use the previously generated files it depends on what you need. First of all the files will exist after the configuration stage.
Single-Config Generators
To use a generated file in a single-config generator scenario with a constant name (e.g. config.out) there should be no additional work necessary.
Single- And Multi-Config Generators
For multi-config generators it is slighty different. As you have to use generator expressions to access the appropriate file at build time. If you have a CMake instruction that supports generator expressions then you can just use the filename config_$<CONFIG>.out.
But if you need the file to be named exactly the same regardless of the build type (like config.out) it gets a little more tricky.
First you have to tell CMake that there should be a file named like config.out by using add_custom_command and specifiying the OUTPUT parameter:
add_custom_command (
COMMAND ${CMAKE_COMMAND} "-E" "copy_if_different" "config_$<CONFIG>.out" "config.out"
VERBATIM
PRE_BUILD
DEPENDS "config_$<CONFIG>.out"
OUTPUT "config.out"
COMMENT "creating config.out file ({event: PRE_BUILD}, {filename: config.out})"
)
CMake will create a file dependency internally and every time one references the filename config.out it will ensure that the add_custom_command gets executed.
But this will not work in every case as it depends on the further instructions which should use the file.
Depending on the commands you are using you can now specify the file config.out as input for some commands (like target_sources, ...) and CMake will detect on a file-dependency level that it has to ensure the existence of config.out.
If you want to generate a file which is not referenced on a file-dependency level (like versioninfo.txt) then you have to ensure that CMake executes the add_custom_command every time your build target gets executed via a target-dependency:
add_custom_target ("generate_config_out" DEPENDS "config.out")
add_dependencies ("MY_LIBRARY_TARGET" "generate_config_out")
Every time CMake builds the MY_LIBRARY_TARGET target it will previously build the generate_config_out target which in turn depends on the config.out that CMake will process on the file-dependency level.

How to avoid name clashes in cmake subprojects?

So I have setup a metaproject, which means I have one directory (the root directory) containing multiple subdirectories, each of which is a cmake project (with a valid cmakelists). The cmakelists in the root directory imports the subprojects using the cmake add_subdirectory command.
Each subproject defines it's own targets (libraries and executables) as well as targets "check" which executes the test suite for that project and "docs" which runs doxygen.
My problem is that CMake does not understand that the check and docs targets are local to each project, and therefore complains about name clashes.
So my question is, is there any way to get cmake to understand that these targets are local to each project?
CMake doesn't allow duplicate target names. The rationale is provided in the docs for policy CMP0002:
Unique names may be referenced unambiguously both in CMake
code and on make tool command lines.
Logical names are used by Xcode and VS IDE generators
to produce meaningful project names for the targets.
Your could try setting CMP0002 to OLD (done via cmake_policy(SET CMP0002 OLD)) which would at least get rid of the CMake error, but I'd recommend against that - it certainly won't work for MSVC/Xcode.
The only option I can see short of hand-coding unique names would be to write a CMake function which generates a unique name per "check" target - for example it could append the name of the target which is being checked, yielding names like check_my_lib_one, check_my_exe_one, etc.
This function could also be used to collect a list of all registered check_<Target> instances and add them to a single target called check which would invoke each subordinate check_<Target>, so running make check runs them all.

How to include a cmake Makefile after a target has been executed?

Given the following minimal example.
cmake_minimum_required(VERSION 2.8)
project(include_test)
add_custom_command(OUTPUT OtherCMakeLists.txt
COMMAND "${CMAKE_CURRENT_BINARY_DIR}/create_other_cmakelists")
add_custom_target(do_something DEPENDS OtherCMakeLists.txt)
What do_something should do here is first create OtherCMakeLists.txt. Now, let's assume that do_something has to do something else afterwards, e.g. compiling some code. I'd like that when the targets from something else are executed, the CMakeLists.txt will behave as if OtherCMakeLists.txt was included with include.
Is this possible?
As an example why this could be useful: OtherCMakeLists.txt might add some compiler flags that have influence on further compiling.
To my knowledge, it is not possible to generate CMakeLists.txt file with a custom target/command and use include CMake command with generated CMakeLists.txt
The problem is that the include command is called at so-called "Configuration time" (when cmake executable tries to parse all CMakeLists.txt), but the generation of file (CMakeLists.txt) is performed at "Build time" (when make command is invoked on generated build system)
add_custom_command has 2 different signatures:
add_custom_command(OUTPUT ...) will be executed at build time, too late to apply rules from a generated CMakeLists.txt generated.
add_custom_command(TARGET ...) to attach a specific command to a target. This command can be run on PRE_BUILD, PRE_LINK or POST_BUILD. Probably not what you want to achieve...
If you are trying to add some dynamic to your compile process, adding custom commands or target may not be your best option.
You should try to read doc for some other CMake commands that can be helpful in your case:
configure_file() that can process a file (OtherCMakeLists.txt.in) into another file (OtherCMakeLists.txt) replacing variables by their values. This is achieved at configuration time
execute_process() to run a command a configuration time (thx to #ComicSansMS)
set_target_properties() to set some compiler or link flags to a specific target depending on some conditions
The list of properties you can set on targets