Browse Source

Apple: Handle generation and comsuption of text-based stubs (.tbd files)

Fixes: #24123
Marc Chevrier 2 years ago
parent
commit
ede33f30cf
84 changed files with 2054 additions and 168 deletions
  1. 18 0
      Help/command/install.rst
  2. 4 0
      Help/manual/cmake-buildsystem.7.rst
  3. 248 3
      Help/manual/cmake-generator-expressions.7.rst
  4. 2 0
      Help/manual/cmake-variables.7.rst
  5. 3 0
      Help/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.rst
  6. 3 0
      Help/prop_tgt/ARCHIVE_OUTPUT_NAME.rst
  7. 27 2
      Help/prop_tgt/ENABLE_EXPORTS.rst
  8. 10 4
      Help/prop_tgt/IMPORTED_IMPLIB.rst
  9. 1 1
      Help/prop_tgt/IMPORTED_LOCATION.rst
  10. 12 0
      Help/prop_tgt/MACOS_IMPORT_FILES.txt
  11. 6 0
      Help/release/dev/Apple-tbd-files-management.rst
  12. 4 0
      Help/variable/CMAKE_ENABLE_EXPORTS.rst
  13. 12 0
      Help/variable/CMAKE_EXECUTABLE_ENABLE_EXPORTS.rst
  14. 10 0
      Help/variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS.rst
  15. 1 0
      Modules/CMakeASMCompiler.cmake.in
  16. 1 0
      Modules/CMakeCCompiler.cmake.in
  17. 1 0
      Modules/CMakeCXXCompiler.cmake.in
  18. 16 1
      Modules/CMakeFindBinUtils.cmake
  19. 1 0
      Modules/CMakeFortranCompiler.cmake.in
  20. 1 0
      Modules/CMakeHIPCompiler.cmake.in
  21. 1 0
      Modules/CMakeOBJCCompiler.cmake.in
  22. 1 0
      Modules/CMakeOBJCXXCompiler.cmake.in
  23. 5 0
      Modules/Platform/Darwin.cmake
  24. 0 1
      Source/cmCommonTargetGenerator.cxx
  25. 6 0
      Source/cmCoreTryCompile.cxx
  26. 1 1
      Source/cmExportBuildFileGenerator.cxx
  27. 2 1
      Source/cmExportFileGenerator.cxx
  28. 14 1
      Source/cmExportInstallFileGenerator.cxx
  29. 362 8
      Source/cmGeneratorExpressionNode.cxx
  30. 108 30
      Source/cmGeneratorTarget.cxx
  31. 12 2
      Source/cmGeneratorTarget.h
  32. 4 0
      Source/cmGlobalNinjaGenerator.cxx
  33. 28 1
      Source/cmGlobalXCodeGenerator.cxx
  34. 58 15
      Source/cmInstallCommand.cxx
  35. 111 74
      Source/cmInstallTargetGenerator.cxx
  36. 7 1
      Source/cmInstallTargetGenerator.h
  37. 8 0
      Source/cmLocalGenerator.cxx
  38. 5 0
      Source/cmMakefile.cxx
  39. 4 0
      Source/cmMakefile.h
  40. 96 6
      Source/cmMakefileLibraryTargetGenerator.cxx
  41. 102 2
      Source/cmNinjaNormalTargetGenerator.cxx
  42. 1 0
      Source/cmNinjaNormalTargetGenerator.h
  43. 13 1
      Source/cmOSXBundleGenerator.cxx
  44. 5 6
      Source/cmOSXBundleGenerator.h
  45. 36 3
      Source/cmTarget.cxx
  46. 4 0
      Source/cmTarget.h
  47. 3 0
      Tests/RunCMake/AppleTextStubs/CMakeLists.txt
  48. 12 0
      Tests/RunCMake/AppleTextStubs/Framework-export.cmake
  49. 62 0
      Tests/RunCMake/AppleTextStubs/Framework-import.cmake
  50. 1 0
      Tests/RunCMake/AppleTextStubs/Framework-install-check.cmake
  51. 59 0
      Tests/RunCMake/AppleTextStubs/Framework.cmake
  52. 12 0
      Tests/RunCMake/AppleTextStubs/Library-export.cmake
  53. 54 0
      Tests/RunCMake/AppleTextStubs/Library-import.cmake
  54. 1 0
      Tests/RunCMake/AppleTextStubs/LibraryWithOutputs-install-check.cmake
  55. 52 0
      Tests/RunCMake/AppleTextStubs/LibraryWithOutputs.cmake
  56. 1 0
      Tests/RunCMake/AppleTextStubs/LibraryWithVersions-install-check.cmake
  57. 96 0
      Tests/RunCMake/AppleTextStubs/LibraryWithVersions.cmake
  58. 58 0
      Tests/RunCMake/AppleTextStubs/RunCMakeTest.cmake
  59. 1 0
      Tests/RunCMake/AppleTextStubs/Simple-install-check.cmake
  60. 41 0
      Tests/RunCMake/AppleTextStubs/Simple.cmake
  61. 1 0
      Tests/RunCMake/AppleTextStubs/foo-config.cmake.in
  62. 5 0
      Tests/RunCMake/AppleTextStubs/foo.c
  63. 7 0
      Tests/RunCMake/AppleTextStubs/main.c
  64. 2 0
      Tests/RunCMake/CMakeLists.txt
  65. 3 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/CMakeLists.txt
  66. 21 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/RunCMakeTest.cmake
  67. 1 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE-check.cmake
  68. 47 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE.cmake
  69. 1 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX-check.cmake
  70. 44 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX.cmake
  71. 1 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-result.txt
  72. 9 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-stderr.txt
  73. 9 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target.cmake
  74. 1 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-result.txt
  75. 9 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-stderr.txt
  76. 9 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target.cmake
  77. 1 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-check.cmake
  78. 1 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-result.txt
  79. 8 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-stderr.txt
  80. 8 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target.cmake
  81. 32 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE.cmake
  82. 0 0
      Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/empty.c
  83. 3 2
      Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-all-stderr.txt
  84. 3 2
      Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-exc-stderr.txt

+ 18 - 0
Help/command/install.rst

@@ -158,6 +158,9 @@ that may be installed:
     ``.lib``, in contrast to the ``.dll`` libraries that go to ``RUNTIME``);
   * On AIX, the *linker import file* created for executables with
     :prop_tgt:`ENABLE_EXPORTS` enabled.
+  * On macOS, the *linker import file* created for shared libraries with
+    :prop_tgt:`ENABLE_EXPORTS` enabled (except when marked as ``FRAMEWORK``,
+    see below).
 
 ``LIBRARY``
   Target artifacts of this kind include:
@@ -308,6 +311,11 @@ the following additional arguments:
   value of ``COMPONENT``. It is an error to use this parameter outside of a
   ``LIBRARY`` block.
 
+  .. versionchanged:: 3.27
+    This parameter is also usable for an ``ARCHIVE`` block to manage
+    the linker import file created, on macOS, for shared libraries with
+    :prop_tgt:`ENABLE_EXPORTS` enabled.
+
   Consider the following example:
 
   .. code-block:: cmake
@@ -342,6 +350,11 @@ the following additional arguments:
   option installs nothing. It is an error to use this parameter outside of a
   ``LIBRARY`` block.
 
+  .. versionchanged:: 3.27
+    This parameter is also usable for an ``ARCHIVE`` block to manage
+    the linker import file created, on macOS, for shared libraries with
+    :prop_tgt:`ENABLE_EXPORTS` enabled.
+
   When ``NAMELINK_ONLY`` is given, either ``NAMELINK_COMPONENT`` or
   ``COMPONENT`` may be used to specify the installation component of the
   namelink, but ``COMPONENT`` should generally be preferred.
@@ -355,6 +368,11 @@ the following additional arguments:
   installs the library. It is an error to use this parameter outside of a
   ``LIBRARY`` block.
 
+  .. versionchanged:: 3.27
+    This parameter is also usable for an ``ARCHIVE`` block to manage
+    the linker import file created, on macOS, for shared libraries with
+    :prop_tgt:`ENABLE_EXPORTS` enabled.
+
   If ``NAMELINK_SKIP`` is specified, ``NAMELINK_COMPONENT`` has no effect. It
   is not recommended to use ``NAMELINK_SKIP`` in conjunction with
   ``NAMELINK_COMPONENT``.

+ 4 - 0
Help/manual/cmake-buildsystem.7.rst

@@ -797,6 +797,10 @@ An *archive* output artifact of a buildsystem target may be:
   created by the :command:`add_executable` command when its
   :prop_tgt:`ENABLE_EXPORTS` target property is set.
 
+* On macOS: the linker import file (e.g. ``.tbd``) of a shared library target
+  created by the :command:`add_library` command with the ``SHARED`` option and
+  when its :prop_tgt:`ENABLE_EXPORTS` target property is set.
+
 The :prop_tgt:`ARCHIVE_OUTPUT_DIRECTORY` and :prop_tgt:`ARCHIVE_OUTPUT_NAME`
 target properties may be used to control archive output artifact locations
 and names in the build tree.

+ 248 - 3
Help/manual/cmake-generator-expressions.7.rst

@@ -1503,6 +1503,76 @@ In the following, the phrase "the ``tgt`` filename" means the name of the
   Note that ``tgt`` is not added as a dependency of the target this
   expression is evaluated on (see policy :policy:`CMP0112`).
 
+.. genex:: $<TARGET_IMPORT_FILE:tgt>
+
+  .. versionadded:: 3.27
+
+  Full path to the linker import file. On DLL platforms, it would be the
+  ``.lib`` file. On AIX, for the executables, and on macOS, for the shared
+  libraries, it could be, respectively, the ``.imp`` or ``.tbd`` import file,
+  depending of the value of :prop_tgt:`ENABLE_EXPORTS` property.
+
+  An empty string is returned when there is no import file associated with the
+  target.
+
+.. genex:: $<TARGET_IMPORT_FILE_BASE_NAME:tgt>
+
+  .. versionadded:: 3.27
+
+  Base name of file linker import file of the target ``tgt`` without prefix and
+  suffix. For example, if target file name is ``libbase.tbd``, the base name is
+  ``base``.
+
+  See also the :prop_tgt:`OUTPUT_NAME` and :prop_tgt:`ARCHIVE_OUTPUT_NAME`
+  target properties and their configuration specific variants
+  :prop_tgt:`OUTPUT_NAME_<CONFIG>` and :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>`.
+
+  The :prop_tgt:`<CONFIG>_POSTFIX` and :prop_tgt:`DEBUG_POSTFIX` target
+  properties can also be considered.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
+.. genex:: $<TARGET_IMPORT_FILE_PREFIX:tgt>
+
+  .. versionadded:: 3.27
+
+  Prefix of the import file of the target ``tgt``.
+
+  See also the :prop_tgt:`IMPORT_PREFIX` target property.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
+.. genex:: $<TARGET_IMPORT_FILE_SUFFIX:tgt>
+
+  .. versionadded:: 3.27
+
+  Suffix of the import file of the target ``tgt``.
+
+  The suffix corresponds to the file extension (such as ".lib" or ".tbd").
+
+  See also the :prop_tgt:`IMPORT_SUFFIX` target property.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
+.. genex:: $<TARGET_IMPORT_FILE_NAME:tgt>
+
+  .. versionadded:: 3.27
+
+  Name of the import file of the target target ``tgt``.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
+.. genex:: $<TARGET_IMPORT_FILE_DIR:tgt>
+
+  Directory of the import file of the target ``tgt``.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
 .. genex:: $<TARGET_LINKER_FILE:tgt>
 
   File used when linking to the ``tgt`` target.  This will usually
@@ -1510,13 +1580,22 @@ In the following, the phrase "the ``tgt`` filename" means the name of the
   but for a shared library on DLL platforms, it would be the ``.lib``
   import library associated with the DLL.
 
+  .. versionadded:: 3.27
+    On macOS, it could be the ``.tbd`` import file associated with the shared
+    library, depending of the value of :prop_tgt:`ENABLE_EXPORTS` property.
+
+  This generator expression is equivalent to
+  :genex:`$<TARGET_LINKER_LIBRARY_FILE>` or
+  :genex:`$<TARGET_LINKER_IMPORT_FILE>` generator expressions, depending of the
+  characteristics of the target and the platform.
+
 .. genex:: $<TARGET_LINKER_FILE_BASE_NAME:tgt>
 
   .. versionadded:: 3.15
 
   Base name of file used to link the target ``tgt``, i.e.
-  ``$<TARGET_LINKER_FILE_NAME:tgt>`` without prefix and suffix. For example,
-  if target file name is ``libbase.a``, the base name is ``base``.
+  :genex:`$<TARGET_LINKER_FILE_NAME:tgt>` without prefix and suffix. For
+  example, if target file name is ``libbase.a``, the base name is ``base``.
 
   See also the :prop_tgt:`OUTPUT_NAME`, :prop_tgt:`ARCHIVE_OUTPUT_NAME`,
   and :prop_tgt:`LIBRARY_OUTPUT_NAME` target properties and their configuration
@@ -1570,9 +1649,151 @@ In the following, the phrase "the ``tgt`` filename" means the name of the
   Note that ``tgt`` is not added as a dependency of the target this
   expression is evaluated on (see policy :policy:`CMP0112`).
 
+.. genex:: $<TARGET_LINKER_LIBRARY_FILE:tgt>
+
+  .. versionadded:: 3.27
+
+  File used when linking o the ``tgt`` target is done using directly the
+  library, and not an import file. This will usually be the library that
+  ``tgt`` represents (``.a``, ``.so``, ``.dylib``). So, on DLL platforms, it
+  will be an empty string.
+
+.. genex:: $<TARGET_LINKER_LIBRARY_FILE_BASE_NAME:tgt>
+
+  .. versionadded:: 3.27
+
+  Base name of library file used to link the target ``tgt``, i.e.
+  :genex:`$<TARGET_LINKER_LIBRARY_FILE_NAME:tgt>` without prefix and suffix.
+  For example, if target file name is ``libbase.a``, the base name is ``base``.
+
+  See also the :prop_tgt:`OUTPUT_NAME`, :prop_tgt:`ARCHIVE_OUTPUT_NAME`,
+  and :prop_tgt:`LIBRARY_OUTPUT_NAME` target properties and their configuration
+  specific variants :prop_tgt:`OUTPUT_NAME_<CONFIG>`,
+  :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>` and
+  :prop_tgt:`LIBRARY_OUTPUT_NAME_<CONFIG>`.
+
+  The :prop_tgt:`<CONFIG>_POSTFIX` and :prop_tgt:`DEBUG_POSTFIX` target
+  properties can also be considered.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_LIBRARY_FILE_PREFIX:tgt>
+
+  .. versionadded:: 3.27
+
+  Prefix of the library file used to link target ``tgt``.
+
+  See also the :prop_tgt:`PREFIX` target property.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_LIBRARY_FILE_SUFFIX:tgt>
+
+  .. versionadded:: 3.27
+
+  Suffix of the library file used to link target ``tgt``.
+
+  The suffix corresponds to the file extension (such as ".a" or ".dylib").
+
+  See also the :prop_tgt:`SUFFIX` target property.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_LIBRARY_FILE_NAME:tgt>
+
+  .. versionadded:: 3.27
+
+  Name of the library file used to link target ``tgt``.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_LIBRARY_FILE_DIR:tgt>
+
+  .. versionadded:: 3.27
+
+  Directory of the library file used to link target ``tgt``.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_IMPORT_FILE:tgt>
+
+  .. versionadded:: 3.27
+
+  File used when linking to the ``tgt`` target is done using an import
+  file.  This will usually be the import file that ``tgt`` represents
+  (``.lib``, ``.tbd``). So, when no import file is involved in the link step,
+  an empty string is returned.
+
+.. genex:: $<TARGET_LINKER_IMPORT_FILE_BASE_NAME:tgt>
+
+  .. versionadded:: 3.27
+
+  Base name of the import file used to link the target ``tgt``, i.e.
+  :genex:`$<TARGET_LINKER_IMPORT_FILE_NAME:tgt>` without prefix and suffix.
+  For example, if target file name is ``libbase.tbd``, the base name is ``base``.
+
+  See also the :prop_tgt:`OUTPUT_NAME` and :prop_tgt:`ARCHIVE_OUTPUT_NAME`,
+  target properties and their configuration
+  specific variants :prop_tgt:`OUTPUT_NAME_<CONFIG>` and
+  :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>`.
+
+  The :prop_tgt:`<CONFIG>_POSTFIX` and :prop_tgt:`DEBUG_POSTFIX` target
+  properties can also be considered.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_IMPORT_FILE_PREFIX:tgt>
+
+  .. versionadded:: 3.27
+
+  Prefix of the import file used to link target ``tgt``.
+
+  See also the :prop_tgt:`IMPORT_PREFIX` target property.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_IMPORT_FILE_SUFFIX:tgt>
+
+  .. versionadded:: 3.27
+
+  Suffix of the import file used to link target ``tgt``.
+
+  The suffix corresponds to the file extension (such as ".lib" or ".tbd").
+
+  See also the :prop_tgt:`IMPORT_SUFFIX` target property.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_IMPORT_FILE_NAME:tgt>
+
+  .. versionadded:: 3.27
+
+  Name of the import file used to link target ``tgt``.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_IMPORT_FILE_DIR:tgt>
+
+  .. versionadded:: 3.27
+
+  Directory of the import file used to link target ``tgt``.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
 .. genex:: $<TARGET_SONAME_FILE:tgt>
 
   File with soname (``.so.3``) where ``tgt`` is the name of a target.
+
 .. genex:: $<TARGET_SONAME_FILE_NAME:tgt>
 
   Name of file with soname (``.so.3``).
@@ -1582,11 +1803,35 @@ In the following, the phrase "the ``tgt`` filename" means the name of the
 
 .. genex:: $<TARGET_SONAME_FILE_DIR:tgt>
 
-  Directory of with soname (``.so.3``).
+  Directory of file with soname (``.so.3``).
 
   Note that ``tgt`` is not added as a dependency of the target this
   expression is evaluated on (see policy :policy:`CMP0112`).
 
+.. genex:: $<TARGET_SONAME_IMPORT_FILE:tgt>
+
+  .. versionadded:: 3.27
+
+  Import file with soname (``.3.tbd``) where ``tgt`` is the name of a target.
+
+.. genex:: $<TARGET_SONAME_IMPORT_FILE_NAME:tgt>
+
+  .. versionadded:: 3.27
+
+  Name of the import file with soname (``.3.tbd``).
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
+.. genex:: $<TARGET_SONAME_IMPORT_FILE_DIR:tgt>
+
+  .. versionadded:: 3.27
+
+  Directory of the import file with soname (``.3.tbd``).
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
+
 .. genex:: $<TARGET_PDB_FILE:tgt>
 
   .. versionadded:: 3.1

+ 2 - 0
Help/manual/cmake-variables.7.rst

@@ -428,6 +428,7 @@ Variables that Control the Build
    /variable/CMAKE_DEPENDS_USE_COMPILER
    /variable/CMAKE_DISABLE_PRECOMPILE_HEADERS
    /variable/CMAKE_ENABLE_EXPORTS
+   /variable/CMAKE_EXECUTABLE_ENABLE_EXPORTS
    /variable/CMAKE_EXE_LINKER_FLAGS
    /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG
    /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG_INIT
@@ -507,6 +508,7 @@ Variables that Control the Build
    /variable/CMAKE_POSITION_INDEPENDENT_CODE
    /variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY
    /variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY_CONFIG
+   /variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS
    /variable/CMAKE_SHARED_LINKER_FLAGS
    /variable/CMAKE_SHARED_LINKER_FLAGS_CONFIG
    /variable/CMAKE_SHARED_LINKER_FLAGS_CONFIG_INIT

+ 3 - 0
Help/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.rst

@@ -6,4 +6,7 @@ ARCHIVE_OUTPUT_DIRECTORY
 .. |CMAKE_XXX_OUTPUT_DIRECTORY| replace:: :variable:`CMAKE_ARCHIVE_OUTPUT_DIRECTORY`
 .. include:: XXX_OUTPUT_DIRECTORY.txt
 
+.. |IDEM| replace:: in the same directory
+.. include:: MACOS_IMPORT_FILES.txt
+
 See also the :prop_tgt:`ARCHIVE_OUTPUT_DIRECTORY_<CONFIG>` target property.

+ 3 - 0
Help/prop_tgt/ARCHIVE_OUTPUT_NAME.rst

@@ -5,4 +5,7 @@ ARCHIVE_OUTPUT_NAME
 .. |xxx| replace:: archive
 .. include:: XXX_OUTPUT_NAME.txt
 
+.. |IDEM| replace:: with the same name
+.. include:: MACOS_IMPORT_FILES.txt
+
 See also the :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>` target property.

+ 27 - 2
Help/prop_tgt/ENABLE_EXPORTS.rst

@@ -1,7 +1,7 @@
 ENABLE_EXPORTS
 --------------
 
-Specify whether an executable exports symbols for loadable modules.
+Specify whether an executable or a shared library exports symbols.
 
 Normally an executable does not export any symbols because it is the
 final program.  It is possible for an executable to export symbols to
@@ -28,4 +28,29 @@ varies by platform:
   automatically bind symbols when the module is loaded.
 
 This property is initialized by the value of the variable
-:variable:`CMAKE_ENABLE_EXPORTS` if it is set when a target is created.
+:variable:`CMAKE_EXECUTABLE_ENABLE_EXPORTS` if it is set when an executable
+target is created.
+
+.. versionadded:: 3.27
+  On macOS, to link with a shared library (standard one as well as framework),
+  a linker import file (e.g. a text-based stubs file, with ``.tbd`` extension)
+  can be used instead of the shared library itself.
+
+The generation of these linker import files, as well as the consumption, is
+controlled by this property. When this property is set to true, CMake will
+generate a ``.tbd`` file for each shared library created by
+:command:`add_library` command. This allow other targets to use this ``.tbd``
+file to link to the library with the :command:`target_link_libraries`
+command.
+
+.. note::
+
+  For compatibility purpose, this property will be ignored if
+  :prop_tgt:`XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS <XCODE_ATTRIBUTE_<an-attribute>>`
+  target property or the
+  :variable:`CMAKE_XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS <CMAKE_XCODE_ATTRIBUTE_<an-attribute>>`
+  variable is set to ``NO``.
+
+This property is initialized by the value of the variable
+:variable:`CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS` if it is set when a shared
+library target is created.

+ 10 - 4
Help/prop_tgt/IMPORTED_IMPLIB.rst

@@ -3,7 +3,13 @@ IMPORTED_IMPLIB
 
 Full path to the import library for an ``IMPORTED`` target.
 
-Set this to the location of the ``.lib`` part of a Windows DLL, or on
-AIX set it to an import file created for executables that export symbols
-(see the :prop_tgt:`ENABLE_EXPORTS` target property).
-Ignored for non-imported targets.
+This property may be set:
+
+* On DLL platforms, to the location of the ``.lib`` part of the DLL.
+* On AIX, to an import file (e.g. ``.imp``) created for executables that export
+  symbols (see the :prop_tgt:`ENABLE_EXPORTS` target property).
+* On macOS, to an import file (e.g. ``.tbd``) created for shared libraries (see
+  the :prop_tgt:`ENABLE_EXPORTS` target property). For frameworks this is the
+  location of the ``.tbd`` file symlink just inside the framework folder.
+
+This property is ignored for non-imported targets.

+ 1 - 1
Help/prop_tgt/IMPORTED_LOCATION.rst

@@ -27,5 +27,5 @@ selected and its :prop_tgt:`IMPORTED_LOCATION_<CONFIG>` value used.
 To get the location of an imported target read one of the :prop_tgt:`LOCATION`
 or ``LOCATION_<CONFIG>`` properties.
 
-For platforms with import libraries (e.g. Windows) see also
+For platforms with import libraries (e.g. Windows, AIX or macOS) see also
 :prop_tgt:`IMPORTED_IMPLIB`.

+ 12 - 0
Help/prop_tgt/MACOS_IMPORT_FILES.txt

@@ -0,0 +1,12 @@
+.. note::
+
+  On macOS, this property will be ignored for the linker import files (e.g.
+  ``.tbd`` files, see :prop_tgt:`ENABLE_EXPORTS` property for details) when:
+
+  * The :prop_tgt:`FRAMEWORK` is set, because the framework layout cannot be
+    changed.
+  * The :generator:`Xcode` generator is used, due to the limitations and
+    constraints of the ``Xcode`` tool.
+
+  In both cases, the linker import files will be generated |IDEM| as the shared
+  library.

+ 6 - 0
Help/release/dev/Apple-tbd-files-management.rst

@@ -0,0 +1,6 @@
+Apple-tbd-files-management
+--------------------------
+
+* Support for text-based stubs (i.e. ``.tbd`` files) was added on macOS
+  platform. This capability is managed through the :prop_tgt:`ENABLE_EXPORTS`
+  property.

+ 4 - 0
Help/variable/CMAKE_ENABLE_EXPORTS.rst

@@ -8,3 +8,7 @@ Specify whether executables export symbols for loadable modules.
 This variable is used to initialize the :prop_tgt:`ENABLE_EXPORTS` target
 property for executable targets when they are created by calls to the
 :command:`add_executable` command.  See the property documentation for details.
+
+This command has been superseded by the
+:variable:`CMAKE_EXECUTABLE_ENABLE_EXPORTS` command.  It is provided for
+compatibility with older CMake code.

+ 12 - 0
Help/variable/CMAKE_EXECUTABLE_ENABLE_EXPORTS.rst

@@ -0,0 +1,12 @@
+CMAKE_EXECUTABLE_ENABLE_EXPORTS
+-------------------------------
+
+.. versionadded:: 3.27
+
+Specify whether executables export symbols for loadable modules.
+
+This variable is used to initialize the :prop_tgt:`ENABLE_EXPORTS` target
+property for executable targets when they are created by calls to the
+:command:`add_executable` command.  See the property documentation for details.
+
+This variable supersede the :variable:`CMAKE_ENABLE_EXPORTS` variable.

+ 10 - 0
Help/variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS.rst

@@ -0,0 +1,10 @@
+CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS
+-----------------------------------
+
+.. versionadded:: 3.27
+
+Specify whether shared library generates an import file.
+
+This variable is used to initialize the :prop_tgt:`ENABLE_EXPORTS` target
+property for shared library targets when they are created by calls to the
+:command:`add_library` command.  See the property documentation for details.

+ 1 - 0
Modules/CMakeASMCompiler.cmake.in

@@ -6,6 +6,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_ASM@ASM_DIALECT@_COMPILER_RANLIB "@_CMAKE_ASM_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LOADED 1)
 set(CMAKE_ASM@ASM_DIALECT@_COMPILER_ID "@_CMAKE_ASM_COMPILER_ID@")
 set(CMAKE_ASM@ASM_DIALECT@_COMPILER_VERSION "@_CMAKE_ASM_COMPILER_VERSION@")

+ 1 - 0
Modules/CMakeCCompiler.cmake.in

@@ -27,6 +27,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_C_COMPILER_RANLIB "@CMAKE_C_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUCC @CMAKE_COMPILER_IS_GNUCC@)
 set(CMAKE_C_COMPILER_LOADED 1)
 set(CMAKE_C_COMPILER_WORKS @CMAKE_C_COMPILER_WORKS@)

+ 1 - 0
Modules/CMakeCXXCompiler.cmake.in

@@ -28,6 +28,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_CXX_COMPILER_RANLIB "@CMAKE_CXX_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUCXX @CMAKE_COMPILER_IS_GNUCXX@)
 set(CMAKE_CXX_COMPILER_LOADED 1)
 set(CMAKE_CXX_COMPILER_WORKS @CMAKE_CXX_COMPILER_WORKS@)

+ 16 - 1
Modules/CMakeFindBinUtils.cmake

@@ -165,6 +165,7 @@ else()
   set(_CMAKE_READELF_NAMES "readelf")
   set(_CMAKE_DLLTOOL_NAMES "dlltool")
   set(_CMAKE_ADDR2LINE_NAMES "addr2line")
+  set(_CMAKE_TAPI_NAMES "tapi")
 
   # Prepend toolchain-specific names.
   if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL Clang)
@@ -201,7 +202,7 @@ else()
     list(PREPEND _CMAKE_LINKER_NAMES "armlink")
   endif()
 
-  list(APPEND _CMAKE_TOOL_VARS AR RANLIB STRIP LINKER NM OBJDUMP OBJCOPY READELF DLLTOOL ADDR2LINE)
+  list(APPEND _CMAKE_TOOL_VARS AR RANLIB STRIP LINKER NM OBJDUMP OBJCOPY READELF DLLTOOL ADDR2LINE TAPI)
 endif()
 
 foreach(_CMAKE_TOOL IN LISTS _CMAKE_TOOL_VARS)
@@ -225,6 +226,20 @@ if(NOT CMAKE_RANLIB)
     set(CMAKE_RANLIB : CACHE INTERNAL "noop for ranlib")
 endif()
 
+if(NOT CMAKE_TAPI)
+  # try to pick-up from Apple toolchain
+  execute_process(COMMAND xcrun --find tapi
+    OUTPUT_VARIABLE _xcrun_out
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    ERROR_QUIET
+    RESULT_VARIABLE _xcrun_failed)
+  if(NOT _xcrun_failed AND EXISTS "${_xcrun_out}")
+    set_property(CACHE CMAKE_TAPI PROPERTY VALUE "${_xcrun_out}")
+  endif()
+  unset(_xcrun_out)
+  unset(_xcrun_failed)
+endif()
+
 
 if(CMAKE_PLATFORM_HAS_INSTALLNAME)
   find_program(CMAKE_INSTALL_NAME_TOOL NAMES ${_CMAKE_TOOLCHAIN_PREFIX}install_name_tool HINTS ${_CMAKE_TOOLCHAIN_LOCATION} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH)

+ 1 - 0
Modules/CMakeFortranCompiler.cmake.in

@@ -14,6 +14,7 @@ set(CMAKE_Fortran_SIMULATE_VERSION "@CMAKE_Fortran_SIMULATE_VERSION@")
 set(CMAKE_AR "@CMAKE_AR@")
 set(CMAKE_Fortran_COMPILER_AR "@CMAKE_Fortran_COMPILER_AR@")
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_Fortran_COMPILER_RANLIB "@CMAKE_Fortran_COMPILER_RANLIB@")
 set(CMAKE_COMPILER_IS_GNUG77 @CMAKE_COMPILER_IS_GNUG77@)
 set(CMAKE_Fortran_COMPILER_LOADED 1)

+ 1 - 0
Modules/CMakeHIPCompiler.cmake.in

@@ -58,3 +58,4 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_HIP_COMPILER_RANLIB "@CMAKE_HIP_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")

+ 1 - 0
Modules/CMakeOBJCCompiler.cmake.in

@@ -25,6 +25,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_OBJC_COMPILER_RANLIB "@CMAKE_OBJC_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUOBJC @CMAKE_COMPILER_IS_GNUOBJC@)
 set(CMAKE_OBJC_COMPILER_LOADED 1)
 set(CMAKE_OBJC_COMPILER_WORKS @CMAKE_OBJC_COMPILER_WORKS@)

+ 1 - 0
Modules/CMakeOBJCXXCompiler.cmake.in

@@ -26,6 +26,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_OBJCXX_COMPILER_RANLIB "@CMAKE_OBJCXX_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUOBJCXX @CMAKE_COMPILER_IS_GNUOBJCXX@)
 set(CMAKE_OBJCXX_COMPILER_LOADED 1)
 set(CMAKE_OBJCXX_COMPILER_WORKS @CMAKE_OBJCXX_COMPILER_WORKS@)

+ 5 - 0
Modules/Platform/Darwin.cmake

@@ -45,6 +45,8 @@ set(CMAKE_SHARED_LIBRARY_SUFFIX ".dylib")
 set(CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES ".tbd" ".so")
 set(CMAKE_SHARED_MODULE_PREFIX "lib")
 set(CMAKE_SHARED_MODULE_SUFFIX ".so")
+set(CMAKE_APPLE_IMPORT_FILE_PREFIX "lib")
+set(CMAKE_APPLE_IMPORT_FILE_SUFFIX ".tbd")
 set(CMAKE_MODULE_EXISTS 1)
 set(CMAKE_DL_LIBS "")
 if(NOT "${_CURRENT_OSX_VERSION}" VERSION_LESS "10.5")
@@ -108,6 +110,9 @@ foreach(lang C CXX Fortran OBJC OBJCXX)
   set(CMAKE_${lang}_FRAMEWORK_SEARCH_FLAG -F)
 endforeach()
 
+# To generate text-based stubs
+set(CMAKE_CREATE_TEXT_STUBS "<CMAKE_TAPI> stubify -isysroot <CMAKE_OSX_SYSROOT> -o <TARGET_IMPLIB> <TARGET>")
+
 # Defines LINK_LIBRARY features for frameworks
 set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK "LINKER:-framework,<LIBRARY>")
 set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)

+ 0 - 1
Source/cmCommonTargetGenerator.cxx

@@ -25,7 +25,6 @@
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
-#include "cmTarget.h"
 #include "cmValue.h"
 
 cmCommonTargetGenerator::cmCommonTargetGenerator(cmGeneratorTarget* gt)

+ 6 - 0
Source/cmCoreTryCompile.cxx

@@ -72,6 +72,10 @@ SETUP_LANGUAGE(swift_properties, Swift);
 std::string const kCMAKE_CUDA_ARCHITECTURES = "CMAKE_CUDA_ARCHITECTURES";
 std::string const kCMAKE_CUDA_RUNTIME_LIBRARY = "CMAKE_CUDA_RUNTIME_LIBRARY";
 std::string const kCMAKE_ENABLE_EXPORTS = "CMAKE_ENABLE_EXPORTS";
+std::string const kCMAKE_EXECUTABLE_ENABLE_EXPORTS =
+  "CMAKE_EXECUTABLE_ENABLE_EXPORTS";
+std::string const kCMAKE_SHARED_LIBRARY_ENABLE_EXPORTS =
+  "CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS";
 std::string const kCMAKE_HIP_ARCHITECTURES = "CMAKE_HIP_ARCHITECTURES";
 std::string const kCMAKE_HIP_RUNTIME_LIBRARY = "CMAKE_HIP_RUNTIME_LIBRARY";
 std::string const kCMAKE_ISPC_INSTRUCTION_SETS = "CMAKE_ISPC_INSTRUCTION_SETS";
