[lld] r276719 - COFF: Implement /linkrepro flag.

Peter Collingbourne via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 25 19:00:44 PDT 2016


Author: pcc
Date: Mon Jul 25 21:00:42 2016
New Revision: 276719

URL: http://llvm.org/viewvc/llvm-project?rev=276719&view=rev
Log:
COFF: Implement /linkrepro flag.

This flag is implemented similarly to --reproduce in the ELF linker.

This patch implements /linkrepro by moving the cpio writer and associated
utility functions to lldCore, and using that implementation in both linkers.

One COFF-specific detail is that we store the object file from which the
resource files were created in our reproducer, rather than the resource
files themselves. This allows the reproducer to be used on non-Windows
systems for example.

Differential Revision: https://reviews.llvm.org/D22418

Added:
    lld/trunk/include/lld/Core/Reproduce.h
    lld/trunk/lib/Core/Reproduce.cpp
    lld/trunk/test/COFF/linkrepro.test
Modified:
    lld/trunk/COFF/CMakeLists.txt
    lld/trunk/COFF/Driver.cpp
    lld/trunk/COFF/Driver.h
    lld/trunk/COFF/Error.h
    lld/trunk/COFF/InputFiles.cpp
    lld/trunk/COFF/Options.td
    lld/trunk/ELF/CMakeLists.txt
    lld/trunk/ELF/Driver.cpp
    lld/trunk/ELF/Driver.h
    lld/trunk/ELF/DriverUtils.cpp
    lld/trunk/ELF/InputFiles.h
    lld/trunk/lib/Core/CMakeLists.txt

Modified: lld/trunk/COFF/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/CMakeLists.txt?rev=276719&r1=276718&r2=276719&view=diff
==============================================================================
--- lld/trunk/COFF/CMakeLists.txt (original)
+++ lld/trunk/COFF/CMakeLists.txt Mon Jul 25 21:00:42 2016
@@ -30,7 +30,9 @@ add_lld_library(lldCOFF
   Option
   Support
 
-  LINK_LIBS ${PTHREAD_LIB}
+  LINK_LIBS
+  lldCore
+  ${PTHREAD_LIB}
   )
 
 add_dependencies(lldCOFF COFFOptionsTableGen)

