1 //===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Builds up (relatively) standard unix archive files (.a) containing LLVM
10 // bitcode or other files.
12 //===----------------------------------------------------------------------===//
14 #include "llvm/ADT/StringSwitch.h"
15 #include "llvm/ADT/Triple.h"
16 #include "llvm/IR/LLVMContext.h"
17 #include "llvm/Object/Archive.h"
18 #include "llvm/Object/ArchiveWriter.h"
19 #include "llvm/Object/MachO.h"
20 #include "llvm/Object/ObjectFile.h"
21 #include "llvm/Support/Chrono.h"
22 #include "llvm/Support/CommandLine.h"
23 #include "llvm/Support/Errc.h"
24 #include "llvm/Support/FileSystem.h"
25 #include "llvm/Support/Format.h"
26 #include "llvm/Support/FormatVariadic.h"
27 #include "llvm/Support/InitLLVM.h"
28 #include "llvm/Support/LineIterator.h"
29 #include "llvm/Support/MemoryBuffer.h"
30 #include "llvm/Support/Path.h"
31 #include "llvm/Support/Process.h"
32 #include "llvm/Support/StringSaver.h"
33 #include "llvm/Support/TargetSelect.h"
34 #include "llvm/Support/ToolOutputFile.h"
35 #include "llvm/Support/WithColor.h"
36 #include "llvm/Support/raw_ostream.h"
37 #include "llvm/ToolDrivers/llvm-dlltool/DlltoolDriver.h"
38 #include "llvm/ToolDrivers/llvm-lib/LibDriver.h"
40 #if !defined(_MSC_VER) && !defined(__MINGW32__)
48 // The name this program was invoked as.
49 static StringRef ToolName
;
51 // The basename of this program.
52 static StringRef Stem
;
54 const char RanlibHelp
[] = R
"(
55 OVERVIEW: LLVM Ranlib (llvm-ranlib)
57 This program generates an index to speed access to archives
59 USAGE: llvm-ranlib <archive-file>
62 -help - Display available options
63 -version - Display the version of this program
66 const char ArHelp
[] = R
"(
67 OVERVIEW: LLVM Archiver
69 USAGE: llvm-ar [options] [-]<operation>[modifiers] [relpos] [count] <archive> [files]
70 llvm-ar -M [<mri-script]
73 --format - Archive format to create
78 --plugin=<string> - Ignored for compatibility
79 --help - Display available options
80 --version - Display the version of this program
81 @<file> - read options from <file>
84 d - delete [files] from the archive
85 m - move [files] in the archive
86 p - print [files] found in the archive
87 q - quick append [files] to the archive
88 r - replace or insert [files] into the archive
90 t - display contents of archive
91 x - extract [files] from the archive
94 [a] - put [files] after [relpos]
95 [b] - put [files] before [relpos] (same as [i])
96 [c] - do not warn if archive had to be created
97 [D] - use zero for timestamps and uids/gids (default)
98 [i] - put [files] before [relpos] (same as [b])
99 [l] - ignored for compatibility
100 [L] - add archive's contents
101 [N] - use instance [count] of name
102 [o] - preserve original dates
103 [P] - use full names when matching (implied for thin archives)
104 [s] - create an archive index (cf. ranlib)
105 [S] - do not build a symbol table
106 [T] - create a thin archive
107 [u] - update only [files] newer than archive contents
108 [U] - use actual timestamps and uids/gids
109 [v] - be verbose about actions taken
112 void printHelpMessage() {
113 if (Stem
.contains_lower("ranlib"))
114 outs() << RanlibHelp
;
115 else if (Stem
.contains_lower("ar"))
119 // Show the error message and exit.
120 LLVM_ATTRIBUTE_NORETURN
static void fail(Twine Error
) {
121 WithColor::error(errs(), ToolName
) << Error
<< "\n";
126 static void failIfError(std::error_code EC
, Twine Context
= "") {
130 std::string ContextStr
= Context
.str();
131 if (ContextStr
.empty())
133 fail(Context
+ ": " + EC
.message());
136 static void failIfError(Error E
, Twine Context
= "") {
140 handleAllErrors(std::move(E
), [&](const llvm::ErrorInfoBase
&EIB
) {
141 std::string ContextStr
= Context
.str();
142 if (ContextStr
.empty())
144 fail(Context
+ ": " + EIB
.message());
148 static SmallVector
<const char *, 256> PositionalArgs
;
153 enum Format
{ Default
, GNU
, BSD
, DARWIN
, Unknown
};
156 static Format FormatType
= Default
;
158 static std::string Options
;
160 // This enumeration delineates the kinds of operations on an archive
161 // that are permitted.
162 enum ArchiveOperation
{
163 Print
, ///< Print the contents of the archive
164 Delete
, ///< Delete the specified members
165 Move
, ///< Move members to end or as given by {a,b,i} modifiers
166 QuickAppend
, ///< Quickly append to end of archive
167 ReplaceOrInsert
, ///< Replace or Insert members
168 DisplayTable
, ///< Display the table of contents
169 Extract
, ///< Extract files back to file system
170 CreateSymTab
///< Create a symbol table in an existing archive
173 // Modifiers to follow operation to vary behavior
174 static bool AddAfter
= false; ///< 'a' modifier
175 static bool AddBefore
= false; ///< 'b' modifier
176 static bool Create
= false; ///< 'c' modifier
177 static bool OriginalDates
= false; ///< 'o' modifier
178 static bool CompareFullPath
= false; ///< 'P' modifier
179 static bool OnlyUpdate
= false; ///< 'u' modifier
180 static bool Verbose
= false; ///< 'v' modifier
181 static bool Symtab
= true; ///< 's' modifier
182 static bool Deterministic
= true; ///< 'D' and 'U' modifiers
183 static bool Thin
= false; ///< 'T' modifier
184 static bool AddLibrary
= false; ///< 'L' modifier
186 // Relative Positional Argument (for insert/move). This variable holds
187 // the name of the archive member to which the 'a', 'b' or 'i' modifier
188 // refers. Only one of 'a', 'b' or 'i' can be specified so we only need
190 static std::string RelPos
;
192 // Count parameter for 'N' modifier. This variable specifies which file should
193 // match for extract/delete operations when there are multiple matches. This is
194 // 1-indexed. A value of 0 is invalid, and implies 'N' is not used.
195 static int CountParam
= 0;
197 // This variable holds the name of the archive file as given on the
199 static std::string ArchiveName
;
201 // This variable holds the list of member files to proecess, as given
202 // on the command line.
203 static std::vector
<StringRef
> Members
;
205 // Static buffer to hold StringRefs.
206 static BumpPtrAllocator Alloc
;
208 // Extract the member filename from the command line for the [relpos] argument
209 // associated with a, b, and i modifiers
210 static void getRelPos() {
211 if (PositionalArgs
.empty())
212 fail("expected [relpos] for 'a', 'b', or 'i' modifier");
213 RelPos
= PositionalArgs
[0];
214 PositionalArgs
.erase(PositionalArgs
.begin());
217 // Extract the parameter from the command line for the [count] argument
218 // associated with the N modifier
219 static void getCountParam() {
220 if (PositionalArgs
.empty())
221 fail("expected [count] for 'N' modifier");
222 auto CountParamArg
= StringRef(PositionalArgs
[0]);
223 if (CountParamArg
.getAsInteger(10, CountParam
))
224 fail("value for [count] must be numeric, got: " + CountParamArg
);
226 fail("value for [count] must be positive, got: " + CountParamArg
);
227 PositionalArgs
.erase(PositionalArgs
.begin());
230 // Get the archive file name from the command line
231 static void getArchive() {
232 if (PositionalArgs
.empty())
233 fail("an archive name must be specified");
234 ArchiveName
= PositionalArgs
[0];
235 PositionalArgs
.erase(PositionalArgs
.begin());
238 // Copy over remaining items in PositionalArgs to our Members vector
239 static void getMembers() {
240 for (auto &Arg
: PositionalArgs
)
241 Members
.push_back(Arg
);
244 std::vector
<std::unique_ptr
<MemoryBuffer
>> ArchiveBuffers
;
245 std::vector
<std::unique_ptr
<object::Archive
>> Archives
;
247 static object::Archive
&readLibrary(const Twine
&Library
) {
248 auto BufOrErr
= MemoryBuffer::getFile(Library
, -1, false);
249 failIfError(BufOrErr
.getError(), "could not open library " + Library
);
250 ArchiveBuffers
.push_back(std::move(*BufOrErr
));
252 object::Archive::create(ArchiveBuffers
.back()->getMemBufferRef());
253 failIfError(errorToErrorCode(LibOrErr
.takeError()),
254 "could not parse library");
255 Archives
.push_back(std::move(*LibOrErr
));
256 return *Archives
.back();
259 static void runMRIScript();
261 // Parse the command line options as presented and return the operation
262 // specified. Process all modifiers and check to make sure that constraints on
263 // modifier/operation pairs have not been violated.
264 static ArchiveOperation
parseCommandLine() {
266 if (!PositionalArgs
.empty() || !Options
.empty())
267 fail("cannot mix -M and other options");
271 // Keep track of number of operations. We can only specify one
273 unsigned NumOperations
= 0;
275 // Keep track of the number of positional modifiers (a,b,i). Only
276 // one can be specified.
277 unsigned NumPositional
= 0;
279 // Keep track of which operation was requested
280 ArchiveOperation Operation
;
282 bool MaybeJustCreateSymTab
= false;
284 for (unsigned i
= 0; i
< Options
.size(); ++i
) {
285 switch (Options
[i
]) {
300 Operation
= QuickAppend
;
304 Operation
= ReplaceOrInsert
;
308 Operation
= DisplayTable
;
317 case 'l': /* accepted but unused */
320 OriginalDates
= true;
323 CompareFullPath
= true;
327 MaybeJustCreateSymTab
= true;
354 Deterministic
= true;
357 Deterministic
= false;
364 // Thin archives store path names, so P should be forced.
365 CompareFullPath
= true;
371 fail(std::string("unknown option ") + Options
[i
]);
375 // At this point, the next thing on the command line must be
379 // Everything on the command line at this point is a member.
382 if (NumOperations
== 0 && MaybeJustCreateSymTab
) {
384 Operation
= CreateSymTab
;
385 if (!Members
.empty())
386 fail("the 's' operation takes only an archive as argument");
389 // Perform various checks on the operation/modifier specification
390 // to make sure we are dealing with a legal request.
391 if (NumOperations
== 0)
392 fail("you must specify at least one of the operations");
393 if (NumOperations
> 1)
394 fail("only one operation may be specified");
395 if (NumPositional
> 1)
396 fail("you may only specify one of 'a', 'b', and 'i' modifiers");
397 if (AddAfter
|| AddBefore
)
398 if (Operation
!= Move
&& Operation
!= ReplaceOrInsert
)
399 fail("the 'a', 'b' and 'i' modifiers can only be specified with "
400 "the 'm' or 'r' operations");
402 if (Operation
!= Extract
&& Operation
!= Delete
)
403 fail("the 'N' modifier can only be specified with the 'x' or 'd' "
405 if (OriginalDates
&& Operation
!= Extract
)
406 fail("the 'o' modifier is only applicable to the 'x' operation");
407 if (OnlyUpdate
&& Operation
!= ReplaceOrInsert
)
408 fail("the 'u' modifier is only applicable to the 'r' operation");
409 if (AddLibrary
&& Operation
!= QuickAppend
)
410 fail("the 'L' modifier is only applicable to the 'q' operation");
412 // Return the parsed operation to the caller
416 // Implements the 'p' operation. This function traverses the archive
417 // looking for members that match the path list.
418 static void doPrint(StringRef Name
, const object::Archive::Child
&C
) {
420 outs() << "Printing " << Name
<< "\n";
422 Expected
<StringRef
> DataOrErr
= C
.getBuffer();
423 failIfError(DataOrErr
.takeError());
424 StringRef Data
= *DataOrErr
;
425 outs().write(Data
.data(), Data
.size());
428 // Utility function for printing out the file mode when the 't' operation is in
430 static void printMode(unsigned mode
) {
431 outs() << ((mode
& 004) ? "r" : "-");
432 outs() << ((mode
& 002) ? "w" : "-");
433 outs() << ((mode
& 001) ? "x" : "-");
436 // Implement the 't' operation. This function prints out just
437 // the file names of each of the members. However, if verbose mode is requested
438 // ('v' modifier) then the file type, permission mode, user, group, size, and
439 // modification time are also printed.
440 static void doDisplayTable(StringRef Name
, const object::Archive::Child
&C
) {
442 Expected
<sys::fs::perms
> ModeOrErr
= C
.getAccessMode();
443 failIfError(ModeOrErr
.takeError());
444 sys::fs::perms Mode
= ModeOrErr
.get();
445 printMode((Mode
>> 6) & 007);
446 printMode((Mode
>> 3) & 007);
447 printMode(Mode
& 007);
448 Expected
<unsigned> UIDOrErr
= C
.getUID();
449 failIfError(UIDOrErr
.takeError());
450 outs() << ' ' << UIDOrErr
.get();
451 Expected
<unsigned> GIDOrErr
= C
.getGID();
452 failIfError(GIDOrErr
.takeError());
453 outs() << '/' << GIDOrErr
.get();
454 Expected
<uint64_t> Size
= C
.getSize();
455 failIfError(Size
.takeError());
456 outs() << ' ' << format("%6llu", Size
.get());
457 auto ModTimeOrErr
= C
.getLastModified();
458 failIfError(ModTimeOrErr
.takeError());
459 // Note: formatv() only handles the default TimePoint<>, which is in
461 // TODO: fix format_provider<TimePoint<>> to allow other units.
462 sys::TimePoint
<> ModTimeInNs
= ModTimeOrErr
.get();
463 outs() << ' ' << formatv("{0:%b %e %H:%M %Y}", ModTimeInNs
);
467 if (C
.getParent()->isThin()) {
468 if (!sys::path::is_absolute(Name
)) {
469 StringRef ParentDir
= sys::path::parent_path(ArchiveName
);
470 if (!ParentDir
.empty())
471 outs() << sys::path::convert_to_slash(ParentDir
) << '/';
474 outs() << Name
<< "\n";
477 static std::string
normalizePath(StringRef Path
) {
478 return CompareFullPath
? sys::path::convert_to_slash(Path
)
479 : std::string(sys::path::filename(Path
));
482 // Implement the 'x' operation. This function extracts files back to the file
484 static void doExtract(StringRef Name
, const object::Archive::Child
&C
) {
485 // Retain the original mode.
486 Expected
<sys::fs::perms
> ModeOrErr
= C
.getAccessMode();
487 failIfError(ModeOrErr
.takeError());
488 sys::fs::perms Mode
= ModeOrErr
.get();
491 failIfError(sys::fs::openFileForWrite(sys::path::filename(Name
), FD
,
492 sys::fs::CD_CreateAlways
,
493 sys::fs::OF_None
, Mode
),
497 raw_fd_ostream
file(FD
, false);
499 // Get the data and its length
500 Expected
<StringRef
> BufOrErr
= C
.getBuffer();
501 failIfError(BufOrErr
.takeError());
502 StringRef Data
= BufOrErr
.get();
505 file
.write(Data
.data(), Data
.size());
508 // If we're supposed to retain the original modification times, etc. do so
511 auto ModTimeOrErr
= C
.getLastModified();
512 failIfError(ModTimeOrErr
.takeError());
514 sys::fs::setLastAccessAndModificationTime(FD
, ModTimeOrErr
.get()));
518 fail("Could not close the file");
521 static bool shouldCreateArchive(ArchiveOperation Op
) {
532 case ReplaceOrInsert
:
536 llvm_unreachable("Missing entry in covered switch.");
539 static void performReadOperation(ArchiveOperation Operation
,
540 object::Archive
*OldArchive
) {
541 if (Operation
== Extract
&& OldArchive
->isThin())
542 fail("extracting from a thin archive is not supported");
544 bool Filter
= !Members
.empty();
545 StringMap
<int> MemberCount
;
547 Error Err
= Error::success();
548 for (auto &C
: OldArchive
->children(Err
)) {
549 Expected
<StringRef
> NameOrErr
= C
.getName();
550 failIfError(NameOrErr
.takeError());
551 StringRef Name
= NameOrErr
.get();
554 auto I
= find_if(Members
, [Name
](StringRef Path
) {
555 return Name
== normalizePath(Path
);
557 if (I
== Members
.end())
559 if (CountParam
&& ++MemberCount
[Name
] != CountParam
)
566 llvm_unreachable("Not a read operation");
571 doDisplayTable(Name
, C
);
578 failIfError(std::move(Err
));
583 for (StringRef Name
: Members
)
584 WithColor::error(errs(), ToolName
) << "'" << Name
<< "' was not found\n";
588 static void addChildMember(std::vector
<NewArchiveMember
> &Members
,
589 const object::Archive::Child
&M
,
590 bool FlattenArchive
= false) {
591 if (Thin
&& !M
.getParent()->isThin())
592 fail("cannot convert a regular archive to a thin one");
593 Expected
<NewArchiveMember
> NMOrErr
=
594 NewArchiveMember::getOldMember(M
, Deterministic
);
595 failIfError(NMOrErr
.takeError());
596 // If the child member we're trying to add is thin, use the path relative to
597 // the archive it's in, so the file resolves correctly.
598 if (Thin
&& FlattenArchive
) {
599 StringSaver
Saver(Alloc
);
600 Expected
<std::string
> FileNameOrErr
= M
.getName();
601 failIfError(FileNameOrErr
.takeError());
602 if (sys::path::is_absolute(*FileNameOrErr
)) {
603 NMOrErr
->MemberName
= Saver
.save(sys::path::convert_to_slash(*FileNameOrErr
));
605 FileNameOrErr
= M
.getFullName();
606 failIfError(FileNameOrErr
.takeError());
607 Expected
<std::string
> PathOrErr
=
608 computeArchiveRelativePath(ArchiveName
, *FileNameOrErr
);
609 NMOrErr
->MemberName
= Saver
.save(
610 PathOrErr
? *PathOrErr
: sys::path::convert_to_slash(*FileNameOrErr
));
613 if (FlattenArchive
&&
614 identify_magic(NMOrErr
->Buf
->getBuffer()) == file_magic::archive
) {
615 Expected
<std::string
> FileNameOrErr
= M
.getFullName();
616 failIfError(FileNameOrErr
.takeError());
617 object::Archive
&Lib
= readLibrary(*FileNameOrErr
);
618 // When creating thin archives, only flatten if the member is also thin.
619 if (!Thin
|| Lib
.isThin()) {
620 Error Err
= Error::success();
621 // Only Thin archives are recursively flattened.
622 for (auto &Child
: Lib
.children(Err
))
623 addChildMember(Members
, Child
, /*FlattenArchive=*/Thin
);
624 failIfError(std::move(Err
));
628 Members
.push_back(std::move(*NMOrErr
));
631 static void addMember(std::vector
<NewArchiveMember
> &Members
,
632 StringRef FileName
, bool FlattenArchive
= false) {
633 Expected
<NewArchiveMember
> NMOrErr
=
634 NewArchiveMember::getFile(FileName
, Deterministic
);
635 failIfError(NMOrErr
.takeError(), FileName
);
636 StringSaver
Saver(Alloc
);
637 // For regular archives, use the basename of the object path for the member
638 // name. For thin archives, use the full relative paths so the file resolves
641 NMOrErr
->MemberName
= sys::path::filename(NMOrErr
->MemberName
);
643 if (sys::path::is_absolute(FileName
))
644 NMOrErr
->MemberName
= Saver
.save(sys::path::convert_to_slash(FileName
));
646 Expected
<std::string
> PathOrErr
=
647 computeArchiveRelativePath(ArchiveName
, FileName
);
648 NMOrErr
->MemberName
= Saver
.save(
649 PathOrErr
? *PathOrErr
: sys::path::convert_to_slash(FileName
));
653 if (FlattenArchive
&&
654 identify_magic(NMOrErr
->Buf
->getBuffer()) == file_magic::archive
) {
655 object::Archive
&Lib
= readLibrary(FileName
);
656 // When creating thin archives, only flatten if the member is also thin.
657 if (!Thin
|| Lib
.isThin()) {
658 Error Err
= Error::success();
659 // Only Thin archives are recursively flattened.
660 for (auto &Child
: Lib
.children(Err
))
661 addChildMember(Members
, Child
, /*FlattenArchive=*/Thin
);
662 failIfError(std::move(Err
));
666 Members
.push_back(std::move(*NMOrErr
));
677 static InsertAction
computeInsertAction(ArchiveOperation Operation
,
678 const object::Archive::Child
&Member
,
680 std::vector
<StringRef
>::iterator
&Pos
,
681 StringMap
<int> &MemberCount
) {
682 if (Operation
== QuickAppend
|| Members
.empty())
683 return IA_AddOldMember
;
685 Members
, [Name
](StringRef Path
) { return Name
== normalizePath(Path
); });
687 if (MI
== Members
.end())
688 return IA_AddOldMember
;
692 if (Operation
== Delete
) {
693 if (CountParam
&& ++MemberCount
[Name
] != CountParam
)
694 return IA_AddOldMember
;
698 if (Operation
== Move
)
699 return IA_MoveOldMember
;
701 if (Operation
== ReplaceOrInsert
) {
704 return IA_AddNewMember
;
705 return IA_MoveNewMember
;
708 // We could try to optimize this to a fstat, but it is not a common
710 sys::fs::file_status Status
;
711 failIfError(sys::fs::status(*MI
, Status
), *MI
);
712 auto ModTimeOrErr
= Member
.getLastModified();
713 failIfError(ModTimeOrErr
.takeError());
714 if (Status
.getLastModificationTime() < ModTimeOrErr
.get()) {
716 return IA_AddOldMember
;
717 return IA_MoveOldMember
;
721 return IA_AddNewMember
;
722 return IA_MoveNewMember
;
724 llvm_unreachable("No such operation");
727 // We have to walk this twice and computing it is not trivial, so creating an
728 // explicit std::vector is actually fairly efficient.
729 static std::vector
<NewArchiveMember
>
730 computeNewArchiveMembers(ArchiveOperation Operation
,
731 object::Archive
*OldArchive
) {
732 std::vector
<NewArchiveMember
> Ret
;
733 std::vector
<NewArchiveMember
> Moved
;
736 std::string PosName
= normalizePath(RelPos
);
737 Error Err
= Error::success();
738 StringMap
<int> MemberCount
;
739 for (auto &Child
: OldArchive
->children(Err
)) {
740 int Pos
= Ret
.size();
741 Expected
<StringRef
> NameOrErr
= Child
.getName();
742 failIfError(NameOrErr
.takeError());
743 StringRef Name
= NameOrErr
.get();
744 if (Name
== PosName
) {
745 assert(AddAfter
|| AddBefore
);
752 std::vector
<StringRef
>::iterator MemberI
= Members
.end();
753 InsertAction Action
=
754 computeInsertAction(Operation
, Child
, Name
, MemberI
, MemberCount
);
756 case IA_AddOldMember
:
757 addChildMember(Ret
, Child
, /*FlattenArchive=*/Thin
);
759 case IA_AddNewMember
:
760 addMember(Ret
, *MemberI
);
764 case IA_MoveOldMember
:
765 addChildMember(Moved
, Child
, /*FlattenArchive=*/Thin
);
767 case IA_MoveNewMember
:
768 addMember(Moved
, *MemberI
);
771 // When processing elements with the count param, we need to preserve the
772 // full members list when iterating over all archive members. For
773 // instance, "llvm-ar dN 2 archive.a member.o" should delete the second
774 // file named member.o it sees; we are not done with member.o the first
775 // time we see it in the archive.
776 if (MemberI
!= Members
.end() && !CountParam
)
777 Members
.erase(MemberI
);
779 failIfError(std::move(Err
));
782 if (Operation
== Delete
)
785 if (!RelPos
.empty() && InsertPos
== -1)
786 fail("insertion point not found");
789 InsertPos
= Ret
.size();
791 assert(unsigned(InsertPos
) <= Ret
.size());
793 for (auto &M
: Moved
) {
794 Ret
.insert(Ret
.begin() + Pos
, std::move(M
));
799 assert(Operation
== QuickAppend
);
800 for (auto &Member
: Members
)
801 addMember(Ret
, Member
, /*FlattenArchive=*/true);
805 std::vector
<NewArchiveMember
> NewMembers
;
806 for (auto &Member
: Members
)
807 addMember(NewMembers
, Member
, /*FlattenArchive=*/Thin
);
808 Ret
.reserve(Ret
.size() + NewMembers
.size());
809 std::move(NewMembers
.begin(), NewMembers
.end(),
810 std::inserter(Ret
, std::next(Ret
.begin(), InsertPos
)));
815 static object::Archive::Kind
getDefaultForHost() {
816 return Triple(sys::getProcessTriple()).isOSDarwin()
817 ? object::Archive::K_DARWIN
818 : object::Archive::K_GNU
;
821 static object::Archive::Kind
getKindFromMember(const NewArchiveMember
&Member
) {
822 Expected
<std::unique_ptr
<object::ObjectFile
>> OptionalObject
=
823 object::ObjectFile::createObjectFile(Member
.Buf
->getMemBufferRef());
826 return isa
<object::MachOObjectFile
>(**OptionalObject
)
827 ? object::Archive::K_DARWIN
828 : object::Archive::K_GNU
;
830 // squelch the error in case we had a non-object file
831 consumeError(OptionalObject
.takeError());
832 return getDefaultForHost();
835 static void performWriteOperation(ArchiveOperation Operation
,
836 object::Archive
*OldArchive
,
837 std::unique_ptr
<MemoryBuffer
> OldArchiveBuf
,
838 std::vector
<NewArchiveMember
> *NewMembersP
) {
839 std::vector
<NewArchiveMember
> NewMembers
;
841 NewMembers
= computeNewArchiveMembers(Operation
, OldArchive
);
843 object::Archive::Kind Kind
;
844 switch (FormatType
) {
847 Kind
= object::Archive::K_GNU
;
849 Kind
= OldArchive
->kind();
850 else if (NewMembersP
)
851 Kind
= !NewMembersP
->empty() ? getKindFromMember(NewMembersP
->front())
852 : getDefaultForHost();
854 Kind
= !NewMembers
.empty() ? getKindFromMember(NewMembers
.front())
855 : getDefaultForHost();
858 Kind
= object::Archive::K_GNU
;
862 fail("only the gnu format has a thin mode");
863 Kind
= object::Archive::K_BSD
;
867 fail("only the gnu format has a thin mode");
868 Kind
= object::Archive::K_DARWIN
;
871 llvm_unreachable("");
875 writeArchive(ArchiveName
, NewMembersP
? *NewMembersP
: NewMembers
, Symtab
,
876 Kind
, Deterministic
, Thin
, std::move(OldArchiveBuf
));
877 failIfError(std::move(E
), ArchiveName
);
880 static void createSymbolTable(object::Archive
*OldArchive
) {
881 // When an archive is created or modified, if the s option is given, the
882 // resulting archive will have a current symbol table. If the S option
883 // is given, it will have no symbol table.
884 // In summary, we only need to update the symbol table if we have none.
885 // This is actually very common because of broken build systems that think
886 // they have to run ranlib.
887 if (OldArchive
->hasSymbolTable())
890 performWriteOperation(CreateSymTab
, OldArchive
, nullptr, nullptr);
893 static void performOperation(ArchiveOperation Operation
,
894 object::Archive
*OldArchive
,
895 std::unique_ptr
<MemoryBuffer
> OldArchiveBuf
,
896 std::vector
<NewArchiveMember
> *NewMembers
) {
901 performReadOperation(Operation
, OldArchive
);
907 case ReplaceOrInsert
:
908 performWriteOperation(Operation
, OldArchive
, std::move(OldArchiveBuf
),
912 createSymbolTable(OldArchive
);
915 llvm_unreachable("Unknown operation.");
918 static int performOperation(ArchiveOperation Operation
,
919 std::vector
<NewArchiveMember
> *NewMembers
) {
920 // Create or open the archive object.
921 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> Buf
=
922 MemoryBuffer::getFile(ArchiveName
, -1, false);
923 std::error_code EC
= Buf
.getError();
924 if (EC
&& EC
!= errc::no_such_file_or_directory
)
925 fail("error opening '" + ArchiveName
+ "': " + EC
.message());
928 Error Err
= Error::success();
929 object::Archive
Archive(Buf
.get()->getMemBufferRef(), Err
);
930 EC
= errorToErrorCode(std::move(Err
));
931 failIfError(EC
, "error loading '" + ArchiveName
+ "': " + EC
.message());
932 if (Archive
.isThin())
933 CompareFullPath
= true;
934 performOperation(Operation
, &Archive
, std::move(Buf
.get()), NewMembers
);
938 assert(EC
== errc::no_such_file_or_directory
);
940 if (!shouldCreateArchive(Operation
)) {
941 failIfError(EC
, Twine("error loading '") + ArchiveName
+ "'");
944 // Produce a warning if we should and we're creating the archive
945 WithColor::warning(errs(), ToolName
)
946 << "creating " << ArchiveName
<< "\n";
950 performOperation(Operation
, nullptr, nullptr, NewMembers
);
954 static void runMRIScript() {
955 enum class MRICommand
{ AddLib
, AddMod
, Create
, CreateThin
, Delete
, Save
, End
, Invalid
};
957 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> Buf
= MemoryBuffer::getSTDIN();
958 failIfError(Buf
.getError());
959 const MemoryBuffer
&Ref
= *Buf
.get();
961 std::vector
<NewArchiveMember
> NewMembers
;
963 for (line_iterator
I(Ref
, /*SkipBlanks*/ false), E
; I
!= E
; ++I
) {
965 Line
= Line
.split(';').first
;
966 Line
= Line
.split('*').first
;
970 StringRef CommandStr
, Rest
;
971 std::tie(CommandStr
, Rest
) = Line
.split(' ');
973 if (!Rest
.empty() && Rest
.front() == '"' && Rest
.back() == '"')
974 Rest
= Rest
.drop_front().drop_back();
975 auto Command
= StringSwitch
<MRICommand
>(CommandStr
.lower())
976 .Case("addlib", MRICommand::AddLib
)
977 .Case("addmod", MRICommand::AddMod
)
978 .Case("create", MRICommand::Create
)
979 .Case("createthin", MRICommand::CreateThin
)
980 .Case("delete", MRICommand::Delete
)
981 .Case("save", MRICommand::Save
)
982 .Case("end", MRICommand::End
)
983 .Default(MRICommand::Invalid
);
986 case MRICommand::AddLib
: {
987 object::Archive
&Lib
= readLibrary(Rest
);
989 Error Err
= Error::success();
990 for (auto &Member
: Lib
.children(Err
))
991 addChildMember(NewMembers
, Member
, /*FlattenArchive=*/Thin
);
992 failIfError(std::move(Err
));
996 case MRICommand::AddMod
:
997 addMember(NewMembers
, Rest
);
999 case MRICommand::CreateThin
:
1002 case MRICommand::Create
:
1004 if (!ArchiveName
.empty())
1005 fail("editing multiple archives not supported");
1007 fail("file already saved");
1010 case MRICommand::Delete
: {
1011 std::string Name
= normalizePath(Rest
);
1012 llvm::erase_if(NewMembers
,
1013 [=](NewArchiveMember
&M
) { return M
.MemberName
== Name
; });
1016 case MRICommand::Save
:
1019 case MRICommand::End
:
1021 case MRICommand::Invalid
:
1022 fail("unknown command: " + CommandStr
);
1026 // Nothing to do if not saved.
1028 performOperation(ReplaceOrInsert
, &NewMembers
);
1032 static bool handleGenericOption(StringRef arg
) {
1033 if (arg
== "-help" || arg
== "--help") {
1037 if (arg
== "-version" || arg
== "--version") {
1038 cl::PrintVersionMessage();
1044 static int ar_main(int argc
, char **argv
) {
1045 SmallVector
<const char *, 0> Argv(argv
, argv
+ argc
);
1046 StringSaver
Saver(Alloc
);
1047 cl::ExpandResponseFiles(Saver
, cl::TokenizeGNUCommandLine
, Argv
);
1048 for (size_t i
= 1; i
< Argv
.size(); ++i
) {
1049 StringRef Arg
= Argv
[i
];
1051 auto MatchFlagWithArg
= [&](const char *expected
) {
1052 size_t len
= strlen(expected
);
1053 if (Arg
== expected
) {
1054 if (++i
>= Argv
.size())
1055 fail(std::string(expected
) + " requires an argument");
1059 if (Arg
.startswith(expected
) && Arg
.size() > len
&& Arg
[len
] == '=') {
1060 match
= Arg
.data() + len
+ 1;
1065 if (handleGenericOption(Argv
[i
]))
1068 for (; i
< Argv
.size(); ++i
)
1069 PositionalArgs
.push_back(Argv
[i
]);
1072 if (Arg
[0] == '-') {
1073 if (Arg
.startswith("--"))
1079 } else if (MatchFlagWithArg("format")) {
1080 FormatType
= StringSwitch
<Format
>(match
)
1081 .Case("default", Default
)
1083 .Case("darwin", DARWIN
)
1086 if (FormatType
== Unknown
)
1087 fail(std::string("Invalid format ") + match
);
1088 } else if (MatchFlagWithArg("plugin")) {
1091 Options
+= Argv
[i
] + 1;
1093 } else if (Options
.empty()) {
1096 PositionalArgs
.push_back(Argv
[i
]);
1099 ArchiveOperation Operation
= parseCommandLine();
1100 return performOperation(Operation
, nullptr);
1103 static int ranlib_main(int argc
, char **argv
) {
1104 bool ArchiveSpecified
= false;
1105 for (int i
= 1; i
< argc
; ++i
) {
1106 if (handleGenericOption(argv
[i
])) {
1109 if (ArchiveSpecified
)
1110 fail("exactly one archive should be specified");
1111 ArchiveSpecified
= true;
1112 ArchiveName
= argv
[i
];
1115 return performOperation(CreateSymTab
, nullptr);
1118 int main(int argc
, char **argv
) {
1119 InitLLVM
X(argc
, argv
);
1122 llvm::InitializeAllTargetInfos();
1123 llvm::InitializeAllTargetMCs();
1124 llvm::InitializeAllAsmParsers();
1126 Stem
= sys::path::stem(ToolName
);
1127 if (Stem
.contains_lower("dlltool"))
1128 return dlltoolDriverMain(makeArrayRef(argv
, argc
));
1130 if (Stem
.contains_lower("ranlib"))
1131 return ranlib_main(argc
, argv
);
1133 if (Stem
.contains_lower("lib"))
1134 return libDriverMain(makeArrayRef(argv
, argc
));
1136 if (Stem
.contains_lower("ar"))
1137 return ar_main(argc
, argv
);
1138 fail("not ranlib, ar, lib or dlltool");