@@ -997,6 +1001,8 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode(
     vars.insert(kCMAKE_CUDA_ARCHITECTURES);
     vars.insert(kCMAKE_CUDA_RUNTIME_LIBRARY);
     vars.insert(kCMAKE_ENABLE_EXPORTS);
+    vars.insert(kCMAKE_EXECUTABLE_ENABLE_EXPORTS);
+    vars.insert(kCMAKE_SHARED_LIBRARY_ENABLE_EXPORTS);
     vars.insert(kCMAKE_HIP_ARCHITECTURES);
     vars.insert(kCMAKE_HIP_RUNTIME_LIBRARY);
     vars.insert(kCMAKE_ISPC_INSTRUCTION_SETS);

+ 1 - 1
Source/cmExportBuildFileGenerator.cxx

@@ -254,7 +254,7 @@ void cmExportBuildFileGenerator::SetImportLocationProperty(
     if (target->HasImportLibrary(config)) {
       std::string prop = cmStrCat("IMPORTED_IMPLIB", suffix);
       std::string value =
-        target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact);
+        target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact, true);
       if (mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) {
         target->GetImplibGNUtoMS(config, value, value,
                                  "${CMAKE_IMPORT_LIBRARY_SUFFIX}");

+ 2 - 1
Source/cmExportFileGenerator.cxx

@@ -1063,7 +1063,8 @@ void cmExportFileGenerator::GenerateImportTargetCode(
   }
 
   // Mark the imported executable if it has exports.
-  if (target->IsExecutableWithExports()) {
+  if (target->IsExecutableWithExports() ||
+      (target->IsSharedLibraryWithExports() && target->HasImportLibrary(""))) {
     os << "set_property(TARGET " << targetName
        << " PROPERTY ENABLE_EXPORTS 1)\n";
   }

+ 14 - 1
Source/cmExportInstallFileGenerator.cxx

@@ -409,7 +409,7 @@ void cmExportInstallFileGenerator::SetImportLocationProperty(
 
     // Append the installed file name.
     value += cmInstallTargetGenerator::GetInstallFilename(
-      target, config, cmInstallTargetGenerator::NameImplib);
+      target, config, cmInstallTargetGenerator::NameImplibReal);
 
     // Store the property.
     properties[prop] = value;
@@ -430,6 +430,19 @@ void cmExportInstallFileGenerator::SetImportLocationProperty(
     properties[prop] = cmJoin(objects, ";");
     importedLocations.insert(prop);
   } else {
+    if (target->IsFrameworkOnApple() && target->HasImportLibrary(config)) {
+      // store as well IMPLIB value
+      auto importProp = cmStrCat("IMPORTED_IMPLIB", suffix);
+      auto importValue =
+        cmStrCat(value,
+                 cmInstallTargetGenerator::GetInstallFilename(
+                   target, config, cmInstallTargetGenerator::NameImplibReal));
+
+      // Store the property.
+      properties[importProp] = importValue;
+      importedLocations.insert(importProp);
+    }
+
     // Construct the property name.
     std::string prop = cmStrCat("IMPORTED_LOCATION", suffix);
 

+ 362 - 8
Source/cmGeneratorExpressionNode.cxx

@@ -2658,10 +2658,14 @@ static const struct InstallPrefixNode : public cmGeneratorExpressionNode
 
 class ArtifactDirTag;
 class ArtifactLinkerTag;
+class ArtifactLinkerLibraryTag;
+class ArtifactLinkerImportTag;
 class ArtifactNameTag;
+class ArtifactImportTag;
 class ArtifactPathTag;
 class ArtifactPdbTag;
 class ArtifactSonameTag;
+class ArtifactSonameImportTag;
 class ArtifactBundleDirTag;
 class ArtifactBundleDirNameTag;
 class ArtifactBundleContentDirTag;
@@ -2770,6 +2774,38 @@ struct TargetFilesystemArtifactResultCreator<ArtifactSonameTag>
   }
 };
 
+template <>
+struct TargetFilesystemArtifactResultCreator<ArtifactSonameImportTag>
+{
+  static std::string Create(cmGeneratorTarget* target,
+                            cmGeneratorExpressionContext* context,
+                            const GeneratorExpressionContent* content)
+  {
+    // The target soname file (.so.1).
+    if (target->IsDLLPlatform()) {
+      ::reportError(context, content->GetOriginalExpression(),
+                    "TARGET_SONAME_IMPORT_FILE is not allowed "
+                    "for DLL target platforms.");
+      return std::string();
+    }
+    if (target->GetType() != cmStateEnums::SHARED_LIBRARY) {
+      ::reportError(context, content->GetOriginalExpression(),
+                    "TARGET_SONAME_IMPORT_FILE is allowed only for "
+                    "SHARED libraries.");
+      return std::string();
+    }
+
+    if (target->HasImportLibrary(context->Config)) {
+      return cmStrCat(target->GetDirectory(
+                        context->Config, cmStateEnums::ImportLibraryArtifact),
+                      '/',
+                      target->GetSOName(context->Config,
+                                        cmStateEnums::ImportLibraryArtifact));
+    }
+    return std::string{};
+  }
+};
+
 template <>
 struct TargetFilesystemArtifactResultCreator<ArtifactPdbTag>
 {
@@ -2817,7 +2853,8 @@ struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag>
                             cmGeneratorExpressionContext* context,
                             const GeneratorExpressionContent* content)
   {
-    // The file used to link to the target (.so, .lib, .a).
+    // The file used to link to the target (.so, .lib, .a) or import file
+    // (.lib,  .tbd).
     if (!target->IsLinkable()) {
       ::reportError(context, content->GetOriginalExpression(),
                     "TARGET_LINKER_FILE is allowed only for libraries and "
@@ -2832,6 +2869,55 @@ struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag>
   }
 };
 
+template <>
+struct TargetFilesystemArtifactResultCreator<ArtifactLinkerLibraryTag>
+{
+  static std::string Create(cmGeneratorTarget* target,
+                            cmGeneratorExpressionContext* context,
+                            const GeneratorExpressionContent* content)
+  {
+    // The file used to link to the target (.dylib, .so, .a).
+    if (!target->IsLinkable() ||
+        target->GetType() == cmStateEnums::EXECUTABLE) {
+      ::reportError(context, content->GetOriginalExpression(),
+                    "TARGET_LINKER_LIBRARY_FILE is allowed only for libraries "
+                    "with ENABLE_EXPORTS.");
+      return std::string();
+    }
+
+    if (!target->IsDLLPlatform() ||
+        target->GetType() == cmStateEnums::STATIC_LIBRARY) {
+      return target->GetFullPath(context->Config,
+                                 cmStateEnums::RuntimeBinaryArtifact);
+    }
+    return std::string{};
+  }
+};
+
+template <>
+struct TargetFilesystemArtifactResultCreator<ArtifactLinkerImportTag>
+{
+  static std::string Create(cmGeneratorTarget* target,
+                            cmGeneratorExpressionContext* context,
+                            const GeneratorExpressionContent* content)
+  {
+    // The file used to link to the target (.lib, .tbd).
+    if (!target->IsLinkable()) {
+      ::reportError(
+        context, content->GetOriginalExpression(),
+        "TARGET_LINKER_IMPORT_FILE is allowed only for libraries and "
+        "executables with ENABLE_EXPORTS.");
+      return std::string();
+    }
+
+    if (target->HasImportLibrary(context->Config)) {
+      return target->GetFullPath(context->Config,
+                                 cmStateEnums::ImportLibraryArtifact);
+    }
+    return std::string{};
+  }
+};
+
 template <>
 struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirTag>
 {
@@ -2929,6 +3015,21 @@ struct TargetFilesystemArtifactResultCreator<ArtifactNameTag>
   }
 };
 
+template <>
+struct TargetFilesystemArtifactResultCreator<ArtifactImportTag>
+{
+  static std::string Create(cmGeneratorTarget* target,
+                            cmGeneratorExpressionContext* context,
+                            const GeneratorExpressionContent* /*unused*/)
+  {
+    if (target->HasImportLibrary(context->Config)) {
+      return target->GetFullPath(context->Config,
+                                 cmStateEnums::ImportLibraryArtifact, true);
+    }
+    return std::string{};
+  }
+};
+
 template <typename ArtifactT>
 struct TargetFilesystemArtifactResultGetter
 {
@@ -3054,12 +3155,24 @@ struct TargetFilesystemArtifactNodeGroup
 static const TargetFilesystemArtifactNodeGroup<ArtifactNameTag>
   targetNodeGroup;
 
+static const TargetFilesystemArtifactNodeGroup<ArtifactImportTag>
+  targetImportNodeGroup;
+
 static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerTag>
   targetLinkerNodeGroup;
 
+static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerLibraryTag>
+  targetLinkerLibraryNodeGroup;
+
+static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerImportTag>
+  targetLinkerImportNodeGroup;
+
 static const TargetFilesystemArtifactNodeGroup<ArtifactSonameTag>
   targetSoNameNodeGroup;
 
+static const TargetFilesystemArtifactNodeGroup<ArtifactSonameImportTag>
+  targetSoNameImportNodeGroup;
+
 static const TargetFilesystemArtifactNodeGroup<ArtifactPdbTag>
   targetPdbNodeGroup;
 
@@ -3098,6 +3211,22 @@ struct TargetOutputNameArtifactResultGetter<ArtifactNameTag>
   }
 };
 
+template <>
+struct TargetOutputNameArtifactResultGetter<ArtifactImportTag>
+{
+  static std::string Get(cmGeneratorTarget* target,
+                         cmGeneratorExpressionContext* context,
+                         const GeneratorExpressionContent* /*unused*/)
+  {
+    if (target->HasImportLibrary(context->Config)) {
+      return target->GetOutputName(context->Config,
+                                   cmStateEnums::ImportLibraryArtifact) +
+        target->GetFilePostfix(context->Config);
+    }
+    return std::string{};
+  }
+};
+
 template <>
 struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag>
 {
@@ -3105,7 +3234,8 @@ struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag>
                          cmGeneratorExpressionContext* context,
                          const GeneratorExpressionContent* content)
   {
-    // The file used to link to the target (.so, .lib, .a).
+    // The library file used to link to the target (.so, .lib, .a) or import
+    // file (.lin,  .tbd).
     if (!target->IsLinkable()) {
       ::reportError(context, content->GetOriginalExpression(),
                     "TARGET_LINKER_FILE_BASE_NAME is allowed only for "
@@ -3121,6 +3251,56 @@ struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag>
   }
 };
 
+template <>
+struct TargetOutputNameArtifactResultGetter<ArtifactLinkerLibraryTag>
+{
+  static std::string Get(cmGeneratorTarget* target,
+                         cmGeneratorExpressionContext* context,
+                         const GeneratorExpressionContent* content)
+  {
+    // The library file used to link to the target (.so, .lib, .a).
+    if (!target->IsLinkable() ||
+        target->GetType() == cmStateEnums::EXECUTABLE) {
+      ::reportError(context, content->GetOriginalExpression(),
+                    "TARGET_LINKER_LIBRARY_FILE_BASE_NAME is allowed only for "
+                    "libraries with ENABLE_EXPORTS.");
+      return std::string();
+    }
+
+    if (!target->IsDLLPlatform() ||
+        target->GetType() == cmStateEnums::STATIC_LIBRARY) {
+      return target->GetOutputName(context->Config,
+                                   cmStateEnums::ImportLibraryArtifact) +
+        target->GetFilePostfix(context->Config);
+    }
+    return std::string{};
+  }
+};
+
+template <>
+struct TargetOutputNameArtifactResultGetter<ArtifactLinkerImportTag>
+{
+  static std::string Get(cmGeneratorTarget* target,
+                         cmGeneratorExpressionContext* context,
+                         const GeneratorExpressionContent* content)
+  {
+    // The import file used to link to the target (.lib, .tbd).
+    if (!target->IsLinkable()) {
+      ::reportError(context, content->GetOriginalExpression(),
+                    "TARGET_LINKER_IMPORT_FILE_BASE_NAME is allowed only for "
+                    "libraries and executables with ENABLE_EXPORTS.");
+      return std::string();
+    }
+
+    if (target->HasImportLibrary(context->Config)) {
+      return target->GetOutputName(context->Config,
+                                   cmStateEnums::ImportLibraryArtifact) +
+        target->GetFilePostfix(context->Config);
+    }
+    return std::string{};
+  }
+};
+
 template <>
 struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag>
 {
@@ -3192,15 +3372,27 @@ struct TargetFileBaseNameArtifact : public TargetArtifactBase
 
 static const TargetFileBaseNameArtifact<ArtifactNameTag>
   targetFileBaseNameNode;
+static const TargetFileBaseNameArtifact<ArtifactImportTag>
+  targetImportFileBaseNameNode;
 static const TargetFileBaseNameArtifact<ArtifactLinkerTag>
   targetLinkerFileBaseNameNode;
+static const TargetFileBaseNameArtifact<ArtifactLinkerLibraryTag>
+  targetLinkerLibraryFileBaseNameNode;
+static const TargetFileBaseNameArtifact<ArtifactLinkerImportTag>
+  targetLinkerImportFileBaseNameNode;
 static const TargetFileBaseNameArtifact<ArtifactPdbTag>
   targetPdbFileBaseNameNode;
 
 class ArtifactFilePrefixTag;
+class ArtifactImportFilePrefixTag;
 class ArtifactLinkerFilePrefixTag;
+class ArtifactLinkerLibraryFilePrefixTag;
+class ArtifactLinkerImportFilePrefixTag;
 class ArtifactFileSuffixTag;
+class ArtifactImportFileSuffixTag;
 class ArtifactLinkerFileSuffixTag;
+class ArtifactLinkerLibraryFileSuffixTag;
+class ArtifactLinkerImportFileSuffixTag;
 
 template <typename ArtifactT>
 struct TargetFileArtifactResultGetter
@@ -3221,6 +3413,20 @@ struct TargetFileArtifactResultGetter<ArtifactFilePrefixTag>
   }
 };
 template <>
+struct TargetFileArtifactResultGetter<ArtifactImportFilePrefixTag>
+{
+  static std::string Get(cmGeneratorTarget* target,
+                         cmGeneratorExpressionContext* context,
+                         const GeneratorExpressionContent*)
+  {
+    if (target->HasImportLibrary(context->Config)) {
+      return target->GetFilePrefix(context->Config,
+                                   cmStateEnums::ImportLibraryArtifact);
+    }
+    return std::string{};
+  }
+};
+template <>
 struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag>
 {
   static std::string Get(cmGeneratorTarget* target,
@@ -3228,9 +3434,10 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag>
                          const GeneratorExpressionContent* content)
   {
     if (!target->IsLinkable()) {
-      ::reportError(context, content->GetOriginalExpression(),
-                    "TARGET_LINKER_PREFIX is allowed only for libraries and "
-                    "executables with ENABLE_EXPORTS.");
+      ::reportError(
+        context, content->GetOriginalExpression(),
+        "TARGET_LINKER_FILE_PREFIX is allowed only for libraries and "
+        "executables with ENABLE_EXPORTS.");
       return std::string();
     }
 
@@ -3243,6 +3450,52 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag>
   }
 };
 template <>
+struct TargetFileArtifactResultGetter<ArtifactLinkerLibraryFilePrefixTag>
+{
+  static std::string Get(cmGeneratorTarget* target,
+                         cmGeneratorExpressionContext* context,
+                         const GeneratorExpressionContent* content)
+  {
+    if (!target->IsLinkable() ||
+        target->GetType() == cmStateEnums::EXECUTABLE) {
+      ::reportError(
+        context, content->GetOriginalExpression(),
+        "TARGET_LINKER_LIBRARY_FILE_PREFIX is allowed only for libraries "
+        "with ENABLE_EXPORTS.");
+      return std::string();
+    }
+
+    if (!target->IsDLLPlatform() ||
+        target->GetType() == cmStateEnums::STATIC_LIBRARY) {
+      return target->GetFilePrefix(context->Config,
+                                   cmStateEnums::RuntimeBinaryArtifact);
+    }
+    return std::string{};
+  }
+};
+template <>
+struct TargetFileArtifactResultGetter<ArtifactLinkerImportFilePrefixTag>
+{
+  static std::string Get(cmGeneratorTarget* target,
+                         cmGeneratorExpressionContext* context,
+                         const GeneratorExpressionContent* content)
+  {
+    if (!target->IsLinkable()) {
+      ::reportError(
+        context, content->GetOriginalExpression(),
+        "TARGET_LINKER_IMPORT_FILE_PREFIX is allowed only for libraries and "
+        "executables with ENABLE_EXPORTS.");
+      return std::string();
+    }
+
+    if (target->HasImportLibrary(context->Config)) {
+      return target->GetFilePrefix(context->Config,
+                                   cmStateEnums::ImportLibraryArtifact);
+    }
+    return std::string{};
+  }
+};
+template <>
 struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag>
 {
   static std::string Get(cmGeneratorTarget* target,
@@ -3253,6 +3506,20 @@ struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag>
   }
 };
 template <>
+struct TargetFileArtifactResultGetter<ArtifactImportFileSuffixTag>
+{
+  static std::string Get(cmGeneratorTarget* target,
+                         cmGeneratorExpressionContext* context,
+                         const GeneratorExpressionContent*)
+  {
+    if (target->HasImportLibrary(context->Config)) {
+      return target->GetFileSuffix(context->Config,
+                                   cmStateEnums::ImportLibraryArtifact);
+    }
+    return std::string{};
+  }
+};
+template <>
 struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag>
 {
   static std::string Get(cmGeneratorTarget* target,
@@ -3260,9 +3527,10 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag>
                          const GeneratorExpressionContent* content)
   {
     if (!target->IsLinkable()) {
-      ::reportError(context, content->GetOriginalExpression(),
-                    "TARGET_LINKER_SUFFIX is allowed only for libraries and "
-                    "executables with ENABLE_EXPORTS.");
+      ::reportError(
+        context, content->GetOriginalExpression(),
+        "TARGET_LINKER_FILE_SUFFIX is allowed only for libraries and "
+        "executables with ENABLE_EXPORTS.");
       return std::string();
     }
 
@@ -3274,6 +3542,51 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag>
     return target->GetFileSuffix(context->Config, artifact);
   }
 };
+template <>
+struct TargetFileArtifactResultGetter<ArtifactLinkerLibraryFileSuffixTag>
+{
+  static std::string Get(cmGeneratorTarget* target,
+                         cmGeneratorExpressionContext* context,
+                         const GeneratorExpressionContent* content)
+  {
+    if (!target->IsLinkable() ||
+        target->GetType() == cmStateEnums::STATIC_LIBRARY) {
+      ::reportError(context, content->GetOriginalExpression(),
+                    "TARGET_LINKER_LIBRARY_FILE_SUFFIX is allowed only for "
+                    "libraries with ENABLE_EXPORTS.");
+      return std::string();
+    }
+
+    if (!target->IsDLLPlatform() ||
+        target->GetType() == cmStateEnums::STATIC_LIBRARY) {
+      return target->GetFileSuffix(context->Config,
+                                   cmStateEnums::RuntimeBinaryArtifact);
+    }
+    return std::string{};
+  }
+};
+template <>
+struct TargetFileArtifactResultGetter<ArtifactLinkerImportFileSuffixTag>
+{
+  static std::string Get(cmGeneratorTarget* target,
+                         cmGeneratorExpressionContext* context,
+                         const GeneratorExpressionContent* content)
+  {
+    if (!target->IsLinkable()) {
+      ::reportError(
+        context, content->GetOriginalExpression(),
+        "TARGET_LINKER_IMPORT_FILE_SUFFIX is allowed only for libraries and "
+        "executables with ENABLE_EXPORTS.");
+      return std::string();
+    }
+
+    if (target->HasImportLibrary(context->Config)) {
+      return target->GetFileSuffix(context->Config,
+                                   cmStateEnums::ImportLibraryArtifact);
+    }
+    return std::string{};
+  }
+};
 
 template <typename ArtifactT>
 struct TargetFileArtifact : public TargetArtifactBase
@@ -3304,11 +3617,23 @@ struct TargetFileArtifact : public TargetArtifactBase
 };
 
 static const TargetFileArtifact<ArtifactFilePrefixTag> targetFilePrefixNode;
+static const TargetFileArtifact<ArtifactImportFilePrefixTag>
+  targetImportFilePrefixNode;
 static const TargetFileArtifact<ArtifactLinkerFilePrefixTag>
   targetLinkerFilePrefixNode;
+static const TargetFileArtifact<ArtifactLinkerLibraryFilePrefixTag>
+  targetLinkerLibraryFilePrefixNode;
+static const TargetFileArtifact<ArtifactLinkerImportFilePrefixTag>
+  targetLinkerImportFilePrefixNode;
 static const TargetFileArtifact<ArtifactFileSuffixTag> targetFileSuffixNode;
+static const TargetFileArtifact<ArtifactImportFileSuffixTag>
+  targetImportFileSuffixNode;
 static const TargetFileArtifact<ArtifactLinkerFileSuffixTag>
   targetLinkerFileSuffixNode;
