diff --git a/llvm/lib/TableGen/Main.cpp b/llvm/lib/TableGen/Main.cpp index b1024a8d39e00..7e66237807bd4 100644 --- a/llvm/lib/TableGen/Main.cpp +++ b/llvm/lib/TableGen/Main.cpp @@ -17,12 +17,14 @@ #include "llvm/TableGen/Main.h" #include "TGLexer.h" #include "TGParser.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/ToolOutputFile.h" @@ -38,32 +40,32 @@ #include using namespace llvm; -static cl::opt -OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), - cl::init("-")); +static cl::opt OutputFilename("o", cl::desc("Output filename"), + cl::value_desc("filename"), + cl::init("-")); -static cl::opt -DependFilename("d", - cl::desc("Dependency filename"), - cl::value_desc("filename"), - cl::init("")); +static cl::opt DependFilename("d", cl::desc("Dependency filename"), + cl::value_desc("filename"), + cl::init("")); static cl::opt -InputFilename(cl::Positional, cl::desc(""), cl::init("-")); + InputFilename(cl::Positional, cl::desc(""), cl::init("-")); -static cl::list -IncludeDirs("I", cl::desc("Directory of include files"), - cl::value_desc("directory"), cl::Prefix); +static cl::list IncludeDirs("I", + cl::desc("Directory of include files"), + cl::value_desc("directory"), + cl::Prefix); static cl::list -MacroNames("D", cl::desc("Name of the macro to be defined"), - cl::value_desc("macro name"), cl::Prefix); + MacroNames("D", cl::desc("Name of the macro to be defined"), + cl::value_desc("macro name"), cl::Prefix); static cl::opt -WriteIfChanged("write-if-changed", cl::desc("Only write output if it changed")); + WriteIfChanged("write-if-changed", + cl::desc("Only write output if it changed")); -static cl::opt -TimePhases("time-phases", cl::desc("Time phases of parser and backend")); +static cl::opt TimePhases("time-phases", + cl::desc("Time phases of parser and backend")); cl::opt llvm::EmitLongStrLiterals( "long-string-literals", @@ -82,6 +84,39 @@ static int reportError(const char *ProgName, Twine Msg) { return 1; } +/// Escape a filename in the dependency file so that it is correctly +/// interpreted by `make`. This is consistent with Clang, GCC, and lld. +static std::string escapeDependencyFilename(StringRef Filename) { + std::string Res; + raw_string_ostream OS(Res); + + // Only transform the path to native on Windows, where backslashes are valid + // path separators. On non-Windows platforms, we don't want backslashes in + // filenames to be incorrectly treated as path separators. +#ifdef _WIN32 + SmallString<256> NativePath; + sys::path::native(Filename, NativePath); +#else + StringRef NativePath = Filename; +#endif + + for (unsigned I = 0, E = NativePath.size(); I != E; ++I) { + if (NativePath[I] == '#') + OS << '\\'; + else if (NativePath[I] == ' ') { + OS << '\\'; + unsigned J = I; + while (J > 0 && NativePath[--J] == '\\') + OS << '\\'; + } else if (NativePath[I] == '$') + OS << '$'; + OS << NativePath[I]; + } + + OS.flush(); + return Res; +} + /// Create a dependency file for `-d` option. /// /// This functionality is really only for the benefit of the build system. @@ -95,9 +130,9 @@ static int createDependencyFile(const TGParser &Parser, const char *argv0) { if (EC) return reportError(argv0, "error opening " + DependFilename + ":" + EC.message() + "\n"); - DepOut.os() << OutputFilename << ":"; + DepOut.os() << escapeDependencyFilename(OutputFilename) << ":"; for (const auto &Dep : Parser.getDependencies()) { - DepOut.os() << ' ' << Dep; + DepOut.os() << ' ' << escapeDependencyFilename(Dep); } DepOut.os() << "\n"; DepOut.keep(); diff --git a/llvm/test/TableGen/escape-dependency-filenames.td b/llvm/test/TableGen/escape-dependency-filenames.td new file mode 100644 index 0000000000000..a97d87eb83ff2 --- /dev/null +++ b/llvm/test/TableGen/escape-dependency-filenames.td @@ -0,0 +1,45 @@ +// RUN: rm -rf %t; split-file %s %t + +//--- normal-file.td +class NormalClass {} + +//--- file with spaces.td +class SpaceClass {} + +//--- file#with#hash.td +class HashClass {} + +//--- file$with$dollar.td +class DollarClass {} + +//--- file\with\escape.td +class EscapeClass {} + +//--- file with escape\ before spaces.td +class EscapeBeforeSpacesClass {} + +//--- main.td +include "normal-file.td" +include "file with spaces.td" +include "file#with#hash.td" +include "file$with$dollar.td" +// backslash itself needs escaping +include "file\\with\\escape.td" +include "file with escape\\ before spaces.td" + +def Normal : NormalClass; +def Spaces : SpaceClass; +def Hash : HashClass; +def Dollar : DollarClass; +def Escape : EscapeClass; +def EscapeBeforeSpaces : EscapeBeforeSpacesClass; + +// RUN: llvm-tblgen -I %t -d %t.d -o %t.out %t/main.td +// RUN: FileCheck --input-file=%t.d %s + +// CHECK-DAG: normal-file.td +// CHECK-DAG: file\ with\ spaces.td +// CHECK-DAG: file\#with\#hash.td +// CHECK-DAG: file$$with$$dollar.td +// CHECK-DAG: file\with\escape.td +// CHECK-DAG: file\ with\ escape\\\ before\ spaces.td