prop_sf/INSTALL_OBJECT_NAME: support custom install object names

These can support configuration-dependent queries much more easily, so
separate it out.
This commit is contained in:
Ben Boeckel
2025-09-09 09:58:07 -04:00
parent 08f1a0fbc6
commit b53837a1bd
52 changed files with 351 additions and 1 deletions
+1
View File
@@ -578,6 +578,7 @@ Properties on Source Files
/prop_sf/GENERATED
/prop_sf/HEADER_FILE_ONLY
/prop_sf/INCLUDE_DIRECTORIES
/prop_sf/INSTALL_OBJECT_NAME
/prop_sf/KEEP_EXTENSION
/prop_sf/LABELS
/prop_sf/LANGUAGE
+25
View File
@@ -0,0 +1,25 @@
INSTALL_OBJECT_NAME
-------------------
.. versionadded:: 4.2
Set the installed object name (without the object extension) of the source
file. An empty string value disables custom object naming. The value must be a
relative path, and may not include special directory components (e.g.,
``..``).
Note that the object name may not be used as-is in
:prop_tgt:`INSTALL_OBJECT_NAME_STRATEGY` strategies, but instead changed as
the strategy requires to fulfill its goals.
This property supports
:manual:`generator expressions <cmake-generator-expressions(7)>`.
.. note::
No collision resistance within a target is performed by CMake. When using
this property, collisions must be avoided in the project code. CMake has a
number of source files it generates that also create object files that may
collide with a given custom name. These include:
* Generated PCH source files (``cmake_pch``)
* Generated Unity compilation files (``unity_``)
* Qt autogen sources (``moc_compilations.cpp``)
+3
View File
@@ -12,6 +12,9 @@ Note that the object name may not be used as-is in
:prop_tgt:`INSTALL_OBJECT_NAME_STRATEGY` strategies, but instead changed as
the strategy requires to fulfill its goals.
When installing objects, the :prop_sf:`INSTALL_OBJECT_NAME` property takes
precedence.
This property supports
:manual:`generator expressions <cmake-generator-expressions(7)>`, but does not
allow for context-sensitive (i.e., configuration-dependent) expressions.
@@ -11,7 +11,9 @@ When installing object files, CMake automatically adds
conflicts. Use this property to suppress these components. Note that when
using a single install prefix for multiple configurations (whether via
multi-config generators or separate build trees), the destination must use
``$<CONFIG>`` to avoid conflicts.
``$<CONFIG>`` to avoid conflicts. Alternatively, the
:prop_sf:`INSTALL_OBJECT_NAME` may be used to avoid configuration-based
conflicts.
This property is initialized by the value of
:variable:`CMAKE_INSTALL_OBJECT_ONLY_USE_DESTINATION` when the target is
@@ -3,3 +3,6 @@ object-name-properties
* The :prop_sf:`OBJECT_NAME` source file property may be used to control
object names of source files.
* The :prop_sf:`INSTALL_OBJECT_NAME` source file property may be used to
control names of installed object files.
+2
View File
@@ -129,5 +129,7 @@ void cmLocalCommonGenerator::ComputeObjectFilenames(
force = false;
si.second.LongLoc.Update(this->GetObjectFileNameWithoutTarget(
*sf, gt->ObjectDirectory, &keptSourceExtension, custom_ext, &force));
this->FillCustomInstallObjectLocations(*sf, config, custom_ext,
si.second.InstallLongLoc);
}
}
+2
View File
@@ -66,6 +66,8 @@ void cmLocalFastbuildGenerator::ComputeObjectFilenames(
cmSourceFile const* sf = si.first;
si.second.LongLoc =
this->GetObjectFileNameWithoutTarget(*sf, gt->ObjectDirectory);
this->FillCustomInstallObjectLocations(*sf, config, nullptr,
si.second.InstallLongLoc);
}
}
+100
View File
@@ -45,6 +45,7 @@
#include "cmList.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmObjectLocation.h"
#include "cmRange.h"
#include "cmRulePlaceholderExpander.h"
#include "cmSourceFile.h"
@@ -4326,6 +4327,105 @@ std::string cmLocalGenerator::GetCustomObjectFileName(
return std::string{};
}
std::string cmLocalGenerator::GetCustomInstallObjectFileName(
cmSourceFile const& source, std::string const& config,
char const* custom_ext) const
{
if (auto objName = source.GetProperty("INSTALL_OBJECT_NAME")) {
cmGeneratorExpression ge(*this->GetCMakeInstance());
auto cge = ge.Parse(objName);
static std::string const INVALID_GENEX =
"_cmake_invalid_object_name_genex";
static std::string const INVALID_VALUE =
"_cmake_invalid_object_name_value";
if (!cge) {
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("The \"INSTALL_OBJECT_NAME\" property for\n ",
source.GetFullPath(),
"\nis not a valid generator expression (", objName, ")."));
return INVALID_GENEX;
}
if (cge->GetHadHeadSensitiveCondition()) {
// Not reachable; all target-sensitive genexes actually fail to parse.
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("The \"INSTALL_OBJECT_NAME\" property for\n ",
source.GetFullPath(),
"\ncontains a condition that queries the consuming target "
"which is not supported (",
objName, ")."));
return INVALID_GENEX;
}
if (cge->GetHadLinkLanguageSensitiveCondition()) {
// Not reachable; all target-sensitive genexes actually fail to parse.
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("The \"INSTALL_OBJECT_NAME\" property for\n ",
source.GetFullPath(),
"\ncontains a condition that queries the link language "
"which is not supported (",
objName, ")."));
return INVALID_GENEX;
}
auto objNameValue = cge->Evaluate(this, config);
// Skip if it evaluates to empty.
if (!objNameValue.empty()) {
cmCMakePath objNamePath = objNameValue;
// Verify that it is a relative path.
if (objNamePath.IsAbsolute()) {
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat(
"The \"INSTALL_OBJECT_NAME\" property for\n ",
source.GetFullPath(),
"\nresolves to an absolute path which is not supported:\n ",
objNameValue));
return INVALID_VALUE;
}
auto isInvalidComponent = [](cmCMakePath const& component) -> bool {
return component == ".."_s;
};
// Verify that it contains no `..` components.
if (std::any_of(objNamePath.begin(), objNamePath.end(),
isInvalidComponent)) {
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("The \"INSTALL_OBJECT_NAME\" property for\n ",
source.GetFullPath(), "\ncontains an invalid component (",
objNameValue, ")."));
return INVALID_VALUE;
}
if (custom_ext) {
objNameValue += custom_ext;
} else {
objNameValue +=
this->GetGlobalGenerator()->GetLanguageOutputExtension(source);
}
return objNameValue;
}
}
return std::string{};
}
void cmLocalGenerator::FillCustomInstallObjectLocations(
cmSourceFile const& source, std::string const& config,
char const* custom_ext,
std::map<std::string, cmObjectLocation>& mapping) const
{
auto installLoc =
this->GetCustomInstallObjectFileName(source, config, custom_ext);
if (!installLoc.empty()) {
mapping[config] = installLoc;
}
}
std::string cmLocalGenerator::GetObjectFileNameWithoutTarget(
cmSourceFile const& source, std::string const& dir_max,
bool* hasSourceExtension, char const* customOutputExtension,
+8
View File
@@ -37,6 +37,7 @@ class cmImplicitDependsList;
class cmLinkLineComputer;
class cmLinkLineDeviceComputer;
class cmMakefile;
struct cmObjectLocation;
struct cmObjectLocations;
class cmRulePlaceholderExpander;
class cmSourceFile;
@@ -453,6 +454,13 @@ public:
virtual std::string ComputeShortTargetDirectory(
cmGeneratorTarget const* gt) const;
std::string GetCustomObjectFileName(cmSourceFile const& source) const;
std::string GetCustomInstallObjectFileName(cmSourceFile const& source,
std::string const& config,
char const* custom_ext) const;
void FillCustomInstallObjectLocations(
cmSourceFile const& source, std::string const& config,
char const* custom_ext,
std::map<std::string, cmObjectLocation>& mapping) const;
/**
* Generate a macOS application bundle Info.plist file.
+2
View File
@@ -100,5 +100,7 @@ void cmLocalGhsMultiGenerator::ComputeObjectFilenames(
}
si.second.ShortLoc.emplace(shortObjectName);
si.second.LongLoc.Update(longObjectName);
this->FillCustomInstallObjectLocations(*sf, config, nullptr,
si.second.InstallLongLoc);
}
}
+2
View File
@@ -104,6 +104,8 @@ void cmLocalVisualStudioGenerator::ComputeObjectFilenames(
}
si.second.ShortLoc.emplace(shortObjectName);
si.second.LongLoc.Update(longObjectName);
this->FillCustomInstallObjectLocations(*sf, config, custom_ext,
si.second.InstallLongLoc);
}
}
+1
View File
@@ -142,6 +142,7 @@ void cmLocalXCodeGenerator::ComputeObjectFilenames(
}
si.second.ShortLoc.emplace(shortObjectName);
si.second.LongLoc.Update(longObjectName);
this->FillCustomInstallObjectLocations(*sf, config, ".o", si.second.InstallLongLoc);
}
}
+1
View File
@@ -570,6 +570,7 @@ if(XCODE_VERSION)
set(ObjectLibrary_ARGS -DXCODE_VERSION=${XCODE_VERSION})
endif()
add_RunCMake_test(ObjectLibrary)
add_RunCMake_test(INSTALL_OBJECT_NAME)
add_RunCMake_test(OBJECT_NAME)
add_RunCMake_test(ParseImplicitIncludeInfo)
add_RunCMake_test(ParseImplicitLinkInfo)
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.16)
project(${RunCMake_TEST} C)
include(${RunCMake_TEST}.cmake)
@@ -0,0 +1 @@
include("${CMAKE_CURRENT_LIST_DIR}/Consume.cmake")
@@ -0,0 +1 @@
include("${CMAKE_CURRENT_LIST_DIR}/Consume.cmake")
@@ -0,0 +1 @@
include("${CMAKE_CURRENT_LIST_DIR}/Consume.cmake")
@@ -0,0 +1 @@
include("${CMAKE_CURRENT_LIST_DIR}/Consume.cmake")
@@ -0,0 +1 @@
include("${CMAKE_CURRENT_LIST_DIR}/Consume.cmake")
@@ -0,0 +1 @@
include("${CMAKE_CURRENT_LIST_DIR}/Consume.cmake")
@@ -0,0 +1,7 @@
find_package(ION CONFIG REQUIRED)
add_executable(main main.c)
target_link_libraries(main PRIVATE ION::objlib)
enable_testing()
add_test(NAME run COMMAND main)
@@ -0,0 +1,5 @@
set(ext_suffix ".c")
if (RunCMake_GENERATOR MATCHES "(Visual Studio|Xcode)")
set(ext_suffix "")
endif ()
check_installed_object("${RunCMake_TEST_BINARY_DIR}/real_install/lib/objlib/objects-Debug/objlib/lib${ext_suffix}${CMAKE_C_OUTPUT_EXTENSION}")
@@ -0,0 +1,6 @@
set(CMAKE_INTERMEDIATE_DIR_STRATEGY FULL CACHE STRING "" FORCE)
add_library(objlib OBJECT lib.c)
set_property(SOURCE lib.c PROPERTY INSTALL_OBJECT_NAME "$<$<BOOL:0>:objlib_lib.c>")
install(TARGETS objlib EXPORT exp OBJECTS DESTINATION "lib/objlib")
install(EXPORT exp DESTINATION lib/cmake/ION FILE ion-config.cmake NAMESPACE ION::)
@@ -0,0 +1 @@
check_installed_object("${RunCMake_TEST_BINARY_DIR}/real_install/lib/objlib/objects-Debug/objlib/1/objlib_lib.c${CMAKE_C_OUTPUT_EXTENSION}")
@@ -0,0 +1,6 @@
set(CMAKE_INTERMEDIATE_DIR_STRATEGY FULL CACHE STRING "" FORCE)
add_library(objlib OBJECT lib.c)
set_property(SOURCE lib.c PROPERTY INSTALL_OBJECT_NAME "$<BOOL:ON>/objlib_lib.c")
install(TARGETS objlib EXPORT exp OBJECTS DESTINATION "lib/objlib")
install(EXPORT exp DESTINATION lib/cmake/ION FILE ion-config.cmake NAMESPACE ION::)
@@ -0,0 +1 @@
check_installed_object("${RunCMake_TEST_BINARY_DIR}/real_install/lib/objlib/objects-Debug/objlib/objlib_lib_debug.c${CMAKE_C_OUTPUT_EXTENSION}")
@@ -0,0 +1,6 @@
set(CMAKE_INTERMEDIATE_DIR_STRATEGY FULL CACHE STRING "" FORCE)
add_library(objlib OBJECT lib.c)
set_property(SOURCE lib.c PROPERTY INSTALL_OBJECT_NAME "objlib_lib_$<LOWER_CASE:$<CONFIG>>.c")
install(TARGETS objlib EXPORT exp OBJECTS DESTINATION "lib/objlib")
install(EXPORT exp DESTINATION lib/cmake/ION FILE ion-config.cmake NAMESPACE ION::)
@@ -0,0 +1,10 @@
enable_language(C)
set(info "")
# Forward information about the C++ compile features.
string(APPEND info "\
set(CMAKE_C_OUTPUT_EXTENSION \"${CMAKE_C_OUTPUT_EXTENSION}\")
")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}")
@@ -0,0 +1,9 @@
CMake Error:
Error evaluating generator expression:
\$<invalid>
Expression did not evaluate to a known generator expression
CMake Generate step failed. Build files cannot be regenerated correctly.
@@ -0,0 +1,2 @@
add_library(objlib OBJECT lib.c)
set_property(SOURCE lib.c PROPERTY INSTALL_OBJECT_NAME "$<invalid>")
@@ -0,0 +1 @@
1
@@ -0,0 +1,11 @@
CMake Error in CMakeLists.txt:
The "INSTALL_OBJECT_NAME" property for
.*/Tests/RunCMake/INSTALL_OBJECT_NAME/lib.c
resolves to an absolute path which is not supported:
.*/Tests/RunCMake/INSTALL_OBJECT_NAME/NoAbsolute-build/lib.c
CMake Generate step failed. Build files cannot be regenerated correctly.
@@ -0,0 +1,2 @@
add_library(static STATIC lib.c)
set_property(SOURCE lib.c PROPERTY INSTALL_OBJECT_NAME "${CMAKE_BINARY_DIR}/lib.c")
@@ -0,0 +1 @@
1
@@ -0,0 +1,9 @@
CMake Error in CMakeLists.txt:
The "INSTALL_OBJECT_NAME" property for
.*/Tests/RunCMake/INSTALL_OBJECT_NAME/lib.c
contains an invalid component \(subdir/\.\./sibling/lib.c\).
CMake Generate step failed. Build files cannot be regenerated correctly.
@@ -0,0 +1,2 @@
add_library(static STATIC lib.c)
set_property(SOURCE lib.c PROPERTY INSTALL_OBJECT_NAME "subdir/../sibling/lib.c")
@@ -0,0 +1 @@
check_installed_object("${RunCMake_TEST_BINARY_DIR}/real_install/lib/objlib/objects-Debug/objlib/objlib_lib.c${CMAKE_C_OUTPUT_EXTENSION}")
@@ -0,0 +1,6 @@
set(CMAKE_INTERMEDIATE_DIR_STRATEGY FULL CACHE STRING "" FORCE)
add_library(objlib OBJECT lib.c)
set_property(SOURCE lib.c PROPERTY INSTALL_OBJECT_NAME "objlib_lib.c")
install(TARGETS objlib EXPORT exp OBJECTS DESTINATION "lib/objlib")
install(EXPORT exp DESTINATION lib/cmake/ION FILE ion-config.cmake NAMESPACE ION::)
@@ -0,0 +1,50 @@
cmake_minimum_required(VERSION 3.16)
include(RunCMake)
# This test does installation of `OBJECT` libraries which does not work with
# multi-arch compilation under Xcode.
if (RunCMake_GENERATOR STREQUAL "Xcode" AND "$ENV{CMAKE_OSX_ARCHITECTURES}" MATCHES "[;$]")
return ()
endif ()
function(run_install_test case)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build)
set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE:STRING=Debug "-DCMAKE_INSTALL_PREFIX:PATH=${RunCMake_TEST_BINARY_DIR}/fake_install")
run_cmake(${case})
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug)
set(prefix "${RunCMake_TEST_BINARY_DIR}/real_install")
run_cmake_command(${case}-install ${CMAKE_COMMAND} --install . --config Debug --prefix "${prefix}")
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Consume-${case}-build)
set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE:STRING=Debug "-DCMAKE_PREFIX_PATH:PATH=${prefix}")
run_cmake(Consume-${case} "-DCMAKE_PREFIX_PATH=${prefix}")
run_cmake_command(Consume-${case}-build ${CMAKE_COMMAND} --build . --config Debug)
run_cmake_command(Consume-${case}-test ${CMAKE_CTEST_COMMAND} -C Debug)
endfunction()
function (check_installed_object path)
if (NOT EXISTS "${path}")
list(APPEND RunCMake_TEST_FAILED
"Could not find installed object at '${path}'")
endif ()
set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
endfunction ()
run_cmake(Inspect)
include("${RunCMake_BINARY_DIR}/Inspect-build/info.cmake")
run_cmake(NoAbsolute)
run_cmake(NoDotDot)
run_cmake(InvalidGeneratorExpression)
run_cmake(UnsupportedGeneratorExpressionHeadTarget)
run_cmake(UnsupportedGeneratorExpressionLinkLanguage)
run_install_test(ObjectLibrary)
run_install_test(DisableOnEmpty)
if (RunCMake_GENERATOR MATCHES "(Ninja|Makefiles|Visual Studio)")
run_install_test(ShortInstallStrategy)
endif ()
run_install_test(GeneratorExpression)
run_install_test(GeneratorExpressionContextSensitive)
@@ -0,0 +1 @@
check_installed_object("${RunCMake_TEST_BINARY_DIR}/real_install/lib/objlib/objects-Debug/objlib/d33a1361${CMAKE_C_OUTPUT_EXTENSION}")
@@ -0,0 +1,6 @@
set(CMAKE_INSTALL_OBJECT_NAME_STRATEGY SHORT)
add_library(objlib OBJECT lib.c)
set_property(SOURCE lib.c PROPERTY INSTALL_OBJECT_NAME "objlib_lib.c")
install(TARGETS objlib EXPORT exp OBJECTS DESTINATION "lib/objlib")
install(EXPORT exp DESTINATION lib/cmake/ION FILE ion-config.cmake NAMESPACE ION::)
@@ -0,0 +1 @@
check_installed_object("${RunCMake_TEST_BINARY_DIR}/real_install/lib/objlib/objects-Debug/objlib/objlib_lib.c${CMAKE_C_OUTPUT_EXTENSION}")
@@ -0,0 +1,6 @@
set(CMAKE_INTERMEDIATE_DIR_STRATEGY SHORT CACHE STRING "" FORCE)
add_library(objlib OBJECT lib.c)
set_property(SOURCE lib.c PROPERTY INSTALL_OBJECT_NAME "objlib_lib.c")
install(TARGETS objlib EXPORT exp OBJECTS DESTINATION "lib/objlib")
install(EXPORT exp DESTINATION lib/cmake/ION FILE ion-config.cmake NAMESPACE ION::)
@@ -0,0 +1,12 @@
CMake Error:
Error evaluating generator expression:
\$<TARGET_PROPERTY:foo>
\$<TARGET_PROPERTY:prop> may only be used with binary targets. It may not
be used with add_custom_command or add_custom_target. Specify the target
to read a property from using the \$<TARGET_PROPERTY:tgt,prop> signature
instead.
CMake Generate step failed. Build files cannot be regenerated correctly.
@@ -0,0 +1,2 @@
add_library(objlib OBJECT lib.c)
set_property(SOURCE lib.c PROPERTY INSTALL_OBJECT_NAME "$<TARGET_PROPERTY:foo>/objlib_lib.c")
@@ -0,0 +1,10 @@
CMake Error:
Error evaluating generator expression:
\$<LINK_LANGUAGE>
\$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
libraries, link directories, link options and link depends.
CMake Generate step failed. Build files cannot be regenerated correctly.
@@ -0,0 +1,2 @@
add_library(objlib OBJECT lib.c)
set_property(SOURCE lib.c PROPERTY INSTALL_OBJECT_NAME "$<LINK_LANGUAGE>/objlib_lib.c")
+4
View File
@@ -0,0 +1,4 @@
int f(int a)
{
return a;
}
@@ -0,0 +1,6 @@
int f(int a);
int main(void)
{
return f(0);
}