Browse Source

Merge topic 'TargetRunTimeDllDirs'

a2c9c4f202 Add test for the new TARGET_RUNTIME_DLL_PATHS genex
aa68de0a27 TARGET_RUNTIME_DLLS: minor refactoring of shared-check.cmake
2ce3d62ffb Help: add documentation for the new TARGET_RUNTIME_DLL_DIRS genex
c351dcd967 TARGET_RUNTIME_DLL_DIRS: add the new genex to cmGeneratorExpressionNode
064c3244da TARGET_RUNTIME_DLLS: fix test for this genex

Acked-by: Kitware Robot <[email protected]>
Tested-by: buildbot <[email protected]>
Acked-by: Alex <[email protected]>
Merge-request: !8247
Kyle Edwards 2 years ago
parent
commit
def618f1ae

+ 17 - 1
Help/manual/cmake-generator-expressions.7.rst

@@ -1922,7 +1922,9 @@ In the following, the phrase "the ``tgt`` filename" means the name of the
 
   List of DLLs that the target depends on at runtime. This is determined by
   the locations of all the ``SHARED`` targets in the target's transitive
-  dependencies. Using this generator expression on targets other than
+  dependencies. If only the directories of the DLLs are needed, see the
+  :genex:`TARGET_RUNTIME_DLL_DIRS` generator expression.
+  Using this generator expression on targets other than
   executables, ``SHARED`` libraries, and ``MODULE`` libraries is an error.
   **On non-DLL platforms, this expression always evaluates to an empty string**.
 
@@ -1954,6 +1956,20 @@ On platforms that support runtime paths (``RPATH``), refer to the
 :prop_tgt:`INSTALL_RPATH` target property.
 On Apple platforms, refer to the :prop_tgt:`INSTALL_NAME_DIR` target property.
 
+.. genex:: $<TARGET_RUNTIME_DLL_DIRS:tgt>
+
+  .. versionadded:: 3.27
+
+  List of the directories which contain the DLLs that the target depends on at
+  runtime (see :genex:`TARGET_RUNTIME_DLLS`). This is determined by
+  the locations of all the ``SHARED`` targets in the target's transitive
+  dependencies. Using this generator expression on targets other than
+  executables, ``SHARED`` libraries, and ``MODULE`` libraries is an error.
+  **On non-DLL platforms, this expression always evaluates to an empty string**.
+
+  This generator expression can e.g. be used to create a batch file using
+  :command:`file(GENERATE)` which sets the PATH environment variable accordingly.
+
 Export And Install Expressions
 ------------------------------
 

+ 46 - 10
Source/cmGeneratorExpressionNode.cxx

@@ -2402,15 +2402,12 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode
   }
 } targetObjectsNode;
 
-static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode
+struct TargetRuntimeDllsBaseNode : public cmGeneratorExpressionNode
 {
-  TargetRuntimeDllsNode() {} // NOLINT(modernize-use-equals-default)
-
-  std::string Evaluate(
+  std::vector<std::string> CollectDlls(
     const std::vector<std::string>& parameters,
     cmGeneratorExpressionContext* context,
-    const GeneratorExpressionContent* content,
-    cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
+    const GeneratorExpressionContent* content) const
   {
     std::string const& tgtName = parameters.front();
     cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
@@ -2419,7 +2416,7 @@ static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode
       e << "Objects of target \"" << tgtName
         << "\" referenced but no such target exists.";
       reportError(context, content->GetOriginalExpression(), e.str());
-      return std::string();
+      return std::vector<std::string>();
     }
     cmStateEnums::TargetType type = gt->GetType();
     if (type != cmStateEnums::EXECUTABLE &&
@@ -2430,7 +2427,7 @@ static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode
         << "\" referenced but is not one of the allowed target types "
         << "(EXECUTABLE, SHARED, MODULE).";
       reportError(context, content->GetOriginalExpression(), e.str());
-      return std::string();
+      return std::vector<std::string>();
     }
 
     if (auto* cli = gt->GetLinkInformation(context->Config)) {
@@ -2443,13 +2440,51 @@ static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode
         }
       }
 
-      return cmJoin(dllPaths, ";");
+      return dllPaths;
     }
 
