Makefile: Support directory names containing '=' (#12934)
Since commitc8ef6430
(Allow directory names containing '=' and warn if necessary, 2012-02-06) we allow directories with '=' instead of rejecting them as was previously done since commit8704525f
(Reject directory names containing '=', 2011-01-14). However, we did not warn in all cases that '=' may cause failure, such as when it appears on the right-hand side of a dependency line. Both commits above were made assuming that '=' cannot be escaped in Make syntax, but it can be achieved with a variable: EQUALS = = left$(EQUALS)side : right$(EQUALS)side Use this approach to escape '=' in dependency lines, thus supporting the character in paths. All our tests now pass when CMake is built in source and build trees both containing '=', except for the "OutOfSource" test. It fails in its coverage of the obscure "OutOfBinary" test case where part of the build tree is located outside the main build tree of the test. The reason is that CMake must invoke a command like $(MAKE) -f /path/with=sign/build.make /path/with=sign/somefile but the make tool interprets the last argument as a variable assignment. This is an acceptable limitation, since the case is so obscure, in exchange for supporting '=' cleanly otherwise.
This commit is contained in:
parent
7687d557dc
commit
ee6c1b8aca
|
@ -35,6 +35,30 @@
|
||||||
#include <memory> // auto_ptr
|
#include <memory> // auto_ptr
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// Escape special characters in Makefile dependency lines
|
||||||
|
class cmMakeSafe
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cmMakeSafe(const char* s): Data(s) {}
|
||||||
|
cmMakeSafe(std::string const& s): Data(s.c_str()) {}
|
||||||
|
private:
|
||||||
|
const char* Data;
|
||||||
|
friend std::ostream& operator<<(std::ostream& os,
|
||||||
|
cmMakeSafe const& self)
|
||||||
|
{
|
||||||
|
for(const char* c = self.Data; *c; ++c)
|
||||||
|
{
|
||||||
|
switch (*c)
|
||||||
|
{
|
||||||
|
case '=': os << "$(EQUALS)"; break;
|
||||||
|
default: os << *c; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// Helper function used below.
|
// Helper function used below.
|
||||||
static std::string cmSplitExtension(std::string const& in, std::string& base)
|
static std::string cmSplitExtension(std::string const& in, std::string& base)
|
||||||
|
@ -555,28 +579,13 @@ cmLocalUnixMakefileGenerator3
|
||||||
space = " ";
|
space = " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warn about paths not supported by Make tools.
|
|
||||||
std::string::size_type pos = tgt.find_first_of("=");
|
|
||||||
if(pos != std::string::npos)
|
|
||||||
{
|
|
||||||
cmOStringStream m;
|
|
||||||
m <<
|
|
||||||
"Make rule for\n"
|
|
||||||
" " << tgt << "\n"
|
|
||||||
"has '=' on left hand side. "
|
|
||||||
"The make tool may not support this.";
|
|
||||||
cmListFileBacktrace bt;
|
|
||||||
this->GlobalGenerator->GetCMakeInstance()
|
|
||||||
->IssueMessage(cmake::WARNING, m.str(), bt);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark the rule as symbolic if requested.
|
// Mark the rule as symbolic if requested.
|
||||||
if(symbolic)
|
if(symbolic)
|
||||||
{
|
{
|
||||||
if(const char* sym =
|
if(const char* sym =
|
||||||
this->Makefile->GetDefinition("CMAKE_MAKE_SYMBOLIC_RULE"))
|
this->Makefile->GetDefinition("CMAKE_MAKE_SYMBOLIC_RULE"))
|
||||||
{
|
{
|
||||||
os << tgt.c_str() << space << ": " << sym << "\n";
|
os << cmMakeSafe(tgt) << space << ": " << sym << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -584,7 +593,7 @@ cmLocalUnixMakefileGenerator3
|
||||||
if(depends.empty())
|
if(depends.empty())
|
||||||
{
|
{
|
||||||
// No dependencies. The commands will always run.
|
// No dependencies. The commands will always run.
|
||||||
os << tgt.c_str() << space << ":\n";
|
os << cmMakeSafe(tgt) << space << ":\n";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -595,7 +604,7 @@ cmLocalUnixMakefileGenerator3
|
||||||
{
|
{
|
||||||
replace = *dep;
|
replace = *dep;
|
||||||
replace = this->Convert(replace.c_str(),HOME_OUTPUT,MAKEFILE);
|
replace = this->Convert(replace.c_str(),HOME_OUTPUT,MAKEFILE);
|
||||||
os << tgt.c_str() << space << ": " << replace.c_str() << "\n";
|
os << cmMakeSafe(tgt) << space << ": " << cmMakeSafe(replace) << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,7 +617,7 @@ cmLocalUnixMakefileGenerator3
|
||||||
}
|
}
|
||||||
if(symbolic && !this->WatcomWMake)
|
if(symbolic && !this->WatcomWMake)
|
||||||
{
|
{
|
||||||
os << ".PHONY : " << tgt.c_str() << "\n";
|
os << ".PHONY : " << cmMakeSafe(tgt) << "\n";
|
||||||
}
|
}
|
||||||
os << "\n";
|
os << "\n";
|
||||||
// Add the output to the local help if requested.
|
// Add the output to the local help if requested.
|
||||||
|
@ -687,6 +696,10 @@ cmLocalUnixMakefileGenerator3
|
||||||
<< this->ConvertShellCommand(cmakecommand, FULL)
|
<< this->ConvertShellCommand(cmakecommand, FULL)
|
||||||
<< " -E remove -f\n"
|
<< " -E remove -f\n"
|
||||||
<< "\n";
|
<< "\n";
|
||||||
|
makefileStream
|
||||||
|
<< "# Escaping for special characters.\n"
|
||||||
|
<< "EQUALS = =\n"
|
||||||
|
<< "\n";
|
||||||
|
|
||||||
if(const char* edit_cmd =
|
if(const char* edit_cmd =
|
||||||
this->Makefile->GetDefinition("CMAKE_EDIT_COMMAND"))
|
this->Makefile->GetDefinition("CMAKE_EDIT_COMMAND"))
|
||||||
|
|
Loading…
Reference in New Issue