Browse Source

cmTarget: Add a way to represent imported shared library stubs

Shared library stubs can be used for linking, but not at runtime.
Their role is similar to import libraries on Windows, so represent
their location with the `IMPORTED_IMPLIB` target property.

Fixes: #24940
Robert Maynard 2 years ago
parent
commit
7351d590ee

+ 6 - 0
Help/prop_tgt/IMPORTED_IMPLIB.rst

@@ -11,6 +11,12 @@ This property may be set:
 * 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.
+* .. versionadded:: 3.28
+    On non-DLL platforms, to the location of a shared library.
+    When set without also specifying an :prop_tgt:`IMPORTED_LOCATION`,
+    the library is considered to be a stub, and its location will not
+    be added as a runtime search path to dependents that link it.
+
 
 The ``IMPORTED_IMPLIB`` target property may be overridden for a
 given configuration ``<CONFIG>`` by the configuration-specific

+ 7 - 0
Help/release/dev/imported-implib-only.rst

@@ -0,0 +1,7 @@
+imported-implib-only
+--------------------
+
+* On imported shared libraries, the :prop_tgt:`IMPORTED_IMPLIB` target
+  property may now be used without :prop_tgt:`IMPORTED_LOCATION`.
+  This can be used to represent a stub library whose location should not
+  be added as a runtime search path to dependents that link it.

+ 11 - 7
Source/cmComputeLinkInformation.cxx

@@ -2241,16 +2241,20 @@ void cmComputeLinkInformation::AddLibraryRuntimeInfo(
   if (target->GetType() != cmStateEnums::SHARED_LIBRARY) {
     return;
   }
+  auto const* info = target->GetImportInfo(this->Config);
 
   // Try to get the soname of the library.  Only files with this name
   // could possibly conflict.
-  std::string soName = target->GetSOName(this->Config);
-  const char* soname = soName.empty() ? nullptr : soName.c_str();
-
-  // Include this library in the runtime path ordering.
-  this->OrderRuntimeSearchPath->AddRuntimeLibrary(fullPath, soname);
-  if (this->LinkWithRuntimePath) {
-    this->OrderLinkerSearchPath->AddRuntimeLibrary(fullPath, soname);
+  const char* soname =
+    (!info || info->SOName.empty()) ? nullptr : info->SOName.c_str();
+
+  // If this shared library has a known runtime artifact (IMPORTED_LOCATION),
+  // include its location in the runtime path ordering.
+  if (!info || !info->Location.empty()) {
+    this->OrderRuntimeSearchPath->AddRuntimeLibrary(fullPath, soname);
+    if (this->LinkWithRuntimePath) {
+      this->OrderLinkerSearchPath->AddRuntimeLibrary(fullPath, soname);
+    }
   }
 }
 

+ 1 - 0
Source/cmGeneratorTarget.h

@@ -1076,6 +1076,7 @@ private:
     std::string SharedDeps;
   };
 
