1 //===-- llvm-libtool-darwin.cpp - a tool for creating libraries -----------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // A utility for creating static and dynamic libraries for Darwin.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/BinaryFormat/Magic.h"
14 #include "llvm/IR/LLVMContext.h"
15 #include "llvm/Object/ArchiveWriter.h"
16 #include "llvm/Object/IRObjectFile.h"
17 #include "llvm/Object/MachO.h"
18 #include "llvm/Object/MachOUniversal.h"
19 #include "llvm/Object/MachOUniversalWriter.h"
20 #include "llvm/Object/ObjectFile.h"
21 #include "llvm/Support/CommandLine.h"
22 #include "llvm/Support/InitLLVM.h"
23 #include "llvm/Support/LineIterator.h"
24 #include "llvm/Support/VirtualFileSystem.h"
25 #include "llvm/Support/WithColor.h"
26 #include "llvm/TextAPI/Architecture.h"
27 #include <map>
28 
29 using namespace llvm;
30 using namespace llvm::object;
31 
32 static LLVMContext LLVMCtx;
33 
34 typedef std::map<uint64_t, std::vector<NewArchiveMember>>
35     MembersPerArchitectureMap;
36 
37 cl::OptionCategory LibtoolCategory("llvm-libtool-darwin Options");
38 
39 static cl::opt<std::string> OutputFile("o", cl::desc("Specify output filename"),
40                                        cl::value_desc("filename"),
41                                        cl::cat(LibtoolCategory));
42 
43 static cl::list<std::string> InputFiles(cl::Positional,
44                                         cl::desc("<input files>"),
45                                         cl::ZeroOrMore,
46                                         cl::cat(LibtoolCategory));
47 
48 static cl::opt<std::string> ArchType(
49     "arch_only", cl::desc("Specify architecture type for output library"),
50     cl::value_desc("arch_type"), cl::ZeroOrMore, cl::cat(LibtoolCategory));
51 
52 enum class Operation { None, Static };
53 
54 static cl::opt<Operation> LibraryOperation(
55     cl::desc("Library Type: "),
56     cl::values(
57         clEnumValN(Operation::Static, "static",
58                    "Produce a statically linked library from the input files")),
59     cl::init(Operation::None), cl::cat(LibtoolCategory));
60 
61 static cl::opt<bool> DeterministicOption(
62     "D", cl::desc("Use zero for timestamps and UIDs/GIDs (Default)"),
63     cl::init(false), cl::cat(LibtoolCategory));
64 
65 static cl::opt<bool>
66     NonDeterministicOption("U", cl::desc("Use actual timestamps and UIDs/GIDs"),
67                            cl::init(false), cl::cat(LibtoolCategory));
68 
69 static cl::opt<std::string>
70     FileList("filelist",
71              cl::desc("Pass in file containing a list of filenames"),
72              cl::value_desc("listfile[,dirname]"), cl::cat(LibtoolCategory));
73 
74 static cl::list<std::string> Libraries(
75     "l",
76     cl::desc(
77         "l<x> searches for the library libx.a in the library search path. If"
78         " the string 'x' ends with '.o', then the library 'x' is searched for"
79         " without prepending 'lib' or appending '.a'"),
80     cl::ZeroOrMore, cl::Prefix, cl::cat(LibtoolCategory));
81 
82 static cl::list<std::string> LibrarySearchDirs(
83     "L",
84     cl::desc(
85         "L<dir> adds <dir> to the list of directories in which to search for"
86         " libraries"),
87     cl::ZeroOrMore, cl::Prefix, cl::cat(LibtoolCategory));
88 
89 static cl::opt<bool>
90     VersionOption("V", cl::desc("Print the version number and exit"),
91                   cl::cat(LibtoolCategory));
92 
93 static cl::opt<bool> NoWarningForNoSymbols(
94     "no_warning_for_no_symbols",
95     cl::desc("Do not warn about files that have no symbols"),
96     cl::cat(LibtoolCategory), cl::init(false));
97 
98 static const std::array<std::string, 3> StandardSearchDirs{
99     "/lib",
100     "/usr/lib",
101     "/usr/local/lib",
102 };
103 
104 struct Config {
105   bool Deterministic = true; // Updated by 'D' and 'U' modifiers.
106   uint32_t ArchCPUType;
107   uint32_t ArchCPUSubtype;
108 };
109 
searchForFile(const Twine & FileName)110 static Expected<std::string> searchForFile(const Twine &FileName) {
111 
112   auto FindLib =
113       [FileName](ArrayRef<std::string> SearchDirs) -> Optional<std::string> {
114     for (StringRef Dir : SearchDirs) {
115       SmallString<128> Path;
116       sys::path::append(Path, Dir, FileName);
117 
118       if (sys::fs::exists(Path))
119         return std::string(Path);
120     }
121     return None;
122   };
123 
124   Optional<std::string> Found = FindLib(LibrarySearchDirs);
125   if (!Found)
126     Found = FindLib(StandardSearchDirs);
127   if (Found)
128     return *Found;
129 
130   return createStringError(std::errc::invalid_argument,
131                            "cannot locate file '%s'", FileName.str().c_str());
132 }
133 
processCommandLineLibraries()134 static Error processCommandLineLibraries() {
135   for (StringRef BaseName : Libraries) {
136     Expected<std::string> FullPath = searchForFile(
137         BaseName.endswith(".o") ? BaseName.str() : "lib" + BaseName + ".a");
138     if (!FullPath)
139       return FullPath.takeError();
140     InputFiles.push_back(FullPath.get());
141   }
142 
143   return Error::success();
144 }
145 
processFileList()146 static Error processFileList() {
147   StringRef FileName, DirName;
148   std::tie(FileName, DirName) = StringRef(FileList).rsplit(",");
149 
150   ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
151       MemoryBuffer::getFileOrSTDIN(FileName, /*IsText=*/false,
152                                    /*RequiresNullTerminator=*/false);
153   if (std::error_code EC = FileOrErr.getError())
154     return createFileError(FileName, errorCodeToError(EC));
155   const MemoryBuffer &Ref = *FileOrErr.get();
156 
157   line_iterator I(Ref, /*SkipBlanks=*/false);
158   if (I.is_at_eof())
159     return createStringError(std::errc::invalid_argument,
160                              "file list file: '%s' is empty",
161                              FileName.str().c_str());
162   for (; !I.is_at_eof(); ++I) {
163     StringRef Line = *I;
164     if (Line.empty())
165       return createStringError(std::errc::invalid_argument,
166                                "file list file: '%s': filename cannot be empty",
167                                FileName.str().c_str());
168 
169     SmallString<128> Path;
170     if (!DirName.empty())
171       sys::path::append(Path, DirName, Line);
172     else
173       sys::path::append(Path, Line);
174     InputFiles.push_back(static_cast<std::string>(Path));
175   }
176   return Error::success();
177 }
178 
validateArchitectureName(StringRef ArchitectureName)179 static Error validateArchitectureName(StringRef ArchitectureName) {
180   if (!MachOObjectFile::isValidArch(ArchitectureName)) {
181     std::string Buf;
182     raw_string_ostream OS(Buf);
183     for (StringRef Arch : MachOObjectFile::getValidArchs())
184       OS << Arch << " ";
185 
186     return createStringError(
187         std::errc::invalid_argument,
188         "invalid architecture '%s': valid architecture names are %s",
189         ArchitectureName.str().c_str(), OS.str().c_str());
190   }
191   return Error::success();
192 }
193 
getCPUID(uint32_t CPUType,uint32_t CPUSubtype)194 static uint64_t getCPUID(uint32_t CPUType, uint32_t CPUSubtype) {
195   switch (CPUType) {
196   case MachO::CPU_TYPE_ARM:
197   case MachO::CPU_TYPE_ARM64:
198   case MachO::CPU_TYPE_ARM64_32:
199   case MachO::CPU_TYPE_X86_64:
200     // We consider CPUSubtype only for the above 4 CPUTypes to match cctools'
201     // libtool behavior.
202     return static_cast<uint64_t>(CPUType) << 32 | CPUSubtype;
203   default:
204     return CPUType;
205   }
206 }
207 
208 // Check that a file's architecture [FileCPUType, FileCPUSubtype]
209 // matches the architecture specified under -arch_only flag.
acceptFileArch(uint32_t FileCPUType,uint32_t FileCPUSubtype,const Config & C)210 static bool acceptFileArch(uint32_t FileCPUType, uint32_t FileCPUSubtype,
211                            const Config &C) {
212   if (C.ArchCPUType != FileCPUType)
213     return false;
214 
215   switch (C.ArchCPUType) {
216   case MachO::CPU_TYPE_ARM:
217   case MachO::CPU_TYPE_ARM64_32:
218   case MachO::CPU_TYPE_X86_64:
219     return C.ArchCPUSubtype == FileCPUSubtype;
220 
221   case MachO::CPU_TYPE_ARM64:
222     if (C.ArchCPUSubtype == MachO::CPU_SUBTYPE_ARM64_ALL)
223       return FileCPUSubtype == MachO::CPU_SUBTYPE_ARM64_ALL ||
224              FileCPUSubtype == MachO::CPU_SUBTYPE_ARM64_V8;
225     else
226       return C.ArchCPUSubtype == FileCPUSubtype;
227 
228   default:
229     return true;
230   }
231 }
232 
verifyAndAddMachOObject(MembersPerArchitectureMap & Members,NewArchiveMember Member,const Config & C)233 static Error verifyAndAddMachOObject(MembersPerArchitectureMap &Members,
234                                      NewArchiveMember Member, const Config &C) {
235   auto MBRef = Member.Buf->getMemBufferRef();
236   Expected<std::unique_ptr<object::ObjectFile>> ObjOrErr =
237       object::ObjectFile::createObjectFile(MBRef);
238 
239   // Throw error if not a valid object file.
240   if (!ObjOrErr)
241     return createFileError(Member.MemberName, ObjOrErr.takeError());
242 
243   // Throw error if not in Mach-O format.
244   if (!isa<object::MachOObjectFile>(**ObjOrErr))
245     return createStringError(std::errc::invalid_argument,
246                              "'%s': format not supported",
247                              Member.MemberName.data());
248 
249   auto *O = dyn_cast<MachOObjectFile>(ObjOrErr->get());
250   uint32_t FileCPUType, FileCPUSubtype;
251   std::tie(FileCPUType, FileCPUSubtype) = MachO::getCPUTypeFromArchitecture(
252       MachO::getArchitectureFromName(O->getArchTriple().getArchName()));
253 
254   // If -arch_only is specified then skip this file if it doesn't match
255   // the architecture specified.
256   if (!ArchType.empty() && !acceptFileArch(FileCPUType, FileCPUSubtype, C)) {
257     return Error::success();
258   }
259 
260   if (!NoWarningForNoSymbols && O->symbols().empty())
261     WithColor::warning() << Member.MemberName + " has no symbols\n";
262 
263   uint64_t FileCPUID = getCPUID(FileCPUType, FileCPUSubtype);
264   Members[FileCPUID].push_back(std::move(Member));
265   return Error::success();
266 }
267 
verifyAndAddIRObject(MembersPerArchitectureMap & Members,NewArchiveMember Member,const Config & C)268 static Error verifyAndAddIRObject(MembersPerArchitectureMap &Members,
269                                   NewArchiveMember Member, const Config &C) {
270   auto MBRef = Member.Buf->getMemBufferRef();
271   Expected<std::unique_ptr<object::IRObjectFile>> IROrErr =
272       object::IRObjectFile::create(MBRef, LLVMCtx);
273 
274   // Throw error if not a valid IR object file.
275   if (!IROrErr)
276     return createFileError(Member.MemberName, IROrErr.takeError());
277 
278   Triple TT = Triple(IROrErr->get()->getTargetTriple());
279 
280   Expected<uint32_t> FileCPUTypeOrErr = MachO::getCPUType(TT);
281   if (!FileCPUTypeOrErr)
282     return FileCPUTypeOrErr.takeError();
283 
284   Expected<uint32_t> FileCPUSubTypeOrErr = MachO::getCPUSubType(TT);
285   if (!FileCPUSubTypeOrErr)
286     return FileCPUSubTypeOrErr.takeError();
287 
288   // If -arch_only is specified then skip this file if it doesn't match
289   // the architecture specified.
290   if (!ArchType.empty() &&
291       !acceptFileArch(*FileCPUTypeOrErr, *FileCPUSubTypeOrErr, C)) {
292     return Error::success();
293   }
294 
295   uint64_t FileCPUID = getCPUID(*FileCPUTypeOrErr, *FileCPUSubTypeOrErr);
296   Members[FileCPUID].push_back(std::move(Member));
297   return Error::success();
298 }
299 
addChildMember(MembersPerArchitectureMap & Members,const object::Archive::Child & M,const Config & C)300 static Error addChildMember(MembersPerArchitectureMap &Members,
301                             const object::Archive::Child &M, const Config &C) {
302   Expected<NewArchiveMember> NMOrErr =
303       NewArchiveMember::getOldMember(M, C.Deterministic);
304   if (!NMOrErr)
305     return NMOrErr.takeError();
306 
307   file_magic Magic = identify_magic(NMOrErr->Buf->getBuffer());
308 
309   if (Magic == file_magic::bitcode)
310     return verifyAndAddIRObject(Members, std::move(*NMOrErr), C);
311 
312   if (Error E = verifyAndAddMachOObject(Members, std::move(*NMOrErr), C))
313     return E;
314 
315   return Error::success();
316 }
317 
processArchive(MembersPerArchitectureMap & Members,object::Archive & Lib,StringRef FileName,const Config & C)318 static Error processArchive(MembersPerArchitectureMap &Members,
319                             object::Archive &Lib, StringRef FileName,
320                             const Config &C) {
321   Error Err = Error::success();
322   for (const object::Archive::Child &Child : Lib.children(Err))
323     if (Error E = addChildMember(Members, Child, C))
324       return createFileError(FileName, std::move(E));
325   if (Err)
326     return createFileError(FileName, std::move(Err));
327 
328   return Error::success();
329 }
330 
331 static Error
addArchiveMembers(MembersPerArchitectureMap & Members,std::vector<std::unique_ptr<MemoryBuffer>> & ArchiveBuffers,NewArchiveMember NM,StringRef FileName,const Config & C)332 addArchiveMembers(MembersPerArchitectureMap &Members,
333                   std::vector<std::unique_ptr<MemoryBuffer>> &ArchiveBuffers,
334                   NewArchiveMember NM, StringRef FileName, const Config &C) {
335   Expected<std::unique_ptr<Archive>> LibOrErr =
336       object::Archive::create(NM.Buf->getMemBufferRef());
337   if (!LibOrErr)
338     return createFileError(FileName, LibOrErr.takeError());
339 
340   if (Error E = processArchive(Members, **LibOrErr, FileName, C))
341     return E;
342 
343   // Update vector ArchiveBuffers with the MemoryBuffers to transfer
344   // ownership.
345   ArchiveBuffers.push_back(std::move(NM.Buf));
346   return Error::success();
347 }
348 
addUniversalMembers(MembersPerArchitectureMap & Members,std::vector<std::unique_ptr<MemoryBuffer>> & UniversalBuffers,NewArchiveMember NM,StringRef FileName,const Config & C)349 static Error addUniversalMembers(
350     MembersPerArchitectureMap &Members,
351     std::vector<std::unique_ptr<MemoryBuffer>> &UniversalBuffers,
352     NewArchiveMember NM, StringRef FileName, const Config &C) {
353   Expected<std::unique_ptr<MachOUniversalBinary>> BinaryOrErr =
354       MachOUniversalBinary::create(NM.Buf->getMemBufferRef());
355   if (!BinaryOrErr)
356     return createFileError(FileName, BinaryOrErr.takeError());
357 
358   auto *UO = BinaryOrErr->get();
359   for (const MachOUniversalBinary::ObjectForArch &O : UO->objects()) {
360 
361     Expected<std::unique_ptr<MachOObjectFile>> MachOObjOrErr =
362         O.getAsObjectFile();
363     if (MachOObjOrErr) {
364       NewArchiveMember NewMember =
365           NewArchiveMember(MachOObjOrErr->get()->getMemoryBufferRef());
366       NewMember.MemberName = sys::path::filename(NewMember.MemberName);
367 
368       if (Error E = verifyAndAddMachOObject(Members, std::move(NewMember), C))
369         return E;
370       continue;
371     }
372 
373     Expected<std::unique_ptr<IRObjectFile>> IRObjectOrError =
374         O.getAsIRObject(LLVMCtx);
375     if (IRObjectOrError) {
376       // A universal file member can be a MachOObjectFile, an IRObject or an
377       // Archive. In case we can successfully cast the member as an IRObject, it
378       // is safe to throw away the error generated due to casting the object as
379       // a MachOObjectFile.
380       consumeError(MachOObjOrErr.takeError());
381 
382       NewArchiveMember NewMember =
383           NewArchiveMember(IRObjectOrError->get()->getMemoryBufferRef());
384       NewMember.MemberName = sys::path::filename(NewMember.MemberName);
385 
386       if (Error E = verifyAndAddIRObject(Members, std::move(NewMember), C))
387         return E;
388       continue;
389     }
390 
391     Expected<std::unique_ptr<Archive>> ArchiveOrError = O.getAsArchive();
392     if (ArchiveOrError) {
393       // A universal file member can be a MachOObjectFile, an IRObject or an
394       // Archive. In case we can successfully cast the member as an Archive, it
395       // is safe to throw away the error generated due to casting the object as
396       // a MachOObjectFile.
397       consumeError(MachOObjOrErr.takeError());
398       consumeError(IRObjectOrError.takeError());
399 
400       if (Error E = processArchive(Members, **ArchiveOrError, FileName, C))
401         return E;
402       continue;
403     }
404 
405     Error CombinedError = joinErrors(
406         ArchiveOrError.takeError(),
407         joinErrors(IRObjectOrError.takeError(), MachOObjOrErr.takeError()));
408     return createFileError(FileName, std::move(CombinedError));
409   }
410 
411   // Update vector UniversalBuffers with the MemoryBuffers to transfer
412   // ownership.
413   UniversalBuffers.push_back(std::move(NM.Buf));
414   return Error::success();
415 }
416 
addMember(MembersPerArchitectureMap & Members,std::vector<std::unique_ptr<MemoryBuffer>> & FileBuffers,StringRef FileName,const Config & C)417 static Error addMember(MembersPerArchitectureMap &Members,
418                        std::vector<std::unique_ptr<MemoryBuffer>> &FileBuffers,
419                        StringRef FileName, const Config &C) {
420   Expected<NewArchiveMember> NMOrErr =
421       NewArchiveMember::getFile(FileName, C.Deterministic);
422   if (!NMOrErr)
423     return createFileError(FileName, NMOrErr.takeError());
424 
425   // For regular archives, use the basename of the object path for the member
426   // name.
427   NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName);
428   file_magic Magic = identify_magic(NMOrErr->Buf->getBuffer());
429 
430   // Flatten archives.
431   if (Magic == file_magic::archive)
432     return addArchiveMembers(Members, FileBuffers, std::move(*NMOrErr),
433                              FileName, C);
434 
435   // Flatten universal files.
436   if (Magic == file_magic::macho_universal_binary)
437     return addUniversalMembers(Members, FileBuffers, std::move(*NMOrErr),
438                                FileName, C);
439 
440   // Bitcode files.
441   if (Magic == file_magic::bitcode)
442     return verifyAndAddIRObject(Members, std::move(*NMOrErr), C);
443 
444   if (Error E = verifyAndAddMachOObject(Members, std::move(*NMOrErr), C))
445     return E;
446   return Error::success();
447 }
448 
449 static Expected<SmallVector<Slice, 2>>
buildSlices(ArrayRef<OwningBinary<Archive>> OutputBinaries)450 buildSlices(ArrayRef<OwningBinary<Archive>> OutputBinaries) {
451   SmallVector<Slice, 2> Slices;
452 
453   for (const auto &OB : OutputBinaries) {
454     const Archive &A = *OB.getBinary();
455     Expected<Slice> ArchiveSlice = Slice::create(A, &LLVMCtx);
456     if (!ArchiveSlice)
457       return ArchiveSlice.takeError();
458     Slices.push_back(*ArchiveSlice);
459   }
460   return Slices;
461 }
462 
createStaticLibrary(const Config & C)463 static Error createStaticLibrary(const Config &C) {
464   MembersPerArchitectureMap NewMembers;
465   std::vector<std::unique_ptr<MemoryBuffer>> FileBuffers;
466   for (StringRef FileName : InputFiles)
467     if (Error E = addMember(NewMembers, FileBuffers, FileName, C))
468       return E;
469 
470   if (!ArchType.empty()) {
471     uint64_t ArchCPUID = getCPUID(C.ArchCPUType, C.ArchCPUSubtype);
472     if (NewMembers.find(ArchCPUID) == NewMembers.end())
473       return createStringError(std::errc::invalid_argument,
474                                "no library created (no object files in input "
475                                "files matching -arch_only %s)",
476                                ArchType.c_str());
477   }
478 
479   if (NewMembers.size() == 1) {
480     if (Error E =
481             writeArchive(OutputFile, NewMembers.begin()->second,
482                          /*WriteSymtab=*/true,
483                          /*Kind=*/object::Archive::K_DARWIN, C.Deterministic,
484                          /*Thin=*/false))
485       return E;
486   } else {
487     SmallVector<OwningBinary<Archive>, 2> OutputBinaries;
488     for (const std::pair<const uint64_t, std::vector<NewArchiveMember>> &M :
489          NewMembers) {
490       Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr =
491           writeArchiveToBuffer(M.second,
492                                /*WriteSymtab=*/true,
493                                /*Kind=*/object::Archive::K_DARWIN,
494                                C.Deterministic,
495                                /*Thin=*/false);
496       if (!OutputBufferOrErr)
497         return OutputBufferOrErr.takeError();
498       std::unique_ptr<MemoryBuffer> &OutputBuffer = OutputBufferOrErr.get();
499 
500       Expected<std::unique_ptr<Archive>> ArchiveOrError =
501           Archive::create(OutputBuffer->getMemBufferRef());
502       if (!ArchiveOrError)
503         return ArchiveOrError.takeError();
504       std::unique_ptr<Archive> &A = ArchiveOrError.get();
505 
506       OutputBinaries.push_back(
507           OwningBinary<Archive>(std::move(A), std::move(OutputBuffer)));
508     }
509 
510     Expected<SmallVector<Slice, 2>> Slices = buildSlices(OutputBinaries);
511     if (!Slices)
512       return Slices.takeError();
513 
514     llvm::stable_sort(*Slices);
515     if (Error E = writeUniversalBinary(*Slices, OutputFile))
516       return E;
517   }
518   return Error::success();
519 }
520 
parseCommandLine(int Argc,char ** Argv)521 static Expected<Config> parseCommandLine(int Argc, char **Argv) {
522   Config C;
523   cl::ParseCommandLineOptions(Argc, Argv, "llvm-libtool-darwin\n");
524 
525   if (LibraryOperation == Operation::None) {
526     if (!VersionOption) {
527       std::string Error;
528       raw_string_ostream Stream(Error);
529       LibraryOperation.error("must be specified", "", Stream);
530       return createStringError(std::errc::invalid_argument, Error.c_str());
531     }
532     return C;
533   }
534 
535   if (OutputFile.empty()) {
536     std::string Error;
537     raw_string_ostream Stream(Error);
538     OutputFile.error("must be specified", "o", Stream);
539     return createStringError(std::errc::invalid_argument, Error.c_str());
540   }
541 
542   if (DeterministicOption && NonDeterministicOption)
543     return createStringError(std::errc::invalid_argument,
544                              "cannot specify both -D and -U flags");
545   else if (NonDeterministicOption)
546     C.Deterministic = false;
547 
548   if (!Libraries.empty())
549     if (Error E = processCommandLineLibraries())
550       return std::move(E);
551 
552   if (!FileList.empty())
553     if (Error E = processFileList())
554       return std::move(E);
555 
556   if (InputFiles.empty())
557     return createStringError(std::errc::invalid_argument,
558                              "no input files specified");
559 
560   if (ArchType.getNumOccurrences()) {
561     if (Error E = validateArchitectureName(ArchType))
562       return std::move(E);
563 
564     std::tie(C.ArchCPUType, C.ArchCPUSubtype) =
565         MachO::getCPUTypeFromArchitecture(
566             MachO::getArchitectureFromName(ArchType));
567   }
568 
569   return C;
570 }
571 
main(int Argc,char ** Argv)572 int main(int Argc, char **Argv) {
573   InitLLVM X(Argc, Argv);
574   cl::HideUnrelatedOptions({&LibtoolCategory, &getColorCategory()});
575   Expected<Config> ConfigOrErr = parseCommandLine(Argc, Argv);
576   if (!ConfigOrErr) {
577     WithColor::defaultErrorHandler(ConfigOrErr.takeError());
578     return EXIT_FAILURE;
579   }
580 
581   if (VersionOption)
582     cl::PrintVersionMessage();
583 
584   Config C = *ConfigOrErr;
585   switch (LibraryOperation) {
586   case Operation::None:
587     break;
588   case Operation::Static:
589     if (Error E = createStaticLibrary(C)) {
590       WithColor::defaultErrorHandler(std::move(E));
591       return EXIT_FAILURE;
592     }
593     break;
594   }
595 }
596