Browse Source

Merge topic 'shell_path'

463c2fba4e Genex: Teach SHELL_PATH to support a list of paths
21da25d2a8 Tests: Generalize GeneratorExpression MSYS path conversion workaround

Acked-by: Kitware Robot <[email protected]>
Merge-request: !3057
Brad King 6 years ago
parent
commit
5822a7bed2

+ 5 - 0
Help/manual/cmake-generator-expressions.7.rst

@@ -455,6 +455,11 @@ Output-Related Expressions
   Content of ``...`` converted to shell path style. For example, slashes are
   converted to backslashes in Windows shells and drive letters are converted
   to posix paths in MSYS shells. The ``...`` must be an absolute path.
+  The ``...`` may be a :ref:`semicolon-separated list <CMake Language Lists>`
+  of paths, in which case each path is converted individually and a result
+  list is generated using the shell path separator (``:`` on POSIX and
+  ``;`` on Windows).  Be sure to enclose the argument containing this genex
+  in double quotes in CMake source code so that ``;`` does not split arguments.
 
 Debugging
 =========

+ 5 - 0
Help/release/dev/shell_path.rst

@@ -0,0 +1,5 @@
+shell_path
+----------
+
+* The ``$<SHELL_PATH:...>`` :manual:`generator expression
+  <cmake-generator-expressions(7)>` gained support for a list of paths.

+ 20 - 4
Source/cmGeneratorExpressionNode.cxx

@@ -15,6 +15,8 @@
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
+#include "cmState.h"
+#include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
@@ -2045,13 +2047,27 @@ static const struct ShellPathNode : public cmGeneratorExpressionNode
     const GeneratorExpressionContent* content,
     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
   {
-    if (!cmSystemTools::FileIsFullPath(parameters.front())) {
+    std::vector<std::string> listIn;
+    cmSystemTools::ExpandListArgument(parameters.front(), listIn);
+    if (listIn.empty()) {
       reportError(context, content->GetOriginalExpression(),
-                  "\"" + parameters.front() + "\" is not an absolute path.");
+                  "\"\" is not an absolute path.");
       return std::string();
     }
-    cmOutputConverter converter(context->LG->GetStateSnapshot());
-    return converter.ConvertDirectorySeparatorsForShell(parameters.front());
+    cmStateSnapshot snapshot = context->LG->GetStateSnapshot();
+    cmOutputConverter converter(snapshot);
+    const char* separator = snapshot.GetState()->UseWindowsShell() ? ";" : ":";
+    std::vector<std::string> listOut;
+    listOut.reserve(listIn.size());
+    for (auto const& in : listIn) {
+      if (!cmSystemTools::FileIsFullPath(in)) {
+        reportError(context, content->GetOriginalExpression(),
+                    "\"" + in + "\" is not an absolute path.");
+        return std::string();
+      }
+      listOut.emplace_back(converter.ConvertDirectorySeparatorsForShell(in));
+    }
+    return cmJoin(listOut, separator);
   }
 } shellPathNode;
 

+ 24 - 7
Tests/GeneratorExpression/CMakeLists.txt

@@ -3,11 +3,26 @@ project(GeneratorExpression)
 
 include(CTest)
 
