From ca6ba3fee544997b72579e97f9c64eb1a24a7fc9 Mon Sep 17 00:00:00 2001 From: Stefan Kislinskiy Date: Thu, 24 Sep 2015 12:33:01 +0200 Subject: [PATCH] Genex: Add a SHELL_PATH expression Some commands on Windows do not understand forward slash paths and require backslashes. In order to help projects generate shell invocations of such commands, provide a generator expression to convert paths to the shell-preferred path format for the current generator. This will allow custom commands to generate paths the same way CMake does for compiler command invocations. --- Help/manual/cmake-generator-expressions.7.rst | 4 ++ Help/release/dev/genex-SHELL_PATH.rst | 6 +++ Source/cmGeneratorExpressionNode.cxx | 23 +++++++++++ Source/cmOutputConverter.cxx | 39 ++++++++++++------- Source/cmOutputConverter.h | 2 + Tests/GeneratorExpression/CMakeLists.txt | 26 +++++++++++-- Tests/GeneratorExpression/check-part4.cmake | 15 +++++++ .../BadSHELL_PATH-result.txt | 1 + .../BadSHELL_PATH-stderr.txt | 17 ++++++++ .../GeneratorExpression/BadSHELL_PATH.cmake | 4 ++ .../GeneratorExpression/RunCMakeTest.cmake | 1 + 11 files changed, 120 insertions(+), 18 deletions(-) create mode 100644 Help/release/dev/genex-SHELL_PATH.rst create mode 100644 Tests/GeneratorExpression/check-part4.cmake create mode 100644 Tests/RunCMake/GeneratorExpression/BadSHELL_PATH-result.txt create mode 100644 Tests/RunCMake/GeneratorExpression/BadSHELL_PATH-stderr.txt create mode 100644 Tests/RunCMake/GeneratorExpression/BadSHELL_PATH.cmake diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index 189c3ef18..13ee4bd3f 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/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 the sources of :command:`add_library` and :command:`add_executable` commands. +``$`` + 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. diff --git a/Help/release/dev/genex-SHELL_PATH.rst b/Help/release/dev/genex-SHELL_PATH.rst new file mode 100644 index 000000000..86af720c6 --- /dev/null +++ b/Help/release/dev/genex-SHELL_PATH.rst @@ -0,0 +1,6 @@ +genex-SHELL_PATH +---------------- + +* A new ``$`` + :manual:`generator expression ` + has been added. diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 31b6766a2..1c350ab2d 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -13,6 +13,7 @@ #include "cmGeneratorExpressionNode.h" #include "cmGlobalGenerator.h" #include "cmAlgorithms.h" +#include "cmOutputConverter.h" //---------------------------------------------------------------------------- std::string cmGeneratorExpressionNode::EvaluateDependentExpression( @@ -1791,6 +1792,27 @@ TargetFilesystemArtifactNodeGroup targetSoNameNodeGroup; static const TargetFilesystemArtifactNodeGroup targetPdbNodeGroup; +//---------------------------------------------------------------------------- +static const struct ShellPathNode : public cmGeneratorExpressionNode +{ + ShellPathNode() {} + + std::string Evaluate(const std::vector ¶meters, + 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* cmGeneratorExpressionNode::GetNode(const std::string &identifier) @@ -1846,6 +1868,7 @@ cmGeneratorExpressionNode::GetNode(const std::string &identifier) nodeMap["JOIN"] = &joinNode; nodeMap["LINK_ONLY"] = &linkOnlyNode; nodeMap["COMPILE_LANGUAGE"] = &languageNode; + nodeMap["SHELL_PATH"] = &shellPathNode; } NodeMap::const_iterator i = nodeMap.find(identifier); if (i == nodeMap.end()) diff --git a/Source/cmOutputConverter.cxx b/Source/cmOutputConverter.cxx index 7be5b3f47..5acae2f82 100644 --- a/Source/cmOutputConverter.cxx +++ b/Source/cmOutputConverter.cxx @@ -142,21 +142,7 @@ std::string cmOutputConverter::ConvertToOutputFormat(const std::string& source, } 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); } else if(output == RESPONSE) @@ -166,6 +152,29 @@ std::string cmOutputConverter::ConvertToOutputFormat(const std::string& source, 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, const std::string& local, diff --git a/Source/cmOutputConverter.h b/Source/cmOutputConverter.h index ed7739edf..852df5d26 100644 --- a/Source/cmOutputConverter.h +++ b/Source/cmOutputConverter.h @@ -45,6 +45,8 @@ public: std::string Convert(RelativeRoot remote, const std::string& local, OutputFormat output = UNCHANGED, bool optional = false) const; + std::string ConvertDirectorySeparatorsForShell( + const std::string& source) const; /** * Get path for the specified relative root. diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt index 758165c80..27f33a2a6 100644 --- a/Tests/GeneratorExpression/CMakeLists.txt +++ b/Tests/GeneratorExpression/CMakeLists.txt @@ -66,7 +66,7 @@ add_custom_target(check-part1 ALL -Dtest_colons_4=$<1:C:\\CMake> -Dtest_colons_5=$<1:C:/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 ) @@ -137,7 +137,7 @@ add_custom_target(check-part2 ALL -Dtest_arbitrary_content_comma_9=$<1:a,,b,,> -Dtest_arbitrary_content_comma_10=$<1:,,a,,b,,> -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 ) @@ -221,7 +221,27 @@ add_custom_target(check-part3 ALL -Dequal22=$ -Dequal23=$ -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}$ + -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 ) diff --git a/Tests/GeneratorExpression/check-part4.cmake b/Tests/GeneratorExpression/check-part4.cmake new file mode 100644 index 000000000..9e516d52c --- /dev/null +++ b/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() diff --git a/Tests/RunCMake/GeneratorExpression/BadSHELL_PATH-result.txt b/Tests/RunCMake/GeneratorExpression/BadSHELL_PATH-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/BadSHELL_PATH-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorExpression/BadSHELL_PATH-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadSHELL_PATH-stderr.txt new file mode 100644 index 000000000..8d3c4ccd6 --- /dev/null +++ b/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: + + \$ + + "" 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: + + \$ + + "Relative/Path" is not an absolute path. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/BadSHELL_PATH.cmake b/Tests/RunCMake/GeneratorExpression/BadSHELL_PATH.cmake new file mode 100644 index 000000000..5eff7bccb --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/BadSHELL_PATH.cmake @@ -0,0 +1,4 @@ +add_custom_target(check ALL COMMAND check + $ + $ + VERBATIM) diff --git a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake index 06790243b..45175d851 100644 --- a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake @@ -10,6 +10,7 @@ run_cmake(BadTargetName) run_cmake(BadTargetTypeInterface) run_cmake(BadTargetTypeObject) run_cmake(BadInstallPrefix) +run_cmake(BadSHELL_PATH) run_cmake(CMP0044-WARN) run_cmake(NonValidTarget-C_COMPILER_ID) run_cmake(NonValidTarget-CXX_COMPILER_ID)