|  | //===-- llvm-libtool-darwin.cpp - a tool for creating libraries -----------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // A utility for creating static and dynamic libraries for Darwin. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "DependencyInfo.h" | 
|  | #include "llvm/ADT/MapVector.h" | 
|  | #include "llvm/BinaryFormat/Magic.h" | 
|  | #include "llvm/IR/LLVMContext.h" | 
|  | #include "llvm/Object/ArchiveWriter.h" | 
|  | #include "llvm/Object/IRObjectFile.h" | 
|  | #include "llvm/Object/MachO.h" | 
|  | #include "llvm/Object/MachOUniversal.h" | 
|  | #include "llvm/Object/MachOUniversalWriter.h" | 
|  | #include "llvm/Object/ObjectFile.h" | 
|  | #include "llvm/Option/ArgList.h" | 
|  | #include "llvm/Option/Option.h" | 
|  | #include "llvm/Support/CommandLine.h" | 
|  | #include "llvm/Support/LLVMDriver.h" | 
|  | #include "llvm/Support/LineIterator.h" | 
|  | #include "llvm/Support/TargetSelect.h" | 
|  | #include "llvm/Support/VirtualFileSystem.h" | 
|  | #include "llvm/Support/WithColor.h" | 
|  | #include "llvm/Support/YAMLTraits.h" | 
|  | #include "llvm/Support/raw_ostream.h" | 
|  | #include "llvm/TextAPI/Architecture.h" | 
|  | #include <cstdlib> | 
|  | #include <map> | 
|  | #include <type_traits> | 
|  |  | 
|  | using namespace llvm; | 
|  | using namespace llvm::object; | 
|  | using namespace llvm::opt; | 
|  |  | 
|  | // Command-line option boilerplate. | 
|  | namespace { | 
|  | enum ID { | 
|  | OPT_INVALID = 0, // This is not an option ID. | 
|  | #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), | 
|  | #include "Opts.inc" | 
|  | #undef OPTION | 
|  | }; | 
|  |  | 
|  | #define OPTTABLE_STR_TABLE_CODE | 
|  | #include "Opts.inc" | 
|  | #undef OPTTABLE_STR_TABLE_CODE | 
|  |  | 
|  | #define OPTTABLE_PREFIXES_TABLE_CODE | 
|  | #include "Opts.inc" | 
|  | #undef OPTTABLE_PREFIXES_TABLE_CODE | 
|  |  | 
|  | static constexpr opt::OptTable::Info InfoTable[] = { | 
|  | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), | 
|  | #include "Opts.inc" | 
|  | #undef OPTION | 
|  | }; | 
|  |  | 
|  | class LibtoolDarwinOptTable : public opt::GenericOptTable { | 
|  | public: | 
|  | LibtoolDarwinOptTable() | 
|  | : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {} | 
|  | }; | 
|  | } // end anonymous namespace | 
|  |  | 
|  | class NewArchiveMemberList; | 
|  | typedef std::map<uint64_t, NewArchiveMemberList> MembersPerArchitectureMap; | 
|  |  | 
|  | static std::string OutputFile; | 
|  | static std::vector<std::string> InputFiles; | 
|  | static std::optional<std::string> ArchType; | 
|  |  | 
|  | enum class Operation { None, Static }; | 
|  | static Operation LibraryOperation = Operation::None; | 
|  |  | 
|  | static bool DeterministicOption; | 
|  | static bool NonDeterministicOption; | 
|  | static std::string FileList; | 
|  | static std::vector<std::string> Libraries; | 
|  | static std::vector<std::string> LibrarySearchDirs; | 
|  | static std::string DependencyInfoPath; | 
|  | static bool VersionOption; | 
|  | static bool NoWarningForNoSymbols; | 
|  | static bool WarningsAsErrors; | 
|  | static std::string IgnoredSyslibRoot; | 
|  |  | 
|  | static const std::array<std::string, 3> StandardSearchDirs{ | 
|  | "/lib", | 
|  | "/usr/lib", | 
|  | "/usr/local/lib", | 
|  | }; | 
|  |  | 
|  | std::unique_ptr<DependencyInfo> GlobalDependencyInfo; | 
|  |  | 
|  | struct Config { | 
|  | bool Deterministic = true; // Updated by 'D' and 'U' modifiers. | 
|  | uint32_t ArchCPUType; | 
|  | uint32_t ArchCPUSubtype; | 
|  | }; | 
|  |  | 
|  | static Expected<std::string> searchForFile(const Twine &FileName) { | 
|  | auto FindLib = | 
|  | [FileName]( | 
|  | ArrayRef<std::string> SearchDirs) -> std::optional<std::string> { | 
|  | for (StringRef Dir : SearchDirs) { | 
|  | SmallString<128> Path; | 
|  | sys::path::append(Path, Dir, FileName); | 
|  |  | 
|  | if (sys::fs::exists(Path)) | 
|  | return std::string(Path); | 
|  |  | 
|  | GlobalDependencyInfo->addMissingInput(Path); | 
|  | } | 
|  | return std::nullopt; | 
|  | }; | 
|  |  | 
|  | std::optional<std::string> Found = FindLib(LibrarySearchDirs); | 
|  | if (!Found) | 
|  | Found = FindLib(StandardSearchDirs); | 
|  | if (Found) | 
|  | return *Found; | 
|  |  | 
|  | return createStringError(std::errc::invalid_argument, | 
|  | "cannot locate file '%s'", FileName.str().c_str()); | 
|  | } | 
|  |  | 
|  | static Error processCommandLineLibraries() { | 
|  | for (StringRef BaseName : Libraries) { | 
|  | Expected<std::string> FullPath = searchForFile( | 
|  | BaseName.ends_with(".o") ? BaseName.str() : "lib" + BaseName + ".a"); | 
|  | if (!FullPath) | 
|  | return FullPath.takeError(); | 
|  | InputFiles.push_back(FullPath.get()); | 
|  | } | 
|  |  | 
|  | return Error::success(); | 
|  | } | 
|  |  | 
|  | static Error processFileList() { | 
|  | StringRef FileName, DirName; | 
|  | std::tie(FileName, DirName) = StringRef(FileList).rsplit(","); | 
|  |  | 
|  | ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = | 
|  | MemoryBuffer::getFileOrSTDIN(FileName, /*IsText=*/false, | 
|  | /*RequiresNullTerminator=*/false); | 
|  | if (std::error_code EC = FileOrErr.getError()) | 
|  | return createFileError(FileName, errorCodeToError(EC)); | 
|  | const MemoryBuffer &Ref = *FileOrErr.get(); | 
|  |  | 
|  | line_iterator I(Ref, /*SkipBlanks=*/false); | 
|  | if (I.is_at_eof()) | 
|  | return createStringError(std::errc::invalid_argument, | 
|  | "file list file: '%s' is empty", | 
|  | FileName.str().c_str()); | 
|  | for (; !I.is_at_eof(); ++I) { | 
|  | StringRef Line = *I; | 
|  | if (Line.empty()) | 
|  | return createStringError(std::errc::invalid_argument, | 
|  | "file list file: '%s': filename cannot be empty", | 
|  | FileName.str().c_str()); | 
|  |  | 
|  | SmallString<128> Path; | 
|  | if (!DirName.empty()) | 
|  | sys::path::append(Path, DirName, Line); | 
|  | else | 
|  | sys::path::append(Path, Line); | 
|  | InputFiles.push_back(static_cast<std::string>(Path)); | 
|  | } | 
|  | return Error::success(); | 
|  | } | 
|  |  | 
|  | static Error validateArchitectureName(StringRef ArchitectureName) { | 
|  | if (!MachOObjectFile::isValidArch(ArchitectureName)) { | 
|  | std::string Buf; | 
|  | raw_string_ostream OS(Buf); | 
|  | for (StringRef Arch : MachOObjectFile::getValidArchs()) | 
|  | OS << Arch << " "; | 
|  |  | 
|  | return createStringError( | 
|  | std::errc::invalid_argument, | 
|  | "invalid architecture '%s': valid architecture names are %s", | 
|  | ArchitectureName.str().c_str(), Buf.c_str()); | 
|  | } | 
|  | return Error::success(); | 
|  | } | 
|  |  | 
|  | static uint64_t getCPUID(uint32_t CPUType, uint32_t CPUSubtype) { | 
|  | switch (CPUType) { | 
|  | case MachO::CPU_TYPE_ARM: | 
|  | case MachO::CPU_TYPE_ARM64: | 
|  | case MachO::CPU_TYPE_ARM64_32: | 
|  | case MachO::CPU_TYPE_X86_64: | 
|  | // We consider CPUSubtype only for the above 4 CPUTypes to match cctools' | 
|  | // libtool behavior. | 
|  | return static_cast<uint64_t>(CPUType) << 32 | CPUSubtype; | 
|  | default: | 
|  | return CPUType; | 
|  | } | 
|  | } | 
|  |  | 
|  | // MembersData is an organized collection of members. | 
|  | struct MembersData { | 
|  | // MembersPerArchitectureMap is a mapping from CPU architecture to a list of | 
|  | // members. | 
|  | MembersPerArchitectureMap MembersPerArchitecture; | 
|  | std::vector<std::unique_ptr<MemoryBuffer>> FileBuffers; | 
|  | }; | 
|  |  | 
|  | // NewArchiveMemberList instances serve as collections of archive members and | 
|  | // information about those members. | 
|  | class NewArchiveMemberList { | 
|  | std::vector<NewArchiveMember> Members; | 
|  | // This vector contains the file that each NewArchiveMember from Members came | 
|  | // from. Therefore, it has the same size as Members. | 
|  | std::vector<StringRef> Files; | 
|  |  | 
|  | public: | 
|  | // Add a NewArchiveMember and the file it came from to the list. | 
|  | void push_back(NewArchiveMember &&Member, StringRef File) { | 
|  | Members.push_back(std::move(Member)); | 
|  | Files.push_back(File); | 
|  | } | 
|  |  | 
|  | ArrayRef<NewArchiveMember> getMembers() const { return Members; } | 
|  |  | 
|  | ArrayRef<StringRef> getFiles() const { return Files; } | 
|  |  | 
|  | static_assert( | 
|  | std::is_same<decltype(MembersData::MembersPerArchitecture)::mapped_type, | 
|  | NewArchiveMemberList>(), | 
|  | "This test makes sure NewArchiveMemberList is used by MembersData since " | 
|  | "the following asserts test invariants required for MembersData."); | 
|  | static_assert( | 
|  | !std::is_copy_constructible_v< | 
|  | decltype(NewArchiveMemberList::Members)::value_type>, | 
|  | "MembersData::MembersPerArchitecture has a dependency on " | 
|  | "MembersData::FileBuffers so it should not be able to " | 
|  | "be copied on its own without FileBuffers. Unfortunately, " | 
|  | "is_copy_constructible does not detect whether the container (ie vector) " | 
|  | "of a non-copyable type is itself non-copyable so we have to test the " | 
|  | "actual type of the stored data (ie, value_type)."); | 
|  | static_assert( | 
|  | !std::is_copy_assignable_v< | 
|  | decltype(NewArchiveMemberList::Members)::value_type>, | 
|  | "MembersData::MembersPerArchitecture has a dependency on " | 
|  | "MembersData::FileBuffers so it should not be able to " | 
|  | "be copied on its own without FileBuffers. Unfortunately, " | 
|  | "is_copy_constructible does not detect whether the container (ie vector) " | 
|  | "of a non-copyable type is itself non-copyable so we have to test the " | 
|  | "actual type of the stored data (ie, value_type)."); | 
|  | }; | 
|  |  | 
|  | // MembersBuilder collects and organizes all members from the files provided by | 
|  | // the user. | 
|  | class MembersBuilder { | 
|  | public: | 
|  | MembersBuilder(LLVMContext &LLVMCtx, const Config &C) | 
|  | : LLVMCtx(LLVMCtx), C(C) {} | 
|  |  | 
|  | Expected<MembersData> build() { | 
|  | for (StringRef FileName : InputFiles) | 
|  | if (Error E = AddMember(*this, FileName)()) | 
|  | return std::move(E); | 
|  |  | 
|  | std::string Arch = ArchType.value_or(""); | 
|  | if (!Arch.empty()) { | 
|  | uint64_t ArchCPUID = getCPUID(C.ArchCPUType, C.ArchCPUSubtype); | 
|  | if (Data.MembersPerArchitecture.find(ArchCPUID) == | 
|  | Data.MembersPerArchitecture.end()) | 
|  | return createStringError(std::errc::invalid_argument, | 
|  | "no library created (no object files in input " | 
|  | "files matching -arch_only %s)", | 
|  | Arch.c_str()); | 
|  | } | 
|  | return std::move(Data); | 
|  | } | 
|  |  | 
|  | private: | 
|  | class AddMember { | 
|  | MembersBuilder &Builder; | 
|  | StringRef FileName; | 
|  |  | 
|  | public: | 
|  | AddMember(MembersBuilder &Builder, StringRef FileName) | 
|  | : Builder(Builder), FileName(FileName) {} | 
|  |  | 
|  | Error operator()() { | 
|  | Expected<NewArchiveMember> NewMemberOrErr = | 
|  | NewArchiveMember::getFile(FileName, Builder.C.Deterministic); | 
|  | if (!NewMemberOrErr) | 
|  | return createFileError(FileName, NewMemberOrErr.takeError()); | 
|  | auto &NewMember = *NewMemberOrErr; | 
|  |  | 
|  | // For regular archives, use the basename of the object path for the | 
|  | // member name. | 
|  | NewMember.MemberName = sys::path::filename(NewMember.MemberName); | 
|  | file_magic Magic = identify_magic(NewMember.Buf->getBuffer()); | 
|  |  | 
|  | // Flatten archives. | 
|  | if (Magic == file_magic::archive) | 
|  | return addArchiveMembers(std::move(NewMember)); | 
|  |  | 
|  | // Flatten universal files. | 
|  | if (Magic == file_magic::macho_universal_binary) | 
|  | return addUniversalMembers(std::move(NewMember)); | 
|  |  | 
|  | // Bitcode files. | 
|  | if (Magic == file_magic::bitcode) | 
|  | return verifyAndAddIRObject(std::move(NewMember)); | 
|  |  | 
|  | return verifyAndAddMachOObject(std::move(NewMember)); | 
|  | } | 
|  |  | 
|  | private: | 
|  | // Check that a file's architecture [FileCPUType, FileCPUSubtype] | 
|  | // matches the architecture specified under -arch_only flag. | 
|  | bool acceptFileArch(uint32_t FileCPUType, uint32_t FileCPUSubtype) { | 
|  | if (Builder.C.ArchCPUType != FileCPUType) | 
|  | return false; | 
|  |  | 
|  | switch (Builder.C.ArchCPUType) { | 
|  | case MachO::CPU_TYPE_ARM: | 
|  | case MachO::CPU_TYPE_ARM64_32: | 
|  | case MachO::CPU_TYPE_X86_64: | 
|  | return Builder.C.ArchCPUSubtype == FileCPUSubtype; | 
|  |  | 
|  | case MachO::CPU_TYPE_ARM64: | 
|  | if (Builder.C.ArchCPUSubtype == MachO::CPU_SUBTYPE_ARM64_ALL) | 
|  | return FileCPUSubtype == MachO::CPU_SUBTYPE_ARM64_ALL || | 
|  | FileCPUSubtype == MachO::CPU_SUBTYPE_ARM64_V8; | 
|  | else | 
|  | return Builder.C.ArchCPUSubtype == FileCPUSubtype; | 
|  |  | 
|  | default: | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | Error verifyAndAddMachOObject(NewArchiveMember Member) { | 
|  | auto MBRef = Member.Buf->getMemBufferRef(); | 
|  | Expected<std::unique_ptr<object::ObjectFile>> ObjOrErr = | 
|  | object::ObjectFile::createObjectFile(MBRef); | 
|  |  | 
|  | // Throw error if not a valid object file. | 
|  | if (!ObjOrErr) | 
|  | return createFileError(Member.MemberName, ObjOrErr.takeError()); | 
|  |  | 
|  | // Throw error if not in Mach-O format. | 
|  | if (!isa<object::MachOObjectFile>(**ObjOrErr)) | 
|  | return createStringError(std::errc::invalid_argument, | 
|  | "'%s': format not supported", | 
|  | Member.MemberName.data()); | 
|  |  | 
|  | auto *O = cast<MachOObjectFile>(ObjOrErr->get()); | 
|  | uint32_t FileCPUType, FileCPUSubtype; | 
|  | std::tie(FileCPUType, FileCPUSubtype) = MachO::getCPUTypeFromArchitecture( | 
|  | MachO::getArchitectureFromName(O->getArchTriple().getArchName())); | 
|  |  | 
|  | // If -arch_only is specified then skip this file if it doesn't match | 
|  | // the architecture specified. | 
|  | if (ArchType && !acceptFileArch(FileCPUType, FileCPUSubtype)) { | 
|  | return Error::success(); | 
|  | } | 
|  |  | 
|  | if (!NoWarningForNoSymbols && O->symbols().empty()) { | 
|  | Error E = createFileError( | 
|  | Member.MemberName, | 
|  | createStringError(std::errc::invalid_argument, | 
|  | "has no symbols for architecture %s", | 
|  | O->getArchTriple().getArchName().str().c_str())); | 
|  |  | 
|  | if (WarningsAsErrors) | 
|  | return E; | 
|  | WithColor::defaultWarningHandler(std::move(E)); | 
|  | } | 
|  |  | 
|  | uint64_t FileCPUID = getCPUID(FileCPUType, FileCPUSubtype); | 
|  | Builder.Data.MembersPerArchitecture[FileCPUID].push_back( | 
|  | std::move(Member), FileName); | 
|  | return Error::success(); | 
|  | } | 
|  |  | 
|  | Error verifyAndAddIRObject(NewArchiveMember Member) { | 
|  | auto MBRef = Member.Buf->getMemBufferRef(); | 
|  | Expected<std::unique_ptr<object::IRObjectFile>> IROrErr = | 
|  | object::IRObjectFile::create(MBRef, Builder.LLVMCtx); | 
|  |  | 
|  | // Throw error if not a valid IR object file. | 
|  | if (!IROrErr) | 
|  | return createFileError(Member.MemberName, IROrErr.takeError()); | 
|  |  | 
|  | Triple TT = Triple(IROrErr->get()->getTargetTriple()); | 
|  |  | 
|  | Expected<uint32_t> FileCPUTypeOrErr = MachO::getCPUType(TT); | 
|  | if (!FileCPUTypeOrErr) | 
|  | return FileCPUTypeOrErr.takeError(); | 
|  |  | 
|  | Expected<uint32_t> FileCPUSubTypeOrErr = MachO::getCPUSubType(TT); | 
|  | if (!FileCPUSubTypeOrErr) | 
|  | return FileCPUSubTypeOrErr.takeError(); | 
|  |  | 
|  | // If -arch_only is specified then skip this file if it doesn't match | 
|  | // the architecture specified. | 
|  | if (ArchType && | 
|  | !acceptFileArch(*FileCPUTypeOrErr, *FileCPUSubTypeOrErr)) { | 
|  | return Error::success(); | 
|  | } | 
|  |  | 
|  | uint64_t FileCPUID = getCPUID(*FileCPUTypeOrErr, *FileCPUSubTypeOrErr); | 
|  | Builder.Data.MembersPerArchitecture[FileCPUID].push_back( | 
|  | std::move(Member), FileName); | 
|  | return Error::success(); | 
|  | } | 
|  |  | 
|  | Error addChildMember(const object::Archive::Child &M) { | 
|  | Expected<NewArchiveMember> NewMemberOrErr = | 
|  | NewArchiveMember::getOldMember(M, Builder.C.Deterministic); | 
|  | if (!NewMemberOrErr) | 
|  | return NewMemberOrErr.takeError(); | 
|  | auto &NewMember = *NewMemberOrErr; | 
|  |  | 
|  | file_magic Magic = identify_magic(NewMember.Buf->getBuffer()); | 
|  |  | 
|  | if (Magic == file_magic::bitcode) | 
|  | return verifyAndAddIRObject(std::move(NewMember)); | 
|  |  | 
|  | return verifyAndAddMachOObject(std::move(NewMember)); | 
|  | } | 
|  |  | 
|  | Error processArchive(object::Archive &Lib) { | 
|  | Error Err = Error::success(); | 
|  | for (const object::Archive::Child &Child : Lib.children(Err)) | 
|  | if (Error E = addChildMember(Child)) | 
|  | return createFileError(FileName, std::move(E)); | 
|  | if (Err) | 
|  | return createFileError(FileName, std::move(Err)); | 
|  |  | 
|  | return Error::success(); | 
|  | } | 
|  |  | 
|  | Error addArchiveMembers(NewArchiveMember NewMember) { | 
|  | Expected<std::unique_ptr<Archive>> LibOrErr = | 
|  | object::Archive::create(NewMember.Buf->getMemBufferRef()); | 
|  | if (!LibOrErr) | 
|  | return createFileError(FileName, LibOrErr.takeError()); | 
|  |  | 
|  | if (Error E = processArchive(**LibOrErr)) | 
|  | return E; | 
|  |  | 
|  | // Update vector FileBuffers with the MemoryBuffers to transfer | 
|  | // ownership. | 
|  | Builder.Data.FileBuffers.push_back(std::move(NewMember.Buf)); | 
|  | return Error::success(); | 
|  | } | 
|  |  | 
|  | Error addUniversalMembers(NewArchiveMember NewMember) { | 
|  | Expected<std::unique_ptr<MachOUniversalBinary>> BinaryOrErr = | 
|  | MachOUniversalBinary::create(NewMember.Buf->getMemBufferRef()); | 
|  | if (!BinaryOrErr) | 
|  | return createFileError(FileName, BinaryOrErr.takeError()); | 
|  |  | 
|  | auto *UO = BinaryOrErr->get(); | 
|  | for (const MachOUniversalBinary::ObjectForArch &O : UO->objects()) { | 
|  |  | 
|  | Expected<std::unique_ptr<MachOObjectFile>> MachOObjOrErr = | 
|  | O.getAsObjectFile(); | 
|  | if (MachOObjOrErr) { | 
|  | NewArchiveMember NewMember = | 
|  | NewArchiveMember(MachOObjOrErr->get()->getMemoryBufferRef()); | 
|  | NewMember.MemberName = sys::path::filename(NewMember.MemberName); | 
|  |  | 
|  | if (Error E = verifyAndAddMachOObject(std::move(NewMember))) | 
|  | return E; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | Expected<std::unique_ptr<IRObjectFile>> IRObjectOrError = | 
|  | O.getAsIRObject(Builder.LLVMCtx); | 
|  | if (IRObjectOrError) { | 
|  | // A universal file member can be a MachOObjectFile, an IRObject or an | 
|  | // Archive. In case we can successfully cast the member as an | 
|  | // IRObject, it is safe to throw away the error generated due to | 
|  | // casting the object as a MachOObjectFile. | 
|  | consumeError(MachOObjOrErr.takeError()); | 
|  |  | 
|  | NewArchiveMember NewMember = | 
|  | NewArchiveMember(IRObjectOrError->get()->getMemoryBufferRef()); | 
|  | NewMember.MemberName = sys::path::filename(NewMember.MemberName); | 
|  |  | 
|  | if (Error E = verifyAndAddIRObject(std::move(NewMember))) | 
|  | return E; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | Expected<std::unique_ptr<Archive>> ArchiveOrError = O.getAsArchive(); | 
|  | if (ArchiveOrError) { | 
|  | // A universal file member can be a MachOObjectFile, an IRObject or an | 
|  | // Archive. In case we can successfully cast the member as an Archive, | 
|  | // it is safe to throw away the error generated due to casting the | 
|  | // object as a MachOObjectFile. | 
|  | consumeError(MachOObjOrErr.takeError()); | 
|  | consumeError(IRObjectOrError.takeError()); | 
|  |  | 
|  | if (Error E = processArchive(**ArchiveOrError)) | 
|  | return E; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | Error CombinedError = joinErrors( | 
|  | ArchiveOrError.takeError(), | 
|  | joinErrors(IRObjectOrError.takeError(), MachOObjOrErr.takeError())); | 
|  | return createFileError(FileName, std::move(CombinedError)); | 
|  | } | 
|  |  | 
|  | // Update vector FileBuffers with the MemoryBuffers to transfer | 
|  | // ownership. | 
|  | Builder.Data.FileBuffers.push_back(std::move(NewMember.Buf)); | 
|  | return Error::success(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | MembersData Data; | 
|  | LLVMContext &LLVMCtx; | 
|  | const Config &C; | 
|  | }; | 
|  |  | 
|  | static Expected<SmallVector<Slice, 2>> | 
|  | buildSlices(LLVMContext &LLVMCtx, | 
|  | ArrayRef<OwningBinary<Archive>> OutputBinaries) { | 
|  | SmallVector<Slice, 2> Slices; | 
|  |  | 
|  | for (const auto &OB : OutputBinaries) { | 
|  | const Archive &A = *OB.getBinary(); | 
|  | Expected<Slice> ArchiveSlice = Slice::create(A, &LLVMCtx); | 
|  | if (!ArchiveSlice) | 
|  | return ArchiveSlice.takeError(); | 
|  | Slices.push_back(*ArchiveSlice); | 
|  | } | 
|  | return Slices; | 
|  | } | 
|  |  | 
|  | static Error | 
|  | checkForDuplicates(const MembersPerArchitectureMap &MembersPerArch) { | 
|  | for (const auto &M : MembersPerArch) { | 
|  | ArrayRef<NewArchiveMember> Members = M.second.getMembers(); | 
|  | ArrayRef<StringRef> Files = M.second.getFiles(); | 
|  | MapVector<StringRef, SmallVector<StringRef, 1>> MembersToFiles; | 
|  | for (auto Iterators = std::make_pair(Members.begin(), Files.begin()); | 
|  | Iterators.first != Members.end(); | 
|  | ++Iterators.first, ++Iterators.second) { | 
|  | assert(Iterators.second != Files.end() && | 
|  | "Files should be the same size as Members."); | 
|  | MembersToFiles[Iterators.first->MemberName].push_back(*Iterators.second); | 
|  | } | 
|  |  | 
|  | std::string ErrorData; | 
|  | raw_string_ostream ErrorStream(ErrorData); | 
|  | for (const auto &[Key, Value] : MembersToFiles) { | 
|  | if (Value.size() > 1) { | 
|  | ErrorStream << "file '" << Key << "' was specified multiple times.\n"; | 
|  |  | 
|  | for (StringRef OriginalFile : Value) | 
|  | ErrorStream << "in: " << OriginalFile.str() << '\n'; | 
|  |  | 
|  | ErrorStream << '\n'; | 
|  | } | 
|  | } | 
|  |  | 
|  | ErrorStream.flush(); | 
|  | if (ErrorData.size() > 0) | 
|  | return createStringError(std::errc::invalid_argument, ErrorData.c_str()); | 
|  | } | 
|  | return Error::success(); | 
|  | } | 
|  |  | 
|  | static Error createStaticLibrary(LLVMContext &LLVMCtx, const Config &C) { | 
|  | MembersBuilder Builder(LLVMCtx, C); | 
|  | auto DataOrError = Builder.build(); | 
|  | if (auto Error = DataOrError.takeError()) | 
|  | return Error; | 
|  |  | 
|  | const auto &NewMembers = DataOrError->MembersPerArchitecture; | 
|  |  | 
|  | if (Error E = checkForDuplicates(NewMembers)) { | 
|  | if (WarningsAsErrors) | 
|  | return E; | 
|  | WithColor::defaultWarningHandler(std::move(E)); | 
|  | } | 
|  |  | 
|  | if (NewMembers.size() == 1) | 
|  | return writeArchive(OutputFile, NewMembers.begin()->second.getMembers(), | 
|  | SymtabWritingMode::NormalSymtab, | 
|  | /*Kind=*/object::Archive::K_DARWIN, C.Deterministic, | 
|  | /*Thin=*/false); | 
|  |  | 
|  | SmallVector<OwningBinary<Archive>, 2> OutputBinaries; | 
|  | for (const std::pair<const uint64_t, NewArchiveMemberList> &M : NewMembers) { | 
|  | Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr = | 
|  | writeArchiveToBuffer( | 
|  | M.second.getMembers(), SymtabWritingMode::NormalSymtab, | 
|  | /*Kind=*/object::Archive::K_DARWIN, C.Deterministic, | 
|  | /*Thin=*/false); | 
|  | if (!OutputBufferOrErr) | 
|  | return OutputBufferOrErr.takeError(); | 
|  | std::unique_ptr<MemoryBuffer> &OutputBuffer = OutputBufferOrErr.get(); | 
|  |  | 
|  | Expected<std::unique_ptr<Archive>> ArchiveOrError = | 
|  | Archive::create(OutputBuffer->getMemBufferRef()); | 
|  | if (!ArchiveOrError) | 
|  | return ArchiveOrError.takeError(); | 
|  | std::unique_ptr<Archive> &A = ArchiveOrError.get(); | 
|  |  | 
|  | OutputBinaries.push_back( | 
|  | OwningBinary<Archive>(std::move(A), std::move(OutputBuffer))); | 
|  | } | 
|  |  | 
|  | Expected<SmallVector<Slice, 2>> Slices = buildSlices(LLVMCtx, OutputBinaries); | 
|  | if (!Slices) | 
|  | return Slices.takeError(); | 
|  |  | 
|  | llvm::stable_sort(*Slices); | 
|  | return writeUniversalBinary(*Slices, OutputFile); | 
|  | } | 
|  |  | 
|  | static void parseRawArgs(int Argc, char **Argv) { | 
|  | LibtoolDarwinOptTable Tbl; | 
|  | llvm::BumpPtrAllocator A; | 
|  | llvm::StringSaver Saver{A}; | 
|  | opt::InputArgList Args = | 
|  | Tbl.parseArgs(Argc, Argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { | 
|  | llvm::errs() << Msg << '\n'; | 
|  | std::exit(1); | 
|  | }); | 
|  |  | 
|  | if (Args.hasArg(OPT_help)) { | 
|  | Tbl.printHelp(llvm::outs(), "llvm-libtool-darwin [options] <input files>", | 
|  | "llvm-libtool-darwin"); | 
|  | std::exit(0); | 
|  | } | 
|  |  | 
|  | InputFiles = Args.getAllArgValues(OPT_INPUT); | 
|  | Libraries = Args.getAllArgValues(OPT_libraries); | 
|  | LibrarySearchDirs = Args.getAllArgValues(OPT_librarySearchDirs); | 
|  |  | 
|  | if (const opt::Arg *A = Args.getLastArg(OPT_outputFile)) | 
|  | OutputFile = A->getValue(); | 
|  |  | 
|  | if (const opt::Arg *A = Args.getLastArg(OPT_archType)) | 
|  | ArchType = std::make_optional(A->getValue()); | 
|  |  | 
|  | if (const opt::Arg *A = Args.getLastArg(OPT_fileList)) | 
|  | FileList = A->getValue(); | 
|  |  | 
|  | if (const opt::Arg *A = Args.getLastArg(OPT_dependencyInfoPath)) | 
|  | DependencyInfoPath = A->getValue(); | 
|  |  | 
|  | if (const opt::Arg *A = Args.getLastArg(OPT_ignoredSyslibRoot)) | 
|  | IgnoredSyslibRoot = A->getValue(); | 
|  |  | 
|  | LibraryOperation = | 
|  | Args.hasArg(OPT_static) ? Operation::Static : Operation::None; | 
|  | DeterministicOption = Args.hasArg(OPT_deterministicOption); | 
|  | NonDeterministicOption = Args.hasArg(OPT_nonDeterministicOption); | 
|  | VersionOption = Args.hasArg(OPT_version); | 
|  | NoWarningForNoSymbols = Args.hasArg(OPT_noWarningForNoSymbols); | 
|  | WarningsAsErrors = Args.hasArg(OPT_warningsAsErrors); | 
|  | } | 
|  |  | 
|  | static Expected<Config> parseCommandLine(int Argc, char **Argv) { | 
|  | Config C; | 
|  | parseRawArgs(Argc, Argv); | 
|  |  | 
|  | if (LibraryOperation == Operation::None) { | 
|  | if (!VersionOption) { | 
|  | return createStringError(std::errc::invalid_argument, | 
|  | "-static option: must be specified"); | 
|  | } | 
|  | return C; | 
|  | } | 
|  |  | 
|  | GlobalDependencyInfo = | 
|  | DependencyInfoPath.empty() | 
|  | ? std::make_unique<DummyDependencyInfo>() | 
|  | : std::make_unique<DependencyInfo>(DependencyInfoPath); | 
|  |  | 
|  | if (OutputFile.empty()) { | 
|  | return createStringError(std::errc::invalid_argument, | 
|  | "-o option: must be specified"); | 
|  | } | 
|  |  | 
|  | if (DeterministicOption && NonDeterministicOption) | 
|  | return createStringError(std::errc::invalid_argument, | 
|  | "cannot specify both -D and -U flags"); | 
|  | else if (NonDeterministicOption) | 
|  | C.Deterministic = false; | 
|  |  | 
|  | if (!Libraries.empty()) | 
|  | if (Error E = processCommandLineLibraries()) | 
|  | return std::move(E); | 
|  |  | 
|  | if (!FileList.empty()) | 
|  | if (Error E = processFileList()) | 
|  | return std::move(E); | 
|  |  | 
|  | if (InputFiles.empty()) | 
|  | return createStringError(std::errc::invalid_argument, | 
|  | "no input files specified"); | 
|  |  | 
|  | if (ArchType) { | 
|  | if (Error E = validateArchitectureName(ArchType.value())) | 
|  | return std::move(E); | 
|  |  | 
|  | std::tie(C.ArchCPUType, C.ArchCPUSubtype) = | 
|  | MachO::getCPUTypeFromArchitecture( | 
|  | MachO::getArchitectureFromName(ArchType.value())); | 
|  | } | 
|  |  | 
|  | GlobalDependencyInfo->write("llvm-libtool-darwin " LLVM_VERSION_STRING, | 
|  | InputFiles, OutputFile); | 
|  |  | 
|  | return C; | 
|  | } | 
|  |  | 
|  | int llvm_libtool_darwin_main(int Argc, char **Argv, const llvm::ToolContext &) { | 
|  | Expected<Config> ConfigOrErr = parseCommandLine(Argc, Argv); | 
|  | if (!ConfigOrErr) { | 
|  | WithColor::defaultErrorHandler(ConfigOrErr.takeError()); | 
|  | return EXIT_FAILURE; | 
|  | } | 
|  |  | 
|  | if (VersionOption) | 
|  | cl::PrintVersionMessage(); | 
|  |  | 
|  | llvm::InitializeAllTargetInfos(); | 
|  | llvm::InitializeAllTargetMCs(); | 
|  | llvm::InitializeAllAsmParsers(); | 
|  |  | 
|  | LLVMContext LLVMCtx; | 
|  | Config C = *ConfigOrErr; | 
|  | switch (LibraryOperation) { | 
|  | case Operation::None: | 
|  | break; | 
|  | case Operation::Static: | 
|  | if (Error E = createStaticLibrary(LLVMCtx, C)) { | 
|  | WithColor::defaultErrorHandler(std::move(E)); | 
|  | return EXIT_FAILURE; | 
|  | } | 
|  | break; | 
|  | } | 
|  | return EXIT_SUCCESS; | 
|  | } |