diff --git a/llvm/test/tools/llvm-ar/absolute-paths.test b/llvm/test/tools/llvm-ar/absolute-paths.test --- a/llvm/test/tools/llvm-ar/absolute-paths.test +++ b/llvm/test/tools/llvm-ar/absolute-paths.test @@ -10,11 +10,19 @@ CHECK-LIST: C:/src/llvm-project/build/dne/a.o Check that a.o comes out and defines foo. -RUN: llvm-ar x %S/Inputs/absolute-paths.lib 'C:/src/llvm-project/build/dne/a.o' +RUN: llvm-ar xP %S/Inputs/absolute-paths.lib 'C:/src/llvm-project/build/dne/a.o' RUN: llvm-nm a.o | FileCheck %s --check-prefix=CHECK-A CHECK-A: T foo Check that b.o comes out and defines bar. -RUN: llvm-ar x %S/Inputs/absolute-paths.lib C:/src/llvm-project/build/dne/b.o +RUN: llvm-ar xP %S/Inputs/absolute-paths.lib C:/src/llvm-project/build/dne/b.o RUN: llvm-nm b.o | FileCheck %s --check-prefix=CHECK-B CHECK-B: T bar + +xP above is only required because we were explicitly extracting items from an +archive with absolute paths. Extracting all objects doesn't need P because we +aren't explicitly requesting any individual object. +RUN: rm -f a.o b.o +RUN: llvm-ar x %S/Inputs/absolute-paths.lib +RUN: llvm-nm a.o | FileCheck %s --check-prefix=CHECK-A +RUN: llvm-nm b.o | FileCheck %s --check-prefix=CHECK-B diff --git a/llvm/test/tools/llvm-ar/full-path-option.test b/llvm/test/tools/llvm-ar/full-path-option.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-ar/full-path-option.test @@ -0,0 +1,108 @@ +# Note: many of these tests depend on relative paths, so we have to cd to a +# test directory first. +RUN: mkdir -p %t && cd %t +RUN: rm -rf a b && mkdir a b +RUN: echo hello-a > a/foo.txt +RUN: echo hello-b > b/foo.txt +RUN: echo hello-parent > foo.txt + +# Sanity test that P is accepted. +RUN: rm -f noop.a && llvm-ar rcP noop.a foo.txt +RUN: llvm-ar p noop.a | FileCheck %s --check-prefix=ACCEPT + +ACCEPT: hello-parent + +# Regular (non-thin) archives cannot be created with full path names as +# members, but P can still affect how lookup works (assuming we're reading an +# archive not created by GNU ar or llvm-ar). +# Looking up a/foo.txt in a regular archive will fail with P because it is +# added to the archive as foo.txt. +RUN: rm -f display.a +RUN: llvm-ar rcS display.a a/foo.txt foo.txt b/foo.txt +RUN: llvm-ar t display.a a/foo.txt | FileCheck %s --check-prefix=DISPLAY-FOUND --match-full-lines +RUN: not llvm-ar tP display.a a/foo.txt 2>&1 | FileCheck %s --check-prefix=DISPLAY-NOT-FOUND + +DISPLAY-FOUND: foo.txt +DISPLAY-NOT-FOUND: 'a/foo.txt' was not found + +# Deleting will fail with P because the members exist as foo.txt, not a/foo.txt. +RUN: rm -f del1.a +RUN: llvm-ar rcS del1.a foo.txt +RUN: llvm-ar dP del1.a a/foo.txt +RUN: llvm-ar t del1.a a/foo.txt | FileCheck %s --check-prefix=DISPLAY-FOUND --match-full-lines +RUN: llvm-ar d del1.a a/foo.txt +RUN: not llvm-ar t del1.a a/foo.txt 2>&1 | FileCheck %s --check-prefix=DISPLAY-NOT-FOUND + +# Run several checks that P is implied when using thin archives. None of these +# checks explicitly use P. + +# Creating an archive in one step. +RUN: rm -f add.a +RUN: llvm-ar rcST add.a a/foo.txt foo.txt b/foo.txt +RUN: llvm-ar t add.a | FileCheck %s --check-prefix=ADD --match-full-lines + +ADD: a/foo.txt +ADD-NEXT: foo.txt +ADD-NEXT: b/foo.txt + +# Create an archive incrementally. +RUN: rm -f add-inc.a +RUN: llvm-ar rcST add-inc.a a/foo.txt +RUN: llvm-ar rcST add-inc.a foo.txt +RUN: llvm-ar rcST add-inc.a b/foo.txt +RUN: llvm-ar t add-inc.a | FileCheck %s --check-prefix=ADD-INC --match-full-lines + +ADD-INC: a/foo.txt +ADD-INC-NEXT: foo.txt +ADD-INC-NEXT: b/foo.txt + +# Nesting a thin archive with a name conflict. +RUN: rm -f a/nested.a b/nested.a nested.a +RUN: llvm-ar rcST a/nested.a a/foo.txt +RUN: llvm-ar rcST b/nested.a b/foo.txt +RUN: llvm-ar rcST nested.a a/nested.a foo.txt b/nested.a +RUN: llvm-ar t nested.a | FileCheck %s --check-prefix=NESTED --match-full-lines + +NESTED: a/foo.txt +NESTED-NEXT: foo.txt +NESTED-NEXT: b/foo.txt + +# Printing members. +RUN: rm -f add.a +RUN: llvm-ar rcST add.a a/foo.txt foo.txt b/foo.txt +RUN: llvm-ar p add.a foo.txt | FileCheck %s --check-prefix=PRINT-PARENT --match-full-lines +RUN: llvm-ar p add.a a/foo.txt | FileCheck %s --check-prefix=PRINT-A --match-full-lines +RUN: llvm-ar p add.a b/foo.txt | FileCheck %s --check-prefix=PRINT-B --match-full-lines +PRINT-PARENT: hello-parent +PRINT-A: hello-a +PRINT-B: hello-b + +# Listing members. +RUN: rm -f add.a +RUN: llvm-ar rcST add.a a/foo.txt foo.txt b/foo.txt +RUN: llvm-ar t add.a foo.txt | FileCheck %s --check-prefix=LIST-PARENT --match-full-lines +RUN: llvm-ar t add.a a/foo.txt | FileCheck %s --check-prefix=LIST-A --match-full-lines +RUN: llvm-ar t add.a b/foo.txt | FileCheck %s --check-prefix=LIST-B --match-full-lines +LIST-PARENT: foo.txt +LIST-PARENT-NOT: a/foo.txt +LIST-PARENT-NOT: b/foo.txt +LIST-A: a/foo.txt +LIST-B: b/foo.txt + +# Deleting members. +RUN: rm -f del1.a +RUN: llvm-ar rcST del1.a a/foo.txt foo.txt b/foo.txt +RUN: llvm-ar d del1.a foo.txt +RUN: llvm-ar t del1.a | FileCheck %s --check-prefix=DEL-1 --match-full-lines + +DEL-1-NOT: foo.txt +DEL-1: a/foo.txt +DEL-1-NEXT: b/foo.txt + +RUN: rm -f del2.a +RUN: llvm-ar rcST del2.a a/foo.txt foo.txt b/foo.txt +RUN: llvm-ar d del2.a a/foo.txt +RUN: llvm-ar t del2.a | FileCheck %s --check-prefix=DEL-2 --match-full-lines +DEL-2-NOT: a/foo.txt +DEL-2: foo.txt +DEL-2-NEXT: b/foo.txt diff --git a/llvm/tools/llvm-ar/llvm-ar.cpp b/llvm/tools/llvm-ar/llvm-ar.cpp --- a/llvm/tools/llvm-ar/llvm-ar.cpp +++ b/llvm/tools/llvm-ar/llvm-ar.cpp @@ -98,6 +98,7 @@ [l] - ignored for compatibility [L] - add archive's contents [o] - preserve original dates + [P] - use full names when matching (implied for thin archives) [s] - create an archive index (cf. ranlib) [S] - do not build a symbol table [T] - create a thin archive @@ -168,16 +169,17 @@ }; // Modifiers to follow operation to vary behavior -static bool AddAfter = false; ///< 'a' modifier -static bool AddBefore = false; ///< 'b' modifier -static bool Create = false; ///< 'c' modifier -static bool OriginalDates = false; ///< 'o' modifier -static bool OnlyUpdate = false; ///< 'u' modifier -static bool Verbose = false; ///< 'v' modifier -static bool Symtab = true; ///< 's' modifier -static bool Deterministic = true; ///< 'D' and 'U' modifiers -static bool Thin = false; ///< 'T' modifier -static bool AddLibrary = false; ///< 'L' modifier +static bool AddAfter = false; ///< 'a' modifier +static bool AddBefore = false; ///< 'b' modifier +static bool Create = false; ///< 'c' modifier +static bool OriginalDates = false; ///< 'o' modifier +static bool CompareFullPath = false; ///< 'P' modifier +static bool OnlyUpdate = false; ///< 'u' modifier +static bool Verbose = false; ///< 'v' modifier +static bool Symtab = true; ///< 's' modifier +static bool Deterministic = true; ///< 'D' and 'U' modifiers +static bool Thin = false; ///< 'T' modifier +static bool AddLibrary = false; ///< 'L' modifier // Relative Positional Argument (for insert/move). This variable holds // the name of the archive member to which the 'a', 'b' or 'i' modifier @@ -297,6 +299,9 @@ case 'o': OriginalDates = true; break; + case 'P': + CompareFullPath = true; + break; case 's': Symtab = true; MaybeJustCreateSymTab = true; @@ -333,6 +338,8 @@ break; case 'T': Thin = true; + // Thin archives store path names, so P should be forced. + CompareFullPath = true; break; case 'L': AddLibrary = true; @@ -439,6 +446,10 @@ outs() << Name << "\n"; } +static StringRef normalizePath(StringRef Path) { + return CompareFullPath ? Path : sys::path::filename(Path); +} + // Implement the 'x' operation. This function extracts files back to the file // system. static void doExtract(StringRef Name, const object::Archive::Child &C) { @@ -510,7 +521,9 @@ StringRef Name = NameOrErr.get(); if (Filter) { - auto I = find(Members, Name); + auto I = find_if(Members, [Name](StringRef Path) { + return Name == normalizePath(Path); + }); if (I == Members.end()) continue; Members.erase(I); @@ -647,9 +660,8 @@ if (Operation == QuickAppend || Members.empty()) return IA_AddOldMember; - auto MI = find_if(Members, [Name](StringRef Path) { - return Name == sys::path::filename(Path); - }); + auto MI = find_if( + Members, [Name](StringRef Path) { return Name == normalizePath(Path); }); if (MI == Members.end()) return IA_AddOldMember; @@ -663,7 +675,7 @@ return IA_MoveOldMember; if (Operation == ReplaceOrInsert) { - StringRef PosName = sys::path::filename(RelPos); + StringRef PosName = normalizePath(RelPos); if (!OnlyUpdate) { if (PosName.empty()) return IA_AddNewMember; @@ -697,7 +709,7 @@ std::vector Ret; std::vector Moved; int InsertPos = -1; - StringRef PosName = sys::path::filename(RelPos); + StringRef PosName = normalizePath(RelPos); if (OldArchive) { Error Err = Error::success(); for (auto &Child : OldArchive->children(Err)) { @@ -889,6 +901,8 @@ EC = errorToErrorCode(std::move(Err)); failIfError(EC, "error loading '" + ArchiveName + "': " + EC.message() + "!"); + if (Archive.isThin()) + CompareFullPath = true; performOperation(Operation, &Archive, std::move(Buf.get()), NewMembers); return 0; } @@ -962,7 +976,7 @@ ArchiveName = Rest; break; case MRICommand::Delete: { - StringRef Name = sys::path::filename(Rest); + StringRef Name = normalizePath(Rest); llvm::erase_if(NewMembers, [=](NewArchiveMember &M) { return M.MemberName == Name; }); break;