-    return "";
+    return std::vector<std::string>();
+  }
+};
+
+static const struct TargetRuntimeDllsNode : public TargetRuntimeDllsBaseNode
+{
+  TargetRuntimeDllsNode() {} // NOLINT(modernize-use-equals-default)
+
+  std::string Evaluate(
+    const std::vector<std::string>& parameters,
+    cmGeneratorExpressionContext* context,
+    const GeneratorExpressionContent* content,
+    cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
+  {
+    std::vector<std::string> dlls = CollectDlls(parameters, context, content);
+    return cmJoin(dlls, ";");
   }
 } targetRuntimeDllsNode;
 
+static const struct TargetRuntimeDllDirsNode : public TargetRuntimeDllsBaseNode
+{
+  TargetRuntimeDllDirsNode() {} // NOLINT(modernize-use-equals-default)
+
+  std::string Evaluate(
+    const std::vector<std::string>& parameters,
+    cmGeneratorExpressionContext* context,
+    const GeneratorExpressionContent* content,
+    cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
+  {
+    std::vector<std::string> dlls = CollectDlls(parameters, context, content);
+    std::vector<std::string> dllDirs;
+    for (const std::string& dll : dlls) {
+      std::string directory = cmSystemTools::GetFilenamePath(dll);
+      if (std::find(dllDirs.begin(), dllDirs.end(), directory) ==
+          dllDirs.end()) {
+        dllDirs.push_back(directory);
+      }
+    }
+    return cmJoin(dllDirs, ";");
+  }
+} targetRuntimeDllDirsNode;
+
 static const struct CompileFeaturesNode : public cmGeneratorExpressionNode
 {
   CompileFeaturesNode() {} // NOLINT(modernize-use-equals-default)
@@ -3774,6 +3809,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
     { "TARGET_NAME_IF_EXISTS", &targetNameIfExistsNode },
     { "TARGET_GENEX_EVAL", &targetGenexEvalNode },
     { "TARGET_RUNTIME_DLLS", &targetRuntimeDllsNode },
+    { "TARGET_RUNTIME_DLL_DIRS", &targetRuntimeDllDirsNode },
     { "GENEX_EVAL", &genexEvalNode },
     { "BUILD_INTERFACE", &buildInterfaceNode },
     { "INSTALL_INTERFACE", &installInterfaceNode },

+ 0 - 15
Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/check.cmake

@@ -1,15 +0,0 @@
-function(check_genex expected actual)
-  if(NOT expected STREQUAL actual)
-    string(APPEND RunCMake_TEST_FAILED "Expected DLLs:\n")
-    foreach(dll IN LISTS expected)
-      string(APPEND RunCMake_TEST_FAILED "  ${dll}\n")
-    endforeach()
-    string(APPEND RunCMake_TEST_FAILED "Actual DLLs:\n")
-    foreach(dll IN LISTS actual)
-      string(APPEND RunCMake_TEST_FAILED "  ${dll}\n")
-    endforeach()
-  endif()
-  set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
-endfunction()
-
-include("${RunCMake_TEST_BINARY_DIR}/dlls.cmake")

+ 15 - 0
Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared-check.cmake

@@ -0,0 +1,15 @@
+function(check_genex expected actual)
+  if(NOT expected STREQUAL actual)
+    string(APPEND RunCMake_TEST_FAILED "Expected items:\n")
+    foreach(item IN LISTS expected)
+      string(APPEND RunCMake_TEST_FAILED "  ${item}\n")
+    endforeach()
+    string(APPEND RunCMake_TEST_FAILED "Actual items:\n")
+    foreach(item IN LISTS actual)
+      string(APPEND RunCMake_TEST_FAILED "  ${item}\n")
+    endforeach()
+  endif()
+  set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+endfunction()
+
+include("${RunCMake_TEST_BINARY_DIR}/dlls.cmake")

+ 12 - 1
Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared.cmake

@@ -4,6 +4,10 @@ add_executable(exe main.c)
 add_library(lib1 SHARED lib1.c)
 add_library(lib2 SHARED lib2.c)
 add_library(lib3 SHARED lib3.c)
+if(WIN32 OR CYGWIN)
+  set_property(TARGET lib3 PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/SomeSubDir/")
+endif()
+
 add_library(static STATIC static.c)
 add_library(imported SHARED IMPORTED)
 set_property(TARGET imported PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/imported.dll")
@@ -26,9 +30,16 @@ if(WIN32 OR CYGWIN)
     "$<TARGET_FILE:lib3>"
     "$<TARGET_FILE:lib2>"
     )
+  set(expected_dll_dirs
+    "$<PATH:GET_PARENT_PATH,$<TARGET_FILE:lib2>>"
+    "$<PATH:GET_PARENT_PATH,$<TARGET_FILE:imported>>"
+    "$<PATH:GET_PARENT_PATH,$<TARGET_FILE:lib3>>"
+    )
 endif()
 
-set(content "check_genex(\"${expected_dlls}\" \"$<TARGET_RUNTIME_DLLS:exe>\")\n")
+set(content "check_genex(\"${expected_dlls}\" \"$<TARGET_RUNTIME_DLLS:exe>\")
+check_genex(\"${expected_dll_dirs}\" \"$<TARGET_RUNTIME_DLL_DIRS:exe>\")\n")
+
 set(condition)
 get_property(multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
 if(multi_config)