Commit 4d46b140 authored by Craig Scott's avatar Craig Scott
Browse files

add_library(): Allow imported object libraries with multi-arch

Fixes: #21276
parent 1a915e89
Pipeline #213008 passed with stages
in 124 minutes and 5 seconds
......@@ -3,11 +3,91 @@ IMPORTED_OBJECTS
.. versionadded:: 3.9
A :ref:`semicolon-separated list <CMake Language Lists>` of absolute paths to the object
files on disk for an :ref:`imported <Imported targets>`
A :ref:`semicolon-separated list <CMake Language Lists>` of absolute paths
to the object files on disk for an :ref:`imported <Imported targets>`
:ref:`object library <object libraries>`.
Ignored for non-imported targets.
Projects may skip ``IMPORTED_OBJECTS`` if the configuration-specific
property :prop_tgt:`IMPORTED_OBJECTS_<CONFIG>` is set instead.
property :prop_tgt:`IMPORTED_OBJECTS_<CONFIG>` 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_<CONFIG>` properties cannot be used, since
CMake cannot determine whether an object file exists at a particular
``<CONFIG>`` 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)``.
......@@ -3,7 +3,18 @@ IMPORTED_OBJECTS_<CONFIG>
.. versionadded:: 3.9
<CONFIG>-specific version of :prop_tgt:`IMPORTED_OBJECTS` property.
``<CONFIG>``-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 ``<CONFIG>``-specific property if you need to use Xcode
variables like ``$(CURRENT_ARCH)`` or ``$(EFFECTIVE_PLATFORM_NAME)`` in
the value. The ``<CONFIG>``-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.
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.
......@@ -238,16 +238,6 @@ bool cmAddLibraryCommand(std::vector<std::string> 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(
......
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()
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 $<TARGET_OBJECTS:A> b.c)
# We use this to find the relevant lines of the project.pbx file
target_link_options(B PRIVATE --findconfig-$<CONFIG>)
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\)
......@@ -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)
......
......@@ -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()
CMake Error at XcodeVariableNoGenexExpansion\.cmake:9 \(file\):
Error evaluating generator expression:
\$\<TARGET_OBJECTS:A\>
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\)
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 "$<TARGET_OBJECTS:A>"
)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment