Browse Source

Merge topic 'save-restore-PACKAGE_PREFIX_DIR'

41f4e1c457 CMakePackageConfigHelpers: Document PACKAGE_PREFIX_DIR for public use
c5231ba29e find_package: Save/restore PACKAGE_PREFIX_DIR
8ac7958e3a generate_apple_*_selection_file: Save/restore PACKAGE_PREFIX_DIR
bf88879f1f generate_apple_architecture_selection_file: Avoid early returns
a4ac2c92f4 Help: Add missing section heading for apple architecture selection
b7fcc44be9 Help: Fix CMakePackageConfigHelpers typos, grammar and formatting
f1cacaa830 Tests/RunCMake/CMakePackage: Define variable closer to where it is used

Acked-by: Kitware Robot <[email protected]>
Merge-request: !9430
Brad King 1 year ago
parent
commit
8b3d48ab94

+ 103 - 72
Modules/CMakePackageConfigHelpers.cmake

@@ -5,7 +5,7 @@
 CMakePackageConfigHelpers
 CMakePackageConfigHelpers
 -------------------------
 -------------------------
 
 
-Helpers functions for creating config files that can be included by other
+Helper functions for creating config files that can be included by other
 projects to find and use a package.
 projects to find and use a package.
 
 
 Generating a Package Configuration File
 Generating a Package Configuration File
@@ -26,11 +26,11 @@ Generating a Package Configuration File
 ``configure_package_config_file()`` should be used instead of the plain
 ``configure_package_config_file()`` should be used instead of the plain
 :command:`configure_file()` command when creating the ``<PackageName>Config.cmake``
 :command:`configure_file()` command when creating the ``<PackageName>Config.cmake``
 or ``<PackageName>-config.cmake`` file for installing a project or library.
 or ``<PackageName>-config.cmake`` file for installing a project or library.
-It helps making the resulting package relocatable by avoiding hardcoded paths
-in the installed ``Config.cmake`` file.
+It helps make the resulting package relocatable by avoiding hardcoded paths
+in the installed ``<PackageName>Config.cmake`` file.
 
 
 In a ``FooConfig.cmake`` file there may be code like this to make the install
 In a ``FooConfig.cmake`` file there may be code like this to make the install
-destinations know to the using project:
+destinations known to the using project:
 
 
 .. code-block:: cmake
 .. code-block:: cmake
 
 
@@ -40,27 +40,25 @@ destinations know to the using project:
    #...logic to determine installedPrefix from the own location...
    #...logic to determine installedPrefix from the own location...
    set(FOO_CONFIG_DIR  "${installedPrefix}/@CONFIG_INSTALL_DIR@" )
    set(FOO_CONFIG_DIR  "${installedPrefix}/@CONFIG_INSTALL_DIR@" )
 
 
-All 4 options shown above are not sufficient, since the first 3 hardcode the
-absolute directory locations, and the 4th case works only if the logic to
+All four options shown above are not sufficient  The first three hardcode the
+absolute directory locations.  The fourth case works only if the logic to
 determine the ``installedPrefix`` is correct, and if ``CONFIG_INSTALL_DIR``
 determine the ``installedPrefix`` is correct, and if ``CONFIG_INSTALL_DIR``
 contains a relative path, which in general cannot be guaranteed.  This has the
 contains a relative path, which in general cannot be guaranteed.  This has the
 effect that the resulting ``FooConfig.cmake`` file would work poorly under
 effect that the resulting ``FooConfig.cmake`` file would work poorly under
-Windows and OSX, where users are used to choose the install location of a
+Windows and macOS, where users are used to choosing the install location of a
 binary package at install time, independent from how
 binary package at install time, independent from how
 :variable:`CMAKE_INSTALL_PREFIX` was set at build/cmake time.
 :variable:`CMAKE_INSTALL_PREFIX` was set at build/cmake time.
 
 
-Using ``configure_package_config_file`` helps.  If used correctly, it makes
+Using ``configure_package_config_file()`` helps.  If used correctly, it makes
 the resulting ``FooConfig.cmake`` file relocatable.  Usage:
 the resulting ``FooConfig.cmake`` file relocatable.  Usage:
 
 
-1. write a ``FooConfig.cmake.in`` file as you are used to
-2. insert a line containing only the string ``@PACKAGE_INIT@``
-3. instead of ``set(FOO_DIR "@SOME_INSTALL_DIR@")``, use
+1. Write a ``FooConfig.cmake.in`` file as you are used to.
+2. Insert a line at the top containing only the string ``@PACKAGE_INIT@``.
+3. Instead of ``set(FOO_DIR "@SOME_INSTALL_DIR@")``, use
    ``set(FOO_DIR "@PACKAGE_SOME_INSTALL_DIR@")`` (this must be after the
    ``set(FOO_DIR "@PACKAGE_SOME_INSTALL_DIR@")`` (this must be after the
-   ``@PACKAGE_INIT@`` line)
-4. instead of using the normal :command:`configure_file()`, use
-   ``configure_package_config_file()``
-
-
+   ``@PACKAGE_INIT@`` line).
+4. Instead of using the normal :command:`configure_file()` command, use
+   ``configure_package_config_file()``.
 
 
 The ``<input>`` and ``<output>`` arguments are the input and output file, the
 The ``<input>`` and ``<output>`` arguments are the input and output file, the
 same way as in :command:`configure_file()`.
 same way as in :command:`configure_file()`.
@@ -70,48 +68,64 @@ the ``FooConfig.cmake`` file will be installed to.  This path can either be
 absolute, or relative to the ``INSTALL_PREFIX`` path.
 absolute, or relative to the ``INSTALL_PREFIX`` path.
 
 
 The variables ``<var1>`` to ``<varN>`` given as ``PATH_VARS`` are the
 The variables ``<var1>`` to ``<varN>`` given as ``PATH_VARS`` are the
-variables which contain install destinations.  For each of them the macro will
+variables which contain install destinations.  For each of them, the macro will
 create a helper variable ``PACKAGE_<var...>``.  These helper variables must be
 create a helper variable ``PACKAGE_<var...>``.  These helper variables must be
 used in the ``FooConfig.cmake.in`` file for setting the installed location.
 used in the ``FooConfig.cmake.in`` file for setting the installed location.
-They are calculated by ``configure_package_config_file`` so that they are
+They are calculated by ``configure_package_config_file()`` so that they are
 always relative to the installed location of the package.  This works both for
 always relative to the installed location of the package.  This works both for
-relative and also for absolute locations.  For absolute locations it works
+relative and also for absolute locations.  For absolute locations, it works
 only if the absolute location is a subdirectory of ``INSTALL_PREFIX``.
 only if the absolute location is a subdirectory of ``INSTALL_PREFIX``.
 
 
+.. versionadded:: 3.30
+  The variable ``PACKAGE_PREFIX_DIR`` will always be defined after the
+  ``@PACKAGE_INIT@`` line.  It will hold the value of the base install
+  location.  In general, variables defined via the ``PATH_VARS`` mechanism
+  should be used instead, but ``PACKAGE_PREFIX_DIR`` can be used for those
+  cases not easily handled by ``PATH_VARS``, such as for files installed
+  directly to the base install location rather than a subdirectory of it.
+
+  .. note::
+    When consumers of the generated file use CMake 3.29 or older, the value
+    of ``PACKAGE_PREFIX_DIR`` can be changed by a call to
+    :command:`find_dependency` or :command:`find_package`.
+    If a project relies on ``PACKAGE_PREFIX_DIR``, it is the project's
+    responsibility to ensure that the value of ``PACKAGE_PREFIX_DIR`` is
+    preserved across any such calls, or any other calls which might include
+    another file generated by ``configure_package_config_file()``.
+
 .. versionadded:: 3.1
 .. versionadded:: 3.1
-  If the ``INSTALL_PREFIX`` argument is passed, this is used as base path to
+  If the ``INSTALL_PREFIX`` argument is passed, this is used as the base path to
   calculate all the relative paths.  The ``<path>`` argument must be an absolute
   calculate all the relative paths.  The ``<path>`` argument must be an absolute
   path.  If this argument is not passed, the :variable:`CMAKE_INSTALL_PREFIX`
   path.  If this argument is not passed, the :variable:`CMAKE_INSTALL_PREFIX`
   variable will be used instead.  The default value is good when generating a
   variable will be used instead.  The default value is good when generating a
-  FooConfig.cmake file to use your package from the install tree.  When
-  generating a FooConfig.cmake file to use your package from the build tree this
-  option should be used.
+  ``FooConfig.cmake`` file to use your package from the install tree.  When
+  generating a ``FooConfig.cmake`` file to use your package from the build tree,
+  this option should be used.
 
 
-By default ``configure_package_config_file`` also generates two helper macros,
-``set_and_check()`` and ``check_required_components()`` into the
+By default, ``configure_package_config_file()`` also generates two helper
+macros, ``set_and_check()`` and ``check_required_components()``, into the
 ``FooConfig.cmake`` file.
 ``FooConfig.cmake`` file.
 
 
-``set_and_check()`` should be used instead of the normal ``set()`` command for
-setting directories and file locations.  Additionally to setting the variable
-it also checks that the referenced file or directory actually exists and fails
-with a ``FATAL_ERROR`` otherwise.  This makes sure that the created
+``set_and_check()`` should be used instead of the normal :command:`set` command
+for setting directories and file locations.  In addition to setting the
+variable, it also checks that the referenced file or directory actually exists
+and fails with a fatal error if it doesn't.  This ensures that the generated
 ``FooConfig.cmake`` file does not contain wrong references.
 ``FooConfig.cmake`` file does not contain wrong references.
-When using the ``NO_SET_AND_CHECK_MACRO``, this macro is not generated
-into the ``FooConfig.cmake`` file.
+Add the ``NO_SET_AND_CHECK_MACRO`` option to prevent the generation of the
+``set_and_check()`` macro in the ``FooConfig.cmake`` file.
 
 
 ``check_required_components(<PackageName>)`` should be called at the end of
 ``check_required_components(<PackageName>)`` should be called at the end of
 the ``FooConfig.cmake`` file. This macro checks whether all requested,
 the ``FooConfig.cmake`` file. This macro checks whether all requested,
-non-optional components have been found, and if this is not the case, sets
-the ``Foo_FOUND`` variable to ``FALSE``, so that the package is considered to
+non-optional components have been found, and if this is not the case, it sets
+the ``Foo_FOUND`` variable to ``FALSE`` so that the package is considered to
 be not found.  It does that by testing the ``Foo_<Component>_FOUND``
 be not found.  It does that by testing the ``Foo_<Component>_FOUND``
 variables for all requested required components.  This macro should be
 variables for all requested required components.  This macro should be
 called even if the package doesn't provide any components to make sure
 called even if the package doesn't provide any components to make sure
-users are not specifying components erroneously.  When using the
-``NO_CHECK_REQUIRED_COMPONENTS_MACRO`` option, this macro is not generated
-into the ``FooConfig.cmake`` file.
+users are not specifying components erroneously.  Add the
+``NO_CHECK_REQUIRED_COMPONENTS_MACRO`` option to prevent the generation of the
+``check_required_components()`` macro in the ``FooConfig.cmake`` file.
 
 
-For an example see below the documentation for
-:command:`write_basic_package_version_file()`.
+See also :ref:`CMakePackageConfigHelpers Examples`.
 
 
 Generating a Package Version File
 Generating a Package Version File
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -126,11 +140,11 @@ Generating a Package Version File
      [ARCH_INDEPENDENT] )
      [ARCH_INDEPENDENT] )
 
 
 
 
-Writes a file for use as ``<PackageName>ConfigVersion.cmake`` file to
+Writes a file for use as a ``<PackageName>ConfigVersion.cmake`` file to
 ``<filename>``.  See the documentation of :command:`find_package()` for
 ``<filename>``.  See the documentation of :command:`find_package()` for
-details on this.
+details on such files.
 
 
-``<filename>`` is the output filename, it should be in the build tree.
+``<filename>`` is the output filename, which should be in the build tree.
 ``<major.minor.patch>`` is the version number of the project to be installed.
 ``<major.minor.patch>`` is the version number of the project to be installed.
 
 
 If no ``VERSION`` is given, the :variable:`PROJECT_VERSION` variable is used.
 If no ``VERSION`` is given, the :variable:`PROJECT_VERSION` variable is used.
@@ -153,9 +167,9 @@ the requested version matches exactly its own version number (not considering
 the tweak version).  For example, version 1.2.3 of a package is only
 the tweak version).  For example, version 1.2.3 of a package is only
 considered compatible to requested version 1.2.3.  This mode is for packages
 considered compatible to requested version 1.2.3.  This mode is for packages
 without compatibility guarantees.
 without compatibility guarantees.
-If your project has more elaborated version matching rules, you will need to
-write your own custom ``ConfigVersion.cmake`` file instead of using this
-macro.
+If your project has more elaborate version matching rules, you will need to
+write your own custom ``<PackageName>ConfigVersion.cmake`` file instead of
+using this macro.
 
 
 .. versionadded:: 3.11
 .. versionadded:: 3.11
   The ``SameMinorVersion`` compatibility mode.
   The ``SameMinorVersion`` compatibility mode.
@@ -170,13 +184,13 @@ macro.
   unless ``ARCH_INDEPENDENT`` is given, in which case the package is considered
   unless ``ARCH_INDEPENDENT`` is given, in which case the package is considered
   compatible on any architecture.
   compatible on any architecture.
 
 
-.. note:: ``ARCH_INDEPENDENT`` is intended for header-only libraries or similar
-  packages with no binaries.
+  .. note:: ``ARCH_INDEPENDENT`` is intended for header-only libraries or
+    similar packages with no binaries.
 
 
 .. versionadded:: 3.19
 .. versionadded:: 3.19
   The version file generated by ``AnyNewerVersion``, ``SameMajorVersion`` and
   The version file generated by ``AnyNewerVersion``, ``SameMajorVersion`` and
-  ``SameMinorVersion`` arguments of ``COMPATIBILITY`` handle the version range
-  if any is specified (see :command:`find_package` command for the details).
+  ``SameMinorVersion`` arguments of ``COMPATIBILITY`` handle the version range,
+  if one is specified (see :command:`find_package` command for the details).
   ``ExactVersion`` mode is incompatible with version ranges and will display an
   ``ExactVersion`` mode is incompatible with version ranges and will display an
   author warning if one is specified.
   author warning if one is specified.
 
 
@@ -184,8 +198,9 @@ Internally, this macro executes :command:`configure_file()` to create the
 resulting version file.  Depending on the ``COMPATIBILITY``, the corresponding
 resulting version file.  Depending on the ``COMPATIBILITY``, the corresponding
 ``BasicConfigVersion-<COMPATIBILITY>.cmake.in`` file is used.
 ``BasicConfigVersion-<COMPATIBILITY>.cmake.in`` file is used.
 Please note that these files are internal to CMake and you should not call
 Please note that these files are internal to CMake and you should not call
-:command:`configure_file()` on them yourself, but they can be used as starting
-point to create more sophisticated custom ``ConfigVersion.cmake`` files.
+:command:`configure_file()` on them yourself, but they can be used as a starting
+point to create more sophisticated custom ``<PackageName>ConfigVersion.cmake``
+files.
 
 
 Generating an Apple Platform Selection File
 Generating an Apple Platform Selection File
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -268,6 +283,9 @@ Generating an Apple Platform Selection File
   consider the platform to be unsupported.  The behavior is determined
   consider the platform to be unsupported.  The behavior is determined
   by the ``ERROR_VARIABLE`` option.
   by the ``ERROR_VARIABLE`` option.
 
 
+Generating an Apple Architecture Selection File
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 .. command:: generate_apple_architecture_selection_file
 .. command:: generate_apple_architecture_selection_file
 
 
   .. versionadded:: 3.29
   .. versionadded:: 3.29
@@ -328,15 +346,16 @@ Generating an Apple Platform Selection File
     information to pretend the package was not found.  If this option
     information to pretend the package was not found.  If this option
     is not given, the default behavior is to issue a fatal error.
     is not given, the default behavior is to issue a fatal error.
 
 
+.. _`CMakePackageConfigHelpers Examples`:
+
 Example Generating Package Files
 Example Generating Package Files
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 
-Example using both :command:`configure_package_config_file` and
-``write_basic_package_version_file()``:
-
-``CMakeLists.txt``:
+Example using both the :command:`configure_package_config_file` and
+:command:`write_basic_package_version_file()` commands:
 
 
 .. code-block:: cmake
 .. code-block:: cmake
+   :caption: ``CMakeLists.txt``
 
 
    include(GNUInstallDirs)
    include(GNUInstallDirs)
    set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR}/Foo
    set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR}/Foo
@@ -357,9 +376,9 @@ Example using both :command:`configure_package_config_file` and
                  ${CMAKE_CURRENT_BINARY_DIR}/FooConfigVersion.cmake
                  ${CMAKE_CURRENT_BINARY_DIR}/FooConfigVersion.cmake
            DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Foo )
            DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Foo )
 
 
-``FooConfig.cmake.in``:
-
-::
+.. code-block:: cmake
+   :caption: ``FooConfig.cmake.in``
+   :force:
 
 
    set(FOO_VERSION x.y.z)
    set(FOO_VERSION x.y.z)
    ...
    ...
@@ -597,8 +616,15 @@ function(generate_apple_architecture_selection_file _output_file)
       )
       )
   endif()
   endif()
   string(APPEND _branch_code
   string(APPEND _branch_code
-    "endif()\n"
+    "endif()\n\n"
+    "set(_cmake_apple_archs \"\${CMAKE_OSX_ARCHITECTURES}\")\n"
     )
     )
+  if(NOT "${_gasf_UNIVERSAL_ARCHITECTURES}" STREQUAL "")
+    string(APPEND _branch_code "list(REMOVE_ITEM _cmake_apple_archs ${_gasf_UNIVERSAL_ARCHITECTURES})\n")
+  endif()
+  string(APPEND _branch_code "\n")
+
+  set(maybe_else "")
 
 
   foreach(pair IN ZIP_LISTS _gasf_SINGLE_ARCHITECTURES _gasf_SINGLE_ARCHITECTURE_INCLUDE_FILES)
   foreach(pair IN ZIP_LISTS _gasf_SINGLE_ARCHITECTURES _gasf_SINGLE_ARCHITECTURE_INCLUDE_FILES)
     set(arch "${pair_0}")
     set(arch "${pair_0}")
@@ -607,40 +633,45 @@ function(generate_apple_architecture_selection_file _output_file)
       string(PREPEND config_file [[${PACKAGE_PREFIX_DIR}/]])
       string(PREPEND config_file [[${PACKAGE_PREFIX_DIR}/]])
     endif()
     endif()
     string(APPEND _branch_code
     string(APPEND _branch_code
-      "\n"
-      "if(CMAKE_OSX_ARCHITECTURES STREQUAL \"${arch}\")\n"
+      "${maybe_else}if(CMAKE_OSX_ARCHITECTURES STREQUAL \"${arch}\")\n"
       "  include(\"${config_file}\")\n"
       "  include(\"${config_file}\")\n"
-      "  return()\n"
-      "endif()\n"
       )
       )
+    set(maybe_else else)
   endforeach()
   endforeach()
 
 
   if(_gasf_UNIVERSAL_ARCHITECTURES AND _gasf_UNIVERSAL_INCLUDE_FILE)
   if(_gasf_UNIVERSAL_ARCHITECTURES AND _gasf_UNIVERSAL_INCLUDE_FILE)
-    string(JOIN " " universal_archs "${_gasf_UNIVERSAL_ARCHITECTURES}")
     set(config_file "${_gasf_UNIVERSAL_INCLUDE_FILE}")
     set(config_file "${_gasf_UNIVERSAL_INCLUDE_FILE}")
     if(NOT IS_ABSOLUTE "${config_file}")
     if(NOT IS_ABSOLUTE "${config_file}")
       string(PREPEND config_file [[${PACKAGE_PREFIX_DIR}/]])
       string(PREPEND config_file [[${PACKAGE_PREFIX_DIR}/]])
     endif()
     endif()
     string(APPEND _branch_code
     string(APPEND _branch_code
-      "\n"
-      "set(_cmake_apple_archs \"\${CMAKE_OSX_ARCHITECTURES}\")\n"
-      "list(REMOVE_ITEM _cmake_apple_archs ${universal_archs})\n"
-      "if(NOT _cmake_apple_archs)\n"
+      "${maybe_else}if(NOT _cmake_apple_archs)\n"
       "  include(\"${config_file}\")\n"
       "  include(\"${config_file}\")\n"
-      "  return()\n"
-      "endif()\n"
       )
       )
+    set(maybe_else else)
   elseif(_gasf_UNIVERSAL_ARCHITECTURES)
   elseif(_gasf_UNIVERSAL_ARCHITECTURES)
     message(FATAL_ERROR "UNIVERSAL_INCLUDE_FILE requires UNIVERSAL_ARCHITECTURES")
     message(FATAL_ERROR "UNIVERSAL_INCLUDE_FILE requires UNIVERSAL_ARCHITECTURES")
   elseif(_gasf_UNIVERSAL_INCLUDE_FILE)
   elseif(_gasf_UNIVERSAL_INCLUDE_FILE)
     message(FATAL_ERROR "UNIVERSAL_ARCHITECTURES requires UNIVERSAL_INCLUDE_FILE")
     message(FATAL_ERROR "UNIVERSAL_ARCHITECTURES requires UNIVERSAL_INCLUDE_FILE")
   endif()
   endif()
 
 
-  string(APPEND _branch_code "\n")
+  if(maybe_else)
+    string(APPEND _branch_code "else()\n")
+    set(_indent "  ")
+  else()
+    set(_indent "")
+  endif()
   if(_gasf_ERROR_VARIABLE)
   if(_gasf_ERROR_VARIABLE)
-    string(APPEND _branch_code "set(\"${_gasf_ERROR_VARIABLE}\" \"Architecture not supported\")")
+    string(APPEND _branch_code
+      "${_indent}set(\"${_gasf_ERROR_VARIABLE}\" \"Architecture not supported\")\n"
+      )
   else()
   else()
-    string(APPEND _branch_code "message(FATAL_ERROR \"Architecture not supported\")")
+    string(APPEND _branch_code
+      "${_indent}message(FATAL_ERROR \"Architecture not supported\")\n"
+      )
+  endif()
+  if(maybe_else)
+    string(APPEND _branch_code "endif()\n")
   endif()
   endif()
 
 
   configure_package_config_file("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Internal/AppleArchitectureSelection.cmake.in" "${_output_file}"
   configure_package_config_file("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Internal/AppleArchitectureSelection.cmake.in" "${_output_file}"

+ 21 - 0
Modules/Internal/AppleArchitectureSelection.cmake.in

@@ -1,2 +1,23 @@
+# Save this now so we can restore it before returning
+if(NOT DEFINED PACKAGE_PREFIX_DIR)
+  list(APPEND _gasf_PACKAGE_PREFIX_DIR "<__CMAKE_UNDEFINED__>")
+elseif("${PACKAGE_PREFIX_DIR}" STREQUAL "")
+  list(APPEND _gasf_PACKAGE_PREFIX_DIR "<__CMAKE_EMPTY__>")
+else()
+  list(APPEND _gasf_PACKAGE_PREFIX_DIR "${PACKAGE_PREFIX_DIR}")
+endif()
+
 @PACKAGE_INIT@
 @PACKAGE_INIT@
 @_branch_code@
 @_branch_code@
+
+# Restore PACKAGE_PREFIX_DIR
+list(LENGTH _gasf_PACKAGE_PREFIX_DIR _gasf_tmp)
+math(EXPR _gasf_tmp "${_gasf_tmp} - 1")
+list(GET _gasf_PACKAGE_PREFIX_DIR ${_gasf_tmp} PACKAGE_PREFIX_DIR)
+list(REMOVE_AT _gasf_PACKAGE_PREFIX_DIR ${_gasf_tmp})
+unset(_gasf_tmp)
+if("${PACKAGE_PREFIX_DIR}" STREQUAL "<__CMAKE_UNDEFINED__>")
+  unset(PACKAGE_PREFIX_DIR)
+elseif("${PACKAGE_PREFIX_DIR}" STREQUAL "<__CMAKE_EMPTY__>")
+  set(PACKAGE_PREFIX_DIR "")
+endif()

+ 21 - 0
Modules/Internal/ApplePlatformSelection.cmake.in

@@ -1,3 +1,12 @@
+# Save this now so we can restore it before returning
+if(NOT DEFINED PACKAGE_PREFIX_DIR)
+  list(APPEND _gpsf_PACKAGE_PREFIX_DIR "<__CMAKE_UNDEFINED__>")
+elseif("${PACKAGE_PREFIX_DIR}" STREQUAL "")
+  list(APPEND _gpsf_PACKAGE_PREFIX_DIR "<__CMAKE_EMPTY__>")
+else()
+  list(APPEND _gpsf_PACKAGE_PREFIX_DIR "${PACKAGE_PREFIX_DIR}")
+endif()
+
 @PACKAGE_INIT@
 @PACKAGE_INIT@
 
 
 string(TOLOWER "${CMAKE_OSX_SYSROOT}" _CMAKE_OSX_SYSROOT_LOWER)
 string(TOLOWER "${CMAKE_OSX_SYSROOT}" _CMAKE_OSX_SYSROOT_LOWER)
@@ -23,3 +32,15 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
 else()
 else()
   @_branch_ELSE@
   @_branch_ELSE@
 endif()
 endif()
+
+# Restore PACKAGE_PREFIX_DIR
+list(LENGTH _gpsf_PACKAGE_PREFIX_DIR _gpsf_tmp)
+math(EXPR _gpsf_tmp "${_gpsf_tmp} - 1")
+list(GET _gpsf_PACKAGE_PREFIX_DIR ${_gpsf_tmp} PACKAGE_PREFIX_DIR)
+list(REMOVE_AT _gpsf_PACKAGE_PREFIX_DIR ${_gpsf_tmp})
+unset(_gpsf_tmp)
+if("${PACKAGE_PREFIX_DIR}" STREQUAL "<__CMAKE_UNDEFINED__>")
+  unset(PACKAGE_PREFIX_DIR)
+elseif("${PACKAGE_PREFIX_DIR}" STREQUAL "<__CMAKE_EMPTY__>")
+  set(PACKAGE_PREFIX_DIR "")
+endif()

+ 30 - 0
Source/cmFindPackageCommand.cxx

@@ -982,6 +982,36 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
     return true;
     return true;
   }
   }
 
 
+  // Restore PACKAGE_PREFIX_DIR to its pre-call value when we return. If our
+  // caller is a file generated by configure_package_config_file(), and if
+  // the package we are about to load also has a config file created by that
+  // command, it will overwrite PACKAGE_PREFIX_DIR. We need to restore it in
+  // case something still refers to it in our caller's scope after we return.
+  class RestoreVariableOnLeavingScope
+  {
+    cmMakefile* makefile_;
+    cm::optional<std::string> value_;
+
+  public:
+    RestoreVariableOnLeavingScope(cmMakefile* makefile)
+      : makefile_(makefile)
+    {
+      cmValue v = makefile->GetDefinition("PACKAGE_PREFIX_DIR");
+      if (v) {
+        value_ = *v;
+      }
+    }
+    ~RestoreVariableOnLeavingScope()
+    {
+      if (this->value_) {
+        makefile_->AddDefinition("PACKAGE_PREFIX_DIR", *value_);
+      } else {
+        makefile_->RemoveDefinition("PACKAGE_PREFIX_DIR");
+      }
+    }
+  };
+  RestoreVariableOnLeavingScope restorePackagePrefixDir(this->Makefile);
+
   // Now choose what method(s) we will use to satisfy the request. Note that
   // Now choose what method(s) we will use to satisfy the request. Note that
   // we still want all the above checking of arguments, etc. regardless of the
   // we still want all the above checking of arguments, etc. regardless of the
   // method used. This will ensure ill-formed arguments are caught earlier,
   // method used. This will ensure ill-formed arguments are caught earlier,

+ 3 - 0
Tests/RunCMake/CMakePackage/ApplePlatformGenSubdir-stdout.txt

@@ -0,0 +1,3 @@
+(-- )?Hello from platform switch
+(-- )?Hello from arch switch
+(-- )?Hello from pkg_a

+ 50 - 0
Tests/RunCMake/CMakePackage/ApplePlatformGenSubdir.cmake

@@ -0,0 +1,50 @@
+set(CMAKE_INSTALL_DATADIR share)
+set(SWITCH_DIR platform/cmake)
+
+include(CMakePackageConfigHelpers)
+
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/pkg_a-config.cmake.in [[
+@PACKAGE_INIT@
+include("@PACKAGE_SWITCH_DIR@/platform-switch.cmake")
+include("@PACKAGE_CMAKE_INSTALL_DATADIR@/pkg_a_included.cmake")
+]])
+configure_package_config_file(
+  ${CMAKE_CURRENT_BINARY_DIR}/pkg_a-config.cmake.in
+  ${CMAKE_CURRENT_BINARY_DIR}/install/pkg_a-config.cmake
+  INSTALL_DESTINATION .
+  PATH_VARS CMAKE_INSTALL_DATADIR SWITCH_DIR
+)
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/install/${CMAKE_INSTALL_DATADIR}/pkg_a_included.cmake
+  [[message(STATUS "Hello from pkg_a")]]
+)
+
+# To expose re-using the same package prefix variable, we need to use a
+# different install prefix. This is really contrived and not representative of
+# what a package should do.
+generate_apple_platform_selection_file(
+  ${CMAKE_CURRENT_BINARY_DIR}/install/platform/cmake/platform-switch.cmake
+  INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/platform
+  INSTALL_DESTINATION cmake
+  MACOS_INCLUDE_FILE cmake/switch_included.cmake  # relative to install prefix
+)
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/install/platform/cmake/switch_included.cmake
+[[
+message(STATUS "Hello from platform switch")
+include("${CMAKE_CURRENT_LIST_DIR}/../arch/cmake/arch-switch.cmake")
+]]
+)
+
+generate_apple_architecture_selection_file(
+  ${CMAKE_CURRENT_BINARY_DIR}/install/platform/arch/cmake/arch-switch.cmake
+  INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/platform/arch
+  INSTALL_DESTINATION cmake
+  UNIVERSAL_ARCHITECTURES i386 x86_64 arm64 $(ARCHS_STANDARD)
+  UNIVERSAL_INCLUDE_FILE cmake/switch_included.cmake  # relative to install prefix
+)
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/install/platform/arch/cmake/switch_included.cmake
+  [[message(STATUS "Hello from arch switch")]]
+)
+
+find_package(pkg_a REQUIRED NO_DEFAULT_PATH
+  PATHS ${CMAKE_CURRENT_BINARY_DIR}/install
+)

+ 6 - 0
Tests/RunCMake/CMakePackage/NestedConfigFile-stdout.txt

@@ -0,0 +1,6 @@
+(-- )?Before find_dependency: PACKAGE_PREFIX_DIR = .*/Tests/RunCMake/CMakePackage/NestedConfigFile-build/install_pkg_b
+(-- )?Hello from pkg_a
+(-- )?Leaving pkg_a-config\.cmake with PACKAGE_PREFIX_DIR = .*/Tests/RunCMake/CMakePackage/NestedConfigFile-build/install_pkg_a
+(-- )?After find_dependency:  PACKAGE_PREFIX_DIR = .*/Tests/RunCMake/CMakePackage/NestedConfigFile-build/install_pkg_b
+(-- )?Hello from pkg_b
+(-- )?Leaving pkg_b-config\.cmake with PACKAGE_PREFIX_DIR = .*/Tests/RunCMake/CMakePackage/NestedConfigFile-build/install_pkg_b

+ 43 - 0
Tests/RunCMake/CMakePackage/NestedConfigFile.cmake

