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
   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
   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.
   executables, ``SHARED`` libraries, and ``MODULE`` libraries is an error.
   **On non-DLL platforms, this expression always evaluates to an empty string**.
   **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.
 :prop_tgt:`INSTALL_RPATH` target property.
 On Apple platforms, refer to the :prop_tgt:`INSTALL_NAME_DIR` 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
 Export And Install Expressions
 ------------------------------
 ------------------------------
 
 

+ 46 - 10
Source/cmGeneratorExpressionNode.cxx

@@ -2402,15 +2402,12 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode
   }
   }
 } targetObjectsNode;
 } 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,
     const std::vector<std::string>& parameters,
     cmGeneratorExpressionContext* context,
     cmGeneratorExpressionContext* context,
-    const GeneratorExpressionContent* content,
-    cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
+    const GeneratorExpressionContent* content) const
   {
   {
     std::string const& tgtName = parameters.front();
     std::string const& tgtName = parameters.front();
     cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
     cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
@@ -2419,7 +2416,7 @@ static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode
       e << "Objects of target \"" << tgtName
       e << "Objects of target \"" << tgtName
         << "\" referenced but no such target exists.";
         << "\" referenced but no such target exists.";
       reportError(context, content->GetOriginalExpression(), e.str());
       reportError(context, content->GetOriginalExpression(), e.str());
-      return std::string();
+      return std::vector<std::string>();
     }
     }
     cmStateEnums::TargetType type = gt->GetType();
     cmStateEnums::TargetType type = gt->GetType();
     if (type != cmStateEnums::EXECUTABLE &&
     if (type != cmStateEnums::EXECUTABLE &&
@@ -2430,7 +2427,7 @@ static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode
         << "\" referenced but is not one of the allowed target types "
         << "\" referenced but is not one of the allowed target types "
         << "(EXECUTABLE, SHARED, MODULE).";
         << "(EXECUTABLE, SHARED, MODULE).";
       reportError(context, content->GetOriginalExpression(), e.str());
       reportError(context, content->GetOriginalExpression(), e.str());
-      return std::string();
+      return std::vector<std::string>();
     }
     }
 
 
     if (auto* cli = gt->GetLinkInformation(context->Config)) {
     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;
 } 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
 static const struct CompileFeaturesNode : public cmGeneratorExpressionNode
 {
 {
   CompileFeaturesNode() {} // NOLINT(modernize-use-equals-default)
   CompileFeaturesNode() {} // NOLINT(modernize-use-equals-default)
@@ -3774,6 +3809,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
     { "TARGET_NAME_IF_EXISTS", &targetNameIfExistsNode },
     { "TARGET_NAME_IF_EXISTS", &targetNameIfExistsNode },
     { "TARGET_GENEX_EVAL", &targetGenexEvalNode },
     { "TARGET_GENEX_EVAL", &targetGenexEvalNode },
     { "TARGET_RUNTIME_DLLS", &targetRuntimeDllsNode },
     { "TARGET_RUNTIME_DLLS", &targetRuntimeDllsNode },
+    { "TARGET_RUNTIME_DLL_DIRS", &targetRuntimeDllDirsNode },
     { "GENEX_EVAL", &genexEvalNode },
     { "GENEX_EVAL", &genexEvalNode },
     { "BUILD_INTERFACE", &buildInterfaceNode },
     { "BUILD_INTERFACE", &buildInterfaceNode },
     { "INSTALL_INTERFACE", &installInterfaceNode },
     { "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(lib1 SHARED lib1.c)
 add_library(lib2 SHARED lib2.c)
 add_library(lib2 SHARED lib2.c)
 add_library(lib3 SHARED lib3.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(static STATIC static.c)
 add_library(imported SHARED IMPORTED)
 add_library(imported SHARED IMPORTED)
 set_property(TARGET imported PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/imported.dll")
 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:lib3>"
     "$<TARGET_FILE:lib2>"
     "$<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()
 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)
 set(condition)
 get_property(multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
 get_property(multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
 if(multi_config)
 if(multi_config)