authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-29 19:31:30-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-05-29 19:31:30-04:00
log322215bf0afb32335dc75622632d411369a11e0a
treeb14fedec819120cf06f5ea6255b0b194e32812e1
parentc12704a3395a11c28569ebb160e6ac33091d983a
parente078b4f20ea0656c02f957520c182735c60593de
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8933 from ziglang/zig-ar

stage2: add `zig ar` subcommand

3 files changed, 1593 insertions(+), 7 deletions(-)

CMakeLists.txt+1
......@@ -319,6 +319,7 @@ set(OPTIMIZED_C_SOURCES
319319set(ZIG_CPP_SOURCES
320320 # These are planned to stay even when we are self-hosted.
321321 "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
322 "${CMAKE_SOURCE_DIR}/src/zig_llvm-ar.cpp"
322323 "${CMAKE_SOURCE_DIR}/src/zig_clang.cpp"
323324 "${CMAKE_SOURCE_DIR}/src/zig_clang_driver.cpp"
324325 "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1_main.cpp"
src/main.zig+38-7
......@@ -43,22 +43,29 @@ const normal_usage =
4343 \\Commands:
4444 \\
4545 \\ build Build project from build.zig
46 \\ init-exe Initialize a `zig build` application in the cwd
47 \\ init-lib Initialize a `zig build` library in the cwd
48 \\
49 \\ ast-check Look for simple compile errors in any set of files
4650 \\ build-exe Create executable from source or object files
4751 \\ build-lib Create library from source or object files
4852 \\ build-obj Create object from source or object files
53 \\ fmt Reformat Zig source into canonical form
54 \\ run Create executable and run immediately
55 \\ test Create and run a test build
56 \\ translate-c Convert C code to Zig code
57 \\
58 \\ ar Use Zig as a drop-in archiver
4959 \\ cc Use Zig as a drop-in C compiler
5060 \\ c++ Use Zig as a drop-in C++ compiler
61 \\ dlltool Use Zig as a drop-in dlltool.exe
62 \\ lib Use Zig as a drop-in lib.exe
63 \\ ranlib Use Zig as a drop-in ranlib
64 \\
5165 \\ env Print lib path, std path, cache directory, and version
52 \\ fmt Reformat Zig source into canonical form
53 \\ ast-check Look for simple compile errors in any set of files
5466 \\ help Print this help and exit
55 \\ init-exe Initialize a `zig build` application in the cwd
56 \\ init-lib Initialize a `zig build` library in the cwd
5767 \\ libc Display native libc paths file or validate one
58 \\ run Create executable and run immediately
59 \\ translate-c Convert C code to Zig code
6068 \\ targets List available compilation targets
61 \\ test Create and run a test build
6269 \\ version Print version number and exit
6370 \\ zen Print Zen of Zig and exit
6471 \\
......@@ -201,6 +208,12 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
201208 return buildOutputType(gpa, arena, args, .zig_test);
202209 } else if (mem.eql(u8, cmd, "run")) {
203210 return buildOutputType(gpa, arena, args, .run);
211 } else if (mem.eql(u8, cmd, "dlltool") or
212 mem.eql(u8, cmd, "ranlib") or
213 mem.eql(u8, cmd, "lib") or
214 mem.eql(u8, cmd, "ar"))
215 {
216 return punt_to_llvm_ar(arena, args);
204217 } else if (mem.eql(u8, cmd, "cc")) {
205218 return buildOutputType(gpa, arena, args, .cc);
206219 } else if (mem.eql(u8, cmd, "c++")) {
......@@ -3197,6 +3210,7 @@ pub const info_zen =
31973210;
31983211
31993212extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
3213extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
32003214
32013215/// TODO https://github.com/ziglang/zig/issues/3257
32023216fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory} {
......@@ -3212,6 +3226,23 @@ fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory}
32123226 process.exit(@bitCast(u8, @truncate(i8, exit_code)));
32133227}
32143228
3229/// TODO https://github.com/ziglang/zig/issues/3257
3230fn punt_to_llvm_ar(arena: *Allocator, args: []const []const u8) error{OutOfMemory} {
3231 if (!build_options.have_llvm)
3232 fatal("`zig ar`, `zig dlltool`, `zig ranlib', and `zig lib` unavailable: compiler built without LLVM extensions", .{});
3233
3234 // Convert the args to the format llvm-ar expects.
3235 // We subtract 1 to shave off the zig binary from args[0].
3236 const argv = try arena.allocSentinel(?[*:0]u8, args.len - 1, null);
3237 for (args[1..]) |arg, i| {
3238 // TODO If there was an argsAllocZ we could avoid this allocation.
3239 argv[i] = try arena.dupeZ(u8, arg);
3240 }
3241 const argc = @intCast(c_int, argv.len);
3242 const exit_code = ZigLlvmAr_main(argc, argv.ptr);
3243 process.exit(@bitCast(u8, @truncate(i8, exit_code)));
3244}
3245
32153246/// The first argument determines which backend is invoked. The options are:
32163247/// * `ld.lld` - ELF
32173248/// * `ld64.lld` - Mach-O
src/zig_llvm-ar.cpp created+1554
......@@ -0,0 +1,1554 @@
1// In this file is copy+pasted WindowsSupport.h from LLVM 12.0.1-rc1.
2// This is so that we can patch it. The upstream sources are incorrectly
3// including "llvm/Config/config.h" which is a private header and thus not
4// available in the include files distributed with LLVM.
5// The patch here changes it to include "llvm/Config/config.h" instead.
6// Patch submitted upstream: https://reviews.llvm.org/D103370
7#if !defined(_WIN32)
8#define LLVM_SUPPORT_WINDOWSSUPPORT_H
9#endif
10
11//===- WindowsSupport.h - Common Windows Include File -----------*- C++ -*-===//
12//
13// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
14// See https://llvm.org/LICENSE.txt for license information.
15// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
16//
17//===----------------------------------------------------------------------===//
18//
19// This file defines things specific to Windows implementations. In addition to
20// providing some helpers for working with win32 APIs, this header wraps
21// <windows.h> with some portability macros. Always include WindowsSupport.h
22// instead of including <windows.h> directly.
23//
24//===----------------------------------------------------------------------===//
25
26//===----------------------------------------------------------------------===//
27//=== WARNING: Implementation here must contain only generic Win32 code that
28//=== is guaranteed to work on *all* Win32 variants.
29//===----------------------------------------------------------------------===//
30
31#ifndef LLVM_SUPPORT_WINDOWSSUPPORT_H
32#define LLVM_SUPPORT_WINDOWSSUPPORT_H
33
34// mingw-w64 tends to define it as 0x0502 in its headers.
35#undef _WIN32_WINNT
36#undef _WIN32_IE
37
38// Require at least Windows 7 API.
39#define _WIN32_WINNT 0x0601
40#define _WIN32_IE 0x0800 // MinGW at it again. FIXME: verify if still needed.
41#define WIN32_LEAN_AND_MEAN
42#ifndef NOMINMAX
43#define NOMINMAX
44#endif
45
46#include "llvm/ADT/SmallVector.h"
47#include "llvm/ADT/StringExtras.h"
48#include "llvm/ADT/StringRef.h"
49#include "llvm/ADT/Twine.h"
50#include "llvm/Config/llvm-config.h" // Get build system configuration settings
51#include "llvm/Support/Allocator.h"
52#include "llvm/Support/Chrono.h"
53#include "llvm/Support/Compiler.h"
54#include "llvm/Support/ErrorHandling.h"
55#include "llvm/Support/VersionTuple.h"
56#include <cassert>
57#include <string>
58#include <system_error>
59#include <windows.h>
60
61// Must be included after windows.h
62#include <wincrypt.h>
63
64namespace llvm {
65
66/// Determines if the program is running on Windows 8 or newer. This
67/// reimplements one of the helpers in the Windows 8.1 SDK, which are intended
68/// to supercede raw calls to GetVersionEx. Old SDKs, Cygwin, and MinGW don't
69/// yet have VersionHelpers.h, so we have our own helper.
70bool RunningWindows8OrGreater();
71
72/// Returns the Windows version as Major.Minor.0.BuildNumber. Uses
73/// RtlGetVersion or GetVersionEx under the hood depending on what is available.
74/// GetVersionEx is deprecated, but this API exposes the build number which can
75/// be useful for working around certain kernel bugs.
76llvm::VersionTuple GetWindowsOSVersion();
77
78bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix);
79
80// Include GetLastError() in a fatal error message.
81LLVM_ATTRIBUTE_NORETURN inline void ReportLastErrorFatal(const char *Msg) {
82 std::string ErrMsg;
83 MakeErrMsg(&ErrMsg, Msg);
84 llvm::report_fatal_error(ErrMsg);
85}
86
87template <typename HandleTraits>
88class ScopedHandle {
89 typedef typename HandleTraits::handle_type handle_type;
90 handle_type Handle;
91
92 ScopedHandle(const ScopedHandle &other) = delete;
93 void operator=(const ScopedHandle &other) = delete;
94public:
95 ScopedHandle()
96 : Handle(HandleTraits::GetInvalid()) {}
97
98 explicit ScopedHandle(handle_type h)
99 : Handle(h) {}
100
101 ~ScopedHandle() {
102 if (HandleTraits::IsValid(Handle))
103 HandleTraits::Close(Handle);
104 }
105
106 handle_type take() {
107 handle_type t = Handle;
108 Handle = HandleTraits::GetInvalid();
109 return t;
110 }
111
112 ScopedHandle &operator=(handle_type h) {
113 if (HandleTraits::IsValid(Handle))
114 HandleTraits::Close(Handle);
115 Handle = h;
116 return *this;
117 }
118
119 // True if Handle is valid.
120 explicit operator bool() const {
121 return HandleTraits::IsValid(Handle) ? true : false;
122 }
123
124 operator handle_type() const {
125 return Handle;
126 }
127};
128
129struct CommonHandleTraits {
130 typedef HANDLE handle_type;
131
132 static handle_type GetInvalid() {
133 return INVALID_HANDLE_VALUE;
134 }
135
136 static void Close(handle_type h) {
137 ::CloseHandle(h);
138 }
139
140 static bool IsValid(handle_type h) {
141 return h != GetInvalid();
142 }
143};
144
145struct JobHandleTraits : CommonHandleTraits {
146 static handle_type GetInvalid() {
147 return NULL;
148 }
149};
150
151struct CryptContextTraits : CommonHandleTraits {
152 typedef HCRYPTPROV handle_type;
153
154 static handle_type GetInvalid() {
155 return 0;
156 }
157
158 static void Close(handle_type h) {
159 ::CryptReleaseContext(h, 0);
160 }
161
162 static bool IsValid(handle_type h) {
163 return h != GetInvalid();
164 }
165};
166
167struct RegTraits : CommonHandleTraits {
168 typedef HKEY handle_type;
169
170 static handle_type GetInvalid() {
171 return NULL;
172 }
173
174 static void Close(handle_type h) {
175 ::RegCloseKey(h);
176 }
177
178 static bool IsValid(handle_type h) {
179 return h != GetInvalid();
180 }
181};
182
183struct FindHandleTraits : CommonHandleTraits {
184 static void Close(handle_type h) {
185 ::FindClose(h);
186 }
187};
188
189struct FileHandleTraits : CommonHandleTraits {};
190
191typedef ScopedHandle<CommonHandleTraits> ScopedCommonHandle;
192typedef ScopedHandle<FileHandleTraits> ScopedFileHandle;
193typedef ScopedHandle<CryptContextTraits> ScopedCryptContext;
194typedef ScopedHandle<RegTraits> ScopedRegHandle;
195typedef ScopedHandle<FindHandleTraits> ScopedFindHandle;
196typedef ScopedHandle<JobHandleTraits> ScopedJobHandle;
197
198template <class T>
199class SmallVectorImpl;
200
201template <class T>
202typename SmallVectorImpl<T>::const_pointer
203c_str(SmallVectorImpl<T> &str) {
204 str.push_back(0);
205 str.pop_back();
206 return str.data();
207}
208
209namespace sys {
210
211inline std::chrono::nanoseconds toDuration(FILETIME Time) {
212 ULARGE_INTEGER TimeInteger;
213 TimeInteger.LowPart = Time.dwLowDateTime;
214 TimeInteger.HighPart = Time.dwHighDateTime;
215
216 // FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond)
217 return std::chrono::nanoseconds(100 * TimeInteger.QuadPart);
218}
219
220inline TimePoint<> toTimePoint(FILETIME Time) {
221 ULARGE_INTEGER TimeInteger;
222 TimeInteger.LowPart = Time.dwLowDateTime;
223 TimeInteger.HighPart = Time.dwHighDateTime;
224
225 // Adjust for different epoch
226 TimeInteger.QuadPart -= 11644473600ll * 10000000;
227
228 // FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond)
229 return TimePoint<>(std::chrono::nanoseconds(100 * TimeInteger.QuadPart));
230}
231
232inline FILETIME toFILETIME(TimePoint<> TP) {
233 ULARGE_INTEGER TimeInteger;
234 TimeInteger.QuadPart = TP.time_since_epoch().count() / 100;
235 TimeInteger.QuadPart += 11644473600ll * 10000000;
236
237 FILETIME Time;
238 Time.dwLowDateTime = TimeInteger.LowPart;
239 Time.dwHighDateTime = TimeInteger.HighPart;
240 return Time;
241}
242
243namespace windows {
244// Returns command line arguments. Unlike arguments given to main(),
245// this function guarantees that the returned arguments are encoded in
246// UTF-8 regardless of the current code page setting.
247std::error_code GetCommandLineArguments(SmallVectorImpl<const char *> &Args,
248 BumpPtrAllocator &Alloc);
249
250/// Convert UTF-8 path to a suitable UTF-16 path for use with the Win32 Unicode
251/// File API.
252std::error_code widenPath(const Twine &Path8, SmallVectorImpl<wchar_t> &Path16,
253 size_t MaxPathLen = MAX_PATH);
254
255} // end namespace windows
256} // end namespace sys
257} // end namespace llvm.
258
259#endif
260
261//===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===//
262//
263// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
264// See https://llvm.org/LICENSE.txt for license information.
265// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
266//
267//===----------------------------------------------------------------------===//
268//
269// Builds up (relatively) standard unix archive files (.a) containing LLVM
270// bitcode or other files.
271//
272//===----------------------------------------------------------------------===//
273
274#include "llvm/ADT/StringExtras.h"
275#include "llvm/ADT/StringSwitch.h"
276#include "llvm/ADT/Triple.h"
277#include "llvm/BinaryFormat/Magic.h"
278#include "llvm/IR/LLVMContext.h"
279#include "llvm/Object/Archive.h"
280#include "llvm/Object/ArchiveWriter.h"
281#include "llvm/Object/IRObjectFile.h"
282#include "llvm/Object/MachO.h"
283#include "llvm/Object/ObjectFile.h"
284#include "llvm/Object/SymbolicFile.h"
285#include "llvm/Support/Chrono.h"
286#include "llvm/Support/CommandLine.h"
287#include "llvm/Support/ConvertUTF.h"
288#include "llvm/Support/Errc.h"
289#include "llvm/Support/FileSystem.h"
290#include "llvm/Support/Format.h"
291#include "llvm/Support/FormatVariadic.h"
292#include "llvm/Support/Host.h"
293#include "llvm/Support/InitLLVM.h"
294#include "llvm/Support/LineIterator.h"
295#include "llvm/Support/MemoryBuffer.h"
296#include "llvm/Support/Path.h"
297#include "llvm/Support/Process.h"
298#include "llvm/Support/StringSaver.h"
299#include "llvm/Support/TargetSelect.h"
300#include "llvm/Support/ToolOutputFile.h"
301#include "llvm/Support/WithColor.h"
302#include "llvm/Support/raw_ostream.h"
303#include "llvm/ToolDrivers/llvm-dlltool/DlltoolDriver.h"
304#include "llvm/ToolDrivers/llvm-lib/LibDriver.h"
305
306#if !defined(_MSC_VER) && !defined(__MINGW32__)
307#include <unistd.h>
308#else
309#include <io.h>
310#endif
311
312#ifdef _WIN32
313#include "llvm/Support/Windows/WindowsSupport.h"
314#endif
315
316using namespace llvm;
317
318// The name this program was invoked as.
319static StringRef ToolName;
320
321// The basename of this program.
322static StringRef Stem;
323
324const char RanlibHelp[] = R"(OVERVIEW: LLVM Ranlib (llvm-ranlib)
325
326 This program generates an index to speed access to archives
327
328USAGE: llvm-ranlib <archive-file>
329
330OPTIONS:
331 -h --help - Display available options
332 -v --version - Display the version of this program
333 -D - Use zero for timestamps and uids/gids (default)
334 -U - Use actual timestamps and uids/gids
335)";
336
337const char ArHelp[] = R"(OVERVIEW: LLVM Archiver
338
339USAGE: llvm-ar [options] [-]<operation>[modifiers] [relpos] [count] <archive> [files]
340 llvm-ar -M [<mri-script]
341
342OPTIONS:
343 --format - archive format to create
344 =default - default
345 =gnu - gnu
346 =darwin - darwin
347 =bsd - bsd
348 --plugin=<string> - ignored for compatibility
349 -h --help - display this help and exit
350 --rsp-quoting - quoting style for response files
351 =posix - posix
352 =windows - windows
353 --version - print the version and exit
354 @<file> - read options from <file>
355
356OPERATIONS:
357 d - delete [files] from the archive
358 m - move [files] in the archive
359 p - print [files] found in the archive
360 q - quick append [files] to the archive
361 r - replace or insert [files] into the archive
362 s - act as ranlib
363 t - display contents of archive
364 x - extract [files] from the archive
365
366MODIFIERS:
367 [a] - put [files] after [relpos]
368 [b] - put [files] before [relpos] (same as [i])
369 [c] - do not warn if archive had to be created
370 [D] - use zero for timestamps and uids/gids (default)
371 [h] - display this help and exit
372 [i] - put [files] before [relpos] (same as [b])
373 [l] - ignored for compatibility
374 [L] - add archive's contents
375 [N] - use instance [count] of name
376 [o] - preserve original dates
377 [O] - display member offsets
378 [P] - use full names when matching (implied for thin archives)
379 [s] - create an archive index (cf. ranlib)
380 [S] - do not build a symbol table
381 [T] - create a thin archive
382 [u] - update only [files] newer than archive contents
383 [U] - use actual timestamps and uids/gids
384 [v] - be verbose about actions taken
385 [V] - display the version and exit
386)";
387
388static void printHelpMessage() {
389 if (Stem.contains_lower("ranlib"))
390 outs() << RanlibHelp;
391 else if (Stem.contains_lower("ar"))
392 outs() << ArHelp;
393}
394
395static unsigned MRILineNumber;
396static bool ParsingMRIScript;
397
398// Show the error plus the usage message, and exit.
399LLVM_ATTRIBUTE_NORETURN static void badUsage(Twine Error) {
400 WithColor::error(errs(), ToolName) << Error << "\n";
401 printHelpMessage();
402 exit(1);
403}
404
405// Show the error message and exit.
406LLVM_ATTRIBUTE_NORETURN static void fail(Twine Error) {
407 if (ParsingMRIScript) {
408 WithColor::error(errs(), ToolName)
409 << "script line " << MRILineNumber << ": " << Error << "\n";
410 } else {
411 WithColor::error(errs(), ToolName) << Error << "\n";
412 }
413 exit(1);
414}
415
416static void failIfError(std::error_code EC, Twine Context = "") {
417 if (!EC)
418 return;
419
420 std::string ContextStr = Context.str();
421 if (ContextStr.empty())
422 fail(EC.message());
423 fail(Context + ": " + EC.message());
424}
425
426static void failIfError(Error E, Twine Context = "") {
427 if (!E)
428 return;
429
430 handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) {
431 std::string ContextStr = Context.str();
432 if (ContextStr.empty())
433 fail(EIB.message());
434 fail(Context + ": " + EIB.message());
435 });
436}
437
438static SmallVector<const char *, 256> PositionalArgs;
439
440static bool MRI;
441
442namespace {
443enum Format { Default, GNU, BSD, DARWIN, Unknown };
444}
445
446static Format FormatType = Default;
447
448static std::string Options;
449
450// This enumeration delineates the kinds of operations on an archive
451// that are permitted.
452enum ArchiveOperation {
453 Print, ///< Print the contents of the archive
454 Delete, ///< Delete the specified members
455 Move, ///< Move members to end or as given by {a,b,i} modifiers
456 QuickAppend, ///< Quickly append to end of archive
457 ReplaceOrInsert, ///< Replace or Insert members
458 DisplayTable, ///< Display the table of contents
459 Extract, ///< Extract files back to file system
460 CreateSymTab ///< Create a symbol table in an existing archive
461};
462
463// Modifiers to follow operation to vary behavior
464static bool AddAfter = false; ///< 'a' modifier
465static bool AddBefore = false; ///< 'b' modifier
466static bool Create = false; ///< 'c' modifier
467static bool OriginalDates = false; ///< 'o' modifier
468static bool DisplayMemberOffsets = false; ///< 'O' modifier
469static bool CompareFullPath = false; ///< 'P' modifier
470static bool OnlyUpdate = false; ///< 'u' modifier
471static bool Verbose = false; ///< 'v' modifier
472static bool Symtab = true; ///< 's' modifier
473static bool Deterministic = true; ///< 'D' and 'U' modifiers
474static bool Thin = false; ///< 'T' modifier
475static bool AddLibrary = false; ///< 'L' modifier
476
477// Relative Positional Argument (for insert/move). This variable holds
478// the name of the archive member to which the 'a', 'b' or 'i' modifier
479// refers. Only one of 'a', 'b' or 'i' can be specified so we only need
480// one variable.
481static std::string RelPos;
482
483// Count parameter for 'N' modifier. This variable specifies which file should
484// match for extract/delete operations when there are multiple matches. This is
485// 1-indexed. A value of 0 is invalid, and implies 'N' is not used.
486static int CountParam = 0;
487
488// This variable holds the name of the archive file as given on the
489// command line.
490static std::string ArchiveName;
491
492static std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
493static std::vector<std::unique_ptr<object::Archive>> Archives;
494
495// This variable holds the list of member files to proecess, as given
496// on the command line.
497static std::vector<StringRef> Members;
498
499// Static buffer to hold StringRefs.
500static BumpPtrAllocator Alloc;
501
502// Extract the member filename from the command line for the [relpos] argument
503// associated with a, b, and i modifiers
504static void getRelPos() {
505 if (PositionalArgs.empty())
506 fail("expected [relpos] for 'a', 'b', or 'i' modifier");
507 RelPos = PositionalArgs[0];
508 PositionalArgs.erase(PositionalArgs.begin());
509}
510
511// Extract the parameter from the command line for the [count] argument
512// associated with the N modifier
513static void getCountParam() {
514 if (PositionalArgs.empty())
515 badUsage("expected [count] for 'N' modifier");
516 auto CountParamArg = StringRef(PositionalArgs[0]);
517 if (CountParamArg.getAsInteger(10, CountParam))
518 badUsage("value for [count] must be numeric, got: " + CountParamArg);
519 if (CountParam < 1)
520 badUsage("value for [count] must be positive, got: " + CountParamArg);
521 PositionalArgs.erase(PositionalArgs.begin());
522}
523
524// Get the archive file name from the command line
525static void getArchive() {
526 if (PositionalArgs.empty())
527 badUsage("an archive name must be specified");
528 ArchiveName = PositionalArgs[0];
529 PositionalArgs.erase(PositionalArgs.begin());
530}
531
532static object::Archive &readLibrary(const Twine &Library) {
533 auto BufOrErr = MemoryBuffer::getFile(Library, -1, false);
534 failIfError(BufOrErr.getError(), "could not open library " + Library);
535 ArchiveBuffers.push_back(std::move(*BufOrErr));
536 auto LibOrErr =
537 object::Archive::create(ArchiveBuffers.back()->getMemBufferRef());
538 failIfError(errorToErrorCode(LibOrErr.takeError()),
539 "could not parse library");
540 Archives.push_back(std::move(*LibOrErr));
541 return *Archives.back();
542}
543
544static void runMRIScript();
545
546// Parse the command line options as presented and return the operation
547// specified. Process all modifiers and check to make sure that constraints on
548// modifier/operation pairs have not been violated.
549static ArchiveOperation parseCommandLine() {
550 if (MRI) {
551 if (!PositionalArgs.empty() || !Options.empty())
552 badUsage("cannot mix -M and other options");
553 runMRIScript();
554 }
555
556 // Keep track of number of operations. We can only specify one
557 // per execution.
558 unsigned NumOperations = 0;
559
560 // Keep track of the number of positional modifiers (a,b,i). Only
561 // one can be specified.
562 unsigned NumPositional = 0;
563
564 // Keep track of which operation was requested
565 ArchiveOperation Operation;
566
567 bool MaybeJustCreateSymTab = false;
568
569 for (unsigned i = 0; i < Options.size(); ++i) {
570 switch (Options[i]) {
571 case 'd':
572 ++NumOperations;
573 Operation = Delete;
574 break;
575 case 'm':
576 ++NumOperations;
577 Operation = Move;
578 break;
579 case 'p':
580 ++NumOperations;
581 Operation = Print;
582 break;
583 case 'q':
584 ++NumOperations;
585 Operation = QuickAppend;
586 break;
587 case 'r':
588 ++NumOperations;
589 Operation = ReplaceOrInsert;
590 break;
591 case 't':
592 ++NumOperations;
593 Operation = DisplayTable;
594 break;
595 case 'x':
596 ++NumOperations;
597 Operation = Extract;
598 break;
599 case 'c':
600 Create = true;
601 break;
602 case 'l': /* accepted but unused */
603 break;
604 case 'o':
605 OriginalDates = true;
606 break;
607 case 'O':
608 DisplayMemberOffsets = true;
609 break;
610 case 'P':
611 CompareFullPath = true;
612 break;
613 case 's':
614 Symtab = true;
615 MaybeJustCreateSymTab = true;
616 break;
617 case 'S':
618 Symtab = false;
619 break;
620 case 'u':
621 OnlyUpdate = true;
622 break;
623 case 'v':
624 Verbose = true;
625 break;
626 case 'a':
627 getRelPos();
628 AddAfter = true;
629 NumPositional++;
630 break;
631 case 'b':
632 getRelPos();
633 AddBefore = true;
634 NumPositional++;
635 break;
636 case 'i':
637 getRelPos();
638 AddBefore = true;
639 NumPositional++;
640 break;
641 case 'D':
642 Deterministic = true;
643 break;
644 case 'U':
645 Deterministic = false;
646 break;
647 case 'N':
648 getCountParam();
649 break;
650 case 'T':
651 Thin = true;
652 // Thin archives store path names, so P should be forced.
653 CompareFullPath = true;
654 break;
655 case 'L':
656 AddLibrary = true;
657 break;
658 case 'V':
659 cl::PrintVersionMessage();
660 exit(0);
661 case 'h':
662 printHelpMessage();
663 exit(0);
664 default:
665 badUsage(std::string("unknown option ") + Options[i]);
666 }
667 }
668
669 // At this point, the next thing on the command line must be
670 // the archive name.
671 getArchive();
672
673 // Everything on the command line at this point is a member.
674 Members.assign(PositionalArgs.begin(), PositionalArgs.end());
675
676 if (NumOperations == 0 && MaybeJustCreateSymTab) {
677 NumOperations = 1;
678 Operation = CreateSymTab;
679 if (!Members.empty())
680 badUsage("the 's' operation takes only an archive as argument");
681 }
682
683 // Perform various checks on the operation/modifier specification
684 // to make sure we are dealing with a legal request.
685 if (NumOperations == 0)
686 badUsage("you must specify at least one of the operations");
687 if (NumOperations > 1)
688 badUsage("only one operation may be specified");
689 if (NumPositional > 1)
690 badUsage("you may only specify one of 'a', 'b', and 'i' modifiers");
691 if (AddAfter || AddBefore)
692 if (Operation != Move && Operation != ReplaceOrInsert)
693 badUsage("the 'a', 'b' and 'i' modifiers can only be specified with "
694 "the 'm' or 'r' operations");
695 if (CountParam)
696 if (Operation != Extract && Operation != Delete)
697 badUsage("the 'N' modifier can only be specified with the 'x' or 'd' "
698 "operations");
699 if (OriginalDates && Operation != Extract)
700 badUsage("the 'o' modifier is only applicable to the 'x' operation");
701 if (OnlyUpdate && Operation != ReplaceOrInsert)
702 badUsage("the 'u' modifier is only applicable to the 'r' operation");
703 if (AddLibrary && Operation != QuickAppend)
704 badUsage("the 'L' modifier is only applicable to the 'q' operation");
705
706 // Return the parsed operation to the caller
707 return Operation;
708}
709
710// Implements the 'p' operation. This function traverses the archive
711// looking for members that match the path list.
712static void doPrint(StringRef Name, const object::Archive::Child &C) {
713 if (Verbose)
714 outs() << "Printing " << Name << "\n";
715
716 Expected<StringRef> DataOrErr = C.getBuffer();
717 failIfError(DataOrErr.takeError());
718 StringRef Data = *DataOrErr;
719 outs().write(Data.data(), Data.size());
720}
721
722// Utility function for printing out the file mode when the 't' operation is in
723// verbose mode.
724static void printMode(unsigned mode) {
725 outs() << ((mode & 004) ? "r" : "-");
726 outs() << ((mode & 002) ? "w" : "-");
727 outs() << ((mode & 001) ? "x" : "-");
728}
729
730// Implement the 't' operation. This function prints out just
731// the file names of each of the members. However, if verbose mode is requested
732// ('v' modifier) then the file type, permission mode, user, group, size, and
733// modification time are also printed.
734static void doDisplayTable(StringRef Name, const object::Archive::Child &C) {
735 if (Verbose) {
736 Expected<sys::fs::perms> ModeOrErr = C.getAccessMode();
737 failIfError(ModeOrErr.takeError());
738 sys::fs::perms Mode = ModeOrErr.get();
739 printMode((Mode >> 6) & 007);
740 printMode((Mode >> 3) & 007);
741 printMode(Mode & 007);
742 Expected<unsigned> UIDOrErr = C.getUID();
743 failIfError(UIDOrErr.takeError());
744 outs() << ' ' << UIDOrErr.get();
745 Expected<unsigned> GIDOrErr = C.getGID();
746 failIfError(GIDOrErr.takeError());
747 outs() << '/' << GIDOrErr.get();
748 Expected<uint64_t> Size = C.getSize();
749 failIfError(Size.takeError());
750 outs() << ' ' << format("%6llu", Size.get());
751 auto ModTimeOrErr = C.getLastModified();
752 failIfError(ModTimeOrErr.takeError());
753 // Note: formatv() only handles the default TimePoint<>, which is in
754 // nanoseconds.
755 // TODO: fix format_provider<TimePoint<>> to allow other units.
756 sys::TimePoint<> ModTimeInNs = ModTimeOrErr.get();
757 outs() << ' ' << formatv("{0:%b %e %H:%M %Y}", ModTimeInNs);
758 outs() << ' ';
759 }
760
761 if (C.getParent()->isThin()) {
762 if (!sys::path::is_absolute(Name)) {
763 StringRef ParentDir = sys::path::parent_path(ArchiveName);
764 if (!ParentDir.empty())
765 outs() << sys::path::convert_to_slash(ParentDir) << '/';
766 }
767 outs() << Name;
768 } else {
769 outs() << Name;
770 if (DisplayMemberOffsets)
771 outs() << " 0x" << utohexstr(C.getDataOffset(), true);
772 }
773 outs() << '\n';
774}
775
776static std::string normalizePath(StringRef Path) {
777 return CompareFullPath ? sys::path::convert_to_slash(Path)
778 : std::string(sys::path::filename(Path));
779}
780
781static bool comparePaths(StringRef Path1, StringRef Path2) {
782// When on Windows this function calls CompareStringOrdinal
783// as Windows file paths are case-insensitive.
784// CompareStringOrdinal compares two Unicode strings for
785// binary equivalence and allows for case insensitivity.
786#ifdef _WIN32
787 SmallVector<wchar_t, 128> WPath1, WPath2;
788 failIfError(sys::windows::UTF8ToUTF16(normalizePath(Path1), WPath1));
789 failIfError(sys::windows::UTF8ToUTF16(normalizePath(Path2), WPath2));
790
791 return CompareStringOrdinal(WPath1.data(), WPath1.size(), WPath2.data(),
792 WPath2.size(), true) == CSTR_EQUAL;
793#else
794 return normalizePath(Path1) == normalizePath(Path2);
795#endif
796}
797
798// Implement the 'x' operation. This function extracts files back to the file
799// system.
800static void doExtract(StringRef Name, const object::Archive::Child &C) {
801 // Retain the original mode.
802 Expected<sys::fs::perms> ModeOrErr = C.getAccessMode();
803 failIfError(ModeOrErr.takeError());
804 sys::fs::perms Mode = ModeOrErr.get();
805
806 llvm::StringRef outputFilePath = sys::path::filename(Name);
807 if (Verbose)
808 outs() << "x - " << outputFilePath << '\n';
809
810 int FD;
811 failIfError(sys::fs::openFileForWrite(outputFilePath, FD,
812 sys::fs::CD_CreateAlways,
813 sys::fs::OF_None, Mode),
814 Name);
815
816 {
817 raw_fd_ostream file(FD, false);
818
819 // Get the data and its length
820 Expected<StringRef> BufOrErr = C.getBuffer();
821 failIfError(BufOrErr.takeError());
822 StringRef Data = BufOrErr.get();
823
824 // Write the data.
825 file.write(Data.data(), Data.size());
826 }
827
828 // If we're supposed to retain the original modification times, etc. do so
829 // now.
830 if (OriginalDates) {
831 auto ModTimeOrErr = C.getLastModified();
832 failIfError(ModTimeOrErr.takeError());
833 failIfError(
834 sys::fs::setLastAccessAndModificationTime(FD, ModTimeOrErr.get()));
835 }
836
837 if (close(FD))
838 fail("Could not close the file");
839}
840
841static bool shouldCreateArchive(ArchiveOperation Op) {
842 switch (Op) {
843 case Print:
844 case Delete:
845 case Move:
846 case DisplayTable:
847 case Extract:
848 case CreateSymTab:
849 return false;
850
851 case QuickAppend:
852 case ReplaceOrInsert:
853 return true;
854 }
855
856 llvm_unreachable("Missing entry in covered switch.");
857}
858
859static void performReadOperation(ArchiveOperation Operation,
860 object::Archive *OldArchive) {
861 if (Operation == Extract && OldArchive->isThin())
862 fail("extracting from a thin archive is not supported");
863
864 bool Filter = !Members.empty();
865 StringMap<int> MemberCount;
866 {
867 Error Err = Error::success();
868 for (auto &C : OldArchive->children(Err)) {
869 Expected<StringRef> NameOrErr = C.getName();
870 failIfError(NameOrErr.takeError());
871 StringRef Name = NameOrErr.get();
872
873 if (Filter) {
874 auto I = find_if(Members, [Name](StringRef Path) {
875 return comparePaths(Name, Path);
876 });
877 if (I == Members.end())
878 continue;
879 if (CountParam && ++MemberCount[Name] != CountParam)
880 continue;
881 Members.erase(I);
882 }
883
884 switch (Operation) {
885 default:
886 llvm_unreachable("Not a read operation");
887 case Print:
888 doPrint(Name, C);
889 break;
890 case DisplayTable:
891 doDisplayTable(Name, C);
892 break;
893 case Extract:
894 doExtract(Name, C);
895 break;
896 }
897 }
898 failIfError(std::move(Err));
899 }
900
901 if (Members.empty())
902 return;
903 for (StringRef Name : Members)
904 WithColor::error(errs(), ToolName) << "'" << Name << "' was not found\n";
905 exit(1);
906}
907
908static void addChildMember(std::vector<NewArchiveMember> &Members,
909 const object::Archive::Child &M,
910 bool FlattenArchive = false) {
911 if (Thin && !M.getParent()->isThin())
912 fail("cannot convert a regular archive to a thin one");
913 Expected<NewArchiveMember> NMOrErr =
914 NewArchiveMember::getOldMember(M, Deterministic);
915 failIfError(NMOrErr.takeError());
916 // If the child member we're trying to add is thin, use the path relative to
917 // the archive it's in, so the file resolves correctly.
918 if (Thin && FlattenArchive) {
919 StringSaver Saver(Alloc);
920 Expected<std::string> FileNameOrErr(M.getName());
921 failIfError(FileNameOrErr.takeError());
922 if (sys::path::is_absolute(*FileNameOrErr)) {
923 NMOrErr->MemberName = Saver.save(sys::path::convert_to_slash(*FileNameOrErr));
924 } else {
925 FileNameOrErr = M.getFullName();
926 failIfError(FileNameOrErr.takeError());
927 Expected<std::string> PathOrErr =
928 computeArchiveRelativePath(ArchiveName, *FileNameOrErr);
929 NMOrErr->MemberName = Saver.save(
930 PathOrErr ? *PathOrErr : sys::path::convert_to_slash(*FileNameOrErr));
931 }
932 }
933 if (FlattenArchive &&
934 identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) {
935 Expected<std::string> FileNameOrErr = M.getFullName();
936 failIfError(FileNameOrErr.takeError());
937 object::Archive &Lib = readLibrary(*FileNameOrErr);
938 // When creating thin archives, only flatten if the member is also thin.
939 if (!Thin || Lib.isThin()) {
940 Error Err = Error::success();
941 // Only Thin archives are recursively flattened.
942 for (auto &Child : Lib.children(Err))
943 addChildMember(Members, Child, /*FlattenArchive=*/Thin);
944 failIfError(std::move(Err));
945 return;
946 }
947 }
948 Members.push_back(std::move(*NMOrErr));
949}
950
951static void addMember(std::vector<NewArchiveMember> &Members,
952 StringRef FileName, bool FlattenArchive = false) {
953 Expected<NewArchiveMember> NMOrErr =
954 NewArchiveMember::getFile(FileName, Deterministic);
955 failIfError(NMOrErr.takeError(), FileName);
956 StringSaver Saver(Alloc);
957 // For regular archives, use the basename of the object path for the member
958 // name. For thin archives, use the full relative paths so the file resolves
959 // correctly.
960 if (!Thin) {
961 NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName);
962 } else {
963 if (sys::path::is_absolute(FileName))
964 NMOrErr->MemberName = Saver.save(sys::path::convert_to_slash(FileName));
965 else {
966 Expected<std::string> PathOrErr =
967 computeArchiveRelativePath(ArchiveName, FileName);
968 NMOrErr->MemberName = Saver.save(
969 PathOrErr ? *PathOrErr : sys::path::convert_to_slash(FileName));
970 }
971 }
972
973 if (FlattenArchive &&
974 identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) {
975 object::Archive &Lib = readLibrary(FileName);
976 // When creating thin archives, only flatten if the member is also thin.
977 if (!Thin || Lib.isThin()) {
978 Error Err = Error::success();
979 // Only Thin archives are recursively flattened.
980 for (auto &Child : Lib.children(Err))
981 addChildMember(Members, Child, /*FlattenArchive=*/Thin);
982 failIfError(std::move(Err));
983 return;
984 }
985 }
986 Members.push_back(std::move(*NMOrErr));
987}
988
989enum InsertAction {
990 IA_AddOldMember,
991 IA_AddNewMember,
992 IA_Delete,
993 IA_MoveOldMember,
994 IA_MoveNewMember
995};
996
997static InsertAction computeInsertAction(ArchiveOperation Operation,
998 const object::Archive::Child &Member,
999 StringRef Name,
1000 std::vector<StringRef>::iterator &Pos,
1001 StringMap<int> &MemberCount) {
1002 if (Operation == QuickAppend || Members.empty())
1003 return IA_AddOldMember;
1004 auto MI = find_if(
1005 Members, [Name](StringRef Path) { return comparePaths(Name, Path); });
1006
1007 if (MI == Members.end())
1008 return IA_AddOldMember;
1009
1010 Pos = MI;
1011
1012 if (Operation == Delete) {
1013 if (CountParam && ++MemberCount[Name] != CountParam)
1014 return IA_AddOldMember;
1015 return IA_Delete;
1016 }
1017
1018 if (Operation == Move)
1019 return IA_MoveOldMember;
1020
1021 if (Operation == ReplaceOrInsert) {
1022 if (!OnlyUpdate) {
1023 if (RelPos.empty())
1024 return IA_AddNewMember;
1025 return IA_MoveNewMember;
1026 }
1027
1028 // We could try to optimize this to a fstat, but it is not a common
1029 // operation.
1030 sys::fs::file_status Status;
1031 failIfError(sys::fs::status(*MI, Status), *MI);
1032 auto ModTimeOrErr = Member.getLastModified();
1033 failIfError(ModTimeOrErr.takeError());
1034 if (Status.getLastModificationTime() < ModTimeOrErr.get()) {
1035 if (RelPos.empty())
1036 return IA_AddOldMember;
1037 return IA_MoveOldMember;
1038 }
1039
1040 if (RelPos.empty())
1041 return IA_AddNewMember;
1042 return IA_MoveNewMember;
1043 }
1044 llvm_unreachable("No such operation");
1045}
1046
1047// We have to walk this twice and computing it is not trivial, so creating an
1048// explicit std::vector is actually fairly efficient.
1049static std::vector<NewArchiveMember>
1050computeNewArchiveMembers(ArchiveOperation Operation,
1051 object::Archive *OldArchive) {
1052 std::vector<NewArchiveMember> Ret;
1053 std::vector<NewArchiveMember> Moved;
1054 int InsertPos = -1;
1055 if (OldArchive) {
1056 Error Err = Error::success();
1057 StringMap<int> MemberCount;
1058 for (auto &Child : OldArchive->children(Err)) {
1059 int Pos = Ret.size();
1060 Expected<StringRef> NameOrErr = Child.getName();
1061 failIfError(NameOrErr.takeError());
1062 std::string Name = std::string(NameOrErr.get());
1063 if (comparePaths(Name, RelPos)) {
1064 assert(AddAfter || AddBefore);
1065 if (AddBefore)
1066 InsertPos = Pos;
1067 else
1068 InsertPos = Pos + 1;
1069 }
1070
1071 std::vector<StringRef>::iterator MemberI = Members.end();
1072 InsertAction Action =
1073 computeInsertAction(Operation, Child, Name, MemberI, MemberCount);
1074 switch (Action) {
1075 case IA_AddOldMember:
1076 addChildMember(Ret, Child, /*FlattenArchive=*/Thin);
1077 break;
1078 case IA_AddNewMember:
1079 addMember(Ret, *MemberI);
1080 break;
1081 case IA_Delete:
1082 break;
1083 case IA_MoveOldMember:
1084 addChildMember(Moved, Child, /*FlattenArchive=*/Thin);
1085 break;
1086 case IA_MoveNewMember:
1087 addMember(Moved, *MemberI);
1088 break;
1089 }
1090 // When processing elements with the count param, we need to preserve the
1091 // full members list when iterating over all archive members. For
1092 // instance, "llvm-ar dN 2 archive.a member.o" should delete the second
1093 // file named member.o it sees; we are not done with member.o the first
1094 // time we see it in the archive.
1095 if (MemberI != Members.end() && !CountParam)
1096 Members.erase(MemberI);
1097 }
1098 failIfError(std::move(Err));
1099 }
1100
1101 if (Operation == Delete)
1102 return Ret;
1103
1104 if (!RelPos.empty() && InsertPos == -1)
1105 fail("insertion point not found");
1106
1107 if (RelPos.empty())
1108 InsertPos = Ret.size();
1109
1110 assert(unsigned(InsertPos) <= Ret.size());
1111 int Pos = InsertPos;
1112 for (auto &M : Moved) {
1113 Ret.insert(Ret.begin() + Pos, std::move(M));
1114 ++Pos;
1115 }
1116
1117 if (AddLibrary) {
1118 assert(Operation == QuickAppend);
1119 for (auto &Member : Members)
1120 addMember(Ret, Member, /*FlattenArchive=*/true);
1121 return Ret;
1122 }
1123
1124 std::vector<NewArchiveMember> NewMembers;
1125 for (auto &Member : Members)
1126 addMember(NewMembers, Member, /*FlattenArchive=*/Thin);
1127 Ret.reserve(Ret.size() + NewMembers.size());
1128 std::move(NewMembers.begin(), NewMembers.end(),
1129 std::inserter(Ret, std::next(Ret.begin(), InsertPos)));
1130
1131 return Ret;
1132}
1133
1134static object::Archive::Kind getDefaultForHost() {
1135 return Triple(sys::getProcessTriple()).isOSDarwin()
1136 ? object::Archive::K_DARWIN
1137 : object::Archive::K_GNU;
1138}
1139
1140static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) {
1141 auto MemBufferRef = Member.Buf->getMemBufferRef();
1142 Expected<std::unique_ptr<object::ObjectFile>> OptionalObject =
1143 object::ObjectFile::createObjectFile(MemBufferRef);
1144
1145 if (OptionalObject)
1146 return isa<object::MachOObjectFile>(**OptionalObject)
1147 ? object::Archive::K_DARWIN
1148 : object::Archive::K_GNU;
1149
1150 // squelch the error in case we had a non-object file
1151 consumeError(OptionalObject.takeError());
1152
1153 // If we're adding a bitcode file to the archive, detect the Archive kind
1154 // based on the target triple.
1155 LLVMContext Context;
1156 if (identify_magic(MemBufferRef.getBuffer()) == file_magic::bitcode) {
1157 if (auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
1158 MemBufferRef, file_magic::bitcode, &Context)) {
1159 auto &IRObject = cast<object::IRObjectFile>(**ObjOrErr);
1160 return Triple(IRObject.getTargetTriple()).isOSDarwin()
1161 ? object::Archive::K_DARWIN
1162 : object::Archive::K_GNU;
1163 } else {
1164 // Squelch the error in case this was not a SymbolicFile.
1165 consumeError(ObjOrErr.takeError());
1166 }
1167 }
1168
1169 return getDefaultForHost();
1170}
1171
1172static void performWriteOperation(ArchiveOperation Operation,
1173 object::Archive *OldArchive,
1174 std::unique_ptr<MemoryBuffer> OldArchiveBuf,
1175 std::vector<NewArchiveMember> *NewMembersP) {
1176 std::vector<NewArchiveMember> NewMembers;
1177 if (!NewMembersP)
1178 NewMembers = computeNewArchiveMembers(Operation, OldArchive);
1179
1180 object::Archive::Kind Kind;
1181 switch (FormatType) {
1182 case Default:
1183 if (Thin)
1184 Kind = object::Archive::K_GNU;
1185 else if (OldArchive)
1186 Kind = OldArchive->kind();
1187 else if (NewMembersP)
1188 Kind = !NewMembersP->empty() ? getKindFromMember(NewMembersP->front())
1189 : getDefaultForHost();
1190 else
1191 Kind = !NewMembers.empty() ? getKindFromMember(NewMembers.front())
1192 : getDefaultForHost();
1193 break;
1194 case GNU:
1195 Kind = object::Archive::K_GNU;
1196 break;
1197 case BSD:
1198 if (Thin)
1199 fail("only the gnu format has a thin mode");
1200 Kind = object::Archive::K_BSD;
1201 break;
1202 case DARWIN:
1203 if (Thin)
1204 fail("only the gnu format has a thin mode");
1205 Kind = object::Archive::K_DARWIN;
1206 break;
1207 case Unknown:
1208 llvm_unreachable("");
1209 }
1210
1211 Error E =
1212 writeArchive(ArchiveName, NewMembersP ? *NewMembersP : NewMembers, Symtab,
1213 Kind, Deterministic, Thin, std::move(OldArchiveBuf));
1214 failIfError(std::move(E), ArchiveName);
1215}
1216
1217static void createSymbolTable(object::Archive *OldArchive) {
1218 // When an archive is created or modified, if the s option is given, the
1219 // resulting archive will have a current symbol table. If the S option
1220 // is given, it will have no symbol table.
1221 // In summary, we only need to update the symbol table if we have none.
1222 // This is actually very common because of broken build systems that think
1223 // they have to run ranlib.
1224 if (OldArchive->hasSymbolTable())
1225 return;
1226
1227 performWriteOperation(CreateSymTab, OldArchive, nullptr, nullptr);
1228}
1229
1230static void performOperation(ArchiveOperation Operation,
1231 object::Archive *OldArchive,
1232 std::unique_ptr<MemoryBuffer> OldArchiveBuf,
1233 std::vector<NewArchiveMember> *NewMembers) {
1234 switch (Operation) {
1235 case Print:
1236 case DisplayTable:
1237 case Extract:
1238 performReadOperation(Operation, OldArchive);
1239 return;
1240
1241 case Delete:
1242 case Move:
1243 case QuickAppend:
1244 case ReplaceOrInsert:
1245 performWriteOperation(Operation, OldArchive, std::move(OldArchiveBuf),
1246 NewMembers);
1247 return;
1248 case CreateSymTab:
1249 createSymbolTable(OldArchive);
1250 return;
1251 }
1252 llvm_unreachable("Unknown operation.");
1253}
1254
1255static int performOperation(ArchiveOperation Operation,
1256 std::vector<NewArchiveMember> *NewMembers) {
1257 // Create or open the archive object.
1258 ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
1259 MemoryBuffer::getFile(ArchiveName, -1, false);
1260 std::error_code EC = Buf.getError();
1261 if (EC && EC != errc::no_such_file_or_directory)
1262 fail("unable to open '" + ArchiveName + "': " + EC.message());
1263
1264 if (!EC) {
1265 Error Err = Error::success();
1266 object::Archive Archive(Buf.get()->getMemBufferRef(), Err);
1267 failIfError(std::move(Err), "unable to load '" + ArchiveName + "'");
1268 if (Archive.isThin())
1269 CompareFullPath = true;
1270 performOperation(Operation, &Archive, std::move(Buf.get()), NewMembers);
1271 return 0;
1272 }
1273
1274 assert(EC == errc::no_such_file_or_directory);
1275
1276 if (!shouldCreateArchive(Operation)) {
1277 failIfError(EC, Twine("unable to load '") + ArchiveName + "'");
1278 } else {
1279 if (!Create) {
1280 // Produce a warning if we should and we're creating the archive
1281 WithColor::warning(errs(), ToolName)
1282 << "creating " << ArchiveName << "\n";
1283 }
1284 }
1285
1286 performOperation(Operation, nullptr, nullptr, NewMembers);
1287 return 0;
1288}
1289
1290static void runMRIScript() {
1291 enum class MRICommand { AddLib, AddMod, Create, CreateThin, Delete, Save, End, Invalid };
1292
1293 ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getSTDIN();
1294 failIfError(Buf.getError());
1295 const MemoryBuffer &Ref = *Buf.get();
1296 bool Saved = false;
1297 std::vector<NewArchiveMember> NewMembers;
1298 ParsingMRIScript = true;
1299
1300 for (line_iterator I(Ref, /*SkipBlanks*/ false), E; I != E; ++I) {
1301 ++MRILineNumber;
1302 StringRef Line = *I;
1303 Line = Line.split(';').first;
1304 Line = Line.split('*').first;
1305 Line = Line.trim();
1306 if (Line.empty())
1307 continue;
1308 StringRef CommandStr, Rest;
1309 std::tie(CommandStr, Rest) = Line.split(' ');
1310 Rest = Rest.trim();
1311 if (!Rest.empty() && Rest.front() == '"' && Rest.back() == '"')
1312 Rest = Rest.drop_front().drop_back();
1313 auto Command = StringSwitch<MRICommand>(CommandStr.lower())
1314 .Case("addlib", MRICommand::AddLib)
1315 .Case("addmod", MRICommand::AddMod)
1316 .Case("create", MRICommand::Create)
1317 .Case("createthin", MRICommand::CreateThin)
1318 .Case("delete", MRICommand::Delete)
1319 .Case("save", MRICommand::Save)
1320 .Case("end", MRICommand::End)
1321 .Default(MRICommand::Invalid);
1322
1323 switch (Command) {
1324 case MRICommand::AddLib: {
1325 object::Archive &Lib = readLibrary(Rest);
1326 {
1327 Error Err = Error::success();
1328 for (auto &Member : Lib.children(Err))
1329 addChildMember(NewMembers, Member, /*FlattenArchive=*/Thin);
1330 failIfError(std::move(Err));
1331 }
1332 break;
1333 }
1334 case MRICommand::AddMod:
1335 addMember(NewMembers, Rest);
1336 break;
1337 case MRICommand::CreateThin:
1338 Thin = true;
1339 LLVM_FALLTHROUGH;
1340 case MRICommand::Create:
1341 Create = true;
1342 if (!ArchiveName.empty())
1343 fail("editing multiple archives not supported");
1344 if (Saved)
1345 fail("file already saved");
1346 ArchiveName = std::string(Rest);
1347 break;
1348 case MRICommand::Delete: {
1349 llvm::erase_if(NewMembers, [=](NewArchiveMember &M) {
1350 return comparePaths(M.MemberName, Rest);
1351 });
1352 break;
1353 }
1354 case MRICommand::Save:
1355 Saved = true;
1356 break;
1357 case MRICommand::End:
1358 break;
1359 case MRICommand::Invalid:
1360 fail("unknown command: " + CommandStr);
1361 }
1362 }
1363
1364 ParsingMRIScript = false;
1365
1366 // Nothing to do if not saved.
1367 if (Saved)
1368 performOperation(ReplaceOrInsert, &NewMembers);
1369 exit(0);
1370}
1371
1372static bool handleGenericOption(StringRef arg) {
1373 if (arg == "-help" || arg == "--help" || arg == "-h") {
1374 printHelpMessage();
1375 return true;
1376 }
1377 if (arg == "-version" || arg == "--version") {
1378 cl::PrintVersionMessage();
1379 return true;
1380 }
1381 return false;
1382}
1383
1384static const char *matchFlagWithArg(StringRef Expected,
1385 ArrayRef<const char *>::iterator &ArgIt,
1386 ArrayRef<const char *> Args) {
1387 StringRef Arg = *ArgIt;
1388
1389 if (Arg.startswith("--"))
1390 Arg = Arg.substr(2);
1391 else if (Arg.startswith("-"))
1392 Arg = Arg.substr(1);
1393
1394 size_t len = Expected.size();
1395 if (Arg == Expected) {
1396 if (++ArgIt == Args.end())
1397 fail(std::string(Expected) + " requires an argument");
1398
1399 return *ArgIt;
1400 }
1401 if (Arg.startswith(Expected) && Arg.size() > len && Arg[len] == '=')
1402 return Arg.data() + len + 1;
1403
1404 return nullptr;
1405}
1406
1407static cl::TokenizerCallback getRspQuoting(ArrayRef<const char *> ArgsArr) {
1408 cl::TokenizerCallback Ret =
1409 Triple(sys::getProcessTriple()).getOS() == Triple::Win32
1410 ? cl::TokenizeWindowsCommandLine
1411 : cl::TokenizeGNUCommandLine;
1412
1413 for (ArrayRef<const char *>::iterator ArgIt = ArgsArr.begin();
1414 ArgIt != ArgsArr.end(); ++ArgIt) {
1415 if (const char *Match = matchFlagWithArg("rsp-quoting", ArgIt, ArgsArr)) {
1416 StringRef MatchRef = Match;
1417 if (MatchRef == "posix")
1418 Ret = cl::TokenizeGNUCommandLine;
1419 else if (MatchRef == "windows")
1420 Ret = cl::TokenizeWindowsCommandLine;
1421 else
1422 fail(std::string("Invalid response file quoting style ") + Match);
1423 }
1424 }
1425
1426 return Ret;
1427}
1428
1429static int ar_main(int argc, char **argv) {
1430 SmallVector<const char *, 0> Argv(argv + 1, argv + argc);
1431 StringSaver Saver(Alloc);
1432
1433 cl::ExpandResponseFiles(Saver, getRspQuoting(makeArrayRef(argv, argc)), Argv);
1434
1435 for (ArrayRef<const char *>::iterator ArgIt = Argv.begin();
1436 ArgIt != Argv.end(); ++ArgIt) {
1437 const char *Match = nullptr;
1438
1439 if (handleGenericOption(*ArgIt))
1440 return 0;
1441 if (strcmp(*ArgIt, "--") == 0) {
1442 ++ArgIt;
1443 for (; ArgIt != Argv.end(); ++ArgIt)
1444 PositionalArgs.push_back(*ArgIt);
1445 break;
1446 }
1447
1448 if (*ArgIt[0] != '-') {
1449 if (Options.empty())
1450 Options += *ArgIt;
1451 else
1452 PositionalArgs.push_back(*ArgIt);
1453 continue;
1454 }
1455
1456 if (strcmp(*ArgIt, "-M") == 0) {
1457 MRI = true;
1458 continue;
1459 }
1460
1461 Match = matchFlagWithArg("format", ArgIt, Argv);
1462 if (Match) {
1463 FormatType = StringSwitch<Format>(Match)
1464 .Case("default", Default)
1465 .Case("gnu", GNU)
1466 .Case("darwin", DARWIN)
1467 .Case("bsd", BSD)
1468 .Default(Unknown);
1469 if (FormatType == Unknown)
1470 fail(std::string("Invalid format ") + Match);
1471 continue;
1472 }
1473
1474 if (matchFlagWithArg("plugin", ArgIt, Argv) ||
1475 matchFlagWithArg("rsp-quoting", ArgIt, Argv))
1476 continue;
1477
1478 Options += *ArgIt + 1;
1479 }
1480
1481 ArchiveOperation Operation = parseCommandLine();
1482 return performOperation(Operation, nullptr);
1483}
1484
1485static int ranlib_main(int argc, char **argv) {
1486 bool ArchiveSpecified = false;
1487 for (int i = 1; i < argc; ++i) {
1488 StringRef arg(argv[i]);
1489 if (handleGenericOption(arg)) {
1490 return 0;
1491 } else if (arg.consume_front("-")) {
1492 // Handle the -D/-U flag
1493 while (!arg.empty()) {
1494 if (arg.front() == 'D') {
1495 Deterministic = true;
1496 } else if (arg.front() == 'U') {
1497 Deterministic = false;
1498 } else if (arg.front() == 'h') {
1499 printHelpMessage();
1500 return 0;
1501 } else if (arg.front() == 'v') {
1502 cl::PrintVersionMessage();
1503 return 0;
1504 } else {
1505 // TODO: GNU ranlib also supports a -t flag
1506 fail("Invalid option: '-" + arg + "'");
1507 }
1508 arg = arg.drop_front(1);
1509 }
1510 } else {
1511 if (ArchiveSpecified)
1512 fail("exactly one archive should be specified");
1513 ArchiveSpecified = true;
1514 ArchiveName = arg.str();
1515 }
1516 }
1517 if (!ArchiveSpecified) {
1518 badUsage("an archive name must be specified");
1519 }
1520 return performOperation(CreateSymTab, nullptr);
1521}
1522
1523extern "C" int ZigLlvmAr_main(int argc, char **argv);
1524int ZigLlvmAr_main(int argc, char **argv) {
1525 InitLLVM X(argc, argv);
1526 ToolName = argv[0];
1527
1528 llvm::InitializeAllTargetInfos();
1529 llvm::InitializeAllTargetMCs();
1530 llvm::InitializeAllAsmParsers();
1531
1532 Stem = sys::path::stem(ToolName);
1533 auto Is = [](StringRef Tool) {
1534 // We need to recognize the following filenames.
1535 //
1536 // Lib.exe -> lib (see D44808, MSBuild runs Lib.exe)
1537 // dlltool.exe -> dlltool
1538 // arm-pokymllib32-linux-gnueabi-llvm-ar-10 -> ar
1539 auto I = Stem.rfind_lower(Tool);
1540 return I != StringRef::npos &&
1541 (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()]));
1542 };
1543
1544 if (Is("dlltool"))
1545 return dlltoolDriverMain(makeArrayRef(argv, argc));
1546 if (Is("ranlib"))
1547 return ranlib_main(argc, argv);
1548 if (Is("lib"))
1549 return libDriverMain(makeArrayRef(argv, argc));
1550 if (Is("ar"))
1551 return ar_main(argc, argv);
1552
1553 fail("not ranlib, ar, lib or dlltool");
1554}