From 4d46b1401f9d1624fa5afc3995844d7aebc7ccf4 Mon Sep 17 00:00:00 2001 From: Craig Scott Date: Thu, 4 Feb 2021 20:30:49 +1100 Subject: add_library(): Allow imported object libraries with multi-arch Fixes: #21276 --- Help/prop_tgt/IMPORTED_OBJECTS.rst | 86 +++++++++++++++++++++- Help/prop_tgt/IMPORTED_OBJECTS_CONFIG.rst | 13 +++- Help/release/dev/object-lib-multiarch.rst | 8 ++ Source/cmAddLibraryCommand.cxx | 10 --- .../ObjectLibrary/ImportMultiArch-check.cmake | 15 ++++ Tests/RunCMake/ObjectLibrary/ImportMultiArch.cmake | 13 ++++ .../ObjectLibrary/ImportNotSupported-result.txt | 1 - .../ObjectLibrary/ImportNotSupported-stderr.txt | 5 -- .../ObjectLibrary/ImportNotSupported.cmake | 1 - Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake | 2 +- Tests/RunCMake/TargetObjects/RunCMakeTest.cmake | 4 + .../XcodeVariableNoGenexExpansion-result.txt | 1 + .../XcodeVariableNoGenexExpansion-stderr.txt | 10 +++ .../XcodeVariableNoGenexExpansion.cmake | 12 +++ 14 files changed, 159 insertions(+), 22 deletions(-) create mode 100644 Help/release/dev/object-lib-multiarch.rst create mode 100644 Tests/RunCMake/ObjectLibrary/ImportMultiArch-check.cmake create mode 100644 Tests/RunCMake/ObjectLibrary/ImportMultiArch.cmake delete mode 100644 Tests/RunCMake/ObjectLibrary/ImportNotSupported-result.txt delete mode 100644 Tests/RunCMake/ObjectLibrary/ImportNotSupported-stderr.txt delete mode 100644 Tests/RunCMake/ObjectLibrary/ImportNotSupported.cmake create mode 100644 Tests/RunCMake/TargetObjects/XcodeVariableNoGenexExpansion-result.txt create mode 100644 Tests/RunCMake/TargetObjects/XcodeVariableNoGenexExpansion-stderr.txt create mode 100644 Tests/RunCMake/TargetObjects/XcodeVariableNoGenexExpansion.cmake diff --git a/Help/prop_tgt/IMPORTED_OBJECTS.rst b/Help/prop_tgt/IMPORTED_OBJECTS.rst index bbbcd86..f3577eb 100644 --- a/Help/prop_tgt/IMPORTED_OBJECTS.rst +++ b/Help/prop_tgt/IMPORTED_OBJECTS.rst @@ -3,11 +3,91 @@ IMPORTED_OBJECTS .. versionadded:: 3.9 -A :ref:`semicolon-separated list ` of absolute paths to the object -files on disk for an :ref:`imported ` +A :ref:`semicolon-separated list ` of absolute paths +to the object files on disk for an :ref:`imported ` :ref:`object library `. Ignored for non-imported targets. Projects may skip ``IMPORTED_OBJECTS`` if the configuration-specific -property :prop_tgt:`IMPORTED_OBJECTS_` is set instead. +property :prop_tgt:`IMPORTED_OBJECTS_` is set instead, except in +situations as noted in the section below. + + +Xcode Generator Considerations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 3.20 + +For Apple platforms, a project may be built for more than one architecture. +This is controlled by the :variable:`CMAKE_OSX_ARCHITECTURES` variable. +For all but the :generator:`Xcode` generator, CMake invokes compilers once +per source file and passes multiple ``-arch`` flags, leading to a single +object file which will be a universal binary. Such object files work well +when listed in the ``IMPORTED_OBJECTS`` of a separate CMake build, even for +the :generator:`Xcode` generator. But producing such object files with the +:generator:`Xcode` generator is more difficult, since it invokes the compiler +once per architecture for each source file. Unlike the other generators, +it does not generate universal object file binaries. + +A further complication with the :generator:`Xcode` generator is that when +targeting device platforms (iOS, tvOS or watchOS), the :generator:`Xcode` +generator has the ability to use either the device or simulator SDK without +needing CMake to be re-run. The SDK can be selected at build time. +But since some architectures can be supported by both the device and the +simulator SDKs (e.g. ``arm64`` with Xcode 12 or later), not all combinations +can be represented in a single universal binary. The only solution in this +case is to have multiple object files. + +``IMPORTED_OBJECTS`` doesn't support generator expressions, so every file +it lists needs to be valid for every architecture and SDK. If incorporating +object files that are not universal binaries, the path and/or file name of +each object file has to somehow encapsulate the different architectures and +SDKs. With the :generator:`Xcode` generator, Xcode variables of the form +``$(...)`` can be used to represent these aspects and Xcode will substitute +the appropriate values at build time. CMake doesn't interpret these +variables and embeds them unchanged in the Xcode project file. +``$(CURRENT_ARCH)`` can be used to represent the architecture, while +``$(EFFECTIVE_PLATFORM_NAME)`` can be used to differentiate between SDKs. + +The following shows one example of how these two variables can be used to +refer to an object file whose location depends on both the SDK and the +architecture: + +.. code-block:: cmake + + add_library(someObjs OBJECT IMPORTED) + + set_property(TARGET someObjs PROPERTY IMPORTED_OBJECTS + # Quotes are required because of the () + "/path/to/somewhere/objects$(EFFECTIVE_PLATFORM_NAME)/$(CURRENT_ARCH)/func.o" + ) + + # Example paths: + # /path/to/somewhere/objects-iphoneos/arm64/func.o + # /path/to/somewhere/objects-iphonesimulator/x86_64/func.o + +In some cases, you may want to have configuration-specific object files +as well. The :variable:`CMAKE_CFG_INTDIR` variable can be a convenient +way of capturing this in combination with the SDK: + +.. code-block:: cmake + + add_library(someObjs OBJECT IMPORTED) + set_property(TARGET someObjs PROPERTY IMPORTED_OBJECTS + "/path/to/somewhere/${CMAKE_CFG_INTDIR}/$(CURRENT_ARCH)/func.o" + ) + + # Example paths: + # /path/to/somewhere/Release-iphoneos/arm64/func.o + # /path/to/somewhere/Debug-iphonesimulator/x86_64/func.o + +When any Xcode variable or :variable:`CMAKE_CFG_INTDIR` is used, CMake is +not able to fully evaluate the path(s) at configure time. One consequence +of this is that the configuration-specific +:prop_tgt:`IMPORTED_OBJECTS_` properties cannot be used, since +CMake cannot determine whether an object file exists at a particular +```` location. The ``IMPORTED_OBJECTS`` property must be used for +these situations and the configuration-specific aspects of the path must be +handled by using :variable:`CMAKE_CFG_INTDIR` or with another Xcode variable +``$(CONFIGURATION)``. diff --git a/Help/prop_tgt/IMPORTED_OBJECTS_CONFIG.rst b/Help/prop_tgt/IMPORTED_OBJECTS_CONFIG.rst index b12ca38..238395a 100644 --- a/Help/prop_tgt/IMPORTED_OBJECTS_CONFIG.rst +++ b/Help/prop_tgt/IMPORTED_OBJECTS_CONFIG.rst @@ -3,7 +3,18 @@ IMPORTED_OBJECTS_ .. versionadded:: 3.9 --specific version of :prop_tgt:`IMPORTED_OBJECTS` property. +````-specific version of :prop_tgt:`IMPORTED_OBJECTS` property. Configuration names correspond to those provided by the project from which the target is imported. + + +Xcode Generator Considerations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Do not use this ````-specific property if you need to use Xcode +variables like ``$(CURRENT_ARCH)`` or ``$(EFFECTIVE_PLATFORM_NAME)`` in +the value. The ````-specific properties will be ignored in such +cases because CMake cannot determine whether a file exists at the +configuration-specific path at configuration time. For such cases, use +:prop_tgt:`IMPORTED_OBJECTS` instead. diff --git a/Help/release/dev/object-lib-multiarch.rst b/Help/release/dev/object-lib-multiarch.rst new file mode 100644 index 0000000..0d309d6 --- /dev/null +++ b/Help/release/dev/object-lib-multiarch.rst @@ -0,0 +1,8 @@ +object-lib-multiarch +-------------------- + +* The :command:`add_library` command previously prohibited imported object + libraries when using potentially multi-architecture configurations. + This mostly affected the :generator:`Xcode` generator, e.g. when targeting + iOS or one of the other device platforms. This restriction has now been + removed. diff --git a/Source/cmAddLibraryCommand.cxx b/Source/cmAddLibraryCommand.cxx index f262fac..92e04e4 100644 --- a/Source/cmAddLibraryCommand.cxx +++ b/Source/cmAddLibraryCommand.cxx @@ -238,16 +238,6 @@ bool cmAddLibraryCommand(std::vector const& args, status.SetError("called with IMPORTED argument but no library type."); return false; } - if (type == cmStateEnums::OBJECT_LIBRARY) { - std::string reason; - if (!mf.GetGlobalGenerator()->HasKnownObjectFileLocation(&reason)) { - mf.IssueMessage( - MessageType::FATAL_ERROR, - "The OBJECT library type may not be used for IMPORTED libraries" + - reason + "."); - return true; - } - } if (type == cmStateEnums::INTERFACE_LIBRARY) { if (!cmGeneratorExpression::IsValidTargetName(libName)) { status.SetError(cmStrCat( diff --git a/Tests/RunCMake/ObjectLibrary/ImportMultiArch-check.cmake b/Tests/RunCMake/ObjectLibrary/ImportMultiArch-check.cmake new file mode 100644 index 0000000..af1df78 --- /dev/null +++ b/Tests/RunCMake/ObjectLibrary/ImportMultiArch-check.cmake @@ -0,0 +1,15 @@ +set(xcProjectFile "${RunCMake_TEST_BINARY_DIR}/ImportMultiArch.xcodeproj/project.pbxproj") +if(NOT EXISTS "${xcProjectFile}") + set(RunCMake_TEST_FAILED "Project file ${xcProjectFile} does not exist.") + return() +endif() + +file(READ ${xcProjectFile} pbxFileContents) +foreach(config IN ITEMS Debug Release RelWithDebInfo MinSizeRel) + set(regex "--findconfig-${config}[^ +]*\\$\\(CURRENT_ARCH\\)") + if(NOT pbxFileContents MATCHES "${regex}") + set(RunCMake_TEST_FAILED "$(CURRENT_ARCH) not preserved for config ${config}") + return() + endif() +endforeach() diff --git a/Tests/RunCMake/ObjectLibrary/ImportMultiArch.cmake b/Tests/RunCMake/ObjectLibrary/ImportMultiArch.cmake new file mode 100644 index 0000000..64029ee --- /dev/null +++ b/Tests/RunCMake/ObjectLibrary/ImportMultiArch.cmake @@ -0,0 +1,13 @@ + +add_library(A OBJECT IMPORTED) + +# We don't actually build this example so just configure dummy +# object files to test. They do not have to exist. +set_target_properties(A PROPERTIES + IMPORTED_OBJECTS "${CMAKE_CURRENT_BINARY_DIR}/$(CURRENT_ARCH)/does_not_exist.o" +) + +add_library(B SHARED $ b.c) + +# We use this to find the relevant lines of the project.pbx file +target_link_options(B PRIVATE --findconfig-$) diff --git a/Tests/RunCMake/ObjectLibrary/ImportNotSupported-result.txt b/Tests/RunCMake/ObjectLibrary/ImportNotSupported-result.txt deleted file mode 100644 index d00491f..0000000 --- a/Tests/RunCMake/ObjectLibrary/ImportNotSupported-result.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/Tests/RunCMake/ObjectLibrary/ImportNotSupported-stderr.txt b/Tests/RunCMake/ObjectLibrary/ImportNotSupported-stderr.txt deleted file mode 100644 index 0fadac2..0000000 --- a/Tests/RunCMake/ObjectLibrary/ImportNotSupported-stderr.txt +++ /dev/null @@ -1,5 +0,0 @@ -CMake Error at ImportNotSupported.cmake:[0-9]+ \(add_library\): - The OBJECT library type may not be used for IMPORTED libraries under Xcode - with multiple architectures. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/ObjectLibrary/ImportNotSupported.cmake b/Tests/RunCMake/ObjectLibrary/ImportNotSupported.cmake deleted file mode 100644 index 806b44a..0000000 --- a/Tests/RunCMake/ObjectLibrary/ImportNotSupported.cmake +++ /dev/null @@ -1 +0,0 @@ -add_library(A OBJECT IMPORTED) diff --git a/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake b/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake index 5ec4018..8515ba5 100644 --- a/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake +++ b/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake @@ -6,7 +6,7 @@ run_cmake(BadSourceExpression3) run_cmake(BadObjSource1) run_cmake(BadObjSource2) if(RunCMake_GENERATOR STREQUAL "Xcode" AND "$ENV{CMAKE_OSX_ARCHITECTURES}" MATCHES "[;$]") - run_cmake(ImportNotSupported) + run_cmake(ImportMultiArch) run_cmake(InstallNotSupported) else() run_cmake(Import) diff --git a/Tests/RunCMake/TargetObjects/RunCMakeTest.cmake b/Tests/RunCMake/TargetObjects/RunCMakeTest.cmake index 30b9fee..d2b3032 100644 --- a/Tests/RunCMake/TargetObjects/RunCMakeTest.cmake +++ b/Tests/RunCMake/TargetObjects/RunCMakeTest.cmake @@ -2,3 +2,7 @@ include(RunCMake) run_cmake(NoTarget) run_cmake(NotObjlibTarget) + +if(RunCMake_GENERATOR STREQUAL "Xcode" AND "$ENV{CMAKE_OSX_ARCHITECTURES}" MATCHES "[;$]") + run_cmake(XcodeVariableNoGenexExpansion) +endif() diff --git a/Tests/RunCMake/TargetObjects/XcodeVariableNoGenexExpansion-result.txt b/Tests/RunCMake/TargetObjects/XcodeVariableNoGenexExpansion-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/TargetObjects/XcodeVariableNoGenexExpansion-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/TargetObjects/XcodeVariableNoGenexExpansion-stderr.txt b/Tests/RunCMake/TargetObjects/XcodeVariableNoGenexExpansion-stderr.txt new file mode 100644 index 0000000..1360015 --- /dev/null +++ b/Tests/RunCMake/TargetObjects/XcodeVariableNoGenexExpansion-stderr.txt @@ -0,0 +1,10 @@ +CMake Error at XcodeVariableNoGenexExpansion\.cmake:9 \(file\): + Error evaluating generator expression: + + \$\ + + The evaluation of the TARGET_OBJECTS generator expression is only suitable + for consumption by CMake \(limited under Xcode with multiple architectures\)\. + It is not suitable for writing out elsewhere\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\) diff --git a/Tests/RunCMake/TargetObjects/XcodeVariableNoGenexExpansion.cmake b/Tests/RunCMake/TargetObjects/XcodeVariableNoGenexExpansion.cmake new file mode 100644 index 0000000..83d7210 --- /dev/null +++ b/Tests/RunCMake/TargetObjects/XcodeVariableNoGenexExpansion.cmake @@ -0,0 +1,12 @@ +add_library(A OBJECT IMPORTED) + +# We don't actually build this example so just configure a dummy +# object file to test. It does not have to exist. +set_target_properties(A PROPERTIES + IMPORTED_OBJECTS "${CMAKE_CURRENT_BINARY_DIR}/$(CURRENT_ARCH)/does_not_exist.o" +) + +file(GENERATE + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/objects.txt + CONTENT "$" +) -- cgit v0.12