@@ -0,0 +1,43 @@
+set(CMAKE_INSTALL_DATADIR share)
+
+include(CMakePackageConfigHelpers)
+
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/pkg_a-config.cmake.in [[
+@PACKAGE_INIT@
+include("@PACKAGE_CMAKE_INSTALL_DATADIR@/pkg_a_included.cmake")
+message(STATUS "Leaving pkg_a-config.cmake with PACKAGE_PREFIX_DIR = ${PACKAGE_PREFIX_DIR}")
+]])
+configure_package_config_file(
+  ${CMAKE_CURRENT_BINARY_DIR}/pkg_a-config.cmake.in
+  ${CMAKE_CURRENT_BINARY_DIR}/install_pkg_a/pkg_a-config.cmake
+  INSTALL_DESTINATION .
+  PATH_VARS CMAKE_INSTALL_DATADIR
+)
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/install_pkg_a/share/pkg_a_included.cmake
+  [[message(STATUS "Hello from pkg_a")]]
+)
+
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/pkg_b-config.cmake.in [[
+@PACKAGE_INIT@
+include(CMakeFindDependencyMacro)
+message(STATUS "Before find_dependency: PACKAGE_PREFIX_DIR = ${PACKAGE_PREFIX_DIR}")
+find_dependency(pkg_a NO_DEFAULT_PATH
+    PATHS "@CMAKE_CURRENT_BINARY_DIR@/install_pkg_a"
+)
+message(STATUS "After find_dependency:  PACKAGE_PREFIX_DIR = ${PACKAGE_PREFIX_DIR}")
+include("@PACKAGE_CMAKE_INSTALL_DATADIR@/pkg_b_included.cmake")
+message(STATUS "Leaving pkg_b-config.cmake with PACKAGE_PREFIX_DIR = ${PACKAGE_PREFIX_DIR}")
+]])
+configure_package_config_file(
+  ${CMAKE_CURRENT_BINARY_DIR}/pkg_b-config.cmake.in
+  ${CMAKE_CURRENT_BINARY_DIR}/install_pkg_b/pkg_b-config.cmake
+  INSTALL_DESTINATION .
+  PATH_VARS CMAKE_INSTALL_DATADIR
+)
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/install_pkg_b/share/pkg_b_included.cmake
+  [[message(STATUS "Hello from pkg_b")]]
+)
+
+find_package(pkg_b REQUIRED NO_DEFAULT_PATH
+  PATHS ${CMAKE_CURRENT_BINARY_DIR}/install_pkg_b
+)

+ 13 - 3
Tests/RunCMake/CMakePackage/RunCMakeTest.cmake

@@ -4,6 +4,8 @@ if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
   set(maybe_CMAKE_BUILD_TYPE -DCMAKE_BUILD_TYPE=Release)
   set(maybe_CMAKE_BUILD_TYPE -DCMAKE_BUILD_TYPE=Release)
 endif()
 endif()
 
 
+run_cmake_with_options(NestedConfigFile ${maybe_CMAKE_BUILD_TYPE})
+
 function(apple_export platform system_name archs sysroot)
 function(apple_export platform system_name archs sysroot)
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/apple-export-${platform}-build)
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/apple-export-${platform}-build)
   string(REPLACE ";" "\\;" archs "${archs}")
   string(REPLACE ";" "\\;" archs "${archs}")
@@ -48,9 +50,6 @@ if(APPLE)
 endif()
 endif()
 
 
 if(APPLE AND CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
 if(APPLE AND CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
-  set(apple_install ${RunCMake_BINARY_DIR}/apple-install)
-  file(REMOVE_RECURSE "${apple_install}")
-
   if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 12)
   if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 12)
     set(macos_archs "x86_64;arm64")
     set(macos_archs "x86_64;arm64")
     set(tvos_sim_archs "x86_64;arm64")
     set(tvos_sim_archs "x86_64;arm64")
@@ -78,6 +77,17 @@ if(APPLE AND CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
     set(enable_visionos 1)
     set(enable_visionos 1)
   endif()
   endif()
 
 
+  string(REPLACE ";" "\\;" macos_archs_for_cmd "${macos_archs}")
+  run_cmake_with_options(ApplePlatformGenSubdir
+    "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_for_cmd}"
+    ${maybe_CMAKE_BUILD_TYPE}
+  )
+  unset(macos_archs_for_cmd)
+
+  # Place all export/import steps in a single install prefix.
+  set(apple_install ${RunCMake_BINARY_DIR}/apple-install)
+  file(REMOVE_RECURSE "${apple_install}")
+
   apple_export(macos Darwin "${macos_archs}" macosx)
   apple_export(macos Darwin "${macos_archs}" macosx)
   apple_export(ios iOS "arm64" iphoneos)
   apple_export(ios iOS "arm64" iphoneos)
   apple_export(tvos tvOS "arm64" appletvos)
   apple_export(tvos tvOS "arm64" appletvos)