Modified: lld/trunk/COFF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Driver.cpp?rev=276719&r1=276718&r2=276719&view=diff
==============================================================================
--- lld/trunk/COFF/Driver.cpp (original)
+++ lld/trunk/COFF/Driver.cpp Mon Jul 25 21:00:42 2016
@@ -69,6 +69,10 @@ MemoryBufferRef LinkerDriver::openFile(S
 }
 
 static std::unique_ptr<InputFile> createFile(MemoryBufferRef MB) {
+  if (Driver->Cpio)
+    Driver->Cpio->append(relativeToRoot(MB.getBufferIdentifier()),
+                         MB.getBuffer());
+
   // File type is detected by contents, not by file extension.
   file_magic Magic = identify_magic(MB.getBuffer());
   if (Magic == file_magic::archive)
@@ -247,6 +251,37 @@ static uint64_t getDefaultImageBase() {
   return Config->DLL ? 0x10000000 : 0x400000;
 }
 
+static std::string createResponseFile(const llvm::opt::InputArgList &Args,
+                                      ArrayRef<MemoryBufferRef> MBs,
+                                      ArrayRef<StringRef> SearchPaths) {
+  SmallString<0> Data;
+  raw_svector_ostream OS(Data);
+
+  for (auto *Arg : Args) {
+    switch (Arg->getOption().getID()) {
+    case OPT_linkrepro:
+    case OPT_INPUT:
+    case OPT_defaultlib:
+    case OPT_libpath:
+      break;
+    default:
+      OS << stringize(Arg) << "\n";
+    }
+  }
+
+  for (StringRef Path : SearchPaths) {
+    std::string RelPath = relativeToRoot(Path);
+    OS << "/libpath:" << quote(RelPath) << "\n";
+  }
+
+  for (MemoryBufferRef MB : MBs) {
+    std::string InputPath = relativeToRoot(MB.getBufferIdentifier());
+    OS << quote(InputPath) << "\n";
+  }
+
+  return Data.str();
+}
+
 void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
   // If the first command line argument is "/lib", link.exe acts like lib.exe.
   // We call our own implementation of lib.exe that understands bitcode files.
@@ -273,6 +308,17 @@ void LinkerDriver::link(llvm::ArrayRef<c
     return;
   }
 
+  if (auto *Arg = Args.getLastArg(OPT_linkrepro)) {
+    SmallString<64> Path = StringRef(Arg->getValue());
+    llvm::sys::path::append(Path, "repro");
+    ErrorOr<CpioFile *> F = CpioFile::create(Path);
+    if (F)
+      Cpio.reset(*F);
+    else
+      llvm::errs() << "/linkrepro: failed to open " << Path
+                   << ".cpio: " << F.getError().message() << '\n';
+  }
+
   if (Args.filtered_begin(OPT_INPUT) == Args.filtered_end())
     fatal("no input files");
 
@@ -511,9 +557,16 @@ void LinkerDriver::link(llvm::ArrayRef<c
   if (!Resources.empty()) {
     std::unique_ptr<MemoryBuffer> MB = convertResToCOFF(Resources);
     Symtab.addFile(createFile(MB->getMemBufferRef()));
+
+    MBs.push_back(MB->getMemBufferRef());
     OwningMBs.push_back(std::move(MB)); // take ownership
   }
 
+  if (Cpio)
+    Cpio->append("response.txt",
+                 createResponseFile(Args, MBs,
+                                    ArrayRef<StringRef>(SearchPaths).slice(1)));
+
   // Handle /largeaddressaware
   if (Config->is64() || Args.hasArg(OPT_largeaddressaware))
     Config->LargeAddressAware = true;

Modified: lld/trunk/COFF/Driver.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Driver.h?rev=276719&r1=276718&r2=276719&view=diff
==============================================================================
--- lld/trunk/COFF/Driver.h (original)
+++ lld/trunk/COFF/Driver.h Mon Jul 25 21:00:42 2016
@@ -13,6 +13,7 @@
 #include "Config.h"
 #include "SymbolTable.h"
 #include "lld/Core/LLVM.h"
+#include "lld/Core/Reproduce.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Object/COFF.h"
@@ -69,6 +70,8 @@ public:
   // Used by the resolver to parse .drectve section contents.
   void parseDirectives(StringRef S);
 
+  std::unique_ptr<CpioFile> Cpio; // for /linkrepro
+
 private:
   llvm::BumpPtrAllocator AllocAux;
   llvm::StringSaver Alloc;

Modified: lld/trunk/COFF/Error.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Error.h?rev=276719&r1=276718&r2=276719&view=diff
==============================================================================
--- lld/trunk/COFF/Error.h (original)
+++ lld/trunk/COFF/Error.h Mon Jul 25 21:00:42 2016
@@ -32,6 +32,12 @@ template <class T> T check(Expected<T> E
   return std::move(*E);
 }
 
+template <class T> T check(ErrorOr<T> EO) {
+  if (!EO)
+    fatal(EO.getError().message());
+  return std::move(*EO);
+}
+
 } // namespace coff
 } // namespace lld
 

Modified: lld/trunk/COFF/InputFiles.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/InputFiles.cpp?rev=276719&r1=276718&r2=276719&view=diff
==============================================================================
--- lld/trunk/COFF/InputFiles.cpp (original)
+++ lld/trunk/COFF/InputFiles.cpp Mon Jul 25 21:00:42 2016
@@ -9,6 +9,7 @@
 
 #include "Chunks.h"
 #include "Config.h"
+#include "Driver.h"
 #include "Error.h"
 #include "InputFiles.h"
 #include "Symbols.h"
