diff options
Diffstat (limited to 'lld/ELF/DriverUtils.cpp')
| -rw-r--r-- | lld/ELF/DriverUtils.cpp | 94 |
1 files changed, 78 insertions, 16 deletions
diff --git a/lld/ELF/DriverUtils.cpp b/lld/ELF/DriverUtils.cpp index 3953d5f2a16..57dfea357a3 100644 --- a/lld/ELF/DriverUtils.cpp +++ b/lld/ELF/DriverUtils.cpp @@ -87,31 +87,93 @@ void elf::printVersion() { outs() << "\n"; } -// Concatenates S and T so that the resulting path becomes S/T. -// There are a few exceptions: -// -// 1. The result will never escape from S. Therefore, all ".." -// are removed from T before concatenatig them. -// 2. Windows drive letters are removed from T before concatenation. -std::string elf::concat_paths(StringRef S, StringRef T) { - // Remove leading '/' or a drive letter, and then remove "..". - SmallString<128> T2(path::relative_path(T)); - path::remove_dots(T2, /*remove_dot_dot=*/true); - +// Makes a given pathname an absolute path first, and then remove +// beginning /. For example, "../foo.o" is converted to "home/john/foo.o", +// assuming that the current directory is "/home/john/bar". +static std::string relative_to_root(StringRef Path) { + SmallString<128> Abs = Path; + if (std::error_code EC = fs::make_absolute(Abs)) + fatal("make_absolute failed: " + EC.message()); + path::remove_dots(Abs, /*remove_dot_dot=*/true); + + // This is Windows specific. root_name() returns a drive letter + // (e.g. "c:") or a UNC name (//net). We want to keep it as part + // of the result. SmallString<128> Res; - path::append(Res, S, T2); + StringRef Root = path::root_name(Path); + if (Path.endswith(":")) + Res = Root.drop_back(); + else if (Path.startswith("//")) + Res = Root.substr(2); + + path::append(Res, path::relative_path(Abs)); return Res.str(); } -void elf::copyFile(StringRef Src, StringRef Dest) { +// Copies file Src to {Config->Reproduce}/Src. +// Returns the new path relative to Config->Reproduce. +static std::string copyFile(StringRef Src) { + std::string Relpath = relative_to_root(Src); + SmallString<128> Dest; + path::append(Dest, Config->Reproduce, Relpath); + SmallString<128> Dir(Dest); path::remove_filename(Dir); - if (std::error_code EC = sys::fs::create_directories(Dir)) { + if (std::error_code EC = sys::fs::create_directories(Dir)) error(EC, Dir + ": can't create directory"); - return; - } if (std::error_code EC = sys::fs::copy_file(Src, Dest)) error(EC, "failed to copy file: " + Dest); + return Relpath; +} + +// Quote a given string if it contains a space character. +static std::string quote(StringRef S) { + if (S.find(' ') == StringRef::npos) + return S; + return ("\"" + S + "\"").str(); +} + +// Copies all input files to Config->Reproduce directory and +// create a response file as "response.txt", so that you can re-run +// the same command with the same inputs just by executing +// "ld.lld @response.txt". Used by --reproduce. This feature is +// supposed to be used by users to report an issue to LLD developers. +void elf::saveLinkerInputs(const llvm::opt::InputArgList &Args) { + // Create the output directory. + if (std::error_code EC = sys::fs::create_directories( + Config->Reproduce, /*IgnoreExisting=*/false)) { + error(EC, Config->Reproduce + ": can't create directory"); + return; + } + + // Open "response.txt". + SmallString<128> Path; + path::append(Path, Config->Reproduce, "response.txt"); + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None); + check(EC); + + // Dump the command line to response.txt while copying files + // and rewriting paths. + for (auto *Arg : Args) { + switch (Arg->getOption().getID()) { + case OPT_reproduce: + break; + case OPT_script: + OS << "--script "; + // fallthrough + case OPT_INPUT: { + StringRef Path = Arg->getValue(); + if (fs::exists(Path)) + OS << quote(copyFile(Path)) << "\n"; + else + OS << quote(Path) << "\n"; + break; + } + default: + OS << Arg->getAsString(Args) << "\n"; + } + } } std::string elf::findFromSearchPaths(StringRef Path) { |