+  friend cmComputeLinkInformation;
   using ImportInfoMapType = std::map<std::string, ImportInfo>;
   mutable ImportInfoMapType ImportInfoMap;
   void ComputeImportInfo(std::string const& desired_config,

+ 20 - 6
Source/cmTarget.cxx

@@ -2779,6 +2779,8 @@ std::string cmTarget::ImportedGetFullPath(
       case cmStateEnums::RuntimeBinaryArtifact:
         if (loc) {
           result = *loc;
+        } else if (imp) {
+          result = *imp;
         } else {
           std::string impProp = cmStrCat("IMPORTED_LOCATION", suffix);
           if (cmValue config_location = this->GetProperty(impProp)) {
@@ -2787,6 +2789,16 @@ std::string cmTarget::ImportedGetFullPath(
                        this->GetProperty("IMPORTED_LOCATION")) {
             result = *location;
           }
+          if (result.empty() &&
+              (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
+               this->IsExecutableWithExports())) {
+            impProp = cmStrCat("IMPORTED_IMPLIB", suffix);
+            if (cmValue config_implib = this->GetProperty(impProp)) {
+              result = *config_implib;
+            } else if (cmValue implib = this->GetProperty("IMPORTED_IMPLIB")) {
+              result = *implib;
+            }
+          }
         }
         break;
 
@@ -2812,7 +2824,10 @@ std::string cmTarget::ImportedGetFullPath(
         std::string unset;
         std::string configuration;
 
-        if (artifact == cmStateEnums::RuntimeBinaryArtifact) {
+        if (this->GetType() == cmStateEnums::SHARED_LIBRARY &&
+            artifact == cmStateEnums::RuntimeBinaryArtifact) {
+          unset = "IMPORTED_LOCATION or IMPORTED_IMPLIB";
+        } else if (artifact == cmStateEnums::RuntimeBinaryArtifact) {
           unset = "IMPORTED_LOCATION";
         } else if (artifact == cmStateEnums::ImportLibraryArtifact) {
           unset = "IMPORTED_IMPLIB";
@@ -2985,11 +3000,10 @@ bool cmTarget::GetMappedConfig(std::string const& desired_config, cmValue& loc,
   }
 
   // If we needed to find one of the mapped configurations but did not
-  // On a DLL platform there may be only IMPORTED_IMPLIB for a shared
-  // library or an executable with exports.
-  bool allowImp = (this->IsDLLPlatform() &&
-                   (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
-                    this->IsExecutableWithExports())) ||
+  // There may be only IMPORTED_IMPLIB for a shared library or an executable
+  // with exports.
+  bool allowImp = (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
+                   this->IsExecutableWithExports()) ||
     (this->IsAIX() && this->IsExecutableWithExports()) ||
     (this->GetMakefile()->PlatformSupportsAppleTextStubs() &&
      this->IsSharedLibraryWithExports());

+ 1 - 0
Tests/Cuda/CMakeLists.txt

@@ -13,6 +13,7 @@ add_cuda_test_macro(Cuda.MixedStandardLevels4 MixedStandardLevels4)
 add_cuda_test_macro(Cuda.MixedStandardLevels5 MixedStandardLevels5)
 add_cuda_test_macro(Cuda.NotEnabled CudaNotEnabled)
 add_cuda_test_macro(Cuda.SeparableCompCXXOnly SeparableCompCXXOnly)
+add_cuda_test_macro(Cuda.StubRPATH StubRPATH)
 add_cuda_test_macro(Cuda.Toolkit Toolkit)
 add_cuda_test_macro(Cuda.IncludePathNoToolkit IncludePathNoToolkit)
 add_cuda_test_macro(Cuda.SharedRuntimePlusToolkit SharedRuntimePlusToolkit)

+ 21 - 0
Tests/Cuda/StubRPATH/CMakeLists.txt

@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 3.18)
+project(StubRPATH CXX)
+
+#Verify that linking to a stub library doesn't cause an `-rpath` entry
+
+# Needed for `CUDAToolkit_LIBRARY_SEARCH_DIRS`
+find_package(CUDAToolkit REQUIRED)
+
+find_library(CUDA_DRIVER_STUB_LIBRARY
+  NAMES cuda
+  HINTS ${CUDAToolkit_LIBRARY_SEARCH_DIRS}
+        ENV CUDA_PATH
+  PATH_SUFFIXES lib64/stubs lib/x64/stubs lib/stubs stubs
+)
+add_library(imported_stub IMPORTED SHARED)
+set_target_properties(imported_stub PROPERTIES IMPORTED_IMPLIB "${CUDA_DRIVER_STUB_LIBRARY}")
+set_target_properties(imported_stub PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CUDAToolkit_INCLUDE_DIRS}")
+
+set(CMAKE_CXX_STANDARD 11)
+add_executable(StubRPATH main.cxx)
+target_link_libraries(StubRPATH PRIVATE imported_stub)

+ 17 - 0
Tests/Cuda/StubRPATH/main.cxx

@@ -0,0 +1,17 @@
+
+#include <iostream>
+
+#include <cuda.h>
+
+int main(int argc, char** argv)
+{
+  int nDevices = 0;
+  cuInit(0);
+  auto err = cuDeviceGetCount(&nDevices);
+  if (err != CUDA_SUCCESS) {
+    std::cerr << "Failed to retrieve the number of CUDA enabled devices "
+              << err << std::endl;
+    return 1;
+  }
+  return 0;
+}

+ 1 - 1
Tests/GeneratorExpression/CMakeLists.txt

@@ -248,7 +248,7 @@ add_custom_target(check-part3 ALL
     -Dconfig=$<CONFIGURATION>
     -Dtest_imported_includes=$<TARGET_PROPERTY:imported4,INCLUDE_DIRECTORIES>
     -Dtest_imported_fallback=$<STREQUAL:$<TARGET_FILE_NAME:importedFallback>,fallback_loc>
-    -Dtest_imported_fallback2=$<IF:$<OR:$<PLATFORM_ID:Windows,CYGWIN,MSYS>,$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${CMAKE_TAPI}>>>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback2>,special_imp>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback2>,fallback_loc>>
+    -Dtest_imported_fallback2=$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback2>,special_imp>
     -Dtest_imported_fallback3=$<IF:$<PLATFORM_ID:Windows,CYGWIN,MSYS>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback3>,imp_loc>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback3>,fallback_loc>>
     -Dtest_imported_fallback4=$<IF:$<PLATFORM_ID:Windows,CYGWIN,MSYS>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback4>,imp_loc>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback4>,fallback_loc>>
     -Dtest_imported_fallback5=$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback5>,imp_loc>

+ 0 - 3
Tests/RunCMake/CMP0111/CMP0111-Common.cmake

@@ -1,6 +1,3 @@
-# Prevent duplicate errors on some platforms.
-set(CMAKE_IMPORT_LIBRARY_SUFFIX "placeholder")
-
 add_library(unknown_lib UNKNOWN IMPORTED)
 add_library(static_lib STATIC IMPORTED)
 add_library(shared_lib SHARED IMPORTED)

+ 4 - 2
Tests/RunCMake/CMP0111/CMP0111-NEW-stderr.txt

@@ -1,4 +1,6 @@
 ^(CMake Error in CMakeLists.txt:
-  IMPORTED_(LOCATION|IMPLIB) not set for imported target "(unknown|static|shared)_lib"( configuration
+  IMPORTED_(LOCATION|IMPLIB) not set for imported target "(unknown|static)_lib"( configuration
   "[^"]+")?.
-+)+CMake Generate step failed.  Build files cannot be regenerated correctly.$
+)+
+.*(IMPORTED_LOCATION or )?IMPORTED_IMPLIB not set for imported target.*"shared_lib".*
+CMake Generate step failed.  Build files cannot be regenerated correctly.$

+ 3 - 2
Tests/RunCMake/CMP0111/CMP0111-WARN-stderr.txt

@@ -4,7 +4,7 @@
   details.  Use the cmake_policy command to set the policy and suppress this
   warning.
 
-  IMPORTED_(LOCATION|IMPLIB) not set for imported target "(unknown|static|shared)_lib"( configuration
+  IMPORTED_(LOCATION|IMPLIB) not set for imported target "(unknown|static)_lib"( configuration
   "[^"]+")?.
 This warning is for project developers.  Use -Wno-dev to suppress it.
 +)+CMake Warning \(dev\) in CMakeLists.txt:
@@ -13,6 +13,7 @@ This warning is for project developers.  Use -Wno-dev to suppress it.
   details.  Use the cmake_policy command to set the policy and suppress this
   warning.
 
-  IMPORTED_(LOCATION|IMPLIB) not set for imported target "(unknown|static|shared)_lib"( configuration
+  IMPORTED_(LOCATION|IMPLIB) not set for imported target "(unknown|static)_lib"( configuration
   "[^"]+")?.
+.*(IMPORTED_LOCATION or )?IMPORTED_IMPLIB not set for imported target.*"shared_lib".*
 This warning is for project developers.  Use -Wno-dev to suppress it.$

+ 7 - 1
Tests/RunCMake/target_link_libraries-ALIAS/AliasTargets.cmake

@@ -14,8 +14,14 @@ set_property(TARGET import-local PROPERTY IMPORTED_LOCATION "${binary_dir}/${CMA
 set_property(TARGET import-local PROPERTY IMPORTED_IMPLIB "${binary_dir}/${CMAKE_STATIC_LIBRARY_PREFIX}func${CMAKE_IMPORT_LIBRARY_SUFFIX}")
 add_library(alias::local ALIAS import-local)
 
+if(NOT DEFINED CMAKE_IMPORT_LIBRARY_SUFFIX)
+  add_library(import-local-stub SHARED IMPORTED)
+  set_property(TARGET import-local-stub PROPERTY IMPORTED_IMPLIB "${binary_dir}/${CMAKE_STATIC_LIBRARY_PREFIX}func${CMAKE_SHARED_LIBRARY_SUFFIX}")
+  add_library(alias::local-stub ALIAS import-local-stub)
+endif()
+
 add_library (lib-local SHARED lib.c)
-target_link_libraries (lib-local PRIVATE alias::local)
+target_link_libraries (lib-local PRIVATE alias::local $<TARGET_NAME_IF_EXISTS:alias::local-stub>)
 
 add_executable (main-local main.c)
 target_link_libraries (main-local PRIVATE alias::local)

+ 2 - 0
Tests/RunCMake/target_link_libraries/ImportedTargetStub.cmake

@@ -0,0 +1,2 @@
+add_library(SharedStubImportedGlobal SHARED IMPORTED GLOBAL)
+set_target_properties(SharedStubImportedGlobal PROPERTIES IMPORTED_IMPLIB z)

+ 1 - 0
Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake

@@ -23,6 +23,7 @@ run_cmake(CMP0079-link-NEW-bogus)
 run_cmake(CMP0108-OLD-self-link)
 run_cmake(CMP0108-NEW-self-link)
 run_cmake(ImportedTarget)
+run_cmake(ImportedTargetStub)
 run_cmake(ImportedTargetFailure)
 run_cmake(MixedSignature)
 run_cmake(Separate-PRIVATE-LINK_PRIVATE-uses)