@@ -93,9 +94,16 @@ MemoryBufferRef ArchiveFile::getMember(c
   // Return an empty buffer if we have already returned the same buffer.
   if (Seen[C.getChildOffset()].test_and_set())
     return MemoryBufferRef();
-  return check(C.getMemoryBufferRef(),
-               "could not get the buffer for the member defining symbol " +
-                   Sym->getName());
+
+  MemoryBufferRef MB =
+      check(C.getMemoryBufferRef(),
+            "could not get the buffer for the member defining symbol " +
+                Sym->getName());
+  if (C.getParent()->isThin() && Driver->Cpio)
+    Driver->Cpio->append(relativeToRoot(check(C.getFullName())),
+                         MB.getBuffer());
+
+  return MB;
 }
 
 void ObjectFile::parse() {

Modified: lld/trunk/COFF/Options.td
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Options.td?rev=276719&r1=276718&r2=276719&view=diff
==============================================================================
--- lld/trunk/COFF/Options.td (original)
+++ lld/trunk/COFF/Options.td Mon Jul 25 21:00:42 2016
@@ -27,6 +27,7 @@ def failifmismatch : P<"failifmismatch",
 def heap    : P<"heap", "Size of the heap">;
 def implib  : P<"implib", "Import library name">;
 def libpath : P<"libpath", "Additional library search path">;
+def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">;
 def machine : P<"machine", "Specify target platform">;
 def merge   : P<"merge", "Combine sections">;
 def mllvm   : P<"mllvm", "Options to pass to LLVM">;

Modified: lld/trunk/ELF/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/CMakeLists.txt?rev=276719&r1=276718&r2=276719&view=diff
==============================================================================
--- lld/trunk/ELF/CMakeLists.txt (original)
+++ lld/trunk/ELF/CMakeLists.txt Mon Jul 25 21:00:42 2016
@@ -44,6 +44,7 @@ add_lld_library(lldELF
 
   LINK_LIBS
   lldConfig
+  lldCore
   ${PTHREAD_LIB}
   )
 

Modified: lld/trunk/ELF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Driver.cpp?rev=276719&r1=276718&r2=276719&view=diff
==============================================================================
--- lld/trunk/ELF/Driver.cpp (original)
+++ lld/trunk/ELF/Driver.cpp Mon Jul 25 21:00:42 2016
@@ -269,11 +269,14 @@ void LinkerDriver::main(ArrayRef<const c
   if (const char *Path = getReproduceOption(Args)) {
     // Note that --reproduce is a debug option so you can ignore it
     // if you are trying to understand the whole picture of the code.
-    Cpio.reset(CpioFile::create(Path));
-    if (Cpio) {
+    ErrorOr<CpioFile *> F = CpioFile::create(Path);
+    if (F) {
+      Cpio.reset(*F);
       Cpio->append("response.txt", createResponseFile(Args));
       Cpio->append("version.txt", getVersionString());
-    }
+    } else
+      error(F.getError(),
+            Twine("--reproduce: failed to open ") + Path + ".cpio");
   }
 
   readConfigs(Args);

Modified: lld/trunk/ELF/Driver.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Driver.h?rev=276719&r1=276718&r2=276719&view=diff
==============================================================================
--- lld/trunk/ELF/Driver.h (original)
+++ lld/trunk/ELF/Driver.h Mon Jul 25 21:00:42 2016
@@ -12,6 +12,7 @@
 
 #include "SymbolTable.h"
 #include "lld/Core/LLVM.h"
+#include "lld/Core/Reproduce.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
@@ -23,8 +24,6 @@ namespace elf {
 
 extern class LinkerDriver *Driver;
 
-class CpioFile;
-
 class LinkerDriver {
 public:
   void main(ArrayRef<const char *> Args);
@@ -69,37 +68,11 @@ enum {
 #undef OPTION
 };
 
-// This is the class to create a .cpio file for --reproduce.
-//
-// If "--reproduce foo" is given, we create a file "foo.cpio" and
-// copy all input files to the archive, along with a response file
-// to re-run the same command with the same inputs.
-// It is useful for reporting issues to LLD developers.
-//
-// Cpio as a file format is a deliberate choice. It's standardized in
-// POSIX and very easy to create. cpio command is available virtually
-// on all Unix systems. See
-// http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_07
-// for the format details.
-class CpioFile {
-public:
-  static CpioFile *create(StringRef OutputPath);
-  void append(StringRef Path, StringRef Data);
-
-private:
-  CpioFile(std::unique_ptr<llvm::raw_fd_ostream> OS, StringRef Basename);
-
-  std::unique_ptr<llvm::raw_fd_ostream> OS;
-  llvm::StringSet<> Seen;
-  std::string Basename;
-};
-
 void printHelp(const char *Argv0);
 std::string getVersionString();
 std::vector<uint8_t> parseHexstring(StringRef S);
 
 std::string createResponseFile(const llvm::opt::InputArgList &Args);
-std::string relativeToRoot(StringRef Path);
 
 std::string findFromSearchPaths(StringRef Path);
 std::string searchLibrary(StringRef Path);

Modified: lld/trunk/ELF/DriverUtils.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/DriverUtils.cpp?rev=276719&r1=276718&r2=276719&view=diff
==============================================================================
--- lld/trunk/ELF/DriverUtils.cpp (original)
+++ lld/trunk/ELF/DriverUtils.cpp Mon Jul 25 21:00:42 2016
@@ -16,6 +16,7 @@
 #include "Driver.h"
 #include "Error.h"
 #include "lld/Config/Version.h"
+#include "lld/Core/Reproduce.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/Triple.h"
 #include "llvm/Option/Option.h"
@@ -105,107 +106,6 @@ std::string elf::getVersionString() {
   return "LLD " + Version + " " + Repo + "\n";
 }
 
-// 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".
-std::string elf::relativeToRoot(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;
-  StringRef Root = path::root_name(Abs);
-  if (Root.endswith(":"))
-    Res = Root.drop_back();
-  else if (Root.startswith("//"))
-    Res = Root.substr(2);
-
-  path::append(Res, path::relative_path(Abs));
-  return Res.str();
-}
-
-CpioFile::CpioFile(std::unique_ptr<raw_fd_ostream> OS, StringRef S)
-    : OS(std::move(OS)), Basename(S) {}
-
-CpioFile *CpioFile::create(StringRef OutputPath) {
-  std::string Path = (OutputPath + ".cpio").str();
-  std::error_code EC;
-  auto OS = llvm::make_unique<raw_fd_ostream>(Path, EC, fs::F_None);
-  if (EC) {
-    error(EC, "--reproduce: failed to open " + Path);
-    return nullptr;
-  }
-  return new CpioFile(std::move(OS), path::filename(OutputPath));
-}
-
-static void writeMember(raw_fd_ostream &OS, StringRef Path, StringRef Data) {
-  // The c_dev/c_ino pair should be unique according to the spec,
-  // but no one seems to care.
-  OS << "070707";                        // c_magic
-  OS << "000000";                        // c_dev
-  OS << "000000";                        // c_ino
-  OS << "100664";                        // c_mode: C_ISREG | rw-rw-r--
-  OS << "000000";                        // c_uid
-  OS << "000000";                        // c_gid
-  OS << "000001";                        // c_nlink
-  OS << "000000";                        // c_rdev
-  OS << "00000000000";                   // c_mtime
-  OS << format("%06o", Path.size() + 1); // c_namesize
-  OS << format("%011o", Data.size());    // c_filesize
-  OS << Path << '\0';                    // c_name
-  OS << Data;                            // c_filedata
-}
-
-void CpioFile::append(StringRef Path, StringRef Data) {
-  if (!Seen.insert(Path).second)
-    return;
-
-  // Construct an in-archive filename so that /home/foo/bar is stored
-  // as baz/home/foo/bar where baz is the basename of the output file.
-  // (i.e. in that case we are creating baz.cpio.)
-  SmallString<128> Fullpath;
-  path::append(Fullpath, Basename, Path);
-
-  // Use unix path separators so the cpio can be extracted on both unix and
-  // windows.
-  std::replace(Fullpath.begin(), Fullpath.end(), '\\', '/');
-
-  writeMember(*OS, Fullpath, Data);
-
-  // Print the trailer and seek back.
-  // This way we have a valid archive if we crash.
-  uint64_t Pos = OS->tell();
-  writeMember(*OS, "TRAILER!!!", "");
-  OS->seek(Pos);
-}
-
-// 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();
-}
-
-static std::string rewritePath(StringRef S) {
-  if (fs::exists(S))
-    return relativeToRoot(S);
-  return S;
-}
-
-static std::string stringize(opt::Arg *Arg) {
-  std::string K = Arg->getSpelling();
-  if (Arg->getNumValues() == 0)
-    return K;
-  std::string V = quote(Arg->getValue());
-  if (Arg->getOption().getRenderStyle() == opt::Option::RenderJoinedStyle)
-    return K + V;
-  return K + " " + V;
-}
-
 // Reconstructs command line arguments so that so that you can re-run
 // the same command with the same inputs. This is for --reproduce.
 std::string elf::createResponseFile(const opt::InputArgList &Args) {
@@ -226,8 +126,8 @@ std::string elf::createResponseFile(cons
     case OPT_alias_script_T:
     case OPT_script:
     case OPT_version_script:
-      OS << Arg->getSpelling() << " "
-         << quote(rewritePath(Arg->getValue())) << "\n";
+      OS << Arg->getSpelling() << " " << quote(rewritePath(Arg->getValue()))
+         << "\n";
       break;
     default:
       OS << stringize(Arg) << "\n";

Modified: lld/trunk/ELF/InputFiles.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/InputFiles.h?rev=276719&r1=276718&r2=276719&view=diff
==============================================================================
--- lld/trunk/ELF/InputFiles.h (original)
+++ lld/trunk/ELF/InputFiles.h Mon Jul 25 21:00:42 2016
@@ -16,6 +16,7 @@
 #include "Symbols.h"
 
 #include "lld/Core/LLVM.h"
+#include "lld/Core/Reproduce.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/IR/Comdat.h"

Added: lld/trunk/include/lld/Core/Reproduce.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/include/lld/Core/Reproduce.h?rev=276719&view=auto
==============================================================================
--- lld/trunk/include/lld/Core/Reproduce.h (added)
+++ lld/trunk/include/lld/Core/Reproduce.h Mon Jul 25 21:00:42 2016
@@ -0,0 +1,70 @@
+//===- Reproduce.h - Utilities for creating reproducers ---------*- C++ -*-===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_CORE_REPRODUCE_H
+#define LLD_CORE_REPRODUCE_H
+
+#include "lld/Core/LLVM.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+
+class raw_fd_ostream;
+
+namespace opt { class Arg; }
+
+}
+
+namespace lld {
+
+// This class creates a .cpio file for --reproduce (ELF) or /linkrepro (COFF).
+//
+// If "--reproduce foo" is given, we create a file "foo.cpio" and
+// copy all input files to the archive, along with a response file
+// to re-run the same command with the same inputs.
+// It is useful for reporting issues to LLD developers.
+//
+// Cpio as a file format is a deliberate choice. It's standardized in
+// POSIX and very easy to create. cpio command is available virtually
+// on all Unix systems. See
+// http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_07
+// for the format details.
+class CpioFile {
+public:
+  static ErrorOr<CpioFile *> create(StringRef OutputPath);
+  void append(StringRef Path, StringRef Data);
+
+private:
+  CpioFile(std::unique_ptr<llvm::raw_fd_ostream> OS, StringRef Basename);
+
+  std::unique_ptr<llvm::raw_fd_ostream> OS;
+  llvm::StringSet<> Seen;
+  std::string Basename;
+};
+
+// 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".
+std::string relativeToRoot(StringRef Path);
+
+// Quote a given string if it contains a space character.
+std::string quote(StringRef S);
+
+// Rewrite the given path if a file exists with that pathname, otherwise
+// returns the original path.
+std::string rewritePath(StringRef S);
+
+// Returns the string form of the given argument.
+std::string stringize(llvm::opt::Arg *Arg);
+
+}
+
+#endif

Modified: lld/trunk/lib/Core/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/Core/CMakeLists.txt?rev=276719&r1=276718&r2=276719&view=diff
==============================================================================
--- lld/trunk/lib/Core/CMakeLists.txt (original)
+++ lld/trunk/lib/Core/CMakeLists.txt Mon Jul 25 21:00:42 2016
@@ -4,6 +4,7 @@ add_lld_library(lldCore
   File.cpp
   LinkingContext.cpp
   Reader.cpp
+  Reproduce.cpp
   Resolver.cpp
   SymbolTable.cpp
   Writer.cpp

Added: lld/trunk/lib/Core/Reproduce.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/Core/Reproduce.cpp?rev=276719&view=auto
==============================================================================
--- lld/trunk/lib/Core/Reproduce.cpp (added)
+++ lld/trunk/lib/Core/Reproduce.cpp Mon Jul 25 21:00:42 2016
@@ -0,0 +1,121 @@
+//===- Reproduce.cpp - Utilities for creating reproducers -----------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Core/Reproduce.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Path.h"
+
+using namespace lld;
+using namespace llvm;
+using namespace sys;
+
+CpioFile::CpioFile(std::unique_ptr<raw_fd_ostream> OS, StringRef S)
+    : OS(std::move(OS)), Basename(S) {}
+
+ErrorOr<CpioFile *> CpioFile::create(StringRef OutputPath) {
+  std::string Path = (OutputPath + ".cpio").str();
+  std::error_code EC;
+  auto OS = make_unique<raw_fd_ostream>(Path, EC, sys::fs::F_None);
+  if (EC)
+    return EC;
+  return new CpioFile(std::move(OS), path::filename(OutputPath));
+}
+
+static void writeMember(raw_fd_ostream &OS, StringRef Path, StringRef Data) {
+  // The c_dev/c_ino pair should be unique according to the spec,
+  // but no one seems to care.
+  OS << "070707";                        // c_magic
+  OS << "000000";                        // c_dev
+  OS << "000000";                        // c_ino
+  OS << "100664";                        // c_mode: C_ISREG | rw-rw-r--
+  OS << "000000";                        // c_uid
+  OS << "000000";                        // c_gid
+  OS << "000001";                        // c_nlink
+  OS << "000000";                        // c_rdev
+  OS << "00000000000";                   // c_mtime
+  OS << format("%06o", Path.size() + 1); // c_namesize
+  OS << format("%011o", Data.size());    // c_filesize
+  OS << Path << '\0';                    // c_name
+  OS << Data;                            // c_filedata
+}
+
+void CpioFile::append(StringRef Path, StringRef Data) {
+  if (!Seen.insert(Path).second)
+    return;
+
+  // Construct an in-archive filename so that /home/foo/bar is stored
+  // as baz/home/foo/bar where baz is the basename of the output file.
+  // (i.e. in that case we are creating baz.cpio.)
+  SmallString<128> Fullpath;
+  path::append(Fullpath, Basename, Path);
+
+  // Use unix path separators so the cpio can be extracted on both unix and
+  // windows.
+  std::replace(Fullpath.begin(), Fullpath.end(), '\\', '/');
+
+  writeMember(*OS, Fullpath, Data);
+
+  // Print the trailer and seek back.
+  // This way we have a valid archive if we crash.
+  uint64_t Pos = OS->tell();
+  writeMember(*OS, "TRAILER!!!", "");
+  OS->seek(Pos);
+}
+
+// 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".
+std::string lld::relativeToRoot(StringRef Path) {
+  SmallString<128> Abs = Path;
+  if (sys::fs::make_absolute(Abs))
+    return Path;
+  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;
+  StringRef Root = path::root_name(Abs);
+  if (Root.endswith(":"))
+    Res = Root.drop_back();
+  else if (Root.startswith("//"))
+    Res = Root.substr(2);
+
+  path::append(Res, path::relative_path(Abs));
+
+  return Res.str();
+}
+
+// Quote a given string if it contains a space character.
+std::string lld::quote(StringRef S) {
+  if (S.find(' ') == StringRef::npos)
+    return S;
+  return ("\"" + S + "\"").str();
+}
+
+std::string lld::rewritePath(StringRef S) {
+  if (fs::exists(S))
+    return relativeToRoot(S);
+  return S;
+}
+
+std::string lld::stringize(opt::Arg *Arg) {
+  std::string K = Arg->getSpelling();
+  if (Arg->getNumValues() == 0)
+    return K;
+  std::string V = quote(Arg->getValue());
+  if (Arg->getOption().getRenderStyle() == opt::Option::RenderJoinedStyle)
+    return K + V;
+  return K + " " + V;
+}

Added: lld/trunk/test/COFF/linkrepro.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/linkrepro.test?rev=276719&view=auto
==============================================================================
--- lld/trunk/test/COFF/linkrepro.test (added)
+++ lld/trunk/test/COFF/linkrepro.test Mon Jul 25 21:00:42 2016
@@ -0,0 +1,35 @@
+# RUN: rm -rf %t.dir
+# RUN: mkdir -p %t.dir/build1 %t.dir/build2 %t.dir/build3
+# RUN: yaml2obj < %p/Inputs/hello32.yaml > %t.obj
+
+# RUN: cd %t.dir/build1
+# RUN: lld-link %t.obj %p/Inputs/std32.lib /subsystem:console \
+# RUN:   /entry:main at 0 /linkrepro:. /out:%t.exe
+# RUN: cpio -id < repro.cpio
+# RUN: diff %t.obj repro/%:t.obj
+# RUN: diff %p/Inputs/std32.lib repro/%:p/Inputs/std32.lib
+# RUN: FileCheck %s --check-prefix=RSP < repro/response.txt
+
+# RUN: cd %t.dir/build2
+# RUN: lld-link %t.obj /libpath:%p/Inputs /defaultlib:std32 /subsystem:console \
+# RUN:   /entry:main at 0 /linkrepro:. /out:%t.exe
+# RUN: cpio -id < repro.cpio
+# RUN: diff %t.obj repro/%:t.obj
+# RUN: diff %p/Inputs/std32.lib repro/%:p/Inputs/std32.lib
+# RUN: FileCheck %s --check-prefix=RSP < repro/response.txt
+
+# RUN: cd %t.dir/build3
+# RUN: env LIB=%p/Inputs lld-link %t.obj /defaultlib:std32 /subsystem:console \
+# RUN:   /entry:main at 0 /linkrepro:. /out:%t.exe
+# RUN: cpio -id < repro.cpio
+# RUN: diff %t.obj repro/%:t.obj
+# RUN: diff %p/Inputs/std32.lib repro/%:p/Inputs/std32.lib
+# RUN: FileCheck %s --check-prefix=RSP < repro/response.txt
+
+# RSP: /subsystem:console
+# RSP: /entry:main at 0
+# RSP-NOT: /linkrepro:
+# RSP: /out:
+# RSP: linkrepro.test.tmp.obj
+# RSP-NOT: defaultlib
+# RSP: std32.lib




More information about the llvm-commits mailing list