Browse Source

Merge topic 'genex-SHELL_PATH'

ca6ba3fe Genex: Add a SHELL_PATH expression
7de868c4 Tests: Simplify GeneratorExpression check implementation
Brad King 10 years ago
parent
commit
cfcd5e8518

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

@@ -278,3 +278,7 @@ Available output expressions are:
   object of type ``OBJECT_LIBRARY``.  This expression may only be used in
   object of type ``OBJECT_LIBRARY``.  This expression may only be used in
   the sources of :command:`add_library` and :command:`add_executable`
   the sources of :command:`add_library` and :command:`add_executable`
   commands.
   commands.
+``$<SHELL_PATH:...>``
+  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.

+ 6 - 0
Help/release/dev/genex-SHELL_PATH.rst

@@ -0,0 +1,6 @@
+genex-SHELL_PATH
+----------------
+
+* A new ``$<SHELL_PATH:...>``
+  :manual:`generator expression <cmake-generator-expressions(7)>`
+  has been added.

+ 23 - 0
Source/cmGeneratorExpressionNode.cxx

@@ -13,6 +13,7 @@
 #include "cmGeneratorExpressionNode.h"
 #include "cmGeneratorExpressionNode.h"
 #include "cmGlobalGenerator.h"
 #include "cmGlobalGenerator.h"
 #include "cmAlgorithms.h"
 #include "cmAlgorithms.h"
+#include "cmOutputConverter.h"
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
 std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
@@ -1791,6 +1792,27 @@ TargetFilesystemArtifactNodeGroup<ArtifactSonameTag> targetSoNameNodeGroup;
 static const
 static const
 TargetFilesystemArtifactNodeGroup<ArtifactPdbTag> targetPdbNodeGroup;
 TargetFilesystemArtifactNodeGroup<ArtifactPdbTag> targetPdbNodeGroup;
 
 
+//----------------------------------------------------------------------------
+static const struct ShellPathNode : public cmGeneratorExpressionNode
+{
+  ShellPathNode() {}
+
+  std::string Evaluate(const std::vector<std::string> &parameters,
+                       cmGeneratorExpressionContext *context,
+                       const GeneratorExpressionContent *content,
+                       cmGeneratorExpressionDAGChecker *) const
+  {
+    if (!cmSystemTools::FileIsFullPath(parameters.front()))
+      {
+      reportError(context, content->GetOriginalExpression(),
+                  "\"" + parameters.front() + "\" is not an absolute path.");
+      return std::string();
+      }
+    cmOutputConverter converter(context->Makefile->GetStateSnapshot());
+    return converter.ConvertDirectorySeparatorsForShell(parameters.front());
+  }
+} shellPathNode;
+
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 const cmGeneratorExpressionNode*
 const cmGeneratorExpressionNode*
 cmGeneratorExpressionNode::GetNode(const std::string &identifier)
 cmGeneratorExpressionNode::GetNode(const std::string &identifier)
@@ -1846,6 +1868,7 @@ cmGeneratorExpressionNode::GetNode(const std::string &identifier)
     nodeMap["JOIN"] = &joinNode;
     nodeMap["JOIN"] = &joinNode;
     nodeMap["LINK_ONLY"] = &linkOnlyNode;
     nodeMap["LINK_ONLY"] = &linkOnlyNode;
     nodeMap["COMPILE_LANGUAGE"] = &languageNode;
     nodeMap["COMPILE_LANGUAGE"] = &languageNode;
+    nodeMap["SHELL_PATH"] = &shellPathNode;
     }
     }
   NodeMap::const_iterator i = nodeMap.find(identifier);
   NodeMap::const_iterator i = nodeMap.find(identifier);
   if (i == nodeMap.end())
   if (i == nodeMap.end())

+ 24 - 15
Source/cmOutputConverter.cxx

