aboutsummaryrefslogtreecommitdiff
path: root/tools/llvm-ar
diff options
context:
space:
mode:
Diffstat (limited to 'tools/llvm-ar')
-rw-r--r--tools/llvm-ar/llvm-ar.cpp168
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) {