+# Real projects normally want the MSYS shell path conversion, but for this test
+# we need to verify that the command line is constructed with the proper string.
+set(msys1_prefix "")
+set(msys2_no_conv "")
+if(CMAKE_GENERATOR STREQUAL "MSYS Makefiles")
+  execute_process(COMMAND "uname" OUTPUT_VARIABLE uname)
+  if("${uname}" MATCHES "^MINGW32")
+    # MinGW.org MSYS 1.0 does not support generic path conversion suppression
+    set(msys1_prefix MSYS1_PREFIX)
+  else()
+    # msys2 supports generic path conversion suppression
+    set(msys2_no_conv env MSYS2_ARG_CONV_EXCL=-D)
+  endif()
+endif()
+
 # This test is split into multiple parts as needed to avoid NMake command
 # length limits.
 
 add_custom_target(check-part1 ALL
-  COMMAND ${CMAKE_COMMAND}
+  COMMAND ${msys2_no_conv} ${CMAKE_COMMAND}
     -Dtest_0=$<0:nothing>
     -Dtest_0_with_comma=$<0:-Wl,--no-undefined>
     -Dtest_1=$<1:content>
@@ -97,7 +112,7 @@ add_library(empty5 empty.cpp)
 target_include_directories(empty5 PRIVATE /empty5/private1 /empty5/private2)
 
 add_custom_target(check-part2 ALL
-  COMMAND ${CMAKE_COMMAND}
+  COMMAND ${msys2_no_conv} ${CMAKE_COMMAND}
     -Dtest_incomplete_1=$<
     -Dtest_incomplete_2=$<something
     -Dtest_incomplete_3=$<something:
@@ -188,7 +203,7 @@ set_property(TARGET importedFallback PROPERTY MAP_IMPORTED_CONFIG_DEBUG "" DEBUG
 set_property(TARGET importedFallback PROPERTY MAP_IMPORTED_CONFIG_RELEASE "")
 
 add_custom_target(check-part3 ALL
-  COMMAND ${CMAKE_COMMAND}
+  COMMAND ${msys2_no_conv} ${CMAKE_COMMAND}
     -Dtest_version_greater_1=$<VERSION_GREATER:1.0,1.1.1>
     -Dtest_version_greater_2=$<VERSION_GREATER:1.1.1,1.0>
     -Dtest_version_less_1=$<VERSION_LESS:1.1.1,1.0>
@@ -241,17 +256,19 @@ add_custom_target(check-part3 ALL
 
 if(WIN32)
   set(test_shell_path c:/shell/path)
+  set(test_shell_path2 c:/shell/path d:/another/path)
 else()
   set(test_shell_path /shell/path)
+  set(test_shell_path2 /shell/path /another/path)
 endif()
-set(path_prefix BYPASS_FURTHER_CONVERSION)
 
 add_custom_target(check-part4 ALL
-  COMMAND ${CMAKE_COMMAND}
+  COMMAND ${msys2_no_conv} ${CMAKE_COMMAND}
     # Prefix path to bypass its further conversion when being processed by
     # CMake as command-line argument
-    -Dtest_shell_path=${path_prefix}$<SHELL_PATH:${test_shell_path}>
-    -Dpath_prefix=${path_prefix}
+    -Dmsys1_prefix=${msys1_prefix}
+    -Dtest_shell_path=${msys1_prefix}$<SHELL_PATH:${test_shell_path}>
+    "-Dtest_shell_path2=$<SHELL_PATH:${test_shell_path2}>"
     -Dif_1=$<IF:1,a,b>
     -Dif_2=$<IF:0,a,b>
     -Dif_3=$<IF:$<EQUAL:10,30>,a,b>

+ 14 - 1
Tests/GeneratorExpression/check-part4.cmake

@@ -1,6 +1,8 @@
 include(${CMAKE_CURRENT_LIST_DIR}/check-common.cmake)
 
-string(REPLACE ${path_prefix} "" test_shell_path ${test_shell_path})
+if(msys1_prefix)
+  string(REPLACE "${msys1_prefix}" "" test_shell_path ${test_shell_path})
+endif()
 
 if(WIN32)
   if(CMAKE_GENERATOR STREQUAL "MSYS Makefiles")
@@ -13,6 +15,17 @@ if(WIN32)
 else()
   check(test_shell_path [[/shell/path]])
 endif()
+if(WIN32)
+  if(CMAKE_GENERATOR STREQUAL "MSYS Makefiles" AND NOT msys1_prefix)
+    check(test_shell_path2 [[/c/shell/path:/d/another/path]])
+  elseif(CMAKE_GENERATOR STREQUAL "Unix Makefiles")
+    check(test_shell_path2 [[c:/shell/path;d:/another/path]])
+  else()
+    check(test_shell_path2 [[c:\shell\path;d:\another\path]])
+  endif()
+else()
+  check(test_shell_path2 [[/shell/path:/another/path]])
+endif()
 
 check(if_1 "a")
 check(if_2 "b")

+ 9 - 0
Tests/RunCMake/GeneratorExpression/BadSHELL_PATH-stderr.txt

@@ -15,3 +15,12 @@ CMake Error at BadSHELL_PATH.cmake:[0-9]+ \(add_custom_target\):
   "Relative/Path" is not an absolute path.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
++
+CMake Error at BadSHELL_PATH.cmake:[0-9]+ \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<SHELL_PATH:;>
+
+  "" is not an absolute path.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/BadSHELL_PATH.cmake

@@ -1,4 +1,5 @@
 add_custom_target(check ALL COMMAND check
   $<SHELL_PATH:>
   $<SHELL_PATH:Relative/Path>
+  "$<SHELL_PATH:;>"
   VERBATIM)