llvm.org GIT mirror llvm / e697f3e
[llvm-ar] Support N [count] modifier Summary: GNU ar supports the 'N' count modifier for the extract (x) and delete (d) operations. When an archive contains multiple members with the same name, this can be used to extract (or delete) them individually. For example: ``` $ llvm-ar t archive.a foo foo $ llvm-ar x archive.a -> Writes foo twice, overwriting it the second time :( :( $ llvm-ar xN 1 archive.a foo && mv foo foo.1 $ llvm-ar xN 2 archive.a foo && mv foo foo.2 -> Write foo twice, renaming it in between invocations to preserve all versions ``` Reviewers: ruiu, MaskRay Reviewed By: ruiu, MaskRay Subscribers: jdoerfert, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D59503 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@356466 91177308-0d34-0410-b5e6-96231b3b80d8 Jordan Rupprecht 5 months ago
2 changed file(s) with 125 addition(s) and 8 deletion(s). Raw diff Collapse all Expand all
0 # Test the 'N' count parameter.
1
2 # Get a temp clean cwd to extract into.
3 RUN: rm -rf %t && mkdir -p %t && cd %t
4
5 RUN: mkdir -p %t/x %t/y %t/z
6 RUN: echo hello > %t/x/foo.txt
7 RUN: echo cool > %t/y/foo.txt
8 RUN: echo world > %t/z/foo.txt
9 RUN: echo fizz > %t/x/bar.txt
10 RUN: echo buzz > %t/y/bar.txt
11 RUN: echo fizbuz > %t/z/bar.txt
12 RUN: llvm-ar rc %t/archive.a %t/x/foo.txt %t/y/foo.txt %t/z/foo.txt \
13 RUN: %t/x/bar.txt %t/y/bar.txt %t/z/bar.txt
14 RUN: llvm-ar t %t/archive.a | FileCheck %s --check-prefix=LIST-MEMBERS
15
16 # Make sure we set it up correctly.
17 LIST-MEMBERS: foo.txt
18 LIST-MEMBERS-NEXT: foo.txt
19 LIST-MEMBERS-NEXT: foo.txt
20 LIST-MEMBERS-NEXT: bar.txt
21 LIST-MEMBERS-NEXT: bar.txt
22 LIST-MEMBERS-NEXT: bar.txt
23
24 # Must be a number.
25 RUN: not llvm-ar xN abc %t/archive.a foo.txt 2>&1 | FileCheck %s --check-prefix=ERR-NOT-NUM
26 RUN: not llvm-ar xN 0x1 %t/archive.a foo.txt 2>&1 | FileCheck %s --check-prefix=ERR-NOT-NUM
27 # Only three members named foo, so 1 <= N <= 3.
28 RUN: not llvm-ar xN 0 %t/archive.a foo.txt 2>&1 | FileCheck %s --check-prefix=ERR-NOT-POS
29 RUN: not llvm-ar xN 4 %t/archive.a foo.txt 2>&1 | FileCheck %s --check-prefix=ERR-NOT-FOUND
30 # N only applies to x/d.
31 RUN: not llvm-ar rN 1 %t/archive.a foo.txt 2>&1 | FileCheck %s --check-prefix=ERR-BAD-OP
32
33 ERR-NOT-NUM: error: Value for [count] must be numeric
34 ERR-NOT-POS: error: Value for [count] must be positive
35 ERR-BAD-OP: error: The 'N' modifier can only be specified with the 'x' or 'd' operations
36 ERR-NOT-FOUND: error: 'foo.txt' was not found
37
38 # Extract individual items.
39
40 RUN: rm -f foo.txt bar.txt
41 RUN: llvm-ar xN 1 %t/archive.a foo.txt bar.txt
42 RUN: cat %t/foo.txt | FileCheck %s --check-prefix=FOO-1
43 RUN: cat %t/bar.txt | FileCheck %s --check-prefix=BAR-1
44
45 RUN: rm -f foo.txt bar.txt
46 RUN: llvm-ar xN 2 %t/archive.a foo.txt bar.txt
47 RUN: cat %t/foo.txt | FileCheck %s --check-prefix=FOO-2
48 RUN: cat %t/bar.txt | FileCheck %s --check-prefix=BAR-2
49
50 RUN: rm -f foo.txt bar.txt
51 RUN: llvm-ar xN 3 %t/archive.a foo.txt bar.txt
52 RUN: cat %t/foo.txt | FileCheck %s --check-prefix=FOO-3
53 RUN: cat %t/bar.txt | FileCheck %s --check-prefix=BAR-3
54
55 # Delete individual items.
56
57 # Deleting the second member named foo means the new second member of the
58 # archive is what used to be the third element.
59 RUN: rm -f foo.txt bar.txt
60 RUN: llvm-ar dN 2 %t/archive.a foo.txt
61 RUN: llvm-ar xN 2 %t/archive.a foo.txt bar.txt
62 RUN: cat %t/foo.txt | FileCheck %s --check-prefix=FOO-3
63 RUN: cat %t/bar.txt | FileCheck %s --check-prefix=BAR-2
64
65 # Deleting the first member from *both* archives means the new first member
66 # named foo is the what used to be the third member, and the new first member
67 # named bar is what used to be the second member.
68 RUN: rm -f foo.txt bar.txt
69 RUN: llvm-ar dN 1 %t/archive.a foo.txt bar.txt
70 RUN: llvm-ar xN 1 %t/archive.a foo.txt bar.txt
71 RUN: cat %t/foo.txt | FileCheck %s --check-prefix=FOO-3
72 RUN: cat %t/bar.txt | FileCheck %s --check-prefix=BAR-2
73
74 FOO-1: hello
75 FOO-2: cool
76 FOO-3: world
77 BAR-1: fizz
78 BAR-2: buzz
79 BAR-3: fizbuz
6565 const char ArHelp[] = R"(
6666 OVERVIEW: LLVM Archiver
6767
68 USAGE: llvm-ar [options] [-][modifiers] [relpos] [files]
68 USAGE: llvm-ar [options] [-][modifiers] [relpos] [count] [files]
6969 llvm-ar -M [
7070
7171 OPTIONS:
9696 [i] - put [files] before [relpos] (same as [b])
9797 [l] - ignored for compatibility
9898 [L] - add archive's contents
99 [N] - use instance [count] of name
99100 [o] - preserve original dates
100101 [P] - use full names when matching (implied for thin archives)
101102 [s] - create an archive index (cf. ranlib)
186187 // one variable.
187188 static std::string RelPos;
188189
190 // Count parameter for 'N' modifier. This variable specifies which file should
191 // match for extract/delete operations when there are multiple matches. This is
192 // 1-indexed. A value of 0 is invalid, and implies 'N' is not used.
193 static int CountParam = 0;
194
189195 // This variable holds the name of the archive file as given on the
190196 // command line.
191197 static std::string ArchiveName;
203209 if (PositionalArgs.empty())
204210 fail("Expected [relpos] for a, b, or i modifier");
205211 RelPos = PositionalArgs[0];
212 PositionalArgs.erase(PositionalArgs.begin());
213 }
214
215 // Extract the parameter from the command line for the [count] argument
216 // associated with the N modifier
217 static void getCountParam() {
218 if (PositionalArgs.empty())
219 fail("Expected [count] for N modifier");
220 auto CountParamArg = StringRef(PositionalArgs[0]);
221 if (CountParamArg.getAsInteger(10, CountParam))
222 fail("Value for [count] must be numeric, got: " + CountParamArg);
223 if (CountParam < 1)
224 fail("Value for [count] must be positive, got: " + CountParamArg);
206225 PositionalArgs.erase(PositionalArgs.begin());
207226 }
208227
335354 case 'U':
336355 Deterministic = false;
337356 break;
357 case 'N':
358 getCountParam();
359 break;
338360 case 'T':
339361 Thin = true;
340362 // Thin archives store path names, so P should be forced.
370392 fail("Only one operation may be specified");
371393 if (NumPositional > 1)
372394 fail("You may only specify one of a, b, and i modifiers");
373 if (AddAfter || AddBefore) {
395 if (AddAfter || AddBefore)
374396 if (Operation != Move && Operation != ReplaceOrInsert)
375397 fail("The 'a', 'b' and 'i' modifiers can only be specified with "
376398 "the 'm' or 'r' operations");
377 }
399 if (CountParam)
400 if (Operation != Extract && Operation != Delete)
401 fail("The 'N' modifier can only be specified with the 'x' or 'd' "
402 "operations");
378403 if (OriginalDates && Operation != Extract)
379404 fail("The 'o' modifier is only applicable to the 'x' operation");
380405 if (OnlyUpdate && Operation != ReplaceOrInsert)
512537 fail("extracting from a thin archive is not supported");
513538
514539 bool Filter = !Members.empty();
540 StringMap MemberCount;
515541 {
516542 Error Err = Error::success();
517543 for (auto &C : OldArchive->children(Err)) {
524550 return Name == normalizePath(Path);
525551 });
526552 if (I == Members.end())
553 continue;
554 if (CountParam && ++MemberCount[Name] != CountParam)
527555 continue;
528556 Members.erase(I);
529557 }
626654 static InsertAction computeInsertAction(ArchiveOperation Operation,
627655 const object::Archive::Child &Member,
628656 StringRef Name,
629 std::vector::iterator &Pos) {
657 std::vector::iterator &Pos,
658 StringMap &MemberCount) {
630659 if (Operation == QuickAppend || Members.empty())
631660 return IA_AddOldMember;
632
633661 auto MI = find_if(
634662 Members, [Name](StringRef Path) { return Name == normalizePath(Path); });
635663
638666
639667 Pos = MI;
640668
641 if (Operation == Delete)
669 if (Operation == Delete) {
670 if (CountParam && ++MemberCount[Name] != CountParam)
671 return IA_AddOldMember;
642672 return IA_Delete;
673 }
643674
644675 if (Operation == Move)
645676 return IA_MoveOldMember;
682713 StringRef PosName = normalizePath(RelPos);
683714 if (OldArchive) {
684715 Error Err = Error::success();
716 StringMap MemberCount;
685717 for (auto &Child : OldArchive->children(Err)) {
686718 int Pos = Ret.size();
687719 Expected NameOrErr = Child.getName();
697729
698730 std::vector::iterator MemberI = Members.end();
699731 InsertAction Action =
700 computeInsertAction(Operation, Child, Name, MemberI);
732 computeInsertAction(Operation, Child, Name, MemberI, MemberCount);
701733 switch (Action) {
702734 case IA_AddOldMember:
703735 addChildMember(Ret, Child, /*FlattenArchive=*/Thin);
714746 addMember(Moved, *MemberI);
715747 break;
716748 }
717 if (MemberI != Members.end())
749 // When processing elements with the count param, we need to preserve the
750 // full members list when iterating over all archive members. For
751 // instance, "llvm-ar dN 2 archive.a member.o" should delete the second
752 // file named member.o it sees; we are not done with member.o the first
753 // time we see it in the archive.
754 if (MemberI != Members.end() && !CountParam)
718755 Members.erase(MemberI);
719756 }
720757 failIfError(std::move(Err));