Produce cpio files for --reproduce.
We want --reproduce to
* not rewrite scripts and thin archives
* work with absolute paths
Given that, it pretty much has to create a full directory tree. On windows that
is problematic because of the very short maximum path limit. On most cases
users can still work around it with "--repro c:\r", but that is annoying and
not viable for automated testing.
We then need to produce some form of archive with the files. The first option
that comes to mind is .a files since we already have code for writing them.
There are a few problems with them
The format has a dedicated string table, so we cannot start writing it until
all members are known.
Regular implementations don't support creating directories. We could make
llvm-ar support that, but that is probably not a good idea.
The next natural option would be tar. The problem is that to support long path
names (which is how this started) it needs a "pax extended header" making this
an annoying format to write.
The next option I looked at seems a natural fit: cpio files.
They are available on pretty much every unix, support directories and long path
names and are really easy to write. The only slightly annoying part is a
terminator, but at least gnu cpio only prints a warning if it is missing, which
is handy for crashes. This patch still makes an effort to always create it.
llvm-svn: 268404
diff --git a/lld/ELF/DriverUtils.cpp b/lld/ELF/DriverUtils.cpp
index 74c50f7..621dfb9 100644
--- a/lld/ELF/DriverUtils.cpp
+++ b/lld/ELF/DriverUtils.cpp
@@ -113,20 +113,48 @@
static std::string getDestPath(StringRef Path) {
std::string Relpath = relativeToRoot(Path);
SmallString<128> Dest;
- path::append(Dest, Config->Reproduce, Relpath);
+ path::append(Dest, path::filename(Config->Reproduce), Relpath);
return Dest.str();
}
-// Copies file Src to {Config->Reproduce}/Src.
-void elf::copyInputFile(StringRef Src) {
- std::string Dest = getDestPath(Src);
- StringRef Dir = path::parent_path(Dest);
- if (std::error_code EC = fs::create_directories(Dir)) {
- error(EC, Dir + ": can't create directory");
+static void maybePrintCpioMember(StringRef Path, StringRef Data) {
+ if (Config->Reproduce.empty())
return;
- }
- if (std::error_code EC = fs::copy_file(Src, Dest))
- error(EC, "failed to copy file: " + Dest);
+
+ if (!Driver->IncludedFiles.insert(Path).second)
+ return;
+
+ raw_fd_ostream &OS = *Driver->ReproduceArchive;
+ OS << "070707"; // c_magic
+
+ // The c_dev/c_ino pair should be unique according to the spec, but no one
+ // seems to care.
+ 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
+}
+
+// Write file Src with content Data to the archive.
+void elf::maybeCopyInputFile(StringRef Src, StringRef Data) {
+ std::string Dest = getDestPath(Src);
+ maybePrintCpioMember(Dest, Data);
+}
+
+void elf::maybeCloseReproArchive() {
+ if (!Driver->ReproduceArchive)
+ return;
+ maybePrintCpioMember("TRAILER!!!", "");
+ Driver->ReproduceArchive.reset();
}
// Quote a given string if it contains a space character.
@@ -148,20 +176,8 @@
// "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::createResponseFile(const opt::InputArgList &Args) {
- // Create the output directory.
- if (std::error_code EC =
- 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, fs::OpenFlags::F_None);
- check(EC);
-
+ SmallString<0> Data;
+ raw_svector_ostream OS(Data);
// Copy the command line to response.txt while rewriting paths.
for (auto *Arg : Args) {
switch (Arg->getOption().getID()) {
@@ -185,6 +201,10 @@
OS << "\n";
}
}
+
+ SmallString<128> Dest;
+ path::append(Dest, path::filename(Config->Reproduce), "response.txt");
+ maybePrintCpioMember(Dest, Data);
}
std::string elf::findFromSearchPaths(StringRef Path) {