@@ -142,21 +142,7 @@ std::string cmOutputConverter::ConvertToOutputFormat(const std::string& source,
     }
     }
   else if(output == SHELL || output == WATCOMQUOTE)
   else if(output == SHELL || output == WATCOMQUOTE)
     {
     {
-        // For the MSYS shell convert drive letters to posix paths, so
-    // that c:/some/path becomes /c/some/path.  This is needed to
-    // avoid problems with the shell path translation.
-    if(this->GetState()->UseMSYSShell() && !this->LinkScriptShell)
-      {
-      if(result.size() > 2 && result[1] == ':')
-        {
-        result[1] = result[0];
-        result[0] = '/';
-        }
-      }
-    if(this->GetState()->UseWindowsShell())
-      {
-      std::replace(result.begin(), result.end(), '/', '\\');
-      }
+    result = this->ConvertDirectorySeparatorsForShell(source);
     result = this->EscapeForShell(result, true, false, output == WATCOMQUOTE);
     result = this->EscapeForShell(result, true, false, output == WATCOMQUOTE);
     }
     }
   else if(output == RESPONSE)
   else if(output == RESPONSE)
@@ -166,6 +152,29 @@ std::string cmOutputConverter::ConvertToOutputFormat(const std::string& source,
   return result;
   return result;
 }
 }
 
 
