diff --git a/llvm/docs/CommandGuide/llvm-libtool-darwin.rst b/llvm/docs/CommandGuide/llvm-libtool-darwin.rst --- a/llvm/docs/CommandGuide/llvm-libtool-darwin.rst +++ b/llvm/docs/CommandGuide/llvm-libtool-darwin.rst @@ -38,6 +38,14 @@ Display the version of this program. +.. option:: -D + + Use zero for timestamps and UIDs/GIDs. This is set by default. + +.. option:: -U + + Use actual timestamps and UIDs/GIDs. + .. option:: -o Specify the output file name. Must be specified exactly once. diff --git a/llvm/test/tools/llvm-libtool-darwin/deterministic-library.test b/llvm/test/tools/llvm-libtool-darwin/deterministic-library.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/deterministic-library.test @@ -0,0 +1,42 @@ +## This test checks that timestamps are set to 0 by default or when the -D +## option is specified, and that they are preserved when the -U option is +## specified. +## We only test timestamps as a proxy for full deterministic writing; i.e. we +## assume UID/GIDs are preserved if timestamps are preserved. + +# RUN: yaml2obj %S/Inputs/input1.yaml -o %t-input1.o +# RUN: touch -t 199505050555.55 %t-input1.o + +## Test values are set to 0 (by default): +# RUN: llvm-libtool-darwin -static -o %t.lib %t-input1.o +# RUN: env TZ=GMT llvm-ar tv %t.lib | FileCheck %s --check-prefix=CHECK-DETERMINISTIC + +## Test values are set to 0 (with -D): +# RUN: llvm-libtool-darwin -static -o %t.lib -D %t-input1.o +# RUN: env TZ=GMT llvm-ar tv %t.lib | FileCheck %s --check-prefix=CHECK-DETERMINISTIC + +# CHECK-DETERMINISTIC: {{[[:space:]]1970[[:space:]]}} + +## Test values are preserved (with -U): +# RUN: llvm-libtool-darwin -static -o %t.lib -U %t-input1.o +# RUN: env TZ=GMT llvm-ar tv %t.lib | FileCheck %s --check-prefix=CHECK-NONDETERMINISTIC + +# CHECK-NONDETERMINISTIC: {{[[:space:]]1995[[:space:]]}} + +## D Flag specified more than once: +# RUN: not llvm-libtool-darwin -static -o %t.lib %t-input1.o -D -D 2>&1 | \ +# RUN: FileCheck %s --check-prefix=CHECK-ERROR-D + +# CHECK-ERROR-D: for the -D option: may only occur zero or one times! + +## U Flag specified more than once: +# RUN: not llvm-libtool-darwin -static -o %t.lib %t-input1.o -U -U 2>&1 | \ +# RUN: FileCheck %s --check-prefix=CHECK-ERROR-U + +# CHECK-ERROR-U: for the -U option: may only occur zero or one times! + +## Both D and U flags specified: +# RUN: not llvm-libtool-darwin -static -o %t.lib %t-input1.o -D -U 2>&1 | \ +# RUN: FileCheck %s --check-prefix=CHECK-ERROR-BOTH + +# CHECK-ERROR-BOTH: error: cannot specify both -D and -U flags diff --git a/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp b/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp --- a/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp +++ b/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp @@ -42,11 +42,23 @@ "Produce a statically linked library from the input files")), cl::Required, cl::cat(LibtoolCategory)); +static cl::opt DeterministicOption( + "D", cl::desc("Use zero for timestamps and UIDs/GIDs (Default)"), + cl::init(false), cl::cat(LibtoolCategory)); + +static cl::opt + NonDeterministicOption("U", cl::desc("Use actual timestamps and UIDs/GIDs"), + cl::init(false), cl::cat(LibtoolCategory)); + static cl::opt FileList("filelist", cl::desc("Pass in file containing a list of filenames"), cl::value_desc("listfile[,dirname]"), cl::cat(LibtoolCategory)); +struct Config { + bool Deterministic = true; // Updated by 'D' and 'U' modifiers. +}; + static Error processFileList() { StringRef FileName, DirName; std::tie(FileName, DirName) = StringRef(FileList).rsplit(","); @@ -99,9 +111,9 @@ } static Error addChildMember(std::vector &Members, - const object::Archive::Child &M) { + const object::Archive::Child &M, const Config &C) { Expected NMOrErr = - NewArchiveMember::getOldMember(M, /*Deterministic=*/true); + NewArchiveMember::getOldMember(M, C.Deterministic); if (!NMOrErr) return NMOrErr.takeError(); @@ -115,9 +127,10 @@ static Error addMember(std::vector &Members, StringRef FileName, - std::vector> &ArchiveBuffers) { + std::vector> &ArchiveBuffers, + const Config &C) { Expected NMOrErr = - NewArchiveMember::getFile(FileName, /*Deterministic=*/true); + NewArchiveMember::getFile(FileName, C.Deterministic); if (!NMOrErr) return createFileError(FileName, NMOrErr.takeError()); @@ -135,7 +148,7 @@ Error Err = Error::success(); for (const object::Archive::Child &Child : Lib.children(Err)) - if (Error E = addChildMember(Members, Child)) + if (Error E = addChildMember(Members, Child, C)) return createFileError(FileName, std::move(E)); if (Err) return createFileError(FileName, std::move(Err)); @@ -154,43 +167,56 @@ return Error::success(); } -static Error createStaticLibrary() { +static Error createStaticLibrary(const Config &C) { std::vector NewMembers; std::vector> ArchiveBuffers; for (StringRef Member : InputFiles) - if (Error E = addMember(NewMembers, Member, ArchiveBuffers)) + if (Error E = addMember(NewMembers, Member, ArchiveBuffers, C)) return E; - if (Error E = writeArchive(OutputFile, NewMembers, - /*WriteSymtab=*/true, - /*Kind=*/object::Archive::K_DARWIN, - /*Deterministic=*/true, - /*Thin=*/false)) + if (Error E = + writeArchive(OutputFile, NewMembers, + /*WriteSymtab=*/true, + /*Kind=*/object::Archive::K_DARWIN, C.Deterministic, + /*Thin=*/false)) return E; return Error::success(); } +static Expected parseCommandLine(int Argc, char **Argv) { + Config C; + cl::ParseCommandLineOptions(Argc, Argv, "llvm-libtool-darwin\n"); + + if (DeterministicOption && NonDeterministicOption) + return createStringError(std::errc::invalid_argument, + "cannot specify both -D and -U flags"); + else if (NonDeterministicOption) + C.Deterministic = false; + + if (!FileList.empty()) + if (Error E = processFileList()) + return std::move(E); + + if (InputFiles.empty()) + return createStringError(std::errc::invalid_argument, + "no input files specified"); + + return C; +} + int main(int Argc, char **Argv) { InitLLVM X(Argc, Argv); cl::HideUnrelatedOptions({&LibtoolCategory, &ColorCategory}); - cl::ParseCommandLineOptions(Argc, Argv, "llvm-libtool-darwin\n"); - if (!FileList.empty()) { - if (Error E = processFileList()) { - WithColor::defaultErrorHandler(std::move(E)); - return EXIT_FAILURE; - } - } - - if (InputFiles.empty()) { - Error E = createStringError(std::errc::invalid_argument, - "no input files specified"); - WithColor::defaultErrorHandler(std::move(E)); + Expected ConfigOrErr = parseCommandLine(Argc, Argv); + if (!ConfigOrErr) { + WithColor::defaultErrorHandler(ConfigOrErr.takeError()); return EXIT_FAILURE; } + Config C = *ConfigOrErr; switch (LibraryOperation) { case Operation::Static: - if (Error E = createStaticLibrary()) { + if (Error E = createStaticLibrary(C)) { WithColor::defaultErrorHandler(std::move(E)); return EXIT_FAILURE; }