英文:
How can I get a target's output-file's name during CMake's configuration phase (not the generation phase)?
问题
我知道我们可以在构建时使用$<TARGET_FILE_NAME:Foo>
来获取add_custom_command
和add_custom_target
的文件名,但我似乎找不到如何在配置时间获取文件名的答案。例如,
add_library(Foo SHARED foo.cpp foo.h)
我得到的最好答案是get_target_property(FOO_NAME Foo NAME)
,但${FOO_NAME}
是Foo
,我想要的是类似于libFoo.so
或libFoo.dylib
,具体取决于平台。我们如何在CMake配置时间获取目标文件名呢?
英文:
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: https://stackoverflow.com/q/75415268/11107541.
答案1
得分: 1
你不能-至少不能覆盖所有可能的情况。构建输出文件名的方式足够复杂,以至于你只能处理“简单”情况,即使这些情况也足够复杂。我是说,看看你必须处理的所有事情,以及它们如何互相覆盖:
variable/CMAKE_BUILD_TYPE
prop_tgt/TYPE
prop_tgt/FRAMEWORK
variable/CMAKE_STATIC_LIBRARY_PREFIX
和variable/CMAKE_STATIC_LIBRARY_SUFFIX
以及语言特定的覆盖variable/CMAKE_SHARED_LIBRARY_PREFIX
和variable/CMAKE_SHARED_LIBRARY_SUFFIX
以及语言特定的覆盖variable/CMAKE_SHARED_MODULE_PREFIX
和variable/CMAKE_SHARED_MODULE_SUFFIX
以及语言特定的覆盖variable/CMAKE_EXECUTABLE_SUFFIX
以及语言特定的覆盖prop_tgt/OUTPUT_NAME
和prop_tgt/OUTPUT_NAME_<CONFIG>
prop_tgt/ARCHIVE_OUTPUT_NAME
和prop_tgt/ARCHIVE_OUTPUT_NAME_<CONFIG>
prop_tgt/LIBRARY_OUTPUT_NAME
和prop_tgt/LIBRARY_OUTPUT_NAME_<CONFIG>
prop_tgt/RUNTIME_OUTPUT_NAME
和prop_tgt/RUNTIME_OUTPUT_NAME_<CONFIG>
我认为你可以做类似这样的事情:
# 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("${out
<details>
<summary>英文:</summary>
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`](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html)
- [`prop_tgt/TYPE`](https://cmake.org/cmake/help/latest/prop_tgt/TYPE.html)
- [`prop_tgt/FRAMEWORK`](https://cmake.org/cmake/help/latest/prop_tgt/FRAMEWORK.html)
- [`variable/CMAKE_STATIC_LIBRARY_PREFIX`](https://cmake.org/cmake/help/latest/variable/CMAKE_STATIC_LIBRARY_PREFIX.html) and [`variable/CMAKE_STATIC_LIBRARY_SUFFIX`](https://cmake.org/cmake/help/latest/variable/CMAKE_STATIC_LIBRARY_SUFFIX.html) and the language-specific overrides
- [`variable/CMAKE_SHARED_LIBRARY_PREFIX`](https://cmake.org/cmake/help/latest/variable/CMAKE_SHARED_LIBRARY_PREFIX.html) and [`variable/CMAKE_SHARED_LIBRARY_SUFFIX`](https://cmake.org/cmake/help/latest/variable/CMAKE_SHARED_LIBRARY_SUFFIX.html) and the language-specific overrides
- [`variable/CMAKE_SHARED_MODULE_PREFIX`](https://cmake.org/cmake/help/latest/variable/CMAKE_SHARED_MODULE_PREFIX.html) and [`variable/CMAKE_SHARED_MODULE_SUFFIX`](https://cmake.org/cmake/help/latest/variable/CMAKE_SHARED_MODULE_SUFFIX.html) and the language-specific overrides
- [`variable/CMAKE_EXECUTABLE_SUFFIX`](https://cmake.org/cmake/help/latest/variable/CMAKE_EXECUTABLE_SUFFIX.html) and the language-specific overrides
- [`prop_tgt/OUTPUT_NAME`](https://cmake.org/cmake/help/latest/prop_tgt/OUTPUT_NAME.html) and [`prop_tgt/OUTPUT_NAME_<CONFIG>`](https://cmake.org/cmake/help/latest/prop_tgt/OUTPUT_NAME_CONFIG.html)
- [`prop_tgt/ARCHIVE_OUTPUT_NAME`](https://cmake.org/cmake/help/latest/prop_tgt/ARCHIVE_OUTPUT_NAME.html) and [`prop_tgt/ARCHIVE_OUTPUT_NAME_<CONFIG>`](https://cmake.org/cmake/help/latest/prop_tgt/ARCHIVE_OUTPUT_NAME_CONFIG.html)
- [`prop_tgt/LIBRARY_OUTPUT_NAME`](https://cmake.org/cmake/help/latest/prop_tgt/LIBRARY_OUTPUT_NAME.html) and [`prop_tgt/LIBRARY_OUTPUT_NAME_<CONFIG>`](https://cmake.org/cmake/help/latest/prop_tgt/LIBRARY_OUTPUT_NAME_CONFIG.html)
- [`prop_tgt/RUNTIME_OUTPUT_NAME`](https://cmake.org/cmake/help/latest/prop_tgt/RUNTIME_OUTPUT_NAME.html) and [`prop_tgt/RUNTIME_OUTPUT_NAME_<CONFIG>`](https://cmake.org/cmake/help/latest/prop_tgt/RUNTIME_OUTPUT_NAME_CONFIG.html)
I believe you could do something like this:
```cmake
# 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.
答案2
得分: 0
A little bit clumsy but the file name can be constructed in the following way:
```cmake
set(FOO_NAME "${CMAKE_SHARED_LIBRARY_PREFIX}Foo${CMAKE_SHARED_LIBRARY_SUFFIX}")
See also cmake doc.
Note that the two variables will be overridden by the respective CMAKE_SHARED_LIBRARY_??FIX_<LANG>
variables. So if that's a possibility, you need make sure you catch the right variable.
Let me add the final remark that the rationale behind CMake is you don't need to know. CMake operates on targets, not files. So whatever you're trying to achieve might be possible without getting the filename.
<details>
<summary>英文:</summary>
A little bit clumsy but the file name can be constructed in the following way:
```cmake
set(FOO_NAME "${CMAKE_SHARED_LIBRARY_PREFIX}Foo${CMAKE_SHARED_LIBRARY_SUFFIX}")
See also cmake doc.
Note that the two variables will be overridden by the respective CMAKE_SHARED_LIBRARY_??FIX_<LANG>
variables. So if that's a possibility, you need make sure you catch the right variable.
Let me add the final remark that the rationale behind CMake is you don't need to know. CMake operates on targets, not files. So whatever you're trying to achieve might be possible without getting the filename.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论