+static const TargetFileArtifact<ArtifactLinkerLibraryFileSuffixTag>
+  targetLinkerLibraryFileSuffixNode;
+static const TargetFileArtifact<ArtifactLinkerImportFileSuffixTag>
+  targetLinkerImportFileSuffixNode;
 
 static const struct ShellPathNode : public cmGeneratorExpressionNode
 {
@@ -3376,23 +3701,52 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
     { "CONFIGURATION", &configurationNode },
     { "CONFIG", &configurationTestNode },
     { "TARGET_FILE", &targetNodeGroup.File },
+    { "TARGET_IMPORT_FILE", &targetImportNodeGroup.File },
     { "TARGET_LINKER_FILE", &targetLinkerNodeGroup.File },
+    { "TARGET_LINKER_LIBRARY_FILE", &targetLinkerLibraryNodeGroup.File },
+    { "TARGET_LINKER_IMPORT_FILE", &targetLinkerImportNodeGroup.File },
     { "TARGET_SONAME_FILE", &targetSoNameNodeGroup.File },
+    { "TARGET_SONAME_IMPORT_FILE", &targetSoNameImportNodeGroup.File },
     { "TARGET_PDB_FILE", &targetPdbNodeGroup.File },
     { "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode },
+    { "TARGET_IMPORT_FILE_BASE_NAME", &targetImportFileBaseNameNode },
     { "TARGET_LINKER_FILE_BASE_NAME", &targetLinkerFileBaseNameNode },
+    { "TARGET_LINKER_LIBRARY_FILE_BASE_NAME",
+      &targetLinkerLibraryFileBaseNameNode },
+    { "TARGET_LINKER_IMPORT_FILE_BASE_NAME",
+      &targetLinkerImportFileBaseNameNode },
     { "TARGET_PDB_FILE_BASE_NAME", &targetPdbFileBaseNameNode },
     { "TARGET_FILE_PREFIX", &targetFilePrefixNode },
+    { "TARGET_IMPORT_FILE_PREFIX", &targetImportFilePrefixNode },
     { "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode },
+    { "TARGET_LINKER_LIBRARY_FILE_PREFIX",
+      &targetLinkerLibraryFilePrefixNode },
+    { "TARGET_LINKER_IMPORT_FILE_PREFIX", &targetLinkerImportFilePrefixNode },
     { "TARGET_FILE_SUFFIX", &targetFileSuffixNode },
+    { "TARGET_IMPORT_FILE_SUFFIX", &targetImportFileSuffixNode },
     { "TARGET_LINKER_FILE_SUFFIX", &targetLinkerFileSuffixNode },
+    { "TARGET_LINKER_LIBRARY_FILE_SUFFIX",
+      &targetLinkerLibraryFileSuffixNode },
+    { "TARGET_LINKER_IMPORT_FILE_SUFFIX", &targetLinkerImportFileSuffixNode },
     { "TARGET_FILE_NAME", &targetNodeGroup.FileName },
+    { "TARGET_IMPORT_FILE_NAME", &targetImportNodeGroup.FileName },
     { "TARGET_LINKER_FILE_NAME", &targetLinkerNodeGroup.FileName },
+    { "TARGET_LINKER_LIBRARY_FILE_NAME",
+      &targetLinkerLibraryNodeGroup.FileName },
+    { "TARGET_LINKER_IMPORT_FILE_NAME",
+      &targetLinkerImportNodeGroup.FileName },
     { "TARGET_SONAME_FILE_NAME", &targetSoNameNodeGroup.FileName },
+    { "TARGET_SONAME_IMPORT_FILE_NAME",
+      &targetSoNameImportNodeGroup.FileName },
     { "TARGET_PDB_FILE_NAME", &targetPdbNodeGroup.FileName },
     { "TARGET_FILE_DIR", &targetNodeGroup.FileDir },
+    { "TARGET_IMPORT_FILE_DIR", &targetImportNodeGroup.FileDir },
     { "TARGET_LINKER_FILE_DIR", &targetLinkerNodeGroup.FileDir },
+    { "TARGET_LINKER_LIBRARY_FILE_DIR",
+      &targetLinkerLibraryNodeGroup.FileDir },
+    { "TARGET_LINKER_IMPORT_FILE_DIR", &targetLinkerImportNodeGroup.FileDir },
     { "TARGET_SONAME_FILE_DIR", &targetSoNameNodeGroup.FileDir },
+    { "TARGET_SONAME_IMPORT_FILE_DIR", &targetSoNameImportNodeGroup.FileDir },
     { "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir },
     { "TARGET_BUNDLE_DIR", &targetBundleDirNode },
     { "TARGET_BUNDLE_DIR_NAME", &targetBundleDirNameNode },

+ 108 - 30
Source/cmGeneratorTarget.cxx

@@ -458,6 +458,11 @@ std::string const& cmGeneratorTarget::GetSafeProperty(
 const char* cmGeneratorTarget::GetOutputTargetType(
   cmStateEnums::ArtifactType artifact) const
 {
+  if (this->IsFrameworkOnApple() || this->GetGlobalGenerator()->IsXcode()) {
+    // import file (i.e. .tbd file) is always in same location as library
+    artifact = cmStateEnums::RuntimeBinaryArtifact;
+  }
+
   switch (this->GetType()) {
     case cmStateEnums::SHARED_LIBRARY:
       if (this->IsDLLPlatform()) {
@@ -470,9 +475,15 @@ const char* cmGeneratorTarget::GetOutputTargetType(
             return "ARCHIVE";
         }
       } else {
-        // For non-DLL platforms shared libraries are treated as
-        // library targets.
-        return "LIBRARY";
+        switch (artifact) {
+          case cmStateEnums::RuntimeBinaryArtifact:
+            // For non-DLL platforms shared libraries are treated as
+            // library targets.
+            return "LIBRARY";
+          case cmStateEnums::ImportLibraryArtifact:
+            // Library import libraries are treated as archive targets.
+            return "ARCHIVE";
+        }
       }
       break;
     case cmStateEnums::STATIC_LIBRARY:
@@ -2518,7 +2529,8 @@ bool cmGeneratorTarget::CanGenerateInstallNameDir(
   return !skip;
 }
 
-std::string cmGeneratorTarget::GetSOName(const std::string& config) const
+std::string cmGeneratorTarget::GetSOName(
+  const std::string& config, cmStateEnums::ArtifactType artifact) const
 {
   if (this->IsImported()) {
     // Lookup the imported soname.
@@ -2546,7 +2558,9 @@ std::string cmGeneratorTarget::GetSOName(const std::string& config) const
     return "";
   }
   // Compute the soname that will be built.
-  return this->GetLibraryNames(config).SharedObject;
+  return artifact == cmStateEnums::RuntimeBinaryArtifact
+    ? this->GetLibraryNames(config).SharedObject
+    : this->GetLibraryNames(config).ImportLibrary;
 }
 
 namespace {
@@ -5088,10 +5102,18 @@ void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const
     f = cmStrCat(dir, '/', targetNames.PDB);
     gg->AddToManifest(f);
   }
+
+  dir = this->GetDirectory(config, cmStateEnums::ImportLibraryArtifact);
+  if (!targetNames.ImportOutput.empty()) {
+    f = cmStrCat(dir, '/', targetNames.ImportOutput);
+    gg->AddToManifest(f);
+  }
   if (!targetNames.ImportLibrary.empty()) {
-    f =
-      cmStrCat(this->GetDirectory(config, cmStateEnums::ImportLibraryArtifact),
-               '/', targetNames.ImportLibrary);
+    f = cmStrCat(dir, '/', targetNames.ImportLibrary);
+    gg->AddToManifest(f);
+  }
+  if (!targetNames.ImportReal.empty()) {
+    f = cmStrCat(dir, '/', targetNames.ImportReal);
     gg->AddToManifest(f);
   }
 }
@@ -5211,14 +5233,20 @@ std::string cmGeneratorTarget::NormalGetFullPath(
       }
       break;
     case cmStateEnums::ImportLibraryArtifact:
-      fpath += this->GetFullName(config, cmStateEnums::ImportLibraryArtifact);
+      if (realname) {
+        fpath +=
+          this->NormalGetRealName(config, cmStateEnums::ImportLibraryArtifact);
+      } else {
+        fpath +=
+          this->GetFullName(config, cmStateEnums::ImportLibraryArtifact);
+      }
       break;
   }
   return fpath;
 }
 
 std::string cmGeneratorTarget::NormalGetRealName(
-  const std::string& config) const
+  const std::string& config, cmStateEnums::ArtifactType artifact) const
 {
   // This should not be called for imported targets.
   // TODO: Split cmTarget into a class hierarchy to get compile-time
@@ -5229,12 +5257,13 @@ std::string cmGeneratorTarget::NormalGetRealName(
     this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
   }
 
-  if (this->GetType() == cmStateEnums::EXECUTABLE) {
-    // Compute the real name that will be built.
-    return this->GetExecutableNames(config).Real;
-  }
+  Names names = this->GetType() == cmStateEnums::EXECUTABLE
+    ? this->GetExecutableNames(config)
+    : this->GetLibraryNames(config);
+
   // Compute the real name that will be built.
-  return this->GetLibraryNames(config).Real;
+  return artifact == cmStateEnums::RuntimeBinaryArtifact ? names.Real
+                                                         : names.ImportReal;
 }
 
 cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames(
@@ -5279,17 +5308,16 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames(
   // The library name.
   targetNames.Base = components.base;
   targetNames.Output =
-    components.prefix + targetNames.Base + components.suffix;
+    cmStrCat(components.prefix, targetNames.Base, components.suffix);
 
   if (this->IsFrameworkOnApple()) {
     targetNames.Real = components.prefix;
     if (!this->Makefile->PlatformIsAppleEmbedded()) {
-      targetNames.Real += "Versions/";
-      targetNames.Real += this->GetFrameworkVersion();
-      targetNames.Real += "/";
+      targetNames.Real +=
+        cmStrCat("Versions/", this->GetFrameworkVersion(), '/');
     }
-    targetNames.Real += targetNames.Base + components.suffix;
-    targetNames.SharedObject = targetNames.Real + components.suffix;
+    targetNames.Real += cmStrCat(targetNames.Base, components.suffix);
+    targetNames.SharedObject = targetNames.Real;
   } else {
     // The library's soname.
     this->ComputeVersionedName(targetNames.SharedObject, components.prefix,
@@ -5302,11 +5330,36 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames(
                                targetNames.Output, version);
   }
 
-  // The import library name.
+  // The import library names.
   if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
       this->GetType() == cmStateEnums::MODULE_LIBRARY) {
-    targetNames.ImportLibrary =
-      this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact);
+    NameComponents const& importComponents =
+      this->GetFullNameInternalComponents(config,
+                                          cmStateEnums::ImportLibraryArtifact);
+    targetNames.ImportOutput = cmStrCat(
+      importComponents.prefix, importComponents.base, importComponents.suffix);
+
+    if (this->IsFrameworkOnApple() && this->IsSharedLibraryWithExports()) {
+      targetNames.ImportReal = components.prefix;
+      if (!this->Makefile->PlatformIsAppleEmbedded()) {
+        targetNames.ImportReal +=
+          cmStrCat("Versions/", this->GetFrameworkVersion(), '/');
+      }
+      targetNames.ImportReal +=
+        cmStrCat(importComponents.base, importComponents.suffix);
+      targetNames.ImportLibrary = targetNames.ImportOutput;
+    } else {
+      // The import library's soname.
+      this->ComputeVersionedName(
+        targetNames.ImportLibrary, importComponents.prefix,
+        importComponents.base, importComponents.suffix,
+        targetNames.ImportOutput, soversion);
+
+      // The import library's real name on disk.
+      this->ComputeVersionedName(
+        targetNames.ImportReal, importComponents.prefix, importComponents.base,
+        importComponents.suffix, targetNames.ImportOutput, version);
+    }
   }
 
   // The program database file name.
@@ -5368,6 +5421,8 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetExecutableNames(
   // The import library name.
   targetNames.ImportLibrary =
     this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact);
+  targetNames.ImportReal = targetNames.ImportLibrary;
+  targetNames.ImportOutput = targetNames.ImportLibrary;
 
   // The program database file name.
   targetNames.PDB = this->GetPDBName(config);
@@ -5449,15 +5504,18 @@ cmGeneratorTarget::GetFullNameInternalComponents(
   }
 
   // Compute the full name for main target types.
-  const std::string configPostfix = this->GetFilePostfix(config);
+  std::string configPostfix = this->GetFilePostfix(config);
 
-  // frameworks have directory prefix but no suffix
+  // frameworks have directory prefix
   std::string fw_prefix;
   if (this->IsFrameworkOnApple()) {
     fw_prefix =
       cmStrCat(this->GetFrameworkDirectory(config, ContentLevel), '/');
     targetPrefix = cmValue(fw_prefix);
-    targetSuffix = nullptr;
+    if (!isImportedLibraryArtifact) {
+      // no suffix
+      targetSuffix = nullptr;
+    }
   }
 
   if (this->IsCFBundleOnApple()) {
@@ -5476,8 +5534,8 @@ cmGeneratorTarget::GetFullNameInternalComponents(
   // When using Xcode, the postfix should be part of the suffix rather than
   // the base, because the suffix ends up being used in Xcode's
   // EXECUTABLE_SUFFIX attribute.
-  if (this->IsFrameworkOnApple() &&
-      this->GetGlobalGenerator()->GetName() == "Xcode") {
+  if (this->IsFrameworkOnApple() && this->GetGlobalGenerator()->IsXcode()) {
+    configPostfix += *targetSuffix;
     targetSuffix = cmValue(configPostfix);
   } else {
     outBase += configPostfix;
@@ -8544,15 +8602,35 @@ bool cmGeneratorTarget::IsExecutableWithExports() const
   return this->Target->IsExecutableWithExports();
 }
 
+bool cmGeneratorTarget::IsSharedLibraryWithExports() const
+{
+  return this->Target->IsSharedLibraryWithExports();
+}
+
 bool cmGeneratorTarget::HasImportLibrary(std::string const& config) const
 {
+  bool generate_Stubs = true;
+  if (this->GetGlobalGenerator()->IsXcode()) {
+    // take care of CMAKE_XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS variable
+    // as well as XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS property
+    if (cmValue propGenStubs =
+          this->GetProperty("XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS")) {
+      generate_Stubs = propGenStubs == "YES";
+    } else if (cmValue varGenStubs = this->Makefile->GetDefinition(
+                 "CMAKE_XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS")) {
+      generate_Stubs = varGenStubs == "YES";
+    }
+  }
+
   return (this->IsDLLPlatform() &&
           (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
            this->IsExecutableWithExports()) &&
           // Assemblies which have only managed code do not have
           // import libraries.
           this->GetManagedType(config) != ManagedType::Managed) ||
-    (this->IsAIX() && this->IsExecutableWithExports());
+    (this->IsAIX() && this->IsExecutableWithExports()) ||
+    (this->Makefile->PlatformSupportsAppleTextStubs() &&
+     this->IsSharedLibraryWithExports() && generate_Stubs);
 }
 
 bool cmGeneratorTarget::NeedImportLibraryName(std::string const& config) const

+ 12 - 2
Source/cmGeneratorTarget.h

@@ -273,7 +273,9 @@ public:
   std::string NormalGetFullPath(const std::string& config,
                                 cmStateEnums::ArtifactType artifact,
                                 bool realname) const;
-  std::string NormalGetRealName(const std::string& config) const;
+  std::string NormalGetRealName(const std::string& config,
+                                cmStateEnums::ArtifactType artifact =
+                                  cmStateEnums::RuntimeBinaryArtifact) const;
 
   /** Get the names of an object library's object files underneath
       its object file directory.  */
@@ -348,7 +350,9 @@ public:
   const std::string* GetExportMacro() const;
 
   /** Get the soname of the target.  Allowed only for a shared library.  */
-  std::string GetSOName(const std::string& config) const;
+  std::string GetSOName(const std::string& config,
+                        cmStateEnums::ArtifactType artifact =
+                          cmStateEnums::RuntimeBinaryArtifact) const;
 
   struct NameComponents
   {
@@ -740,6 +744,8 @@ public:
     std::string Base;
     std::string Output;
     std::string Real;
+    std::string ImportOutput;
+    std::string ImportReal;
     std::string ImportLibrary;
     std::string PDB;
     std::string SharedObject;
@@ -786,6 +792,10 @@ public:
 
   bool IsExecutableWithExports() const;
 
+  /* Return whether this target is a shared library with capability to generate
+   * a file describing symbols exported (for example, .tbd file on Apple). */
+  bool IsSharedLibraryWithExports() const;
+
   /** Return whether or not the target has a DLL import library.  */
   bool HasImportLibrary(std::string const& config) const;
 

+ 4 - 0
Source/cmGlobalNinjaGenerator.cxx

@@ -1270,6 +1270,10 @@ void cmGlobalNinjaGenerator::AppendTargetOutputs(
     }
       CM_FALLTHROUGH;
     case cmStateEnums::EXECUTABLE: {
+      if (target->IsApple() && target->HasImportLibrary(config)) {
+        outputs.push_back(this->ConvertToNinjaPath(target->GetFullPath(
+          config, cmStateEnums::ImportLibraryArtifact, realname)));
+      }
       outputs.push_back(this->ConvertToNinjaPath(target->GetFullPath(
         config, cmStateEnums::RuntimeBinaryArtifact, realname)));
       break;

+ 28 - 1
Source/cmGlobalXCodeGenerator.cxx

@@ -1739,7 +1739,7 @@ void cmGlobalXCodeGenerator::CreateCustomCommands(
     std::string str_so_file =
       cmStrCat("$<TARGET_SONAME_FILE:", gtgt->GetName(), '>');
     std::string str_link_file =
-      cmStrCat("$<TARGET_LINKER_FILE:", gtgt->GetName(), '>');
+      cmStrCat("$<TARGET_LINKER_LIBRARY_FILE:", gtgt->GetName(), '>');
     cmCustomCommandLines cmd = cmMakeSingleCommandLine(
       { cmSystemTools::GetCMakeCommand(), "-E", "cmake_symlink_library",
         str_file, str_so_file, str_link_file });
@@ -1754,6 +1754,27 @@ void cmGlobalXCodeGenerator::CreateCustomCommands(
     postbuild.push_back(std::move(command));
   }
 
+  if (gtgt->HasImportLibrary("") && !gtgt->IsFrameworkOnApple()) {
+    // create symbolic links for .tbd file
+    std::string file = cmStrCat("$<TARGET_IMPORT_FILE:", gtgt->GetName(), '>');
+    std::string soFile =
+      cmStrCat("$<TARGET_SONAME_IMPORT_FILE:", gtgt->GetName(), '>');
+    std::string linkFile =
+      cmStrCat("$<TARGET_LINKER_IMPORT_FILE:", gtgt->GetName(), '>');
+    cmCustomCommandLines symlink_command = cmMakeSingleCommandLine(
+      { cmSystemTools::GetCMakeCommand(), "-E", "cmake_symlink_library", file,
+        soFile, linkFile });
+
+    cmCustomCommand command;
+    command.SetCommandLines(symlink_command);
+    command.SetComment("Creating import symlinks");
+    command.SetWorkingDirectory("");
+    command.SetBacktrace(this->CurrentMakefile->GetBacktrace());
+    command.SetStdPipesUTF8(true);
+
+    postbuild.push_back(std::move(command));
+  }
+
   cmXCodeObject* legacyCustomCommandsBuildPhase = nullptr;
   cmXCodeObject* preBuildPhase = nullptr;
   cmXCodeObject* preLinkPhase = nullptr;
@@ -2682,6 +2703,12 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
 
       buildSettings->AddAttribute("LIBRARY_STYLE",
                                   this->CreateString("DYNAMIC"));
+
+      if (gtgt->HasImportLibrary(configName)) {
+        // Request .tbd file generation
+        buildSettings->AddAttribute("GENERATE_TEXT_BASED_STUBS",
+                                    this->CreateString("YES"));
+      }
       break;
     }
     case cmStateEnums::EXECUTABLE: {

+ 58 - 15
Source/cmInstallCommand.cxx

@@ -553,34 +553,35 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
 
   // Enforce argument rules too complex to specify for the
   // general-purpose parser.
-  if (archiveArgs.GetNamelinkOnly() || runtimeArgs.GetNamelinkOnly() ||
-      objectArgs.GetNamelinkOnly() || frameworkArgs.GetNamelinkOnly() ||
-      bundleArgs.GetNamelinkOnly() || privateHeaderArgs.GetNamelinkOnly() ||
+  if (runtimeArgs.GetNamelinkOnly() || objectArgs.GetNamelinkOnly() ||
+      frameworkArgs.GetNamelinkOnly() || bundleArgs.GetNamelinkOnly() ||
+      privateHeaderArgs.GetNamelinkOnly() ||
       publicHeaderArgs.GetNamelinkOnly() || resourceArgs.GetNamelinkOnly() ||
       std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
                   [](const cmInstallCommandFileSetArguments& fileSetArg)
                     -> bool { return fileSetArg.GetNamelinkOnly(); }) ||
       cxxModuleBmiArgs.GetNamelinkOnly()) {
     status.SetError(
-      "TARGETS given NAMELINK_ONLY option not in LIBRARY group.  "
-      "The NAMELINK_ONLY option may be specified only following LIBRARY.");
+      "TARGETS given NAMELINK_ONLY option not in LIBRARY or ARCHIVE group.  "
+      "The NAMELINK_ONLY option may be specified only following LIBRARY or "
+      "ARCHIVE.");
     return false;
   }
-  if (archiveArgs.GetNamelinkSkip() || runtimeArgs.GetNamelinkSkip() ||
-      objectArgs.GetNamelinkSkip() || frameworkArgs.GetNamelinkSkip() ||
-      bundleArgs.GetNamelinkSkip() || privateHeaderArgs.GetNamelinkSkip() ||
+  if (runtimeArgs.GetNamelinkSkip() || objectArgs.GetNamelinkSkip() ||
+      frameworkArgs.GetNamelinkSkip() || bundleArgs.GetNamelinkSkip() ||
+      privateHeaderArgs.GetNamelinkSkip() ||
       publicHeaderArgs.GetNamelinkSkip() || resourceArgs.GetNamelinkSkip() ||
       std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
                   [](const cmInstallCommandFileSetArguments& fileSetArg)
                     -> bool { return fileSetArg.GetNamelinkSkip(); }) ||
       cxxModuleBmiArgs.GetNamelinkSkip()) {
     status.SetError(
-      "TARGETS given NAMELINK_SKIP option not in LIBRARY group.  "
-      "The NAMELINK_SKIP option may be specified only following LIBRARY.");
+      "TARGETS given NAMELINK_SKIP option not in LIBRARY or ARCHIVE group.  "
+      "The NAMELINK_SKIP option may be specified only following LIBRARY or "
+      "ARCHIVE.");
     return false;
   }
-  if (archiveArgs.HasNamelinkComponent() ||
-      runtimeArgs.HasNamelinkComponent() ||
+  if (runtimeArgs.HasNamelinkComponent() ||
       objectArgs.HasNamelinkComponent() ||
       frameworkArgs.HasNamelinkComponent() ||
       bundleArgs.HasNamelinkComponent() ||
@@ -592,9 +593,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
                     -> bool { return fileSetArg.HasNamelinkComponent(); }) ||
       cxxModuleBmiArgs.HasNamelinkComponent()) {
     status.SetError(
-      "TARGETS given NAMELINK_COMPONENT option not in LIBRARY group.  "
-      "The NAMELINK_COMPONENT option may be specified only following "
-      "LIBRARY.");
+      "TARGETS given NAMELINK_COMPONENT option not in LIBRARY or ARCHIVE "
+      "group.  The NAMELINK_COMPONENT option may be specified only following "
+      "LIBRARY or ARCHIVE.");
     return false;
   }
   if (libraryArgs.GetNamelinkOnly() && libraryArgs.GetNamelinkSkip()) {
@@ -674,6 +675,14 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   } else if (libraryArgs.GetNamelinkSkip()) {
     namelinkMode = cmInstallTargetGenerator::NamelinkModeSkip;
   }
+  // Select the mode for installing symlinks to versioned imported libraries.
+  cmInstallTargetGenerator::NamelinkModeType importlinkMode =
+    cmInstallTargetGenerator::NamelinkModeNone;
+  if (archiveArgs.GetNamelinkOnly()) {
+    importlinkMode = cmInstallTargetGenerator::NamelinkModeOnly;
+  } else if (archiveArgs.GetNamelinkSkip()) {
+    importlinkMode = cmInstallTargetGenerator::NamelinkModeSkip;
+  }
 
   // Check if there is something to do.
   if (targetList.empty()) {
@@ -725,6 +734,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   bool installsArchive = false;
   bool installsLibrary = false;
   bool installsNamelink = false;
+  bool installsImportlink = false;
   bool installsRuntime = false;
   bool installsObject = false;
   bool installsFramework = false;
@@ -742,6 +752,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
     std::unique_ptr<cmInstallTargetGenerator> archiveGenerator;
     std::unique_ptr<cmInstallTargetGenerator> libraryGenerator;
     std::unique_ptr<cmInstallTargetGenerator> namelinkGenerator;
+    std::unique_ptr<cmInstallTargetGenerator> importlinkGenerator;
     std::unique_ptr<cmInstallTargetGenerator> runtimeGenerator;
     std::unique_ptr<cmInstallTargetGenerator> objectGenerator;
     std::unique_ptr<cmInstallTargetGenerator> frameworkGenerator;
@@ -885,6 +896,32 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
             }
             namelinkOnly =
               (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
+
+            if (target.GetMakefile()->PlatformSupportsAppleTextStubs() &&
+                target.IsSharedLibraryWithExports()) {
+              // Apple .tbd files use the ARCHIVE properties
+              if (!archiveArgs.GetDestination().empty()) {
+                artifactsSpecified = true;
+              }
+              if (importlinkMode !=
+                  cmInstallTargetGenerator::NamelinkModeOnly) {
+                archiveGenerator = CreateInstallTargetGenerator(
+                  target, archiveArgs, true, helper.Makefile->GetBacktrace(),
+                  helper.GetLibraryDestination(&archiveArgs));
+                archiveGenerator->SetImportlinkMode(
+                  cmInstallTargetGenerator::NamelinkModeSkip);
+              }
+              if (importlinkMode !=
+                  cmInstallTargetGenerator::NamelinkModeSkip) {
+                importlinkGenerator = CreateInstallTargetGenerator(
+                  target, archiveArgs, true, helper.Makefile->GetBacktrace(),
+                  helper.GetLibraryDestination(&archiveArgs), false, true);
+                importlinkGenerator->SetImportlinkMode(
+                  cmInstallTargetGenerator::NamelinkModeOnly);
+              }
+              namelinkOnly =
+                (importlinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
+            }
           }
           if (runtimeDependencySet && libraryGenerator) {
             runtimeDependencySet->AddLibrary(libraryGenerator.get());
@@ -1157,6 +1194,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
     installsArchive = installsArchive || archiveGenerator;
     installsLibrary = installsLibrary || libraryGenerator;
     installsNamelink = installsNamelink || namelinkGenerator;
+    installsImportlink = installsImportlink || importlinkGenerator;
     installsRuntime = installsRuntime || runtimeGenerator;
     installsObject = installsObject || objectGenerator;
     installsFramework = installsFramework || frameworkGenerator;
@@ -1169,6 +1207,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
     helper.Makefile->AddInstallGenerator(std::move(archiveGenerator));
     helper.Makefile->AddInstallGenerator(std::move(libraryGenerator));
     helper.Makefile->AddInstallGenerator(std::move(namelinkGenerator));
+    helper.Makefile->AddInstallGenerator(std::move(importlinkGenerator));
     helper.Makefile->AddInstallGenerator(std::move(runtimeGenerator));
     helper.Makefile->AddInstallGenerator(std::move(objectGenerator));
     helper.Makefile->AddInstallGenerator(std::move(frameworkGenerator));
@@ -1203,6 +1242,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
     helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       libraryArgs.GetNamelinkComponent());
   }
+  if (installsImportlink) {
+    helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
+      archiveArgs.GetNamelinkComponent());
+  }
   if (installsRuntime) {
     helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       runtimeArgs.GetComponent());

+ 111 - 74
Source/cmInstallTargetGenerator.cxx

@@ -4,12 +4,15 @@
 
 #include <algorithm>
 #include <cassert>
+#include <functional>
 #include <map>
 #include <set>
 #include <sstream>
 #include <utility>
 #include <vector>
 
+#include <cm/optional>
+
 #include "cmComputeLinkInformation.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
@@ -40,6 +43,84 @@ std::string computeInstallObjectDir(cmGeneratorTarget* gt,
   objectDir += gt->GetName();
   return objectDir;
 }
+
+void computeFilesToInstall(
+  cmInstallTargetGenerator::Files& files,
+  cmInstallTargetGenerator::NamelinkModeType namelinkMode,
+  std::string const& fromDirConfig, std::string const& output,
+  std::string const& library, std::string const& real,
+  cm::optional<std::function<void(std::string const&)>> GNUToMS = cm::nullopt)
+{
+  bool haveNamelink = false;
+  auto convert = [&GNUToMS](std::string const& file) {
+    if (GNUToMS) {
+      (*GNUToMS)(file);
+    }
+  };
+
+  // Library link name.
+  std::string fromName = cmStrCat(fromDirConfig, output);
+  std::string toName = output;
+
+  // Library interface name.
+  std::string fromSOName;
+  std::string toSOName;
+  if (library != output) {
+    haveNamelink = true;
+    fromSOName = cmStrCat(fromDirConfig, library);
+    toSOName = library;
+  }
+
+  // Library implementation name.
+  std::string fromRealName;
+  std::string toRealName;
+  if (real != output && real != library) {
+    haveNamelink = true;
+    fromRealName = cmStrCat(fromDirConfig, real);
+    toRealName = real;
+  }
+
+  // Add the names based on the current namelink mode.
+  if (haveNamelink) {
+    files.NamelinkMode = namelinkMode;
+    // With a namelink we need to check the mode.
+    if (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly) {
+      // Install the namelink only.
+      files.From.emplace_back(fromName);
+      files.To.emplace_back(toName);
+      convert(output);
+    } else {
+      // Install the real file if it has its own name.
+      if (!fromRealName.empty()) {
+        files.From.emplace_back(fromRealName);
+        files.To.emplace_back(toRealName);
+        convert(real);
+      }
+
+      // Install the soname link if it has its own name.
+      if (!fromSOName.empty()) {
+        files.From.emplace_back(fromSOName);
+        files.To.emplace_back(toSOName);
+        convert(library);
+      }
+
+      // Install the namelink if it is not to be skipped.
+      if (namelinkMode != cmInstallTargetGenerator::NamelinkModeSkip) {
+        files.From.emplace_back(fromName);
+        files.To.emplace_back(toName);
+        convert(output);
+      }
+    }
+  } else {
+    // Without a namelink there will be only one file.  Install it
+    // if this is not a namelink-only rule.
+    if (namelinkMode != cmInstallTargetGenerator::NamelinkModeOnly) {
+      files.From.emplace_back(fromName);
+      files.To.emplace_back(toName);
+      convert(output);
+    }
+  }
+}
 }
 
 cmInstallTargetGenerator::cmInstallTargetGenerator(
@@ -56,6 +137,7 @@ cmInstallTargetGenerator::cmInstallTargetGenerator(
 {
   this->ActionsPerConfig = true;
   this->NamelinkMode = NamelinkModeNone;
+  this->ImportlinkMode = NamelinkModeNone;
 }
 
 cmInstallTargetGenerator::~cmInstallTargetGenerator() = default;
@@ -247,18 +329,21 @@ cmInstallTargetGenerator::Files cmInstallTargetGenerator::GetFiles(
       this->Target->GetLibraryNames(config);
     if (this->ImportLibrary) {
       // There is a bug in cmInstallCommand if this fails.
-      assert(this->NamelinkMode == NamelinkModeNone);
+      assert(this->Target->Makefile->PlatformSupportsAppleTextStubs() ||
+             this->ImportlinkMode == NamelinkModeNone);
+
+      auto GNUToMS = [this, &config, &files,
+                      &fromDirConfig](const std::string& lib) {
+        std::string importLib;
+        if (this->Target->GetImplibGNUtoMS(config, lib, importLib)) {
+          files.From.emplace_back(fromDirConfig + importLib);
+          files.To.emplace_back(importLib);
+        }
+      };
 
-      std::string from1 = fromDirConfig + targetNames.ImportLibrary;
-      std::string to1 = targetNames.ImportLibrary;
-      files.From.emplace_back(std::move(from1));
-      files.To.emplace_back(std::move(to1));
-      std::string targetNameImportLib;
-      if (this->Target->GetImplibGNUtoMS(config, targetNames.ImportLibrary,
-                                         targetNameImportLib)) {
-        files.From.emplace_back(fromDirConfig + targetNameImportLib);
-        files.To.emplace_back(targetNameImportLib);
-      }
+      computeFilesToInstall(
+        files, this->ImportlinkMode, fromDirConfig, targetNames.ImportOutput,
+        targetNames.ImportLibrary, targetNames.ImportReal, GNUToMS);
 
       // An import library looks like a static library.
       files.Type = cmInstallType_STATIC_LIBRARY;
@@ -318,66 +403,9 @@ cmInstallTargetGenerator::Files cmInstallTargetGenerator::GetFiles(
       files.From.emplace_back(std::move(from1));
       files.To.emplace_back(std::move(to1));
     } else {
-      bool haveNamelink = false;
-
-      // Library link name.
-      std::string fromName = fromDirConfig + targetNames.Output;
-      std::string toName = targetNames.Output;
-
-      // Library interface name.
-      std::string fromSOName;
-      std::string toSOName;
-      if (targetNames.SharedObject != targetNames.Output) {
-        haveNamelink = true;
-        fromSOName = fromDirConfig + targetNames.SharedObject;
-        toSOName = targetNames.SharedObject;
-      }
-
-      // Library implementation name.
-      std::string fromRealName;
-      std::string toRealName;
-      if (targetNames.Real != targetNames.Output &&
-          targetNames.Real != targetNames.SharedObject) {
-        haveNamelink = true;
-        fromRealName = fromDirConfig + targetNames.Real;
-        toRealName = targetNames.Real;
-      }
-
-      // Add the names based on the current namelink mode.
-      if (haveNamelink) {
-        files.NamelinkMode = this->NamelinkMode;
-        // With a namelink we need to check the mode.
-        if (this->NamelinkMode == NamelinkModeOnly) {
-          // Install the namelink only.
-          files.From.emplace_back(fromName);
-          files.To.emplace_back(toName);
-        } else {
-          // Install the real file if it has its own name.
-          if (!fromRealName.empty()) {
-            files.From.emplace_back(fromRealName);
-            files.To.emplace_back(toRealName);
-          }
-
-          // Install the soname link if it has its own name.
-          if (!fromSOName.empty()) {
-            files.From.emplace_back(fromSOName);
-            files.To.emplace_back(toSOName);
-          }
-
-          // Install the namelink if it is not to be skipped.
-          if (this->NamelinkMode != NamelinkModeSkip) {
-            files.From.emplace_back(fromName);
-            files.To.emplace_back(toName);
-          }
-        }
-      } else {
-        // Without a namelink there will be only one file.  Install it
-        // if this is not a namelink-only rule.
-        if (this->NamelinkMode != NamelinkModeOnly) {
-          files.From.emplace_back(fromName);
-          files.To.emplace_back(toName);
-        }
-      }
+      computeFilesToInstall(files, this->NamelinkMode, fromDirConfig,
+                            targetNames.Output, targetNames.SharedObject,
+                            targetNames.Real);
     }
   }
 
@@ -425,6 +453,12 @@ std::string cmInstallTargetGenerator::GetInstallFilename(
                                     "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
         fname = targetNames.ImportLibrary;
       }
+    } else if (nameType == NameImplibReal) {
+      // Use the import library name.
+      if (!target->GetImplibGNUtoMS(config, targetNames.ImportReal, fname,
+                                    "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
+        fname = targetNames.ImportReal;
+      }
     } else if (nameType == NameReal) {
       // Use the canonical name.
       fname = targetNames.Real;
@@ -434,11 +468,14 @@ std::string cmInstallTargetGenerator::GetInstallFilename(
     }
   } else {
     cmGeneratorTarget::Names targetNames = target->GetLibraryNames(config);
-    if (nameType == NameImplib) {
+    if (nameType == NameImplib || nameType == NameImplibReal) {
+      const auto& importName = nameType == NameImplib
+        ? targetNames.ImportLibrary
+        : targetNames.ImportReal;
       // Use the import library name.
-      if (!target->GetImplibGNUtoMS(config, targetNames.ImportLibrary, fname,
+      if (!target->GetImplibGNUtoMS(config, importName, fname,
                                     "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
-        fname = targetNames.ImportLibrary;
+        fname = importName;
       }
     } else if (nameType == NameSO) {
       // Use the soname.

+ 7 - 1
Source/cmInstallTargetGenerator.h

@@ -39,6 +39,10 @@ public:
     NamelinkModeSkip
   };
   void SetNamelinkMode(NamelinkModeType mode) { this->NamelinkMode = mode; }
+  void SetImportlinkMode(NamelinkModeType mode)
+  {
+    this->ImportlinkMode = mode;
+  }
 
   std::string GetInstallFilename(const std::string& config) const;
 
@@ -50,7 +54,8 @@ public:
     NameNormal,
     NameImplib,
     NameSO,
-    NameReal
+    NameReal,
+    NameImplibReal
   };
 
   static std::string GetInstallFilename(const cmGeneratorTarget* target,
@@ -121,6 +126,7 @@ protected:
   cmGeneratorTarget* Target = nullptr;
   std::string const FilePermissions;
   NamelinkModeType NamelinkMode;
+  NamelinkModeType ImportlinkMode;
   bool const ImportLibrary;
   bool const Optional;
 };

+ 8 - 0
Source/cmLocalGenerator.cxx

@@ -85,6 +85,7 @@ static auto ruleReplaceVars = { "CMAKE_${LANG}_COMPILER",
                                 "CMAKE_RANLIB",
                                 "CMAKE_LINKER",
                                 "CMAKE_MT",
+                                "CMAKE_TAPI",
                                 "CMAKE_CUDA_HOST_COMPILER",
                                 "CMAKE_CUDA_HOST_LINK_LAUNCHER",
                                 "CMAKE_CL_SHOWINCLUDES_PREFIX" };
@@ -134,6 +135,13 @@ cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile)
     this->LinkerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
   }
 
+  // OSX SYSROOT can be required by some tools, like tapi
+  {
+    cmValue osxSysroot = this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT");
+    this->VariableMappings["CMAKE_OSX_SYSROOT"] =
+      osxSysroot.IsEmpty() ? "/" : this->EscapeForShell(*osxSysroot, true);
+  }
+
   if (cmValue appleArchSysroots =
         this->Makefile->GetDefinition("CMAKE_APPLE_ARCH_SYSROOTS")) {
     std::string const& appleArchs =

+ 5 - 0
Source/cmMakefile.cxx

@@ -2493,6 +2493,11 @@ bool cmMakefile::PlatformIsAppleEmbedded() const
   return this->GetAppleSDKType() != AppleSDK::MacOS;
 }
 
+bool cmMakefile::PlatformSupportsAppleTextStubs() const
+{
+  return this->IsOn("APPLE") && this->IsSet("CMAKE_TAPI");
+}
+
 const char* cmMakefile::GetSONameFlag(const std::string& language) const
 {
   std::string name = "CMAKE_SHARED_LIBRARY_SONAME";

+ 4 - 0
Source/cmMakefile.h

@@ -562,6 +562,10 @@ public:
   /** Return whether the target platform is Apple iOS.  */
   bool PlatformIsAppleEmbedded() const;
 
+  /** Return whether the target platform supports generation of text base stubs
+     (.tbd file) describing exports (Apple specific). */
+  bool PlatformSupportsAppleTextStubs() const;
+
   /** Retrieve soname flag for the specified language if supported */
   const char* GetSONameFlag(const std::string& language) const;
 

+ 96 - 6
Source/cmMakefileLibraryTargetGenerator.cxx

@@ -465,9 +465,20 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
   std::string outpathImp;
   if (this->GeneratorTarget->IsFrameworkOnApple()) {
     outpath = this->GeneratorTarget->GetDirectory(this->GetConfigName());
+    cmOSXBundleGenerator::SkipParts bundleSkipParts;
+    if (this->GeneratorTarget->HasImportLibrary(this->GetConfigName())) {
+      bundleSkipParts.TextStubs = false;
+    }
     this->OSXBundleGenerator->CreateFramework(this->TargetNames.Output,
-                                              outpath, this->GetConfigName());
+                                              outpath, this->GetConfigName(),
+                                              bundleSkipParts);
     outpath += '/';
+    if (!this->TargetNames.ImportLibrary.empty()) {
+      outpathImp = this->GeneratorTarget->GetDirectory(
+        this->GetConfigName(), cmStateEnums::ImportLibraryArtifact);
+      cmSystemTools::MakeDirectory(outpathImp);
+      outpathImp += '/';
+    }
   } else if (this->GeneratorTarget->IsCFBundleOnApple()) {
     outpath = this->GeneratorTarget->GetDirectory(this->GetConfigName());
     this->OSXBundleGenerator->CreateCFBundle(this->TargetNames.Output, outpath,
@@ -679,11 +690,12 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
   }
 
   // Expand the rule variables.
+  std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
+    this->LocalGenerator->CreateRulePlaceholderExpander());
+  bool useWatcomQuote =
+    this->Makefile->IsOn(linkRuleVar + "_USE_WATCOM_QUOTE");
   std::vector<std::string> real_link_commands;
   {
-    bool useWatcomQuote =
-      this->Makefile->IsOn(linkRuleVar + "_USE_WATCOM_QUOTE");
-
     // Set path conversion for link script shells.
     this->LocalGenerator->SetLinkScriptShell(useLinkScript);
 
@@ -816,8 +828,6 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
       launcher = cmStrCat(val, ' ');
     }
 
-    std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
-      this->LocalGenerator->CreateRulePlaceholderExpander());
     // Construct the main link rule and expand placeholders.
     rulePlaceholderExpander->SetTargetImpLib(targetOutPathImport);
     if (useArchiveRules) {
@@ -950,6 +960,86 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
   this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends,
                       commands, false);
 
+  // Add rule to generate text-based stubs, if required
+  if (this->GeneratorTarget->IsApple() &&
+      this->GeneratorTarget->HasImportLibrary(this->GetConfigName())) {
+    auto genStubsRule =
+      this->Makefile->GetDefinition("CMAKE_CREATE_TEXT_STUBS");
+    auto genStubs_commands = cmExpandedList(genStubsRule);
+
+    std::string TBDFullPath =
+      cmStrCat(outpathImp, this->TargetNames.ImportOutput);
+    std::string TBDFullPathReal =
+      cmStrCat(outpathImp, this->TargetNames.ImportReal);
+    std::string TBDFullPathSO =
+      cmStrCat(outpathImp, this->TargetNames.ImportLibrary);
+
+    // Expand placeholders.
+    cmRulePlaceholderExpander::RuleVariables vars;
+    std::string target = this->LocalGenerator->ConvertToOutputFormat(
+      this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathReal),
+      cmOutputConverter::SHELL, useWatcomQuote);
+    vars.Target = target.c_str();
+    std::string TBDOutPathReal = this->LocalGenerator->ConvertToOutputFormat(
+      this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPathReal),
+      cmOutputConverter::SHELL, useWatcomQuote);
+    rulePlaceholderExpander->SetTargetImpLib(TBDOutPathReal);
+    for (std::string& command : genStubs_commands) {
+      rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
+                                                   command, vars);
+    }
+    outputs.clear();
+    outputs.push_back(TBDFullPathReal);
+    if (this->TargetNames.ImportLibrary != this->TargetNames.ImportReal) {
+      outputs.push_back(TBDFullPathSO);
+    }
+    if (this->TargetNames.ImportOutput != this->TargetNames.ImportLibrary &&
+        this->TargetNames.ImportOutput != this->TargetNames.ImportReal) {
+      outputs.push_back(TBDFullPath);
+    }
+    this->ExtraFiles.insert(TBDFullPath);
+
+    depends.clear();
+    depends.push_back(targetFullPathReal);
+
+    // Add a rule to create necessary symlinks for the library.
+    // Frameworks are handled by cmOSXBundleGenerator.
+    if (TBDFullPath != TBDFullPathReal &&
+        !this->GeneratorTarget->IsFrameworkOnApple()) {
+      auto TBDOutPathSO = this->LocalGenerator->ConvertToOutputFormat(
+        this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPathSO),
+        cmOutputConverter::SHELL, useWatcomQuote);
+      auto TBDOutPath = this->LocalGenerator->ConvertToOutputFormat(
+        this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPath),
+        cmOutputConverter::SHELL, useWatcomQuote);
+
+      std::string symlink =
+        cmStrCat("$(CMAKE_COMMAND) -E cmake_symlink_library ", TBDOutPathReal,
+                 ' ', TBDOutPathSO, ' ', TBDOutPath);
+      commands1.push_back(std::move(symlink));
+      this->LocalGenerator->CreateCDCommand(
+        commands1, this->Makefile->GetCurrentBinaryDirectory(),
+        this->LocalGenerator->GetBinaryDirectory());
+      cm::append(genStubs_commands, commands1);
+      commands1.clear();
+    }
+
+    this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends,
+                        genStubs_commands, false);
+
+    // clean actions for apple specific outputs
+    // clean actions for ImportLibrary are already specified
+    if (this->TargetNames.ImportReal != this->TargetNames.ImportLibrary) {
+      libCleanFiles.insert(
+        this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPathReal));
+    }
+    if (this->TargetNames.ImportOutput != this->TargetNames.ImportReal &&
+        this->TargetNames.ImportOutput != this->TargetNames.ImportLibrary) {
+      libCleanFiles.insert(
+        this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPath));
+    }
+  }
+
   // Write the main driver rule to build everything in this target.
   this->WriteTargetDriverRule(targetFullPath, relink);
 

+ 102 - 2
Source/cmNinjaNormalTargetGenerator.cxx

@@ -204,6 +204,15 @@ std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaFatbinaryRule(
     '_', config);
 }
 
