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.
This commit is contained in:
parent
7de868c4d7
commit
ca6ba3fee5
|
@ -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.
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
genex-SHELL_PATH
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* A new ``$<SHELL_PATH:...>``
|
||||||
|
:manual:`generator expression <cmake-generator-expressions(7)>`
|
||||||
|
has been added.
|
|
@ -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> ¶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*
|
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())
|
||||||
|
|
|
@ -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
|
result = this->ConvertDirectorySeparatorsForShell(source);
|
||||||
// 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->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,
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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()
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -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\)
|
|
@ -0,0 +1,4 @@
|
||||||
|
add_custom_target(check ALL COMMAND check
|
||||||
|
$<SHELL_PATH:>
|
||||||
|
$<SHELL_PATH:Relative/Path>
|
||||||
|
VERBATIM)
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue