Browse Source

Merge topic 'object-lib-multiarch'

4d46b1401f add_library(): Allow imported object libraries with multi-arch
1a915e8953 Tests: TargetObjects does not need any languages enabled

Acked-by: Kitware Robot <[email protected]>
Merge-request: !5771
Brad King 4 years ago
parent
commit
40f4147224

+ 83 - 3
Help/prop_tgt/IMPORTED_OBJECTS.rst

@@ -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)``.

+ 12 - 1
Help/prop_tgt/IMPORTED_OBJECTS_CONFIG.rst

@@ -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.

+ 8 - 0
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.

+ 0 - 10
Source/cmAddLibraryCommand.cxx

@@ -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(

+ 15 - 0
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()

+ 13 - 0
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 $<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>)

+ 0 - 5
Tests/RunCMake/ObjectLibrary/ImportNotSupported-stderr.txt

@@ -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\)

+ 0 - 1
Tests/RunCMake/ObjectLibrary/ImportNotSupported.cmake

@@ -1 +0,0 @@
-add_library(A OBJECT IMPORTED)

+ 1 - 1
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)

+ 1 - 1
Tests/RunCMake/TargetObjects/CMakeLists.txt

@@ -1,3 +1,3 @@
 cmake_minimum_required(VERSION 3.3)
-project(${RunCMake_TEST})
+project(${RunCMake_TEST} LANGUAGES NONE)
 include(${RunCMake_TEST}.cmake)

+ 4 - 0
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()

+ 0 - 0
Tests/RunCMake/ObjectLibrary/ImportNotSupported-result.txt → Tests/RunCMake/TargetObjects/XcodeVariableNoGenexExpansion-result.txt


+ 10 - 0
Tests/RunCMake/TargetObjects/XcodeVariableNoGenexExpansion-stderr.txt

@@ -0,0 +1,10 @@
+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\)

+ 12 - 0
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 "$<TARGET_OBJECTS:A>"
+)

+ 0 - 4
Tests/RunCMake/TargetObjects/empty.cpp

@@ -1,4 +0,0 @@
-int empty()
-{
-  return 0;
-}