+//----------------------------------------------------------------------------
+std::string cmOutputConverter::ConvertDirectorySeparatorsForShell(
+                                              const std::string& source) const
+{
+  std::string result = source;
+  // For the MSYS shell convert drive letters to posix paths, so
+  // that c:/some/path becomes /c/some/path.  This is needed to
+  // avoid problems with the shell path translation.
+  if(this->GetState()->UseMSYSShell() && !this->LinkScriptShell)
+    {
+    if(result.size() > 2 && result[1] == ':')
+      {
+      result[1] = result[0];
+      result[0] = '/';
+      }
+    }
+  if(this->GetState()->UseWindowsShell())
+    {
+    std::replace(result.begin(), result.end(), '/', '\\');
+    }
+  return result;
+}
+
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 std::string cmOutputConverter::Convert(RelativeRoot remote,
 std::string cmOutputConverter::Convert(RelativeRoot remote,
                                       const std::string& local,
                                       const std::string& local,

+ 2 - 0
Source/cmOutputConverter.h

@@ -45,6 +45,8 @@ public:
   std::string Convert(RelativeRoot remote, const std::string& local,
   std::string Convert(RelativeRoot remote, const std::string& local,
                       OutputFormat output = UNCHANGED,
                       OutputFormat output = UNCHANGED,
                       bool optional = false) const;
                       bool optional = false) const;
+  std::string ConvertDirectorySeparatorsForShell(
+                                             const std::string& source) const;
 
 
   /**
   /**
     * Get path for the specified relative root.
     * Get path for the specified relative root.

+ 23 - 3
Tests/GeneratorExpression/CMakeLists.txt

@@ -66,7 +66,7 @@ add_custom_target(check-part1 ALL
     -Dtest_colons_4=$<1:C:\\CMake>
     -Dtest_colons_4=$<1:C:\\CMake>
     -Dtest_colons_5=$<1:C:/CMake>
     -Dtest_colons_5=$<1:C:/CMake>
     -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part1.cmake
     -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part1.cmake
-  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 1 of 3)"
+  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 1 of 4)"
   VERBATIM
   VERBATIM
   )
   )
 
 
@@ -137,7 +137,7 @@ add_custom_target(check-part2 ALL
     -Dtest_arbitrary_content_comma_9=$<1:a,,b,,>
     -Dtest_arbitrary_content_comma_9=$<1:a,,b,,>
     -Dtest_arbitrary_content_comma_10=$<1:,,a,,b,,>
     -Dtest_arbitrary_content_comma_10=$<1:,,a,,b,,>
     -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part2.cmake
     -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part2.cmake
-  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 2 of 3)"
+  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 2 of 4)"
   VERBATIM
   VERBATIM
 )
 )
 
 
@@ -221,7 +221,27 @@ add_custom_target(check-part3 ALL
     -Dequal22=$<EQUAL:10,-012>
     -Dequal22=$<EQUAL:10,-012>
     -Dequal23=$<EQUAL:-10,-012>
     -Dequal23=$<EQUAL:-10,-012>
     -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part3.cmake
     -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part3.cmake
-  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 3 of 3)"
+  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 3 of 4)"
+  VERBATIM
+  )
+
+if(WIN32)
+  set(test_shell_path c:/shell/path)
+else()
+  set(test_shell_path /shell/path)
+endif()
+set(path_prefix BYPASS_FURTHER_CONVERSION)
+
+add_custom_target(check-part4 ALL
+  COMMAND ${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}
+    -DWIN32=${WIN32}
+    -DCMAKE_GENERATOR=${CMAKE_GENERATOR}
+    -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part4.cmake
+  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 4 of 4)"
   VERBATIM
   VERBATIM
   )
   )
 
 

+ 2 - 2
Tests/GeneratorExpression/check-common.cmake

@@ -1,5 +1,5 @@
-macro(check var val)
+function(check var val)
   if(NOT "${${var}}" STREQUAL "${val}")
   if(NOT "${${var}}" STREQUAL "${val}")
     message(SEND_ERROR "${var} is \"${${var}}\", not \"${val}\"")
     message(SEND_ERROR "${var} is \"${${var}}\", not \"${val}\"")
   endif()
   endif()
-endmacro()
+endfunction()

+ 1 - 1
Tests/GeneratorExpression/check-part1.cmake

@@ -55,5 +55,5 @@ check(test_semicolon ";")
 check(test_colons_1 ":")
 check(test_colons_1 ":")
 check(test_colons_2 "::")
 check(test_colons_2 "::")
 check(test_colons_3 "Qt5::Core")
 check(test_colons_3 "Qt5::Core")
-check(test_colons_4 "C:\\\\CMake")
+check(test_colons_4 [[C:\CMake]])
 check(test_colons_5 "C:/CMake")
 check(test_colons_5 "C:/CMake")

+ 15 - 0
Tests/GeneratorExpression/check-part4.cmake

@@ -0,0 +1,15 @@
+include(${CMAKE_CURRENT_LIST_DIR}/check-common.cmake)
+
+string(REPLACE ${path_prefix} "" test_shell_path ${test_shell_path})
+
+if(WIN32)
+  if(CMAKE_GENERATOR STREQUAL "MSYS Makefiles")
+    check(test_shell_path [[/c/shell/path]])
+  elseif(CMAKE_GENERATOR STREQUAL "Unix Makefiles")
+    check(test_shell_path [[c:/shell/path]])
+  else()
+    check(test_shell_path [[c:\shell\path]])
+  endif()
+else()
+  check(test_shell_path [[/shell/path]])
+endif()

+ 1 - 0
Tests/RunCMake/GeneratorExpression/BadSHELL_PATH-result.txt

@@ -0,0 +1 @@
+1

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

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

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

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

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

@@ -10,6 +10,7 @@ run_cmake(BadTargetName)
 run_cmake(BadTargetTypeInterface)
 run_cmake(BadTargetTypeInterface)
 run_cmake(BadTargetTypeObject)
 run_cmake(BadTargetTypeObject)
 run_cmake(BadInstallPrefix)
 run_cmake(BadInstallPrefix)
+run_cmake(BadSHELL_PATH)
 run_cmake(CMP0044-WARN)
 run_cmake(CMP0044-WARN)
 run_cmake(NonValidTarget-C_COMPILER_ID)
 run_cmake(NonValidTarget-C_COMPILER_ID)
 run_cmake(NonValidTarget-CXX_COMPILER_ID)
 run_cmake(NonValidTarget-CXX_COMPILER_ID)