diff options
Diffstat (limited to 'tools/llvm-ar')
-rw-r--r-- | tools/llvm-ar/llvm-ar.cpp | 168 |
1 files changed, 130 insertions, 38 deletions
diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp index 1c453ee0b569..91746d0fab37 100644 --- a/tools/llvm-ar/llvm-ar.cpp +++ b/tools/llvm-ar/llvm-ar.cpp @@ -1,9 +1,8 @@ //===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -67,7 +66,7 @@ OPTIONS: const char ArHelp[] = R"( OVERVIEW: LLVM Archiver -USAGE: llvm-ar [options] [-]<operation>[modifiers] [relpos] <archive> [files] +USAGE: llvm-ar [options] [-]<operation>[modifiers] [relpos] [count] <archive> [files] llvm-ar -M [<mri-script] OPTIONS: @@ -79,6 +78,7 @@ OPTIONS: --plugin=<string> - Ignored for compatibility --help - Display available options --version - Display the version of this program + @<file> - read options from <file> OPERATIONS: d - delete [files] from the archive @@ -98,7 +98,9 @@ MODIFIERS: [i] - put [files] before [relpos] (same as [b]) [l] - ignored for compatibility [L] - add archive's contents + [N] - use instance [count] of name [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 @@ -169,16 +171,17 @@ enum ArchiveOperation { }; // 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 @@ -186,6 +189,11 @@ static bool AddLibrary = false; ///< 'L' modifier // one variable. static std::string RelPos; +// Count parameter for 'N' modifier. This variable specifies which file should +// match for extract/delete operations when there are multiple matches. This is +// 1-indexed. A value of 0 is invalid, and implies 'N' is not used. +static int CountParam = 0; + // This variable holds the name of the archive file as given on the // command line. static std::string ArchiveName; @@ -194,6 +202,9 @@ static std::string ArchiveName; // on the command line. static std::vector<StringRef> Members; +// Static buffer to hold StringRefs. +static BumpPtrAllocator Alloc; + // Extract the member filename from the command line for the [relpos] argument // associated with a, b, and i modifiers static void getRelPos() { @@ -203,6 +214,19 @@ static void getRelPos() { PositionalArgs.erase(PositionalArgs.begin()); } +// Extract the parameter from the command line for the [count] argument +// associated with the N modifier +static void getCountParam() { + if (PositionalArgs.empty()) + fail("Expected [count] for N modifier"); + auto CountParamArg = StringRef(PositionalArgs[0]); + if (CountParamArg.getAsInteger(10, CountParam)) + fail("Value for [count] must be numeric, got: " + CountParamArg); + if (CountParam < 1) + fail("Value for [count] must be positive, got: " + CountParamArg); + PositionalArgs.erase(PositionalArgs.begin()); +} + // Get the archive file name from the command line static void getArchive() { if (PositionalArgs.empty()) @@ -295,6 +319,9 @@ static ArchiveOperation parseCommandLine() { case 'o': OriginalDates = true; break; + case 'P': + CompareFullPath = true; + break; case 's': Symtab = true; MaybeJustCreateSymTab = true; @@ -329,8 +356,13 @@ static ArchiveOperation parseCommandLine() { case 'U': Deterministic = false; break; + case 'N': + getCountParam(); + break; case 'T': Thin = true; + // Thin archives store path names, so P should be forced. + CompareFullPath = true; break; case 'L': AddLibrary = true; @@ -362,11 +394,14 @@ static ArchiveOperation parseCommandLine() { fail("Only one operation may be specified"); if (NumPositional > 1) fail("You may only specify one of a, b, and i modifiers"); - if (AddAfter || AddBefore) { + if (AddAfter || AddBefore) if (Operation != Move && Operation != ReplaceOrInsert) fail("The 'a', 'b' and 'i' modifiers can only be specified with " "the 'm' or 'r' operations"); - } + if (CountParam) + if (Operation != Extract && Operation != Delete) + fail("The 'N' modifier can only be specified with the 'x' or 'd' " + "operations"); if (OriginalDates && Operation != Extract) fail("The 'o' modifier is only applicable to the 'x' operation"); if (OnlyUpdate && Operation != ReplaceOrInsert) @@ -430,12 +465,19 @@ static void doDisplayTable(StringRef Name, const object::Archive::Child &C) { } if (C.getParent()->isThin()) { - outs() << sys::path::parent_path(ArchiveName); - outs() << '/'; + if (!sys::path::is_absolute(Name)) { + StringRef ParentDir = sys::path::parent_path(ArchiveName); + if (!ParentDir.empty()) + outs() << sys::path::convert_to_slash(ParentDir) << '/'; + } } 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) { @@ -499,6 +541,7 @@ static void performReadOperation(ArchiveOperation Operation, fail("extracting from a thin archive is not supported"); bool Filter = !Members.empty(); + StringMap<int> MemberCount; { Error Err = Error::success(); for (auto &C : OldArchive->children(Err)) { @@ -507,9 +550,13 @@ static void performReadOperation(ArchiveOperation Operation, 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; + if (CountParam && ++MemberCount[Name] != CountParam) + continue; Members.erase(I); } @@ -545,6 +592,23 @@ static void addChildMember(std::vector<NewArchiveMember> &Members, Expected<NewArchiveMember> NMOrErr = NewArchiveMember::getOldMember(M, Deterministic); failIfError(NMOrErr.takeError()); + // If the child member we're trying to add is thin, use the path relative to + // the archive it's in, so the file resolves correctly. + if (Thin && FlattenArchive) { + StringSaver Saver(Alloc); + Expected<std::string> FileNameOrErr = M.getName(); + failIfError(FileNameOrErr.takeError()); + if (sys::path::is_absolute(*FileNameOrErr)) { + NMOrErr->MemberName = Saver.save(sys::path::convert_to_slash(*FileNameOrErr)); + } else { + FileNameOrErr = M.getFullName(); + failIfError(FileNameOrErr.takeError()); + Expected<std::string> PathOrErr = + computeArchiveRelativePath(ArchiveName, *FileNameOrErr); + NMOrErr->MemberName = Saver.save( + PathOrErr ? *PathOrErr : sys::path::convert_to_slash(*FileNameOrErr)); + } + } if (FlattenArchive && identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { Expected<std::string> FileNameOrErr = M.getFullName(); @@ -568,6 +632,23 @@ static void addMember(std::vector<NewArchiveMember> &Members, Expected<NewArchiveMember> NMOrErr = NewArchiveMember::getFile(FileName, Deterministic); failIfError(NMOrErr.takeError(), FileName); + StringSaver Saver(Alloc); + // For regular archives, use the basename of the object path for the member + // name. For thin archives, use the full relative paths so the file resolves + // correctly. + if (!Thin) { + NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); + } else { + if (sys::path::is_absolute(FileName)) + NMOrErr->MemberName = Saver.save(sys::path::convert_to_slash(FileName)); + else { + Expected<std::string> PathOrErr = + computeArchiveRelativePath(ArchiveName, FileName); + NMOrErr->MemberName = Saver.save( + PathOrErr ? *PathOrErr : sys::path::convert_to_slash(FileName)); + } + } + if (FlattenArchive && identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { object::Archive &Lib = readLibrary(FileName); @@ -581,8 +662,6 @@ static void addMember(std::vector<NewArchiveMember> &Members, return; } } - // Use the basename of the object path for the member name. - NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); Members.push_back(std::move(*NMOrErr)); } @@ -597,27 +676,29 @@ enum InsertAction { static InsertAction computeInsertAction(ArchiveOperation Operation, const object::Archive::Child &Member, StringRef Name, - std::vector<StringRef>::iterator &Pos) { + std::vector<StringRef>::iterator &Pos, + StringMap<int> &MemberCount) { 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; Pos = MI; - if (Operation == Delete) + if (Operation == Delete) { + if (CountParam && ++MemberCount[Name] != CountParam) + return IA_AddOldMember; return IA_Delete; + } if (Operation == Move) return IA_MoveOldMember; if (Operation == ReplaceOrInsert) { - StringRef PosName = sys::path::filename(RelPos); + StringRef PosName = normalizePath(RelPos); if (!OnlyUpdate) { if (PosName.empty()) return IA_AddNewMember; @@ -651,9 +732,10 @@ computeNewArchiveMembers(ArchiveOperation Operation, std::vector<NewArchiveMember> Ret; std::vector<NewArchiveMember> Moved; int InsertPos = -1; - StringRef PosName = sys::path::filename(RelPos); + StringRef PosName = normalizePath(RelPos); if (OldArchive) { Error Err = Error::success(); + StringMap<int> MemberCount; for (auto &Child : OldArchive->children(Err)) { int Pos = Ret.size(); Expected<StringRef> NameOrErr = Child.getName(); @@ -669,10 +751,10 @@ computeNewArchiveMembers(ArchiveOperation Operation, std::vector<StringRef>::iterator MemberI = Members.end(); InsertAction Action = - computeInsertAction(Operation, Child, Name, MemberI); + computeInsertAction(Operation, Child, Name, MemberI, MemberCount); switch (Action) { case IA_AddOldMember: - addChildMember(Ret, Child); + addChildMember(Ret, Child, /*FlattenArchive=*/Thin); break; case IA_AddNewMember: addMember(Ret, *MemberI); @@ -680,13 +762,18 @@ computeNewArchiveMembers(ArchiveOperation Operation, case IA_Delete: break; case IA_MoveOldMember: - addChildMember(Moved, Child); + addChildMember(Moved, Child, /*FlattenArchive=*/Thin); break; case IA_MoveNewMember: addMember(Moved, *MemberI); break; } - if (MemberI != Members.end()) + // When processing elements with the count param, we need to preserve the + // full members list when iterating over all archive members. For + // instance, "llvm-ar dN 2 archive.a member.o" should delete the second + // file named member.o it sees; we are not done with member.o the first + // time we see it in the archive. + if (MemberI != Members.end() && !CountParam) Members.erase(MemberI); } failIfError(std::move(Err)); @@ -843,6 +930,8 @@ static int performOperation(ArchiveOperation Operation, 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; } @@ -864,7 +953,7 @@ static int performOperation(ArchiveOperation Operation, } static void runMRIScript() { - enum class MRICommand { AddLib, AddMod, Create, Delete, Save, End, Invalid }; + enum class MRICommand { AddLib, AddMod, Create, CreateThin, Delete, Save, End, Invalid }; ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getSTDIN(); failIfError(Buf.getError()); @@ -888,6 +977,7 @@ static void runMRIScript() { .Case("addlib", MRICommand::AddLib) .Case("addmod", MRICommand::AddMod) .Case("create", MRICommand::Create) + .Case("createthin", MRICommand::CreateThin) .Case("delete", MRICommand::Delete) .Case("save", MRICommand::Save) .Case("end", MRICommand::End) @@ -899,7 +989,7 @@ static void runMRIScript() { { Error Err = Error::success(); for (auto &Member : Lib.children(Err)) - addChildMember(NewMembers, Member); + addChildMember(NewMembers, Member, /*FlattenArchive=*/Thin); failIfError(std::move(Err)); } break; @@ -907,6 +997,9 @@ static void runMRIScript() { case MRICommand::AddMod: addMember(NewMembers, Rest); break; + case MRICommand::CreateThin: + Thin = true; + LLVM_FALLTHROUGH; case MRICommand::Create: Create = true; if (!ArchiveName.empty()) @@ -916,7 +1009,7 @@ static void runMRIScript() { 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; @@ -951,7 +1044,6 @@ static bool handleGenericOption(StringRef arg) { static int ar_main(int argc, char **argv) { SmallVector<const char *, 0> Argv(argv, argv + argc); - BumpPtrAllocator Alloc; StringSaver Saver(Alloc); cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv); for (size_t i = 1; i < Argv.size(); ++i) { |