浏览代码

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

Fixes: #24123
Marc Chevrier 2 年之前
父节点
当前提交
ede33f30cf
共有 84 个文件被更改,包括 2054 次插入168 次删除
  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``);
     ``.lib``, in contrast to the ``.dll`` libraries that go to ``RUNTIME``);
   * On AIX, the *linker import file* created for executables with
   * On AIX, the *linker import file* created for executables with
     :prop_tgt:`ENABLE_EXPORTS` enabled.
     :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``
 ``LIBRARY``
   Target artifacts of this kind include:
   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
   value of ``COMPONENT``. It is an error to use this parameter outside of a
   ``LIBRARY`` block.
   ``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:
   Consider the following example:
 
 
   .. code-block:: cmake
   .. 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
   option installs nothing. It is an error to use this parameter outside of a
   ``LIBRARY`` block.
   ``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
   When ``NAMELINK_ONLY`` is given, either ``NAMELINK_COMPONENT`` or
   ``COMPONENT`` may be used to specify the installation component of the
   ``COMPONENT`` may be used to specify the installation component of the
   namelink, but ``COMPONENT`` should generally be preferred.
   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
   installs the library. It is an error to use this parameter outside of a
   ``LIBRARY`` block.
   ``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
   If ``NAMELINK_SKIP`` is specified, ``NAMELINK_COMPONENT`` has no effect. It
   is not recommended to use ``NAMELINK_SKIP`` in conjunction with
   is not recommended to use ``NAMELINK_SKIP`` in conjunction with
   ``NAMELINK_COMPONENT``.
   ``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
   created by the :command:`add_executable` command when its
   :prop_tgt:`ENABLE_EXPORTS` target property is set.
   :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`
 The :prop_tgt:`ARCHIVE_OUTPUT_DIRECTORY` and :prop_tgt:`ARCHIVE_OUTPUT_NAME`
 target properties may be used to control archive output artifact locations
 target properties may be used to control archive output artifact locations
 and names in the build tree.
 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
   Note that ``tgt`` is not added as a dependency of the target this
   expression is evaluated on (see policy :policy:`CMP0112`).
   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>
 .. genex:: $<TARGET_LINKER_FILE:tgt>
 
 
   File used when linking to the ``tgt`` target.  This will usually
   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``
   but for a shared library on DLL platforms, it would be the ``.lib``
   import library associated with the DLL.
   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>
 .. genex:: $<TARGET_LINKER_FILE_BASE_NAME:tgt>
 
 
   .. versionadded:: 3.15
   .. versionadded:: 3.15
 
 
   Base name of file used to link the target ``tgt``, i.e.
   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`,
   See also the :prop_tgt:`OUTPUT_NAME`, :prop_tgt:`ARCHIVE_OUTPUT_NAME`,
   and :prop_tgt:`LIBRARY_OUTPUT_NAME` target properties and their configuration
   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
   Note that ``tgt`` is not added as a dependency of the target this
   expression is evaluated on (see policy :policy:`CMP0112`).
   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>
 .. genex:: $<TARGET_SONAME_FILE:tgt>
 
 
   File with soname (``.so.3``) where ``tgt`` is the name of a target.
   File with soname (``.so.3``) where ``tgt`` is the name of a target.
+
 .. genex:: $<TARGET_SONAME_FILE_NAME:tgt>
 .. genex:: $<TARGET_SONAME_FILE_NAME:tgt>
 
 
   Name of file with soname (``.so.3``).
   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>
 .. 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
   Note that ``tgt`` is not added as a dependency of the target this
   expression is evaluated on (see policy :policy:`CMP0112`).
   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>
 .. genex:: $<TARGET_PDB_FILE:tgt>
 
 
   .. versionadded:: 3.1
   .. 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_DEPENDS_USE_COMPILER
    /variable/CMAKE_DISABLE_PRECOMPILE_HEADERS
    /variable/CMAKE_DISABLE_PRECOMPILE_HEADERS
    /variable/CMAKE_ENABLE_EXPORTS
    /variable/CMAKE_ENABLE_EXPORTS
+   /variable/CMAKE_EXECUTABLE_ENABLE_EXPORTS
    /variable/CMAKE_EXE_LINKER_FLAGS
    /variable/CMAKE_EXE_LINKER_FLAGS
    /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG
    /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG
    /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG_INIT
    /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG_INIT
@@ -507,6 +508,7 @@ Variables that Control the Build
    /variable/CMAKE_POSITION_INDEPENDENT_CODE
    /variable/CMAKE_POSITION_INDEPENDENT_CODE
    /variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY
    /variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY
    /variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY_CONFIG
    /variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY_CONFIG
+   /variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS
    /variable/CMAKE_SHARED_LINKER_FLAGS
    /variable/CMAKE_SHARED_LINKER_FLAGS
    /variable/CMAKE_SHARED_LINKER_FLAGS_CONFIG
    /variable/CMAKE_SHARED_LINKER_FLAGS_CONFIG
    /variable/CMAKE_SHARED_LINKER_FLAGS_CONFIG_INIT
    /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`
 .. |CMAKE_XXX_OUTPUT_DIRECTORY| replace:: :variable:`CMAKE_ARCHIVE_OUTPUT_DIRECTORY`
 .. include:: XXX_OUTPUT_DIRECTORY.txt
 .. 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.
 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
 .. |xxx| replace:: archive
 .. include:: XXX_OUTPUT_NAME.txt
 .. 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.
 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
 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
 Normally an executable does not export any symbols because it is the
 final program.  It is possible for an executable to export symbols to
 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.
   automatically bind symbols when the module is loaded.
 
 
 This property is initialized by the value of the variable
 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.
 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`
 To get the location of an imported target read one of the :prop_tgt:`LOCATION`
 or ``LOCATION_<CONFIG>`` properties.
 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`.
 :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
 This variable is used to initialize the :prop_tgt:`ENABLE_EXPORTS` target
 property for executable targets when they are created by calls to the
 property for executable targets when they are created by calls to the
 :command:`add_executable` command.  See the property documentation for details.
 :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_ASM@ASM_DIALECT@_COMPILER_RANLIB "@_CMAKE_ASM_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LOADED 1)
 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_ID "@_CMAKE_ASM_COMPILER_ID@")
 set(CMAKE_ASM@ASM_DIALECT@_COMPILER_VERSION "@_CMAKE_ASM_COMPILER_VERSION@")
 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_C_COMPILER_RANLIB "@CMAKE_C_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUCC @CMAKE_COMPILER_IS_GNUCC@)
 set(CMAKE_COMPILER_IS_GNUCC @CMAKE_COMPILER_IS_GNUCC@)
 set(CMAKE_C_COMPILER_LOADED 1)
 set(CMAKE_C_COMPILER_LOADED 1)
 set(CMAKE_C_COMPILER_WORKS @CMAKE_C_COMPILER_WORKS@)
 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_CXX_COMPILER_RANLIB "@CMAKE_CXX_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUCXX @CMAKE_COMPILER_IS_GNUCXX@)
 set(CMAKE_COMPILER_IS_GNUCXX @CMAKE_COMPILER_IS_GNUCXX@)
 set(CMAKE_CXX_COMPILER_LOADED 1)
 set(CMAKE_CXX_COMPILER_LOADED 1)
 set(CMAKE_CXX_COMPILER_WORKS @CMAKE_CXX_COMPILER_WORKS@)
 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_READELF_NAMES "readelf")
   set(_CMAKE_DLLTOOL_NAMES "dlltool")
   set(_CMAKE_DLLTOOL_NAMES "dlltool")
   set(_CMAKE_ADDR2LINE_NAMES "addr2line")
   set(_CMAKE_ADDR2LINE_NAMES "addr2line")
+  set(_CMAKE_TAPI_NAMES "tapi")
 
 
   # Prepend toolchain-specific names.
   # Prepend toolchain-specific names.
   if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL Clang)
   if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL Clang)
@@ -201,7 +202,7 @@ else()
     list(PREPEND _CMAKE_LINKER_NAMES "armlink")
     list(PREPEND _CMAKE_LINKER_NAMES "armlink")
   endif()
   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()
 endif()
 
 
 foreach(_CMAKE_TOOL IN LISTS _CMAKE_TOOL_VARS)
 foreach(_CMAKE_TOOL IN LISTS _CMAKE_TOOL_VARS)
@@ -225,6 +226,20 @@ if(NOT CMAKE_RANLIB)
     set(CMAKE_RANLIB : CACHE INTERNAL "noop for ranlib")
     set(CMAKE_RANLIB : CACHE INTERNAL "noop for ranlib")
 endif()
 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)
 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)
   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_AR "@CMAKE_AR@")
 set(CMAKE_Fortran_COMPILER_AR "@CMAKE_Fortran_COMPILER_AR@")
 set(CMAKE_Fortran_COMPILER_AR "@CMAKE_Fortran_COMPILER_AR@")
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_Fortran_COMPILER_RANLIB "@CMAKE_Fortran_COMPILER_RANLIB@")
 set(CMAKE_Fortran_COMPILER_RANLIB "@CMAKE_Fortran_COMPILER_RANLIB@")
 set(CMAKE_COMPILER_IS_GNUG77 @CMAKE_COMPILER_IS_GNUG77@)
 set(CMAKE_COMPILER_IS_GNUG77 @CMAKE_COMPILER_IS_GNUG77@)
 set(CMAKE_Fortran_COMPILER_LOADED 1)
 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_HIP_COMPILER_RANLIB "@CMAKE_HIP_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_MT "@CMAKE_MT@")
 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_OBJC_COMPILER_RANLIB "@CMAKE_OBJC_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUOBJC @CMAKE_COMPILER_IS_GNUOBJC@)
 set(CMAKE_COMPILER_IS_GNUOBJC @CMAKE_COMPILER_IS_GNUOBJC@)
 set(CMAKE_OBJC_COMPILER_LOADED 1)
 set(CMAKE_OBJC_COMPILER_LOADED 1)
 set(CMAKE_OBJC_COMPILER_WORKS @CMAKE_OBJC_COMPILER_WORKS@)
 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_OBJCXX_COMPILER_RANLIB "@CMAKE_OBJCXX_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUOBJCXX @CMAKE_COMPILER_IS_GNUOBJCXX@)
 set(CMAKE_COMPILER_IS_GNUOBJCXX @CMAKE_COMPILER_IS_GNUOBJCXX@)
 set(CMAKE_OBJCXX_COMPILER_LOADED 1)
 set(CMAKE_OBJCXX_COMPILER_LOADED 1)
 set(CMAKE_OBJCXX_COMPILER_WORKS @CMAKE_OBJCXX_COMPILER_WORKS@)
 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_EXTRA_SHARED_LIBRARY_SUFFIXES ".tbd" ".so")
 set(CMAKE_SHARED_MODULE_PREFIX "lib")
 set(CMAKE_SHARED_MODULE_PREFIX "lib")
 set(CMAKE_SHARED_MODULE_SUFFIX ".so")
 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_MODULE_EXISTS 1)
 set(CMAKE_DL_LIBS "")
 set(CMAKE_DL_LIBS "")
 if(NOT "${_CURRENT_OSX_VERSION}" VERSION_LESS "10.5")
 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)
   set(CMAKE_${lang}_FRAMEWORK_SEARCH_FLAG -F)
 endforeach()
 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
 # Defines LINK_LIBRARY features for frameworks
 set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK "LINKER:-framework,<LIBRARY>")
 set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK "LINKER:-framework,<LIBRARY>")
 set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)
 set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)

+ 0 - 1
Source/cmCommonTargetGenerator.cxx

@@ -25,7 +25,6 @@
 #include "cmState.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringAlgorithms.h"
-#include "cmTarget.h"
 #include "cmValue.h"
 #include "cmValue.h"
 
 
 cmCommonTargetGenerator::cmCommonTargetGenerator(cmGeneratorTarget* gt)
 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_ARCHITECTURES = "CMAKE_CUDA_ARCHITECTURES";
 std::string const kCMAKE_CUDA_RUNTIME_LIBRARY = "CMAKE_CUDA_RUNTIME_LIBRARY";
 std::string const kCMAKE_CUDA_RUNTIME_LIBRARY = "CMAKE_CUDA_RUNTIME_LIBRARY";
 std::string const kCMAKE_ENABLE_EXPORTS = "CMAKE_ENABLE_EXPORTS";
 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_ARCHITECTURES = "CMAKE_HIP_ARCHITECTURES";
 std::string const kCMAKE_HIP_RUNTIME_LIBRARY = "CMAKE_HIP_RUNTIME_LIBRARY";
 std::string const kCMAKE_HIP_RUNTIME_LIBRARY = "CMAKE_HIP_RUNTIME_LIBRARY";
 std::string const kCMAKE_ISPC_INSTRUCTION_SETS = "CMAKE_ISPC_INSTRUCTION_SETS";
 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_ARCHITECTURES);
     vars.insert(kCMAKE_CUDA_RUNTIME_LIBRARY);
     vars.insert(kCMAKE_CUDA_RUNTIME_LIBRARY);
     vars.insert(kCMAKE_ENABLE_EXPORTS);
     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_ARCHITECTURES);
     vars.insert(kCMAKE_HIP_RUNTIME_LIBRARY);
     vars.insert(kCMAKE_HIP_RUNTIME_LIBRARY);
     vars.insert(kCMAKE_ISPC_INSTRUCTION_SETS);
     vars.insert(kCMAKE_ISPC_INSTRUCTION_SETS);

+ 1 - 1
Source/cmExportBuildFileGenerator.cxx

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

+ 2 - 1
Source/cmExportFileGenerator.cxx

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

+ 14 - 1
Source/cmExportInstallFileGenerator.cxx

@@ -409,7 +409,7 @@ void cmExportInstallFileGenerator::SetImportLocationProperty(
 
 
     // Append the installed file name.
     // Append the installed file name.
     value += cmInstallTargetGenerator::GetInstallFilename(
     value += cmInstallTargetGenerator::GetInstallFilename(
-      target, config, cmInstallTargetGenerator::NameImplib);
+      target, config, cmInstallTargetGenerator::NameImplibReal);
 
 
     // Store the property.
     // Store the property.
     properties[prop] = value;
     properties[prop] = value;
@@ -430,6 +430,19 @@ void cmExportInstallFileGenerator::SetImportLocationProperty(
     properties[prop] = cmJoin(objects, ";");
     properties[prop] = cmJoin(objects, ";");
     importedLocations.insert(prop);
     importedLocations.insert(prop);
   } else {
   } 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.
     // Construct the property name.
     std::string prop = cmStrCat("IMPORTED_LOCATION", suffix);
     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 ArtifactDirTag;
 class ArtifactLinkerTag;
 class ArtifactLinkerTag;
+class ArtifactLinkerLibraryTag;
+class ArtifactLinkerImportTag;
 class ArtifactNameTag;
 class ArtifactNameTag;
+class ArtifactImportTag;
 class ArtifactPathTag;
 class ArtifactPathTag;
 class ArtifactPdbTag;
 class ArtifactPdbTag;
 class ArtifactSonameTag;
 class ArtifactSonameTag;
+class ArtifactSonameImportTag;
 class ArtifactBundleDirTag;
 class ArtifactBundleDirTag;
 class ArtifactBundleDirNameTag;
 class ArtifactBundleDirNameTag;
 class ArtifactBundleContentDirTag;
 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 <>
 template <>
 struct TargetFilesystemArtifactResultCreator<ArtifactPdbTag>
 struct TargetFilesystemArtifactResultCreator<ArtifactPdbTag>
 {
 {
@@ -2817,7 +2853,8 @@ struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag>
                             cmGeneratorExpressionContext* context,
                             cmGeneratorExpressionContext* context,
                             const GeneratorExpressionContent* content)
                             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()) {
     if (!target->IsLinkable()) {
       ::reportError(context, content->GetOriginalExpression(),
       ::reportError(context, content->GetOriginalExpression(),
                     "TARGET_LINKER_FILE is allowed only for libraries and "
                     "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 <>
 template <>
 struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirTag>
 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>
 template <typename ArtifactT>
 struct TargetFilesystemArtifactResultGetter
 struct TargetFilesystemArtifactResultGetter
 {
 {
@@ -3054,12 +3155,24 @@ struct TargetFilesystemArtifactNodeGroup
 static const TargetFilesystemArtifactNodeGroup<ArtifactNameTag>
 static const TargetFilesystemArtifactNodeGroup<ArtifactNameTag>
   targetNodeGroup;
   targetNodeGroup;
 
 
+static const TargetFilesystemArtifactNodeGroup<ArtifactImportTag>
+  targetImportNodeGroup;
+
 static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerTag>
 static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerTag>
   targetLinkerNodeGroup;
   targetLinkerNodeGroup;
 
 
+static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerLibraryTag>
+  targetLinkerLibraryNodeGroup;
+
+static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerImportTag>
+  targetLinkerImportNodeGroup;
+
 static const TargetFilesystemArtifactNodeGroup<ArtifactSonameTag>
 static const TargetFilesystemArtifactNodeGroup<ArtifactSonameTag>
   targetSoNameNodeGroup;
   targetSoNameNodeGroup;
 
 
+static const TargetFilesystemArtifactNodeGroup<ArtifactSonameImportTag>
+  targetSoNameImportNodeGroup;
+
 static const TargetFilesystemArtifactNodeGroup<ArtifactPdbTag>
 static const TargetFilesystemArtifactNodeGroup<ArtifactPdbTag>
   targetPdbNodeGroup;
   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 <>
 template <>
 struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag>
 struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag>
 {
 {
@@ -3105,7 +3234,8 @@ struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag>
                          cmGeneratorExpressionContext* context,
                          cmGeneratorExpressionContext* context,
                          const GeneratorExpressionContent* content)
                          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()) {
     if (!target->IsLinkable()) {
       ::reportError(context, content->GetOriginalExpression(),
       ::reportError(context, content->GetOriginalExpression(),
                     "TARGET_LINKER_FILE_BASE_NAME is allowed only for "
                     "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 <>
 template <>
 struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag>
 struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag>
 {
 {
@@ -3192,15 +3372,27 @@ struct TargetFileBaseNameArtifact : public TargetArtifactBase
 
 
 static const TargetFileBaseNameArtifact<ArtifactNameTag>
 static const TargetFileBaseNameArtifact<ArtifactNameTag>
   targetFileBaseNameNode;
   targetFileBaseNameNode;
+static const TargetFileBaseNameArtifact<ArtifactImportTag>
+  targetImportFileBaseNameNode;
 static const TargetFileBaseNameArtifact<ArtifactLinkerTag>
 static const TargetFileBaseNameArtifact<ArtifactLinkerTag>
   targetLinkerFileBaseNameNode;
   targetLinkerFileBaseNameNode;
+static const TargetFileBaseNameArtifact<ArtifactLinkerLibraryTag>
+  targetLinkerLibraryFileBaseNameNode;
+static const TargetFileBaseNameArtifact<ArtifactLinkerImportTag>
+  targetLinkerImportFileBaseNameNode;
 static const TargetFileBaseNameArtifact<ArtifactPdbTag>
 static const TargetFileBaseNameArtifact<ArtifactPdbTag>
   targetPdbFileBaseNameNode;
   targetPdbFileBaseNameNode;
 
 
 class ArtifactFilePrefixTag;
 class ArtifactFilePrefixTag;
+class ArtifactImportFilePrefixTag;
 class ArtifactLinkerFilePrefixTag;
 class ArtifactLinkerFilePrefixTag;
+class ArtifactLinkerLibraryFilePrefixTag;
+class ArtifactLinkerImportFilePrefixTag;
 class ArtifactFileSuffixTag;
 class ArtifactFileSuffixTag;
+class ArtifactImportFileSuffixTag;
 class ArtifactLinkerFileSuffixTag;
 class ArtifactLinkerFileSuffixTag;
+class ArtifactLinkerLibraryFileSuffixTag;
+class ArtifactLinkerImportFileSuffixTag;
 
 
 template <typename ArtifactT>
 template <typename ArtifactT>
 struct TargetFileArtifactResultGetter
 struct TargetFileArtifactResultGetter
@@ -3221,6 +3413,20 @@ struct TargetFileArtifactResultGetter<ArtifactFilePrefixTag>
   }
   }
 };
 };
 template <>
 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>
 struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag>
 {
 {
   static std::string Get(cmGeneratorTarget* target,
   static std::string Get(cmGeneratorTarget* target,
@@ -3228,9 +3434,10 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag>
                          const GeneratorExpressionContent* content)
                          const GeneratorExpressionContent* content)
   {
   {
     if (!target->IsLinkable()) {
     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();
       return std::string();
     }
     }
 
 
@@ -3243,6 +3450,52 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag>
   }
   }
 };
 };
 template <>
 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>
 struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag>
 {
 {
   static std::string Get(cmGeneratorTarget* target,
   static std::string Get(cmGeneratorTarget* target,
@@ -3253,6 +3506,20 @@ struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag>
   }
   }
 };
 };
 template <>
 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>
 struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag>
 {
 {
   static std::string Get(cmGeneratorTarget* target,
   static std::string Get(cmGeneratorTarget* target,
@@ -3260,9 +3527,10 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag>
                          const GeneratorExpressionContent* content)
                          const GeneratorExpressionContent* content)
   {
   {
     if (!target->IsLinkable()) {
     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();
       return std::string();
     }
     }
 
 
@@ -3274,6 +3542,51 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag>
     return target->GetFileSuffix(context->Config, artifact);
     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>
 template <typename ArtifactT>
 struct TargetFileArtifact : public TargetArtifactBase
 struct TargetFileArtifact : public TargetArtifactBase
@@ -3304,11 +3617,23 @@ struct TargetFileArtifact : public TargetArtifactBase
 };
 };
 
 
 static const TargetFileArtifact<ArtifactFilePrefixTag> targetFilePrefixNode;
 static const TargetFileArtifact<ArtifactFilePrefixTag> targetFilePrefixNode;
+static const TargetFileArtifact<ArtifactImportFilePrefixTag>
+  targetImportFilePrefixNode;
 static const TargetFileArtifact<ArtifactLinkerFilePrefixTag>
 static const TargetFileArtifact<ArtifactLinkerFilePrefixTag>
   targetLinkerFilePrefixNode;
   targetLinkerFilePrefixNode;
+static const TargetFileArtifact<ArtifactLinkerLibraryFilePrefixTag>
+  targetLinkerLibraryFilePrefixNode;
+static const TargetFileArtifact<ArtifactLinkerImportFilePrefixTag>
+  targetLinkerImportFilePrefixNode;
 static const TargetFileArtifact<ArtifactFileSuffixTag> targetFileSuffixNode;
 static const TargetFileArtifact<ArtifactFileSuffixTag> targetFileSuffixNode;
+static const TargetFileArtifact<ArtifactImportFileSuffixTag>
+  targetImportFileSuffixNode;
 static const TargetFileArtifact<ArtifactLinkerFileSuffixTag>
 static const TargetFileArtifact<ArtifactLinkerFileSuffixTag>
   targetLinkerFileSuffixNode;
   targetLinkerFileSuffixNode;
+static const TargetFileArtifact<ArtifactLinkerLibraryFileSuffixTag>
+  targetLinkerLibraryFileSuffixNode;
+static const TargetFileArtifact<ArtifactLinkerImportFileSuffixTag>
+  targetLinkerImportFileSuffixNode;
 
 
 static const struct ShellPathNode : public cmGeneratorExpressionNode
 static const struct ShellPathNode : public cmGeneratorExpressionNode
 {
 {
@@ -3376,23 +3701,52 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
     { "CONFIGURATION", &configurationNode },
     { "CONFIGURATION", &configurationNode },
     { "CONFIG", &configurationTestNode },
     { "CONFIG", &configurationTestNode },
     { "TARGET_FILE", &targetNodeGroup.File },
     { "TARGET_FILE", &targetNodeGroup.File },
+    { "TARGET_IMPORT_FILE", &targetImportNodeGroup.File },
     { "TARGET_LINKER_FILE", &targetLinkerNodeGroup.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_FILE", &targetSoNameNodeGroup.File },
+    { "TARGET_SONAME_IMPORT_FILE", &targetSoNameImportNodeGroup.File },
     { "TARGET_PDB_FILE", &targetPdbNodeGroup.File },
     { "TARGET_PDB_FILE", &targetPdbNodeGroup.File },
     { "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode },
     { "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode },
+    { "TARGET_IMPORT_FILE_BASE_NAME", &targetImportFileBaseNameNode },
     { "TARGET_LINKER_FILE_BASE_NAME", &targetLinkerFileBaseNameNode },
     { "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_PDB_FILE_BASE_NAME", &targetPdbFileBaseNameNode },
     { "TARGET_FILE_PREFIX", &targetFilePrefixNode },
     { "TARGET_FILE_PREFIX", &targetFilePrefixNode },
+    { "TARGET_IMPORT_FILE_PREFIX", &targetImportFilePrefixNode },
     { "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode },
     { "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode },
+    { "TARGET_LINKER_LIBRARY_FILE_PREFIX",
+      &targetLinkerLibraryFilePrefixNode },
+    { "TARGET_LINKER_IMPORT_FILE_PREFIX", &targetLinkerImportFilePrefixNode },
     { "TARGET_FILE_SUFFIX", &targetFileSuffixNode },
     { "TARGET_FILE_SUFFIX", &targetFileSuffixNode },
+    { "TARGET_IMPORT_FILE_SUFFIX", &targetImportFileSuffixNode },
     { "TARGET_LINKER_FILE_SUFFIX", &targetLinkerFileSuffixNode },
     { "TARGET_LINKER_FILE_SUFFIX", &targetLinkerFileSuffixNode },
+    { "TARGET_LINKER_LIBRARY_FILE_SUFFIX",
+      &targetLinkerLibraryFileSuffixNode },
+    { "TARGET_LINKER_IMPORT_FILE_SUFFIX", &targetLinkerImportFileSuffixNode },
     { "TARGET_FILE_NAME", &targetNodeGroup.FileName },
     { "TARGET_FILE_NAME", &targetNodeGroup.FileName },
+    { "TARGET_IMPORT_FILE_NAME", &targetImportNodeGroup.FileName },
     { "TARGET_LINKER_FILE_NAME", &targetLinkerNodeGroup.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_FILE_NAME", &targetSoNameNodeGroup.FileName },
+    { "TARGET_SONAME_IMPORT_FILE_NAME",
+      &targetSoNameImportNodeGroup.FileName },
     { "TARGET_PDB_FILE_NAME", &targetPdbNodeGroup.FileName },
     { "TARGET_PDB_FILE_NAME", &targetPdbNodeGroup.FileName },
     { "TARGET_FILE_DIR", &targetNodeGroup.FileDir },
     { "TARGET_FILE_DIR", &targetNodeGroup.FileDir },
+    { "TARGET_IMPORT_FILE_DIR", &targetImportNodeGroup.FileDir },
     { "TARGET_LINKER_FILE_DIR", &targetLinkerNodeGroup.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_FILE_DIR", &targetSoNameNodeGroup.FileDir },
+    { "TARGET_SONAME_IMPORT_FILE_DIR", &targetSoNameImportNodeGroup.FileDir },
     { "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir },
     { "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir },
     { "TARGET_BUNDLE_DIR", &targetBundleDirNode },
     { "TARGET_BUNDLE_DIR", &targetBundleDirNode },
     { "TARGET_BUNDLE_DIR_NAME", &targetBundleDirNameNode },
     { "TARGET_BUNDLE_DIR_NAME", &targetBundleDirNameNode },

+ 108 - 30
Source/cmGeneratorTarget.cxx

@@ -458,6 +458,11 @@ std::string const& cmGeneratorTarget::GetSafeProperty(
 const char* cmGeneratorTarget::GetOutputTargetType(
 const char* cmGeneratorTarget::GetOutputTargetType(
   cmStateEnums::ArtifactType artifact) const
   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()) {
   switch (this->GetType()) {
     case cmStateEnums::SHARED_LIBRARY:
     case cmStateEnums::SHARED_LIBRARY:
       if (this->IsDLLPlatform()) {
       if (this->IsDLLPlatform()) {
@@ -470,9 +475,15 @@ const char* cmGeneratorTarget::GetOutputTargetType(
             return "ARCHIVE";
             return "ARCHIVE";
         }
         }
       } else {
       } 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;
       break;
     case cmStateEnums::STATIC_LIBRARY:
     case cmStateEnums::STATIC_LIBRARY:
@@ -2518,7 +2529,8 @@ bool cmGeneratorTarget::CanGenerateInstallNameDir(
   return !skip;
   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()) {
   if (this->IsImported()) {
     // Lookup the imported soname.
     // Lookup the imported soname.
@@ -2546,7 +2558,9 @@ std::string cmGeneratorTarget::GetSOName(const std::string& config) const
     return "";
     return "";
   }
   }
   // Compute the soname that will be built.
   // 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 {
 namespace {
@@ -5088,10 +5102,18 @@ void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const
     f = cmStrCat(dir, '/', targetNames.PDB);
     f = cmStrCat(dir, '/', targetNames.PDB);
     gg->AddToManifest(f);
     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()) {
   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);
     gg->AddToManifest(f);
   }
   }
 }
 }
@@ -5211,14 +5233,20 @@ std::string cmGeneratorTarget::NormalGetFullPath(
       }
       }
       break;
       break;
     case cmStateEnums::ImportLibraryArtifact:
     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;
       break;
   }
   }
   return fpath;
   return fpath;
 }
 }
 
 
 std::string cmGeneratorTarget::NormalGetRealName(
 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.
   // This should not be called for imported targets.
   // TODO: Split cmTarget into a class hierarchy to get compile-time
   // 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);
     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.
   // 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(
 cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames(
@@ -5279,17 +5308,16 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames(
   // The library name.
   // The library name.
   targetNames.Base = components.base;
   targetNames.Base = components.base;
   targetNames.Output =
   targetNames.Output =
-    components.prefix + targetNames.Base + components.suffix;
+    cmStrCat(components.prefix, targetNames.Base, components.suffix);
 
 
   if (this->IsFrameworkOnApple()) {
   if (this->IsFrameworkOnApple()) {
     targetNames.Real = components.prefix;
     targetNames.Real = components.prefix;
     if (!this->Makefile->PlatformIsAppleEmbedded()) {
     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 {
   } else {
     // The library's soname.
     // The library's soname.
     this->ComputeVersionedName(targetNames.SharedObject, components.prefix,
     this->ComputeVersionedName(targetNames.SharedObject, components.prefix,
@@ -5302,11 +5330,36 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames(
                                targetNames.Output, version);
                                targetNames.Output, version);
   }
   }
 
 
-  // The import library name.
+  // The import library names.
   if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
   if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
       this->GetType() == cmStateEnums::MODULE_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.
   // The program database file name.
@@ -5368,6 +5421,8 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetExecutableNames(
   // The import library name.
   // The import library name.
   targetNames.ImportLibrary =
   targetNames.ImportLibrary =
     this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact);
     this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact);
+  targetNames.ImportReal = targetNames.ImportLibrary;
+  targetNames.ImportOutput = targetNames.ImportLibrary;
 
 
   // The program database file name.
   // The program database file name.
   targetNames.PDB = this->GetPDBName(config);
   targetNames.PDB = this->GetPDBName(config);
@@ -5449,15 +5504,18 @@ cmGeneratorTarget::GetFullNameInternalComponents(
   }
   }
 
 
   // Compute the full name for main target types.
   // 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;
   std::string fw_prefix;
   if (this->IsFrameworkOnApple()) {
   if (this->IsFrameworkOnApple()) {
     fw_prefix =
     fw_prefix =
       cmStrCat(this->GetFrameworkDirectory(config, ContentLevel), '/');
       cmStrCat(this->GetFrameworkDirectory(config, ContentLevel), '/');
     targetPrefix = cmValue(fw_prefix);
     targetPrefix = cmValue(fw_prefix);
-    targetSuffix = nullptr;
+    if (!isImportedLibraryArtifact) {
+      // no suffix
+      targetSuffix = nullptr;
+    }
   }
   }
 
 
   if (this->IsCFBundleOnApple()) {
   if (this->IsCFBundleOnApple()) {
@@ -5476,8 +5534,8 @@ cmGeneratorTarget::GetFullNameInternalComponents(
   // When using Xcode, the postfix should be part of the suffix rather than
   // 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
   // the base, because the suffix ends up being used in Xcode's
   // EXECUTABLE_SUFFIX attribute.
   // EXECUTABLE_SUFFIX attribute.
-  if (this->IsFrameworkOnApple() &&
-      this->GetGlobalGenerator()->GetName() == "Xcode") {
+  if (this->IsFrameworkOnApple() && this->GetGlobalGenerator()->IsXcode()) {
+    configPostfix += *targetSuffix;
     targetSuffix = cmValue(configPostfix);
     targetSuffix = cmValue(configPostfix);
   } else {
   } else {
     outBase += configPostfix;
     outBase += configPostfix;
@@ -8544,15 +8602,35 @@ bool cmGeneratorTarget::IsExecutableWithExports() const
   return this->Target->IsExecutableWithExports();
   return this->Target->IsExecutableWithExports();
 }
 }
 
 
+bool cmGeneratorTarget::IsSharedLibraryWithExports() const
+{
+  return this->Target->IsSharedLibraryWithExports();
+}
+
 bool cmGeneratorTarget::HasImportLibrary(std::string const& config) const
 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() &&
   return (this->IsDLLPlatform() &&
           (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
           (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
            this->IsExecutableWithExports()) &&
            this->IsExecutableWithExports()) &&
           // Assemblies which have only managed code do not have
           // Assemblies which have only managed code do not have
           // import libraries.
           // import libraries.
           this->GetManagedType(config) != ManagedType::Managed) ||
           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
 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,
   std::string NormalGetFullPath(const std::string& config,
                                 cmStateEnums::ArtifactType artifact,
                                 cmStateEnums::ArtifactType artifact,
                                 bool realname) const;
                                 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
   /** Get the names of an object library's object files underneath
       its object file directory.  */
       its object file directory.  */
@@ -348,7 +350,9 @@ public:
   const std::string* GetExportMacro() const;
   const std::string* GetExportMacro() const;
 
 
   /** Get the soname of the target.  Allowed only for a shared library.  */
   /** 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
   struct NameComponents
   {
   {
@@ -740,6 +744,8 @@ public:
     std::string Base;
     std::string Base;
     std::string Output;
     std::string Output;
     std::string Real;
     std::string Real;
+    std::string ImportOutput;
+    std::string ImportReal;
     std::string ImportLibrary;
     std::string ImportLibrary;
     std::string PDB;
     std::string PDB;
     std::string SharedObject;
     std::string SharedObject;
@@ -786,6 +792,10 @@ public:
 
 
   bool IsExecutableWithExports() const;
   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.  */
   /** Return whether or not the target has a DLL import library.  */
   bool HasImportLibrary(std::string const& config) const;
   bool HasImportLibrary(std::string const& config) const;
 
 

+ 4 - 0
Source/cmGlobalNinjaGenerator.cxx

@@ -1270,6 +1270,10 @@ void cmGlobalNinjaGenerator::AppendTargetOutputs(
     }
     }
       CM_FALLTHROUGH;
       CM_FALLTHROUGH;
     case cmStateEnums::EXECUTABLE: {
     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(
       outputs.push_back(this->ConvertToNinjaPath(target->GetFullPath(
         config, cmStateEnums::RuntimeBinaryArtifact, realname)));
         config, cmStateEnums::RuntimeBinaryArtifact, realname)));
       break;
       break;

+ 28 - 1
Source/cmGlobalXCodeGenerator.cxx

@@ -1739,7 +1739,7 @@ void cmGlobalXCodeGenerator::CreateCustomCommands(
     std::string str_so_file =
     std::string str_so_file =
       cmStrCat("$<TARGET_SONAME_FILE:", gtgt->GetName(), '>');
       cmStrCat("$<TARGET_SONAME_FILE:", gtgt->GetName(), '>');
     std::string str_link_file =
     std::string str_link_file =
-      cmStrCat("$<TARGET_LINKER_FILE:", gtgt->GetName(), '>');
+      cmStrCat("$<TARGET_LINKER_LIBRARY_FILE:", gtgt->GetName(), '>');
     cmCustomCommandLines cmd = cmMakeSingleCommandLine(
     cmCustomCommandLines cmd = cmMakeSingleCommandLine(
       { cmSystemTools::GetCMakeCommand(), "-E", "cmake_symlink_library",
       { cmSystemTools::GetCMakeCommand(), "-E", "cmake_symlink_library",
         str_file, str_so_file, str_link_file });
         str_file, str_so_file, str_link_file });
@@ -1754,6 +1754,27 @@ void cmGlobalXCodeGenerator::CreateCustomCommands(
     postbuild.push_back(std::move(command));
     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* legacyCustomCommandsBuildPhase = nullptr;
   cmXCodeObject* preBuildPhase = nullptr;
   cmXCodeObject* preBuildPhase = nullptr;
   cmXCodeObject* preLinkPhase = nullptr;
   cmXCodeObject* preLinkPhase = nullptr;
@@ -2682,6 +2703,12 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
 
 
       buildSettings->AddAttribute("LIBRARY_STYLE",
       buildSettings->AddAttribute("LIBRARY_STYLE",
                                   this->CreateString("DYNAMIC"));
                                   this->CreateString("DYNAMIC"));
+
+      if (gtgt->HasImportLibrary(configName)) {
+        // Request .tbd file generation
+        buildSettings->AddAttribute("GENERATE_TEXT_BASED_STUBS",
+                                    this->CreateString("YES"));
+      }
       break;
       break;
     }
     }
     case cmStateEnums::EXECUTABLE: {
     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
   // Enforce argument rules too complex to specify for the
   // general-purpose parser.
   // 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() ||
       publicHeaderArgs.GetNamelinkOnly() || resourceArgs.GetNamelinkOnly() ||
       std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
       std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
                   [](const cmInstallCommandFileSetArguments& fileSetArg)
                   [](const cmInstallCommandFileSetArguments& fileSetArg)
                     -> bool { return fileSetArg.GetNamelinkOnly(); }) ||
                     -> bool { return fileSetArg.GetNamelinkOnly(); }) ||
       cxxModuleBmiArgs.GetNamelinkOnly()) {
       cxxModuleBmiArgs.GetNamelinkOnly()) {
     status.SetError(
     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;
     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() ||
       publicHeaderArgs.GetNamelinkSkip() || resourceArgs.GetNamelinkSkip() ||
       std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
       std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
                   [](const cmInstallCommandFileSetArguments& fileSetArg)
                   [](const cmInstallCommandFileSetArguments& fileSetArg)
                     -> bool { return fileSetArg.GetNamelinkSkip(); }) ||
                     -> bool { return fileSetArg.GetNamelinkSkip(); }) ||
       cxxModuleBmiArgs.GetNamelinkSkip()) {
       cxxModuleBmiArgs.GetNamelinkSkip()) {
     status.SetError(
     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;
     return false;
   }
   }
-  if (archiveArgs.HasNamelinkComponent() ||
-      runtimeArgs.HasNamelinkComponent() ||
+  if (runtimeArgs.HasNamelinkComponent() ||
       objectArgs.HasNamelinkComponent() ||
       objectArgs.HasNamelinkComponent() ||
       frameworkArgs.HasNamelinkComponent() ||
       frameworkArgs.HasNamelinkComponent() ||
       bundleArgs.HasNamelinkComponent() ||
       bundleArgs.HasNamelinkComponent() ||
@@ -592,9 +593,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
                     -> bool { return fileSetArg.HasNamelinkComponent(); }) ||
                     -> bool { return fileSetArg.HasNamelinkComponent(); }) ||
       cxxModuleBmiArgs.HasNamelinkComponent()) {
       cxxModuleBmiArgs.HasNamelinkComponent()) {
     status.SetError(
     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;
     return false;
   }
   }
   if (libraryArgs.GetNamelinkOnly() && libraryArgs.GetNamelinkSkip()) {
   if (libraryArgs.GetNamelinkOnly() && libraryArgs.GetNamelinkSkip()) {
@@ -674,6 +675,14 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   } else if (libraryArgs.GetNamelinkSkip()) {
   } else if (libraryArgs.GetNamelinkSkip()) {
     namelinkMode = cmInstallTargetGenerator::NamelinkModeSkip;
     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.
   // Check if there is something to do.
   if (targetList.empty()) {
   if (targetList.empty()) {
@@ -725,6 +734,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   bool installsArchive = false;
   bool installsArchive = false;
   bool installsLibrary = false;
   bool installsLibrary = false;
   bool installsNamelink = false;
   bool installsNamelink = false;
+  bool installsImportlink = false;
   bool installsRuntime = false;
   bool installsRuntime = false;
   bool installsObject = false;
   bool installsObject = false;
   bool installsFramework = 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> archiveGenerator;
     std::unique_ptr<cmInstallTargetGenerator> libraryGenerator;
     std::unique_ptr<cmInstallTargetGenerator> libraryGenerator;
     std::unique_ptr<cmInstallTargetGenerator> namelinkGenerator;
     std::unique_ptr<cmInstallTargetGenerator> namelinkGenerator;
+    std::unique_ptr<cmInstallTargetGenerator> importlinkGenerator;
     std::unique_ptr<cmInstallTargetGenerator> runtimeGenerator;
     std::unique_ptr<cmInstallTargetGenerator> runtimeGenerator;
     std::unique_ptr<cmInstallTargetGenerator> objectGenerator;
     std::unique_ptr<cmInstallTargetGenerator> objectGenerator;
     std::unique_ptr<cmInstallTargetGenerator> frameworkGenerator;
     std::unique_ptr<cmInstallTargetGenerator> frameworkGenerator;
@@ -885,6 +896,32 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
             }
             }
             namelinkOnly =
             namelinkOnly =
               (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
               (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) {
           if (runtimeDependencySet && libraryGenerator) {
             runtimeDependencySet->AddLibrary(libraryGenerator.get());
             runtimeDependencySet->AddLibrary(libraryGenerator.get());
@@ -1157,6 +1194,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
     installsArchive = installsArchive || archiveGenerator;
     installsArchive = installsArchive || archiveGenerator;
     installsLibrary = installsLibrary || libraryGenerator;
     installsLibrary = installsLibrary || libraryGenerator;
     installsNamelink = installsNamelink || namelinkGenerator;
     installsNamelink = installsNamelink || namelinkGenerator;
+    installsImportlink = installsImportlink || importlinkGenerator;
     installsRuntime = installsRuntime || runtimeGenerator;
     installsRuntime = installsRuntime || runtimeGenerator;
     installsObject = installsObject || objectGenerator;
     installsObject = installsObject || objectGenerator;
     installsFramework = installsFramework || frameworkGenerator;
     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(archiveGenerator));
     helper.Makefile->AddInstallGenerator(std::move(libraryGenerator));
     helper.Makefile->AddInstallGenerator(std::move(libraryGenerator));
     helper.Makefile->AddInstallGenerator(std::move(namelinkGenerator));
     helper.Makefile->AddInstallGenerator(std::move(namelinkGenerator));
+    helper.Makefile->AddInstallGenerator(std::move(importlinkGenerator));
     helper.Makefile->AddInstallGenerator(std::move(runtimeGenerator));
     helper.Makefile->AddInstallGenerator(std::move(runtimeGenerator));
     helper.Makefile->AddInstallGenerator(std::move(objectGenerator));
     helper.Makefile->AddInstallGenerator(std::move(objectGenerator));
     helper.Makefile->AddInstallGenerator(std::move(frameworkGenerator));
     helper.Makefile->AddInstallGenerator(std::move(frameworkGenerator));
@@ -1203,6 +1242,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
     helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
     helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       libraryArgs.GetNamelinkComponent());
       libraryArgs.GetNamelinkComponent());
   }
   }
+  if (installsImportlink) {
+    helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
+      archiveArgs.GetNamelinkComponent());
+  }
   if (installsRuntime) {
   if (installsRuntime) {
     helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
     helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       runtimeArgs.GetComponent());
       runtimeArgs.GetComponent());

+ 111 - 74
Source/cmInstallTargetGenerator.cxx

@@ -4,12 +4,15 @@
 
 
 #include <algorithm>
 #include <algorithm>
 #include <cassert>
 #include <cassert>
+#include <functional>
 #include <map>
 #include <map>
 #include <set>
 #include <set>
 #include <sstream>
 #include <sstream>
 #include <utility>
 #include <utility>
 #include <vector>
 #include <vector>
 
 
+#include <cm/optional>
+
 #include "cmComputeLinkInformation.h"
 #include "cmComputeLinkInformation.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGeneratorTarget.h"
@@ -40,6 +43,84 @@ std::string computeInstallObjectDir(cmGeneratorTarget* gt,
   objectDir += gt->GetName();
   objectDir += gt->GetName();
   return objectDir;
   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(
 cmInstallTargetGenerator::cmInstallTargetGenerator(
@@ -56,6 +137,7 @@ cmInstallTargetGenerator::cmInstallTargetGenerator(
 {
 {
   this->ActionsPerConfig = true;
   this->ActionsPerConfig = true;
   this->NamelinkMode = NamelinkModeNone;
   this->NamelinkMode = NamelinkModeNone;
+  this->ImportlinkMode = NamelinkModeNone;
 }
 }
 
 
 cmInstallTargetGenerator::~cmInstallTargetGenerator() = default;
 cmInstallTargetGenerator::~cmInstallTargetGenerator() = default;
@@ -247,18 +329,21 @@ cmInstallTargetGenerator::Files cmInstallTargetGenerator::GetFiles(
       this->Target->GetLibraryNames(config);
       this->Target->GetLibraryNames(config);
     if (this->ImportLibrary) {
     if (this->ImportLibrary) {
       // There is a bug in cmInstallCommand if this fails.
       // 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.
       // An import library looks like a static library.
       files.Type = cmInstallType_STATIC_LIBRARY;
       files.Type = cmInstallType_STATIC_LIBRARY;
@@ -318,66 +403,9 @@ cmInstallTargetGenerator::Files cmInstallTargetGenerator::GetFiles(
       files.From.emplace_back(std::move(from1));
       files.From.emplace_back(std::move(from1));
       files.To.emplace_back(std::move(to1));
       files.To.emplace_back(std::move(to1));
     } else {
     } 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}")) {
                                     "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
         fname = targetNames.ImportLibrary;
         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) {
     } else if (nameType == NameReal) {
       // Use the canonical name.
       // Use the canonical name.
       fname = targetNames.Real;
       fname = targetNames.Real;
@@ -434,11 +468,14 @@ std::string cmInstallTargetGenerator::GetInstallFilename(
     }
     }
   } else {
   } else {
     cmGeneratorTarget::Names targetNames = target->GetLibraryNames(config);
     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.
       // Use the import library name.
-      if (!target->GetImplibGNUtoMS(config, targetNames.ImportLibrary, fname,
+      if (!target->GetImplibGNUtoMS(config, importName, fname,
                                     "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
                                     "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
-        fname = targetNames.ImportLibrary;
+        fname = importName;
       }
       }
     } else if (nameType == NameSO) {
     } else if (nameType == NameSO) {
       // Use the soname.
       // Use the soname.

+ 7 - 1
Source/cmInstallTargetGenerator.h

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

+ 8 - 0
Source/cmLocalGenerator.cxx

@@ -85,6 +85,7 @@ static auto ruleReplaceVars = { "CMAKE_${LANG}_COMPILER",
                                 "CMAKE_RANLIB",
                                 "CMAKE_RANLIB",
                                 "CMAKE_LINKER",
                                 "CMAKE_LINKER",
                                 "CMAKE_MT",
                                 "CMAKE_MT",
+                                "CMAKE_TAPI",
                                 "CMAKE_CUDA_HOST_COMPILER",
                                 "CMAKE_CUDA_HOST_COMPILER",
                                 "CMAKE_CUDA_HOST_LINK_LAUNCHER",
                                 "CMAKE_CUDA_HOST_LINK_LAUNCHER",
                                 "CMAKE_CL_SHOWINCLUDES_PREFIX" };
                                 "CMAKE_CL_SHOWINCLUDES_PREFIX" };
@@ -134,6 +135,13 @@ cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile)
     this->LinkerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
     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 =
   if (cmValue appleArchSysroots =
         this->Makefile->GetDefinition("CMAKE_APPLE_ARCH_SYSROOTS")) {
         this->Makefile->GetDefinition("CMAKE_APPLE_ARCH_SYSROOTS")) {
     std::string const& appleArchs =
     std::string const& appleArchs =

+ 5 - 0
Source/cmMakefile.cxx

@@ -2493,6 +2493,11 @@ bool cmMakefile::PlatformIsAppleEmbedded() const
   return this->GetAppleSDKType() != AppleSDK::MacOS;
   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
 const char* cmMakefile::GetSONameFlag(const std::string& language) const
 {
 {
   std::string name = "CMAKE_SHARED_LIBRARY_SONAME";
   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.  */
   /** Return whether the target platform is Apple iOS.  */
   bool PlatformIsAppleEmbedded() const;
   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 */
   /** Retrieve soname flag for the specified language if supported */
   const char* GetSONameFlag(const std::string& language) const;
   const char* GetSONameFlag(const std::string& language) const;
 
 

+ 96 - 6
Source/cmMakefileLibraryTargetGenerator.cxx

@@ -465,9 +465,20 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
   std::string outpathImp;
   std::string outpathImp;
   if (this->GeneratorTarget->IsFrameworkOnApple()) {
   if (this->GeneratorTarget->IsFrameworkOnApple()) {
     outpath = this->GeneratorTarget->GetDirectory(this->GetConfigName());
     outpath = this->GeneratorTarget->GetDirectory(this->GetConfigName());
+    cmOSXBundleGenerator::SkipParts bundleSkipParts;
+    if (this->GeneratorTarget->HasImportLibrary(this->GetConfigName())) {
+      bundleSkipParts.TextStubs = false;
+    }
     this->OSXBundleGenerator->CreateFramework(this->TargetNames.Output,
     this->OSXBundleGenerator->CreateFramework(this->TargetNames.Output,
-                                              outpath, this->GetConfigName());
+                                              outpath, this->GetConfigName(),
+                                              bundleSkipParts);
     outpath += '/';
     outpath += '/';
+    if (!this->TargetNames.ImportLibrary.empty()) {
+      outpathImp = this->GeneratorTarget->GetDirectory(
+        this->GetConfigName(), cmStateEnums::ImportLibraryArtifact);
+      cmSystemTools::MakeDirectory(outpathImp);
+      outpathImp += '/';
+    }
   } else if (this->GeneratorTarget->IsCFBundleOnApple()) {
   } else if (this->GeneratorTarget->IsCFBundleOnApple()) {
     outpath = this->GeneratorTarget->GetDirectory(this->GetConfigName());
     outpath = this->GeneratorTarget->GetDirectory(this->GetConfigName());
     this->OSXBundleGenerator->CreateCFBundle(this->TargetNames.Output, outpath,
     this->OSXBundleGenerator->CreateCFBundle(this->TargetNames.Output, outpath,
@@ -679,11 +690,12 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
   }
   }
 
 
   // Expand the rule variables.
   // 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;
   std::vector<std::string> real_link_commands;
   {
   {
-    bool useWatcomQuote =
-      this->Makefile->IsOn(linkRuleVar + "_USE_WATCOM_QUOTE");
-
     // Set path conversion for link script shells.
     // Set path conversion for link script shells.
     this->LocalGenerator->SetLinkScriptShell(useLinkScript);
     this->LocalGenerator->SetLinkScriptShell(useLinkScript);
 
 
@@ -816,8 +828,6 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
       launcher = cmStrCat(val, ' ');
       launcher = cmStrCat(val, ' ');
     }
     }
 
 
-    std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
-      this->LocalGenerator->CreateRulePlaceholderExpander());
     // Construct the main link rule and expand placeholders.
     // Construct the main link rule and expand placeholders.
     rulePlaceholderExpander->SetTargetImpLib(targetOutPathImport);
     rulePlaceholderExpander->SetTargetImpLib(targetOutPathImport);
     if (useArchiveRules) {
     if (useArchiveRules) {
@@ -950,6 +960,86 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
   this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends,
   this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends,
                       commands, false);
                       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.
   // Write the main driver rule to build everything in this target.
   this->WriteTargetDriverRule(targetFullPath, relink);
   this->WriteTargetDriverRule(targetFullPath, relink);
 
 

+ 102 - 2
Source/cmNinjaNormalTargetGenerator.cxx

@@ -204,6 +204,15 @@ std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaFatbinaryRule(
     '_', config);
     '_', config);
 }
 }
 
 
+std::string cmNinjaNormalTargetGenerator::TextStubsGeneratorRule(
+  const std::string& config) const
+{
+  return cmStrCat(
+    "TEXT_STUBS_GENERATOR__",
+    cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
+    '_', config);
+}
+
 struct cmNinjaRemoveNoOpCommands
 struct cmNinjaRemoveNoOpCommands
 {
 {
   bool operator()(std::string const& cmd)
   bool operator()(std::string const& cmd)
@@ -527,6 +536,45 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile,
       this->GetGlobalGenerator()->AddRule(rule);
       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()
 std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd()
@@ -1030,9 +1078,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
       // the current configuration has a postfix. The non-postfix configuration
       // the current configuration has a postfix. The non-postfix configuration
       // Info.plist can be used by all the other configurations.
       // Info.plist can be used by all the other configurations.
       if (!postFix.empty()) {
       if (!postFix.empty()) {
-        bundleSkipParts.infoPlist = true;
+        bundleSkipParts.InfoPlist = true;
       }
       }
     }
     }
+    if (gt->HasImportLibrary(config)) {
+      bundleSkipParts.TextStubs = false;
+    }
 
 
     this->OSXBundleGenerator->CreateFramework(
     this->OSXBundleGenerator->CreateFramework(
       tgtNames.Output, gt->GetDirectory(config), config, bundleSkipParts);
       tgtNames.Output, gt->GetDirectory(config), config, bundleSkipParts);
@@ -1214,7 +1265,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
 
 
   cmGlobalNinjaGenerator::CCOutputs byproducts(this->GetGlobalGenerator());
   cmGlobalNinjaGenerator::CCOutputs byproducts(this->GetGlobalGenerator());
 
 
-  if (!tgtNames.ImportLibrary.empty()) {
+  if (!gt->IsApple() && !tgtNames.ImportLibrary.empty()) {
     const std::string impLibPath = localGen.ConvertToOutputFormat(
     const std::string impLibPath = localGen.ConvertToOutputFormat(
       targetOutputImplib, cmOutputConverter::SHELL);
       targetOutputImplib, cmOutputConverter::SHELL);
     vars["TARGET_IMPLIB"] = impLibPath;
     vars["TARGET_IMPLIB"] = impLibPath;
@@ -1471,6 +1522,55 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
   // Add aliases for the file name and the target name.
   // Add aliases for the file name and the target name.
   globalGen->AddTargetAlias(tgtNames.Output, gt, config);
   globalGen->AddTargetAlias(tgtNames.Output, gt, config);
   globalGen->AddTargetAlias(this->GetTargetName(), 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(
 void cmNinjaNormalTargetGenerator::WriteObjectLibStatement(

+ 1 - 0
Source/cmNinjaNormalTargetGenerator.h

@@ -25,6 +25,7 @@ private:
   std::string LanguageLinkerCudaDeviceCompileRule(
   std::string LanguageLinkerCudaDeviceCompileRule(
     const std::string& config) const;
     const std::string& config) const;
   std::string LanguageLinkerCudaFatbinaryRule(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;
   const char* GetVisibleTypeName() const;
   void WriteLanguagesRules(const std::string& config);
   void WriteLanguagesRules(const std::string& config);

+ 13 - 1
Source/cmOSXBundleGenerator.cxx

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

+ 5 - 6
Source/cmOSXBundleGenerator.h

@@ -20,11 +20,10 @@ public:
 
 
   struct SkipParts
   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
   // create an app bundle at a given root, and return
@@ -35,7 +34,7 @@ public:
   // create a framework at a given root
   // create a framework at a given root
   void CreateFramework(const std::string& targetName, const std::string& root,
   void CreateFramework(const std::string& targetName, const std::string& root,
                        const std::string& config,
                        const std::string& config,
-                       const SkipParts& skipParts = SkipParts());
+                       const SkipParts& skipParts = SkipParts{});
 
 
   // create a cf bundle at a given root
   // create a cf bundle at a given root
   void CreateCFBundle(const std::string& targetName, const std::string& 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 },
   { "AUTORCC_OPTIONS"_s, IC::CanCompileSources },
 
 
   // Linking properties
   // Linking properties
-  { "ENABLE_EXPORTS"_s, IC::ExecutableTarget },
+  { "ENABLE_EXPORTS"_s, IC::TargetWithSymbolExports },
   { "LINK_LIBRARIES_ONLY_TARGETS"_s, IC::NormalNonImportedTarget },
   { "LINK_LIBRARIES_ONLY_TARGETS"_s, IC::NormalNonImportedTarget },
   { "LINK_SEARCH_START_STATIC"_s, IC::CanCompileSources },
   { "LINK_SEARCH_START_STATIC"_s, IC::CanCompileSources },
   { "LINK_SEARCH_END_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_";
   defKey += "CMAKE_";
   auto initProperty = [this, mf, &defKey](const std::string& property,
   auto initProperty = [this, mf, &defKey](const std::string& property,
                                           const char* default_value) {
                                           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_"
     // Replace everything after "CMAKE_"
     defKey.replace(defKey.begin() + 6, defKey.end(), property);
     defKey.replace(defKey.begin() + 6, defKey.end(), property);
     if (cmValue value = mf->GetDefinition(defKey)) {
     if (cmValue value = mf->GetDefinition(defKey)) {
@@ -1205,6 +1230,12 @@ bool cmTarget::IsExecutableWithExports() const
           this->GetPropertyAsBool("ENABLE_EXPORTS"));
           this->GetPropertyAsBool("ENABLE_EXPORTS"));
 }
 }
 
 
+bool cmTarget::IsSharedLibraryWithExports() const
+{
+  return (this->GetType() == cmStateEnums::SHARED_LIBRARY &&
+          this->GetPropertyAsBool("ENABLE_EXPORTS"));
+}
+
 bool cmTarget::IsFrameworkOnApple() const
 bool cmTarget::IsFrameworkOnApple() const
 {
 {
   return ((this->GetType() == cmStateEnums::SHARED_LIBRARY ||
   return ((this->GetType() == cmStateEnums::SHARED_LIBRARY ||
@@ -2657,7 +2688,8 @@ const char* cmTarget::GetSuffixVariableInternal(
         case cmStateEnums::RuntimeBinaryArtifact:
         case cmStateEnums::RuntimeBinaryArtifact:
           return "CMAKE_SHARED_LIBRARY_SUFFIX";
           return "CMAKE_SHARED_LIBRARY_SUFFIX";
         case cmStateEnums::ImportLibraryArtifact:
         case cmStateEnums::ImportLibraryArtifact:
-          return "CMAKE_IMPORT_LIBRARY_SUFFIX";
+          return this->IsApple() ? "CMAKE_APPLE_IMPORT_FILE_SUFFIX"
+                                 : "CMAKE_IMPORT_LIBRARY_SUFFIX";
       }
       }
       break;
       break;
     case cmStateEnums::MODULE_LIBRARY:
     case cmStateEnums::MODULE_LIBRARY:
@@ -2698,7 +2730,8 @@ const char* cmTarget::GetPrefixVariableInternal(
         case cmStateEnums::RuntimeBinaryArtifact:
         case cmStateEnums::RuntimeBinaryArtifact:
           return "CMAKE_SHARED_LIBRARY_PREFIX";
           return "CMAKE_SHARED_LIBRARY_PREFIX";
         case cmStateEnums::ImportLibraryArtifact:
         case cmStateEnums::ImportLibraryArtifact:
-          return "CMAKE_IMPORT_LIBRARY_PREFIX";
+          return this->IsApple() ? "CMAKE_APPLE_IMPORT_FILE_PREFIX"
+                                 : "CMAKE_IMPORT_LIBRARY_PREFIX";
       }
       }
       break;
       break;
     case cmStateEnums::MODULE_LIBRARY:
     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.
   //! Return whether this target is an executable with symbol exports enabled.
   bool IsExecutableWithExports() const;
   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.
   //! Return whether this target is a shared library Framework on Apple.
   bool IsFrameworkOnApple() const;
   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_LIBRARY)
 add_RunCMake_test(GenEx-LINK_GROUP)
 add_RunCMake_test(GenEx-LINK_GROUP)
 add_RunCMake_test(GenEx-TARGET_FILE -DLINKER_SUPPORTS_PDB=${LINKER_SUPPORTS_PDB})
 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-GENEX_EVAL)
 add_RunCMake_test(GenEx-TARGET_PROPERTY)
 add_RunCMake_test(GenEx-TARGET_PROPERTY)
 add_RunCMake_test(GenEx-TARGET_RUNTIME_DLLS)
 add_RunCMake_test(GenEx-TARGET_RUNTIME_DLLS)
@@ -515,6 +516,7 @@ add_RunCMake_test(BundleUtilities)
 if(APPLE)
 if(APPLE)
   add_RunCMake_test(INSTALL_NAME_DIR)
   add_RunCMake_test(INSTALL_NAME_DIR)
   add_RunCMake_test(MacOSVersions)
   add_RunCMake_test(MacOSVersions)
+  add_RunCMake_test(AppleTextStubs)
 endif()
 endif()
 
 
 function(add_RunCMake_test_try_compile)
 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\):
 ^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\):
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)$
   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\):
 ^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\):
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)$
   CMakeLists\.txt:[0-9]+ \(include\)$