+std::string cmNinjaNormalTargetGenerator::TextStubsGeneratorRule(
+  const std::string& config) const
+{
+  return cmStrCat(
+    "TEXT_STUBS_GENERATOR__",
+    cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
+    '_', config);
+}
+
 struct cmNinjaRemoveNoOpCommands
 {
   bool operator()(std::string const& cmd)
@@ -527,6 +536,45 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile,
       this->GetGlobalGenerator()->AddRule(rule);
     }
   }
+
+  if (this->GetGeneratorTarget()->IsApple() &&
+      this->GetGeneratorTarget()->HasImportLibrary(config)) {
+    cmNinjaRule rule(this->TextStubsGeneratorRule(config));
+    rule.Comment = cmStrCat("Rule for generating text-based stubs for ",
+                            this->GetVisibleTypeName(), '.');
+    rule.Description = "Creating text-based stubs $out";
+
+    std::string cmd =
+      this->GetMakefile()->GetDefinition("CMAKE_CREATE_TEXT_STUBS");
+    std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
+      this->GetLocalGenerator()->CreateRulePlaceholderExpander());
+    cmRulePlaceholderExpander::RuleVariables vars;
+    vars.Target = "$in";
+    rulePlaceholderExpander->SetTargetImpLib("$out");
+    rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
+                                                 cmd, vars);
+
+    rule.Command =
+      this->GetLocalGenerator()->BuildCommandLine({ cmd }, config, config);
+    this->GetGlobalGenerator()->AddRule(rule);
+
+    if (tgtNames.ImportOutput != tgtNames.ImportReal &&
+        !this->GetGeneratorTarget()->IsFrameworkOnApple()) {
+      cmNinjaRule slRule("CMAKE_SYMLINK_IMPORT_LIBRARY");
+      {
+        std::string cmakeCommand =
+          this->GetLocalGenerator()->ConvertToOutputFormat(
+            cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
+        std::string slCmd =
+          cmStrCat(cmakeCommand, " -E cmake_symlink_library $in $SONAME $out");
+        slRule.Command = this->GetLocalGenerator()->BuildCommandLine(
+          { slCmd }, config, config);
+      }
+      slRule.Description = "Creating import library symlink $out";
+      slRule.Comment = "Rule for creating import library symlink.";
+      this->GetGlobalGenerator()->AddRule(slRule);
+    }
+  }
 }
 
 std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd()
