Issue
I know we can use $<TARGET_FILE_NAME:Foo>
to get the filename for add_custom_command
and add_custom_target
during build time, but I can't seem to find the answer on out how to get the filename during config time. For example,
add_library(Foo SHARED foo.cpp foo.h)
The best I get is get_target_property(FOO_NAME Foo NAME)
, but ${FOO_NAME}
is Foo
, what I want is something like libFoo.so
or libFoo.dylib
depends on the platform. How can we get the target file name during cmake config time?
For context on why I thought I initially thought I needed to be able to do this, see this other question: In CMake how can I copy a target file to a custom location when target based generator expression for OUTPUT is not supported?.
Solution
You can't- at least- not covering all the possible scenarios. The way a output file name is constructed is complicated enough that you can only handle "simple" cases, and even doing that is complicated enough. I mean- look at all the things you have to handle, and the ways that they can override each other:
variable/CMAKE_BUILD_TYPE
prop_tgt/TYPE
prop_tgt/FRAMEWORK
variable/CMAKE_STATIC_LIBRARY_PREFIX
andvariable/CMAKE_STATIC_LIBRARY_SUFFIX
and the language-specific overridesvariable/CMAKE_SHARED_LIBRARY_PREFIX
andvariable/CMAKE_SHARED_LIBRARY_SUFFIX
and the language-specific overridesvariable/CMAKE_SHARED_MODULE_PREFIX
andvariable/CMAKE_SHARED_MODULE_SUFFIX
and the language-specific overridesvariable/CMAKE_EXECUTABLE_SUFFIX
and the language-specific overridesprop_tgt/OUTPUT_NAME
andprop_tgt/OUTPUT_NAME_<CONFIG>
prop_tgt/ARCHIVE_OUTPUT_NAME
andprop_tgt/ARCHIVE_OUTPUT_NAME_<CONFIG>
prop_tgt/LIBRARY_OUTPUT_NAME
andprop_tgt/LIBRARY_OUTPUT_NAME_<CONFIG>
prop_tgt/RUNTIME_OUTPUT_NAME
andprop_tgt/RUNTIME_OUTPUT_NAME_<CONFIG>
I believe you could do something like this:
# cmake_minimum_required(VERSION 3.x)
project(hello)
function(get_target_filename target outvar)
set(outname "${target}")
get_target_property(prop_outname "${target}" OUTPUT_NAME)
if(NOT ("${prop_outname}" STREQUAL "prop_outname-NOTFOUND"))
set(outname "${prop_outname}")
endif()
get_target_property(prop_cfg_outname "${target}" "${OUTPUT_NAME}_${CMAKE_BUILD_TYPE}")
if(NOT ("${prop_cfg_outname}" STREQUAL "prop_cfg_outname-NOTFOUND"))
set(outname "${prop_cfg_outname}")
endif()
get_target_property(prop_archive_outname "${target}" ARCHIVE_OUTPUT_NAME)
get_target_property(prop_library_outname "${target}" LIBRARY_OUTPUT_NAME)
get_target_property(prop_runtime_outname "${target}" RUNTIME_OUTPUT_NAME)
get_target_property(prop_archive_cfg_outname "${target}" "${ARCHIVE_OUTPUT_NAME}_${CMAKE_BUILD_TYPE}")
get_target_property(prop_library_cfg_outname "${target}" "${LIBRARY_OUTPUT_NAME}_${CMAKE_BUILD_TYPE}")
get_target_property(prop_runtime_cfg_outname "${target}" "${RUNTIME_OUTPUT_NAME}_${CMAKE_BUILD_TYPE}")
if(NOT ("${prop_archive_cfg_outname}" STREQUAL "prop_archive_cfg_outname-NOTFOUND"))
set(prop_archive_outname "${prop_archive_cfg_outname}")
endif()
if(NOT ("${prop_library_cfg_outname}" STREQUAL "prop_library_cfg_outname-NOTFOUND"))
set(prop_library_outname "${prop_library_cfg_outname}")
endif()
if(NOT ("${prop_runtime_cfg_outname}" STREQUAL "prop_runtime_cfg_outname-NOTFOUND"))
set(prop_runtime_outname "${prop_runtime_cfg_outname}")
endif()
get_target_property(prop_type "${target}" TYPE)
get_target_property(prop_is_framework "${target}" FRAMEWORK)
if("${prop_is_framework}")
set(filename "${outname}")
elseif(prop_type STREQUAL "STATIC_LIBRARY")
if(NOT ("${prop_archive_outname}" STREQUAL "prop_archive_outname-NOTFOUND"))
set(outname "${prop_archive_outname}")
endif()
set(prefix "${CMAKE_STATIC_LIBRARY_PREFIX}")
set(suffix "${CMAKE_STATIC_LIBRARY_SUFFIX}")
elseif(prop_type STREQUAL "MODULE_LIBRARY")
if(NOT ("${prop_library_outname}" STREQUAL "prop_library_outname-NOTFOUND"))
set(outname "${prop_library_outname}")
endif()
set(prefix "${CMAKE_SHARED_MODULE_LIBRARY_PREFIX}")
set(suffix "${CMAKE_SHARED_MODULE_LIBRARY_SUFFIX}")
elseif(prop_type STREQUAL "SHARED_LIBRARY")
if(WIN32)
if(NOT ("${prop_runtime_outname}" STREQUAL "prop_runtime_outname-NOTFOUND"))
set(outname "${prop_runtime_outname}")
endif()
else()
if(NOT ("${prop_library_outname}" STREQUAL "prop_library_outname-NOTFOUND"))
set(outname "${prop_library_outname}")
endif()
endif()
set(prefix "${CMAKE_SHARED_LIBRARY_PREFIX}")
set(suffix "${CMAKE_SHARED_LIBRARY_SUFFIX}")
elseif(prop_type STREQUAL "EXECUTABLE")
if(NOT ("${prop_runtime_outname}" STREQUAL "prop_runtime_outname-NOTFOUND"))
set(outname "${prop_runtime_outname}")
endif()
set(prefix "${CMAKE_EXECUTABLE_PREFIX}")
set(suffix "${CMAKE_EXECUTABLE_SUFFIX}")
else()
message(FATAL_ERROR "target \"${target}\" is not of type STATIC_LIBRARY, MODULE_LIBRARY, SHARED_LIBRARY, or EXECUTABLE.")
endif()
get_target_property(prop_prefix "${target}" PREFIX)
if(NOT ("${prop_prefix}" STREQUAL "prop_prefix-NOTFOUND"))
set(prefix "${prop_prefix}")
endif()
get_target_property(prop_suffix "${target}" PREFIX)
if(NOT ("${prop_suffix}" STREQUAL "prop_suffix-NOTFOUND"))
set(suffix "${prop_suffix}")
endif()
set("${outvar}" "${prefix}${outname}${suffix}" PARENT_SCOPE)
endfunction()
add_library(static_lib STATIC test.cpp)
add_library(shared_lib SHARED test.cpp)
add_executable(executable test.cpp)
get_target_filename(static_lib static_lib_filename)
get_target_filename(shared_lib shared_lib_filename)
get_target_filename(executable executable_filename)
message(STATUS "static_lib_filename: ${static_lib_filename}")
message(STATUS "shared_lib_filename: ${shared_lib_filename}")
message(STATUS "executable_filename: ${executable_filename}")
The above is a basic implementation. I got too lazy to handle language-specific overrides, and it doesn't handle some (perhaps important) nuances like:
- The fact that most of those target properties can themselves have generator expressions in them (see their docs), which, if it happens to you, I think you're out of luck.
- The fact that
CMAKE_BUILD_TYPE
is only relevant for single-config generators- not multi-config generators. - https://cmake.org/cmake/help/latest/variable/CMAKE_EXECUTABLE_SUFFIX_LANG.html
- Other language-specific overrides like
CMAKE_SHARED_LIBRARY_PREFIX_<LANG>
You'd need to check if those exist and handle them if they do... except in honesty I'm not quite sure how, given that it doesn't seem like targets have a LANGUAGE
property. Source files do, but that's not what we need here. One might need to go to the CMake Discourse to ask about this.
Note: If you want the full path to the target output file... oh boy...
prop_tgt/ARCHIVE_OUTPUT_DIRECTORY
andprop_tgt/ARCHIVE_OUTPUT_DIRECTORY_<CONFIG>
prop_tgt/LIBRARY_OUTPUT_DIRECTORY
andprop_tgt/LIBRARY_OUTPUT_DIRECTORY_<CONFIG>
prop_tgt/RUNTIME_OUTPUT_DIRECTORY
andprop_tgt/RUNTIME_OUTPUT_DIRECTORY_<CONFIG>
More fun notes: If you want to evaluate generator expressions recursively at generation time (for generator expressions that themselves evaluate to generator expressions), you can use the $<GENEX_EVAL:...>
generator expression, but of course- that doesn't apply to this question, which is about configure time.
Answered By - starball Answer Checked By - Timothy Miller (WPSolving Admin)