@@ -1030,9 +1078,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
       // the current configuration has a postfix. The non-postfix configuration
       // Info.plist can be used by all the other configurations.
       if (!postFix.empty()) {
-        bundleSkipParts.infoPlist = true;
+        bundleSkipParts.InfoPlist = true;
       }
     }
+    if (gt->HasImportLibrary(config)) {
+      bundleSkipParts.TextStubs = false;
+    }
 
     this->OSXBundleGenerator->CreateFramework(
       tgtNames.Output, gt->GetDirectory(config), config, bundleSkipParts);
@@ -1214,7 +1265,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
 
   cmGlobalNinjaGenerator::CCOutputs byproducts(this->GetGlobalGenerator());
 
-  if (!tgtNames.ImportLibrary.empty()) {
+  if (!gt->IsApple() && !tgtNames.ImportLibrary.empty()) {
     const std::string impLibPath = localGen.ConvertToOutputFormat(
       targetOutputImplib, cmOutputConverter::SHELL);
     vars["TARGET_IMPLIB"] = impLibPath;
@@ -1471,6 +1522,55 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
   // Add aliases for the file name and the target name.
   globalGen->AddTargetAlias(tgtNames.Output, gt, config);
   globalGen->AddTargetAlias(this->GetTargetName(), gt, config);
+
+  if (this->GetGeneratorTarget()->IsApple() &&
+      this->GetGeneratorTarget()->HasImportLibrary(config)) {
+    auto dirTBD =
+      gt->GetDirectory(config, cmStateEnums::ImportLibraryArtifact);
+    auto targetTBD =
+      this->ConvertToNinjaPath(cmStrCat(dirTBD, '/', tgtNames.ImportReal));
+    this->EnsureParentDirectoryExists(targetTBD);
+    cmNinjaBuild build(this->TextStubsGeneratorRule(config));
+    build.Comment = cmStrCat("Generate the text-based stubs file ", targetTBD);
+    build.Outputs.push_back(targetTBD);
+    build.ExplicitDeps.push_back(targetOutputReal);
+    globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build);
+
+    if (tgtNames.ImportOutput != tgtNames.ImportReal &&
+        !this->GetGeneratorTarget()->IsFrameworkOnApple()) {
+      auto outputTBD =
+        this->ConvertToNinjaPath(cmStrCat(dirTBD, '/', tgtNames.ImportOutput));
+      std::string const soNameTBD = this->ConvertToNinjaPath(
+        cmStrCat(dirTBD, '/', tgtNames.ImportLibrary));
+
+      cmNinjaBuild slBuild("CMAKE_SYMLINK_IMPORT_LIBRARY");
+      slBuild.Comment = cmStrCat("Create import library symlink ", outputTBD);
+      cmNinjaVars slVars;
+
+      // If one link has to be created.
+      if (targetTBD == soNameTBD || outputTBD == soNameTBD) {
+        slVars["SONAME"] = this->GetLocalGenerator()->ConvertToOutputFormat(
+          soNameTBD, cmOutputConverter::SHELL);
+      } else {
+        slVars["SONAME"].clear();
+        slBuild.Outputs.push_back(soNameTBD);
+        if (firstForConfig) {
+          globalGen->GetByproductsForCleanTarget(config).push_back(soNameTBD);
+        }
+      }
+      slBuild.Outputs.push_back(outputTBD);
+      if (firstForConfig) {
+        globalGen->GetByproductsForCleanTarget(config).push_back(outputTBD);
+      }
+      slBuild.ExplicitDeps.push_back(targetTBD);
+      slBuild.Variables = std::move(slVars);
+
+      globalGen->WriteBuild(this->GetImplFileStream(fileConfig), slBuild);
+    }
+
+    // Add alias for the import file name
+    globalGen->AddTargetAlias(tgtNames.ImportOutput, gt, config);
+  }
 }
 
 void cmNinjaNormalTargetGenerator::WriteObjectLibStatement(

+ 1 - 0
Source/cmNinjaNormalTargetGenerator.h

@@ -25,6 +25,7 @@ private:
   std::string LanguageLinkerCudaDeviceCompileRule(
     const std::string& config) const;
   std::string LanguageLinkerCudaFatbinaryRule(const std::string& config) const;
+  std::string TextStubsGeneratorRule(const std::string& config) const;
 
   const char* GetVisibleTypeName() const;
   void WriteLanguagesRules(const std::string& config);

+ 13 - 1
Source/cmOSXBundleGenerator.cxx

@@ -11,6 +11,7 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 
 class cmSourceFile;
 
@@ -77,7 +78,7 @@ void cmOSXBundleGenerator::CreateFramework(
   std::string frameworkVersion = this->GT->GetFrameworkVersion();
 
   std::string name = cmSystemTools::GetFilenameName(targetName);
-  if (!skipParts.infoPlist) {
+  if (!skipParts.InfoPlist) {
     // Configure the Info.plist file
     std::string plist = newoutpath;
     if (!this->Makefile->PlatformIsAppleEmbedded()) {
@@ -120,6 +121,17 @@ void cmOSXBundleGenerator::CreateFramework(
   cmSystemTools::CreateSymlink(oldName, newName);
   this->Makefile->AddCMakeOutputFile(newName);
 
+  if (!skipParts.TextStubs) {
+    // foo.tbd -> Versions/Current/foo.tbd
+    cmValue tbdSuffix =
+      this->Makefile->GetDefinition("CMAKE_APPLE_IMPORT_FILE_SUFFIX");
+    oldName = cmStrCat("Versions/Current/", name, tbdSuffix);
+    newName = cmStrCat(contentdir, name, tbdSuffix);
+    cmSystemTools::RemoveFile(newName);
+    cmSystemTools::CreateSymlink(oldName, newName);
+    this->Makefile->AddCMakeOutputFile(newName);
+  }
+
   // Resources -> Versions/Current/Resources
   if (this->MacContentFolders->find("Resources") !=
       this->MacContentFolders->end()) {

+ 5 - 6
Source/cmOSXBundleGenerator.h

@@ -20,11 +20,10 @@ public:
 
   struct SkipParts
   {
-    SkipParts()
-      : infoPlist(false)
-    {
-    }
-    bool infoPlist; // NOLINT(modernize-use-default-member-init)
+    SkipParts() {} // NOLINT(modernize-use-equals-default)
+
+    bool InfoPlist = false;
+    bool TextStubs = true;
   };
 
   // create an app bundle at a given root, and return
@@ -35,7 +34,7 @@ public:
   // create a framework at a given root
   void CreateFramework(const std::string& targetName, const std::string& root,
                        const std::string& config,
-                       const SkipParts& skipParts = SkipParts());
+                       const SkipParts& skipParts = SkipParts{});
 
   // create a cf bundle at a given root
   void CreateCFBundle(const std::string& targetName, const std::string& root,

+ 36 - 3
Source/cmTarget.cxx

@@ -445,7 +445,7 @@ TargetProperty const StaticTargetProperties[] = {
   { "AUTORCC_OPTIONS"_s, IC::CanCompileSources },
 
   // Linking properties
-  { "ENABLE_EXPORTS"_s, IC::ExecutableTarget },
+  { "ENABLE_EXPORTS"_s, IC::TargetWithSymbolExports },
   { "LINK_LIBRARIES_ONLY_TARGETS"_s, IC::NormalNonImportedTarget },
   { "LINK_SEARCH_START_STATIC"_s, IC::CanCompileSources },
   { "LINK_SEARCH_END_STATIC"_s, IC::CanCompileSources },
@@ -1031,6 +1031,31 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
   defKey += "CMAKE_";
   auto initProperty = [this, mf, &defKey](const std::string& property,
                                           const char* default_value) {
+    // special init for ENABLE_EXPORTS
+    // For SHARED_LIBRARY, only CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS variable
+    // is used
+    // For EXECUTABLE, CMAKE_EXECUTABLE_ENABLE_EXPORTS or else
+    // CMAKE_ENABLE_EXPORTS variables are used
+    if (property == "ENABLE_EXPORTS"_s) {
+      // Replace everything after "CMAKE_"
+      defKey.replace(
+        defKey.begin() + 6, defKey.end(),
+        cmStrCat(this->impl->TargetType == cmStateEnums::EXECUTABLE
+                   ? "EXECUTABLE"
+                   : "SHARED_LIBRARY",
+                 '_', property));
+      if (cmValue value = mf->GetDefinition(defKey)) {
+        this->SetProperty(property, value);
+        return;
+      }
+      if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY) {
+        if (default_value) {
+          this->SetProperty(property, default_value);
+        }
+        return;
+      }
+    }
+
     // Replace everything after "CMAKE_"
     defKey.replace(defKey.begin() + 6, defKey.end(), property);
     if (cmValue value = mf->GetDefinition(defKey)) {
@@ -1205,6 +1230,12 @@ bool cmTarget::IsExecutableWithExports() const
           this->GetPropertyAsBool("ENABLE_EXPORTS"));
 }
 
+bool cmTarget::IsSharedLibraryWithExports() const
+{
+  return (this->GetType() == cmStateEnums::SHARED_LIBRARY &&
+          this->GetPropertyAsBool("ENABLE_EXPORTS"));
+}
+
 bool cmTarget::IsFrameworkOnApple() const
 {
   return ((this->GetType() == cmStateEnums::SHARED_LIBRARY ||
@@ -2657,7 +2688,8 @@ const char* cmTarget::GetSuffixVariableInternal(
         case cmStateEnums::RuntimeBinaryArtifact:
           return "CMAKE_SHARED_LIBRARY_SUFFIX";
         case cmStateEnums::ImportLibraryArtifact:
-          return "CMAKE_IMPORT_LIBRARY_SUFFIX";
+          return this->IsApple() ? "CMAKE_APPLE_IMPORT_FILE_SUFFIX"
+                                 : "CMAKE_IMPORT_LIBRARY_SUFFIX";
       }
       break;
     case cmStateEnums::MODULE_LIBRARY:
@@ -2698,7 +2730,8 @@ const char* cmTarget::GetPrefixVariableInternal(
         case cmStateEnums::RuntimeBinaryArtifact:
           return "CMAKE_SHARED_LIBRARY_PREFIX";
         case cmStateEnums::ImportLibraryArtifact:
-          return "CMAKE_IMPORT_LIBRARY_PREFIX";
+          return this->IsApple() ? "CMAKE_APPLE_IMPORT_FILE_PREFIX"
+                                 : "CMAKE_IMPORT_LIBRARY_PREFIX";
       }
       break;
     case cmStateEnums::MODULE_LIBRARY:

+ 4 - 0
Source/cmTarget.h

@@ -221,6 +221,10 @@ public:
   //! Return whether this target is an executable with symbol exports enabled.
   bool IsExecutableWithExports() const;
 
+  //! Return whether this target is a shared library with symbol exports
+  //! enabled.
+  bool IsSharedLibraryWithExports() const;
+
   //! Return whether this target is a shared library Framework on Apple.
   bool IsFrameworkOnApple() const;
 

+ 3 - 0
Tests/RunCMake/AppleTextStubs/CMakeLists.txt

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

+ 12 - 0
Tests/RunCMake/AppleTextStubs/Framework-export.cmake

@@ -0,0 +1,12 @@
+enable_language(C)
+
+add_library(foo SHARED foo.c)
+set_property(TARGET foo PROPERTY FRAMEWORK TRUE)
+set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE)
+set_property(TARGET foo PROPERTY LIBRARY_OUTPUT_DIRECTORY $<CONFIG>)
+
+install(TARGETS foo EXPORT foo FRAMEWORK DESTINATION  DESTINATION "${CMAKE_BINARY_DIR}/$<CONFIG>")
+install(EXPORT foo DESTINATION lib/foo NAMESPACE foo-install::)
+install(FILES foo-config.cmake.in RENAME foo-config.cmake DESTINATION lib/foo)
+
+export(TARGETS foo NAMESPACE foo-build:: FILE Release/foo.cmake)

+ 62 - 0
Tests/RunCMake/AppleTextStubs/Framework-import.cmake

@@ -0,0 +1,62 @@
+enable_language(C)
+
+find_package(foo REQUIRED CONFIG NO_DEFAULT_PATH)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE foo-install::foo)
+
+get_property(is_framework TARGET foo-install::foo PROPERTY FRAMEWORK)
+if (NOT is_framework)
+  message(SEND_ERROR "foo-build::foo: FRAMEWORK not set.")
+endif()
+get_property(enable_exports TARGET foo-install::foo PROPERTY ENABLE_EXPORTS)
+if (CAMKE_TAPI AND NOT enable_exports)
+  message(SEND_ERROR "foo-install::foo: ENABLE_EXPORTS not set.")
+endif()
+
+get_property(implib TARGET foo-install::foo PROPERTY IMPORTED_IMPLIB_RELEASE)
+if (CAMKE_TAPI AND NOT implib)
+  message(SEND_ERROR "foo-install::foo: IMPORTED_IMPLIB_RELEASE not set.")
+endif()
+if (CAMKE_TAPI AND NOT implib MATCHES "foo.framework/Versions/A/foo.tbd$")
+  message(SEND_ERROR "foo-install::foo: ${implib}: wrong value for IMPORTED_IMPLIB_RELEASE.")
+endif()
+
+get_property(location TARGET foo-install::foo PROPERTY IMPORTED_LOCATION_RELEASE)
+if (NOT location)
+  message(SEND_ERROR "foo-install::foo: IMPORTED_LOCATION_RELEASE not set.")
+endif()
+if (NOT location MATCHES "foo.framework/Versions/A/foo$")
+  message(SEND_ERROR "foo-install::foo: ${location}: wrong value for IMPORTED_LOCATION_RELEASE.")
+endif()
+
+
+include(${foo_BUILD}/foo.cmake)
+
+add_executable(main2 main.c)
+target_link_libraries(main2 PRIVATE foo-build::foo)
+
+get_property(is_framework TARGET foo-build::foo PROPERTY FRAMEWORK)
+if (NOT is_framework)
+  message(SEND_ERROR "foo-build::foo: FRAMEWORK not set.")
+endif()
+get_property(enable_exports TARGET foo-build::foo PROPERTY ENABLE_EXPORTS)
+if (CAMKE_TAPI AND NOT enable_exports)
+  message(SEND_ERROR "foo-build::foo: ENABLE_EXPORTS not set.")
+endif()
+
+get_property(implib TARGET foo-build::foo PROPERTY IMPORTED_IMPLIB_RELEASE)
+if (CAMKE_TAPI AND NOT implib)
+  message(SEND_ERROR "foo-build::foo: IMPORTED_IMPLIB_RELEASE not set.")
+endif()
+if (CAMKE_TAPI AND NOT implib STREQUAL "${foo_BUILD}/foo.framework/Versions/A/foo.tbd")
+  message(SEND_ERROR "foo-build::foo: ${implib}: wrong value for IMPORTED_IMPLIB_RELEASE.")
+endif()
+
+get_property(location TARGET foo-build::foo PROPERTY IMPORTED_LOCATION_RELEASE)
+if (NOT location)
+  message(SEND_ERROR "foo-build::foo: IMPORTED_LOCATION_RELEASE not set.")
+endif()
+if (NOT location STREQUAL "${foo_BUILD}/foo.framework/Versions/A/foo")
+  message(SEND_ERROR "foo-build::foo: ${location}: wrong value for IMPORTED_LOCATION_RELEASE.")
+endif()

+ 1 - 0
Tests/RunCMake/AppleTextStubs/Framework-install-check.cmake

@@ -0,0 +1 @@
+include ("${RunCMake_TEST_BINARY_DIR}/Framework-Release-generated.cmake")

+ 59 - 0
Tests/RunCMake/AppleTextStubs/Framework.cmake

@@ -0,0 +1,59 @@
+enable_language(C)
+
+add_library(foo SHARED foo.c)
+set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE)
+set_property(TARGET foo PROPERTY FRAMEWORK TRUE)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE foo)
+
+
+install(TARGETS foo FRAMEWORK DESTINATION "${CMAKE_BINARY_DIR}/INSTALL")
+
+# LIBRARY and ARCHIVE should be ignored
+install(TARGETS foo FRAMEWORK DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2"
+                    LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/lib"
+                    ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/dev")
+
+
+set (GENERATE_CONTENT "if (\"${CMAKE_TAPI}\")
+  set (APPLE_TEXT_STUBS_SUPPORTED TRUE)
+endif()\n\n")
+
+string (APPEND GENERATE_CONTENT [[
+macro (CHECK_FILE test_msg path)
+  if (NOT EXISTS "${path}")
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not found\n")
+  endif()
+endmacro()
+
+macro (CHECK_SYMLINK test_msg path)
+  if(NOT IS_SYMLINK "${path}")
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" is not a symbolic link\n")
+  elseif (NOT EXISTS "${path}")
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" is not a valid symlink\n")
+  endif()
+endmacro()
+
+check_file("DYLIB file" "$<TARGET_FILE:foo>")
+check_symlink("Public DYLIB file" "$<TARGET_LINKER_LIBRARY_FILE:foo>")
+check_file("executable file" "$<TARGET_FILE:main>")
+
+check_file("Installed DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/foo.framework/Versions/A/$<TARGET_FILE_NAME:foo>")
+check_symlink("Installed Public DULIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/foo.framework/$<TARGET_FILE_NAME:foo>")
+check_file("Installed DULIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/foo.framework/Versions/A/$<TARGET_FILE_NAME:foo>")
+check_symlink("Installed Public DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/foo.framework/$<TARGET_FILE_NAME:foo>")
+
+if (APPLE_TEXT_STUBS_SUPPORTED)
+  check_file("TBD file" "$<TARGET_IMPORT_FILE:foo>")
+  check_symlink("Public TBD file" "$<TARGET_LINKER_IMPORT_FILE:foo>")
+
+  check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/foo.framework/Versions/A/$<TARGET_IMPORT_FILE_NAME:foo>")
+  check_symlink("Installed Public TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/foo.framework/$<TARGET_IMPORT_FILE_NAME:foo>")
+  check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/foo.framework/Versions/A/$<TARGET_IMPORT_FILE_NAME:foo>")
+  check_symlink("Installed Public TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/foo.framework/$<TARGET_IMPORT_FILE_NAME:foo>")
+endif()
+]])
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Framework-$<CONFIG>-generated.cmake"
+  CONTENT "${GENERATE_CONTENT}")

+ 12 - 0
Tests/RunCMake/AppleTextStubs/Library-export.cmake

@@ -0,0 +1,12 @@
+enable_language(C)
+
+add_library(foo SHARED foo.c)
+set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE)
+set_property(TARGET foo PROPERTY LIBRARY_OUTPUT_DIRECTORY $<CONFIG>)
+set_property(TARGET foo PROPERTY ARCHIVE_OUTPUT_DIRECTORY $<CONFIG>)
+
+install(TARGETS foo EXPORT foo DESTINATION "${CMAKE_BINARY_DIR}/$<CONFIG>")
+install(EXPORT foo DESTINATION lib/foo NAMESPACE foo-install::)
+install(FILES foo-config.cmake.in RENAME foo-config.cmake DESTINATION lib/foo)
+
+export(TARGETS foo NAMESPACE foo-build:: FILE Release/foo.cmake)

+ 54 - 0
Tests/RunCMake/AppleTextStubs/Library-import.cmake

@@ -0,0 +1,54 @@
+enable_language(C)
+
+find_package(foo REQUIRED CONFIG NO_DEFAULT_PATH)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE foo-install::foo)
+
+get_property(enable_exports TARGET foo-install::foo PROPERTY ENABLE_EXPORTS)
+if (CMAKE_TAPI AND NOT enable_exports)
+  message(SEND_ERROR "foo-install::foo: ENABLE_EXPORTS not set.")
+endif()
+
+get_property(implib TARGET foo-install::foo PROPERTY IMPORTED_IMPLIB_RELEASE)
+if (CMAKE_TAPI AND NOT implib)
+  message(SEND_ERROR "foo-install::foo: IMPORTED_IMPLIB_RELEASE not set.")
+endif()
+if (CMAKE_TAPI AND NOT implib MATCHES "Release/libfoo.tbd$")
+  message(SEND_ERROR "foo-install::foo: ${implib}: wrong value for IMPORTED_IMPLIB_RELEASE.")
+endif()
+
+get_property(location TARGET foo-install::foo PROPERTY IMPORTED_LOCATION_RELEASE)
+if (NOT location)
+  message(SEND_ERROR "foo-install::foo: IMPORTED_LOCATION_RELEASE not set.")
+endif()
+if (NOT location MATCHES "Release/libfoo.dylib$")
+  message(SEND_ERROR "foo-install::foo: ${location}: wrong value for IMPORTED_LOCATION_RELEASE.")
+endif()
+
+
+include(${foo_BUILD}/foo.cmake)
+
+add_executable(main2 main.c)
+target_link_libraries(main2 PRIVATE foo-build::foo)
+
+get_property(enable_exports TARGET foo-build::foo PROPERTY ENABLE_EXPORTS)
+if (CMAKE_TAPI AND NOT enable_exports)
+  message(SEND_ERROR "foo-build::foo: ENABLE_EXPORTS not set.")
+endif()
+
+get_property(implib TARGET foo-build::foo PROPERTY IMPORTED_IMPLIB_RELEASE)
+if (CMAKE_TAPI AND NOT implib)
+  message(SEND_ERROR "foo-build::foo: IMPORTED_IMPLIB_RELEASE not set.")
+endif()
+if (CMAKE_TAPI AND NOT implib STREQUAL "${foo_BUILD}/libfoo.tbd")
+  message(SEND_ERROR "foo-build::foo: ${implib}: wrong value for IMPORTED_IMPLIB_RELEASE.")
+endif()
+
+get_property(location TARGET foo-build::foo PROPERTY IMPORTED_LOCATION_RELEASE)
+if (NOT location)
+  message(SEND_ERROR "foo-build::foo: IMPORTED_LOCATION_RELEASE not set.")
+endif()
+if (NOT location STREQUAL "${foo_BUILD}/libfoo.dylib")
+  message(SEND_ERROR "foo-build::foo: ${location}: wrong value for IMPORTED_LOCATION_RELEASE.")
+endif()

+ 1 - 0
Tests/RunCMake/AppleTextStubs/LibraryWithOutputs-install-check.cmake

@@ -0,0 +1 @@
+include ("${RunCMake_TEST_BINARY_DIR}/LibraryWithOutputs-Release-generated.cmake")

+ 52 - 0
Tests/RunCMake/AppleTextStubs/LibraryWithOutputs.cmake

@@ -0,0 +1,52 @@
+enable_language(C)
+
+add_library(foo SHARED foo.c)
+set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE)
+set_property(TARGET foo PROPERTY ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/TBD/$<CONFIG>")
+set_property(TARGET foo PROPERTY ARCHIVE_OUTPUT_NAME "tbd")
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE foo)
+
+
+set (GENERATE_CONTENT "if (\"${CMAKE_TAPI}\")
+  set (APPLE_TEXT_STUBS_SUPPORTED TRUE)
+endif()\n\n")
+
+string (APPEND GENERATE_CONTENT [[
+macro (CHECK_FILE test_msg path)
+  if (NOT EXISTS "${path}")
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not found\n")
+  endif()
+endmacro()
+
+check_file("DYLIB file" "$<TARGET_FILE:foo>")
+check_file("executable file" "$<TARGET_FILE:main>")
+
+if (APPLE_TEXT_STUBS_SUPPORTED)
+  check_file("TBD file" "$<TARGET_IMPORT_FILE:foo>")
+]])
+
+if (CMAKE_GENERATOR STREQUAL "Xcode")
+  # ARCHIVE outputs are ignored by this generator
+  string (APPEND GENERATE_CONTENT
+    "\n  if (NOT \"$<TARGET_IMPORT_FILE_DIR:foo>\" STREQUAL \"${CMAKE_BINARY_DIR}/$<CONFIG>\")
+    string (APPEND RunCMake_TEST_FAILED \"Wrong directory for TBD file: \\\"$<TARGET_IMPORT_FILE_DIR:foo>\\\"\n\")
+  endif()
+  if (NOT \"$<TARGET_IMPORT_FILE_BASE_NAME:foo>\" STREQUAL \"foo\")
+    string (APPEND RunCMake_TEST_FAILED \"Wrong base name for TBD file: \\\"$<TARGET_IMPORT_FILE_BASE_NAME:foo>\\\"\n\")
+  endif()\n")
+else()
+  string (APPEND GENERATE_CONTENT
+    "\n  if (NOT \"$<TARGET_IMPORT_FILE_DIR:foo>\" STREQUAL \"${CMAKE_BINARY_DIR}/TBD/$<CONFIG>\")
+    string (APPEND RunCMake_TEST_FAILED \"Wrong directory for TBD file: \\\"$<TARGET_IMPORT_FILE_DIR:foo>\\\"\n\")
+  endif()
+  if (NOT \"$<TARGET_IMPORT_FILE_BASE_NAME:foo>\" STREQUAL \"tbd\")
+    string (APPEND RunCMake_TEST_FAILED \"Wrong base name for TBD file: \\\"$<TARGET_IMPORT_FILE_BASE_NAME:foo>\\\"\n\")
+  endif()\n")
+endif()
+string (APPEND GENERATE_CONTENT "endif()\n")
+
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/LibraryWithOutputs-$<CONFIG>-generated.cmake"
+  CONTENT "${GENERATE_CONTENT}")

+ 1 - 0
Tests/RunCMake/AppleTextStubs/LibraryWithVersions-install-check.cmake

@@ -0,0 +1 @@
+include ("${RunCMake_TEST_BINARY_DIR}/LibraryWithVersions-Release-generated.cmake")

+ 96 - 0
Tests/RunCMake/AppleTextStubs/LibraryWithVersions.cmake

@@ -0,0 +1,96 @@
+enable_language(C)
+
+add_library(foo SHARED foo.c)
+set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE)
+set_property (TARGET foo PROPERTY VERSION 2.5.0)
+set_property (TARGET foo PROPERTY SOVERSION 2.0.0)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE foo)
+
+
+install(TARGETS foo DESTINATION "${CMAKE_BINARY_DIR}/INSTALL" COMPONENT default)
+
+install(TARGETS foo ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/dev1" NAMELINK_SKIP COMPONENT default)
+install(TARGETS foo ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/dev2" NAMELINK_ONLY COMPONENT default)
+
+install(TARGETS foo ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL3"
+                            COMPONENT lib3 NAMELINK_COMPONENT dev3)
+install(TARGETS foo ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL4"
+                            COMPONENT lib4 NAMELINK_COMPONENT dev4)
+
+
+set (GENERATE_CONTENT "if (\"${CMAKE_TAPI}\")
+  set (APPLE_TEXT_STUBS_SUPPORTED TRUE)
+endif()\n\n")
+
+string (APPEND GENERATE_CONTENT [[
+cmake_policy (SET CMP0011 NEW)
+cmake_policy (SET CMP0057 NEW)
+
+macro (CHECK_FILE test_msg path)
+  if (NOT EXISTS "${path}")
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not found\n")
+  endif()
+endmacro()
+
+macro (CHECK_SYMLINK test_msg path)
+  if (NOT IS_SYMLINK "${path}")
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" is not a symbolic link\n")
+  elseif (NOT EXISTS "${path}")
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not a valid symlink\n")
+  endif()
+endmacro()
+
+macro (CHECK_NOFILE test_msg path)
+  if (EXISTS "${path}")
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" was found\n")
+  endif()
+endmacro()
+
+macro (CHECK_INSTALLED test_msg dir file)
+  file(GLOB installed_files LIST_DIRECTORIES FALSE RELATIVE "${dir}" "${dir}/*")
+  if (NOT "${file}" IN_LIST installed_files)
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${dir}/${file}\" not found\n")
+  endif()
+endmacro()
+
+
+check_file("DYLIB file" "$<TARGET_FILE:foo>")
+check_symlink("Linkable DYLIB file" "$<TARGET_LINKER_LIBRARY_FILE:foo>")
+check_symlink("SONAME DYLIB file" "$<TARGET_SONAME_FILE:foo>")
+check_file("executable file" "$<TARGET_FILE:main>")
+
+check_file("Installed DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_FILE_NAME:foo>")
+check_symlink("Installed Linkable DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_LINKER_LIBRARY_FILE_NAME:foo>")
+check_symlink("Installed SONAME DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_SONAME_FILE_NAME:foo>")
+
+if (APPLE_TEXT_STUBS_SUPPORTED)
+  check_file("TBD file" "$<TARGET_IMPORT_FILE:foo>")
+  check_symlink("Linkable TBD file" "$<TARGET_LINKER_IMPORT_FILE:foo>")
+  check_symlink("SONAME TBD file" "$<TARGET_SONAME_IMPORT_FILE:foo>")
+
+  check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_IMPORT_FILE_NAME:foo>")
+  check_symlink("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_LINKER_IMPORT_FILE_NAME:foo>")
+  check_symlink("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>")
+
+  check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev1/$<TARGET_IMPORT_FILE_NAME:foo>")
+  check_symlink("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev1/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>")
+  check_nofile("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev1/$<TARGET_LINKER_IMPORT_FILE_NAME:foo>")
+
+  check_installed("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev2" "$<TARGET_LINKER_IMPORT_FILE_NAME:foo>")
+  check_nofile("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev2/$<TARGET_IMPORT_FILE_NAME:foo>")
+  check_nofile("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev2/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>")
+
+  check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL3/$<TARGET_IMPORT_FILE_NAME:foo>")
+  check_symlink("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL3/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>")
+  check_nofile("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL3/$<TARGET_LINKER_IMPORT_FILE_NAME:foo>")
+
+  check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL4/$<TARGET_IMPORT_FILE_NAME:foo>")
+  check_symlink("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL4/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>")
+  check_symlink("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL4/$<TARGET_LINKER_IMPORT_FILE_NAME:foo>")
+endif()
+]])
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/LibraryWithVersions-$<CONFIG>-generated.cmake"
+  CONTENT "${GENERATE_CONTENT}")

+ 58 - 0
Tests/RunCMake/AppleTextStubs/RunCMakeTest.cmake

@@ -0,0 +1,58 @@
+include(RunCMake)
+
+function(build_project test)
+  if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
+  endif()
+  run_cmake(${test})
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+
+  run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release)
+  if ("${ARGC}" GREATER "1")
+    # custom install step
+    cmake_language(CALL ${ARGV1})
+  else()
+    run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --config Release)
+  endif()
+endfunction()
+
+build_project(Simple)
+build_project(Framework)
+build_project(LibraryWithOutputs)
+
+
+function(LibraryWithVersions-install)
+  run_cmake_command(LibraryWithVersions-install-component-lib3 ${CMAKE_COMMAND} --install . --config Release --component lib3)
+  run_cmake_command(LibraryWithVersions-install-component-lib4 ${CMAKE_COMMAND} --install . --config Release --component lib4)
+  run_cmake_command(LibraryWithVersions-install-components-dev4 ${CMAKE_COMMAND} --install . --config Release --component dev4)
+  run_cmake_command(LibraryWithVersions-install ${CMAKE_COMMAND} --install . --config Release  --component default)
+endfunction()
+
+build_project(LibraryWithVersions LibraryWithVersions-install)
+
+
+function(build_ExportImport_project test)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-export-build)
+  set(CMAKE_INSTALL_PREFIX ${RunCMake_TEST_BINARY_DIR}/root)
+  if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
+  endif()
+  run_cmake(${test}-export)
+  unset(RunCMake_TEST_OPTIONS)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${test}-export-build ${CMAKE_COMMAND} --build . --config Release)
+  run_cmake_command(${test}-export-install ${CMAKE_COMMAND} --install . --prefix ${CMAKE_INSTALL_PREFIX} --config Release)
+
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-import-build)
+  set (foo_BUILD "${RunCMake_BINARY_DIR}/${test}-export-build")
+  if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    string (APPEND foo_BUILD "/Release")
+  endif()
+  run_cmake_with_options(${test}-import -Dfoo_DIR=${CMAKE_INSTALL_PREFIX}/lib/foo
+                                        -Dfoo_BUILD=${RunCMake_BINARY_DIR}/${test}-export-build/Release)
+  run_cmake_command(${test}-import-build ${CMAKE_COMMAND} --build . --config Release)
+endfunction()
+
+build_ExportImport_project(Library)
+build_ExportImport_project(Framework)

+ 1 - 0
Tests/RunCMake/AppleTextStubs/Simple-install-check.cmake

@@ -0,0 +1 @@
+include ("${RunCMake_TEST_BINARY_DIR}/Simple-Release-generated.cmake")

+ 41 - 0
Tests/RunCMake/AppleTextStubs/Simple.cmake

@@ -0,0 +1,41 @@
+enable_language(C)
+
+add_library(foo SHARED foo.c)
+set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE foo)
+
+
+install(TARGETS foo DESTINATION "${CMAKE_BINARY_DIR}/INSTALL")
+
+install(TARGETS foo LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/lib"
+                    ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/dev")
+
+
+set (GENERATE_CONTENT "if (\"${CMAKE_TAPI}\")
+  set (APPLE_TEXT_STUBS_SUPPORTED TRUE)
+endif()\n\n")
+
+string (APPEND GENERATE_CONTENT [[
+macro (CHECK_FILE test_msg path)
+  if (NOT EXISTS "${path}")
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not found\n")
+  endif()
+endmacro()
+
+check_file("DYLIB file" "$<TARGET_FILE:foo>")
+check_file("executable file" "$<TARGET_FILE:main>")
+
+check_file("Installed DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/lib/$<TARGET_FILE_NAME:foo>")
+
+if (APPLE_TEXT_STUBS_SUPPORTED)
+  check_file("TBD file" "$<TARGET_IMPORT_FILE:foo>")
+
+  check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_IMPORT_FILE_NAME:foo>")
+  check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev/$<TARGET_IMPORT_FILE_NAME:foo>")
+endif()
+]])
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Simple-$<CONFIG>-generated.cmake"
+  CONTENT "${GENERATE_CONTENT}")

+ 1 - 0
Tests/RunCMake/AppleTextStubs/foo-config.cmake.in

@@ -0,0 +1 @@
+include(${CMAKE_CURRENT_LIST_DIR}/foo.cmake)

+ 5 - 0
Tests/RunCMake/AppleTextStubs/foo.c

@@ -0,0 +1,5 @@
+
+int foo()
+{
+  return 0;
+}

+ 7 - 0
Tests/RunCMake/AppleTextStubs/main.c

@@ -0,0 +1,7 @@
+
+extern int foo(void);
+
+int main()
+{
+  return foo();
+}

+ 2 - 0
Tests/RunCMake/CMakeLists.txt

@@ -357,6 +357,7 @@ add_RunCMake_test(GenEx-DEVICE_LINK)
 add_RunCMake_test(GenEx-LINK_LIBRARY)
 add_RunCMake_test(GenEx-LINK_GROUP)
 add_RunCMake_test(GenEx-TARGET_FILE -DLINKER_SUPPORTS_PDB=${LINKER_SUPPORTS_PDB})
+add_RunCMake_test(GenEx-TARGET_IMPORT_FILE)
 add_RunCMake_test(GenEx-GENEX_EVAL)
 add_RunCMake_test(GenEx-TARGET_PROPERTY)
 add_RunCMake_test(GenEx-TARGET_RUNTIME_DLLS)
@@ -515,6 +516,7 @@ add_RunCMake_test(BundleUtilities)
 if(APPLE)
   add_RunCMake_test(INSTALL_NAME_DIR)
   add_RunCMake_test(MacOSVersions)
+  add_RunCMake_test(AppleTextStubs)
 endif()
 
 function(add_RunCMake_test_try_compile)

+ 3 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/CMakeLists.txt

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

+ 21 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/RunCMakeTest.cmake

@@ -0,0 +1,21 @@
+include(RunCMake)
+
+cmake_policy(SET CMP0057 NEW)
+
+function(run_cmake_with_config test)
+  if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
+  endif()
+  run_cmake(${test})
+endfunction()
+
+run_cmake(TARGET_LINKER_IMPORT_FILE-non-valid-target)
+run_cmake(TARGET_LINKER_LIBRARY_FILE-non-valid-target)
+run_cmake_with_config(TARGET_IMPORT_FILE)
+run_cmake_with_config(TARGET_IMPORT_FILE_SUFFIX)
+
+set (Windows_platforms Windows CYGWIN MSYS)
+if (NOT CMAKE_HOST_SYSTEM_NAME IN_LIST Windows_platforms)
+  run_cmake(TARGET_SONAME_IMPORT_FILE-non-valid-target)
+  run_cmake_with_config(TARGET_SONAME_IMPORT_FILE)
+endif()

+ 1 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE-check.cmake

@@ -0,0 +1 @@
+include ("${RunCMake_TEST_BINARY_DIR}/TARGET_IMPORT_FILE-Release-generated.cmake")

+ 47 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE.cmake

@@ -0,0 +1,47 @@
+enable_language(C)
+
+set (platforms_with_import Windows CYGWIN MSYS)
+
+set (GENERATE_CONTENT [[
+macro (CHECK_VALUE test_msg value expected)
+  if (NOT "${value}" STREQUAL "${expected}")
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
+  endif()
+endmacro()
+]])
+
+add_library (shared1 SHARED empty.c)
+add_library (static1 STATIC empty.c)
+add_executable (exec1 empty.c)
+
+
+string (APPEND GENERATE_CONTENT
+"\ncheck_value (\"TARGET_IMPORT_FILE shared library\" \"$<TARGET_IMPORT_FILE:shared1>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE:shared1>,>\")
+check_value (\"TARGET_LINKER_FILE shared library\" \"$<TARGET_LINKER_FILE:shared1>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE:shared1>,$<TARGET_LINKER_LIBRARY_FILE:shared1>>\")
+check_value (\"TARGET_IMPORT_FILE static library\" \"$<TARGET_IMPORT_FILE:static1>\" \"\")
+check_value (\"TARGET_IMPORT_FILE executable\" \"$<TARGET_IMPORT_FILE:exec1>\" \"\")\n")
+
+
+set(lib_with_import ${platforms_with_import})
+set(exec_with_import ${platforms_with_import})
+if (APPLE AND CMAKE_TAPI)
+  list(APPEND lib_with_import Darwin)
+endif()
+if (CMAKE_SYSTEM_NAME STREQUAL "AIX")
+  list(APPEND exec_with_import "AIX")
+endif()
+set(CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS TRUE)
+set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE)
+
+add_library (shared2 SHARED empty.c)
+add_executable (exec2 empty.c)
+
+
+string (APPEND GENERATE_CONTENT
+"\ncheck_value (\"TARGET_IMPORT_FILE shared library\" \"$<TARGET_IMPORT_FILE:shared2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${lib_with_import}>,$<TARGET_LINKER_IMPORT_FILE:shared2>,>\")
+check_value (\"TARGET_LINKER_FILE shared library\" \"$<TARGET_LINKER_FILE:shared2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${lib_with_import}>,$<TARGET_LINKER_IMPORT_FILE:shared2>,$<TARGET_LINKER_LIBRARY_FILE:shared2>>\")
+check_value (\"TARGET_IMPORT_FILE executable\" \"$<TARGET_IMPORT_FILE:exec2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${exec_with_import}>,$<TARGET_LINKER_IMPORT_FILE:exec2>,>\")\n")
+
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_IMPORT_FILE-$<CONFIG>-generated.cmake"
+  CONTENT "${GENERATE_CONTENT}")

+ 1 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX-check.cmake

@@ -0,0 +1 @@
+include ("${RunCMake_TEST_BINARY_DIR}/TARGET_IMPORT_FILE_SUFFIX-Release-generated.cmake")

+ 44 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX.cmake

@@ -0,0 +1,44 @@
+enable_language (C)
+
+set (platforms_with_import Windows CYGWIN MSYS)
+
+set (GENERATE_CONTENT [[
+macro (CHECK_VALUE test_msg value expected)
+  if (NOT "${value}" STREQUAL "${expected}")
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
+  endif()
+endmacro()
+]])
+
+add_library (shared1 SHARED empty.c)
+add_library (static1 STATIC empty.c)
+add_executable (exec1 empty.c)
+
+string (APPEND GENERATE_CONTENT
+"\ncheck_value (\"TARGET_IMPORT_FILE_SUFFIX executable default\" \"$<TARGET_IMPORT_FILE_SUFFIX:exec1>\" \"\")
+check_value (\"TARGET_IMPORT_FILE_SUFFIX shared default\" \"$<TARGET_IMPORT_FILE_SUFFIX:shared1>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE_SUFFIX:shared1>,>\")
+check_value (\"TARGET_FILE_SUFFIX static default\" \"$<TARGET_IMPORT_FILE_SUFFIX:static1>\" \"\")
+check_value (\"TARGET_IMPORT_FILE_SUFFIX executable default\" \"$<TARGET_IMPORT_FILE_SUFFIX:exec1>\" \"\")\n")
+
+
+
+if (APPLE AND CMAKE_TAPI)
+  list(APPEND platforms_with_import Darwin)
+endif()
+if (CMAKE_SYSTEM_NAME STREQUAL "AIX")
+  list(APPEND platforms_with_import AIX)
+endif()
+set(CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS TRUE)
+set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE)
+
+add_library (shared2 SHARED empty.c)
+add_executable (exec2 empty.c)
+
+string (APPEND GENERATE_CONTENT
+"\ncheck_value (\"TARGET_IMPORT_FILE_SUFFIX executable default\" \"$<TARGET_IMPORT_FILE_SUFFIX:exec2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE_SUFFIX:exec2>,>\")
+check_value (\"TARGET_IMPORT_FILE_SUFFIX shared default\" \"$<TARGET_IMPORT_FILE_SUFFIX:shared2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE_SUFFIX:shared2>,>\")\n")
+
+
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_IMPORT_FILE_SUFFIX-$<CONFIG>-generated.cmake"
+  CONTENT "${GENERATE_CONTENT}")

+ 1 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-result.txt

@@ -0,0 +1 @@
+1

+ 9 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at TARGET_LINKER_IMPORT_FILE-non-valid-target.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<TARGET_LINKER_IMPORT_FILE:exe1>
+
+  TARGET_LINKER_IMPORT_FILE is allowed only for libraries and executables
+  with ENABLE_EXPORTS.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 9 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target.cmake

@@ -0,0 +1,9 @@
+
+enable_language(C)
+
+add_executable(exe1 empty.c)
+
+file(GENERATE
+  OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
+  CONTENT "[$<TARGET_LINKER_IMPORT_FILE:exe1>]"
+)

+ 1 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-result.txt

@@ -0,0 +1 @@
+1

+ 9 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at TARGET_LINKER_LIBRARY_FILE-non-valid-target.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<TARGET_LINKER_LIBRARY_FILE:exe1>
+
+  TARGET_LINKER_LIBRARY_FILE is allowed only for libraries with
+  ENABLE_EXPORTS.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 9 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target.cmake

@@ -0,0 +1,9 @@
+
+enable_language(C)
+
+add_executable(exe1 empty.c)
+
+file(GENERATE
+  OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
+  CONTENT "[$<TARGET_LINKER_LIBRARY_FILE:exe1>]"
+)

+ 1 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-check.cmake

@@ -0,0 +1 @@
+include ("${RunCMake_TEST_BINARY_DIR}/TARGET_SONAME_IMPORT_FILE-Release-generated.cmake")

+ 1 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-result.txt

@@ -0,0 +1 @@
+1

+ 8 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at TARGET_SONAME_IMPORT_FILE-non-valid-target.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<TARGET_SONAME_IMPORT_FILE:static1>
+
+  TARGET_SONAME_IMPORT_FILE is allowed only for SHARED libraries.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 8 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target.cmake

@@ -0,0 +1,8 @@
+enable_language(C)
+
+add_library (static1 STATIC empty.c)
+set_property (TARGET static1 PROPERTY VERSION 2.5.0)
+set_property (TARGET static1 PROPERTY SOVERSION 2.0.0)
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
+  CONTENT "[$<TARGET_SONAME_IMPORT_FILE:static1>]")

+ 32 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE.cmake

@@ -0,0 +1,32 @@
+enable_language(C)
+
+set (GENERATE_CONTENT [[
+macro (CHECK_VALUE test_msg value expected)
+  if (NOT "${value}" STREQUAL "${expected}")
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
+  endif()
+endmacro()
+]])
+
+add_library (shared1 SHARED empty.c)
+set_property (TARGET shared1 PROPERTY VERSION 2.5.0)
+set_property (TARGET shared1 PROPERTY SOVERSION 2.0.0)
+
+
+string (APPEND GENERATE_CONTENT
+"\ncheck_value (\"TARGET_SONAME_IMPORT_FILE shared library\" \"$<TARGET_SONAME_IMPORT_FILE:shared1>\" \"\")\n")
+
+
+
+add_library (shared2 SHARED empty.c)
+set_property(TARGET shared2 PROPERTY ENABLE_EXPORTS ON)
+set_property (TARGET shared2 PROPERTY VERSION 2.5.0)
+set_property (TARGET shared2 PROPERTY SOVERSION 2.0.0)
+
+
+string (APPEND GENERATE_CONTENT
+"\ncheck_value (\"TARGET_SONAME_IMPORT_FILE shared library\" \"$<TARGET_SONAME_IMPORT_FILE:shared2>\" \"$<$<BOOL:${CMAKE_TAPI}>:$<PATH:REPLACE_EXTENSION,LAST_ONLY,$<TARGET_SONAME_FILE:shared2>,.tbd>>\")\n")
+
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_SONAME_IMPORT_FILE-$<CONFIG>-generated.cmake"
+  CONTENT "${GENERATE_CONTENT}")

+ 0 - 0
Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/empty.c


+ 3 - 2
Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-all-stderr.txt

@@ -1,5 +1,6 @@
 ^CMake Error at TARGETS-NAMELINK_COMPONENT-bad-all\.cmake:5 \(install\):
-  install TARGETS given NAMELINK_COMPONENT option not in LIBRARY group\.  The
-  NAMELINK_COMPONENT option may be specified only following LIBRARY\.
+  install TARGETS given NAMELINK_COMPONENT option not in LIBRARY or ARCHIVE
+  group\.  The NAMELINK_COMPONENT option may be specified only following
+  LIBRARY or ARCHIVE\.
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)$

+ 3 - 2
Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-exc-stderr.txt

@@ -1,5 +1,6 @@
 ^CMake Error at TARGETS-NAMELINK_COMPONENT-bad-exc\.cmake:5 \(install\):
-  install TARGETS given NAMELINK_COMPONENT option not in LIBRARY group\.  The
-  NAMELINK_COMPONENT option may be specified only following LIBRARY\.
+  install TARGETS given NAMELINK_COMPONENT option not in LIBRARY or ARCHIVE
+  group\.  The NAMELINK_COMPONENT option may be specified only following
+  LIBRARY or ARCHIVE\.
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)$