[llvm] 4993eff - [llvm-libtool-darwin] Print a warning if object file names are repeated

Shoaib Meenai via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 11 14:50:14 PST 2022


Author: Roger Kim
Date: 2022-01-11T14:47:51-08:00
New Revision: 4993eff3e253a1c04e1a1a2fa5d68f6b33423419

URL: https://github.com/llvm/llvm-project/commit/4993eff3e253a1c04e1a1a2fa5d68f6b33423419
DIFF: https://github.com/llvm/llvm-project/commit/4993eff3e253a1c04e1a1a2fa5d68f6b33423419.diff

LOG: [llvm-libtool-darwin] Print a warning if object file names are repeated

Print a warning if `llvm-libtool-darwin` if any of the object
files provided by the user have the same file name.

The tool will now print a warning if there is a name collision across:

* Two object files
* An object file and an object file from within a static library
* Two object files from different static libraries

Here is an example of the error:

```
$ llvm-libtool-darwin -static -o archive.a out.o out.o
error: file 'out.o' was specified multiple times.
in: out.o
in: out.o

$ llvm-libtool-darwin -static -o archive.a out.o
$ llvm-libtool-darwin -static -o combined.a archive.a out.o
error: file 'out.o' was specified multiple times.
in: archive.a
in: out.o
```

This change mimics apple's cctools libtool's behavior which always shows a warning in such cases.

Reviewed By: smeenai

Differential Revision: https://reviews.llvm.org/D113130

Added: 
    

Modified: 
    llvm/test/tools/llvm-libtool-darwin/L-and-l.test
    llvm/test/tools/llvm-libtool-darwin/archive-flattening.test
    llvm/test/tools/llvm-libtool-darwin/create-static-lib.test
    llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/test/tools/llvm-libtool-darwin/L-and-l.test b/llvm/test/tools/llvm-libtool-darwin/L-and-l.test
index 182b923b4ec83..e92501e779f91 100644
--- a/llvm/test/tools/llvm-libtool-darwin/L-and-l.test
+++ b/llvm/test/tools/llvm-libtool-darwin/L-and-l.test
@@ -62,6 +62,20 @@
 # RUN: llvm-nm --print-armap %t.lib | \
 # RUN:   FileCheck %s --check-prefix=SINGLE-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines
 
+## Check that if two 
diff erent files with the same names are explicitly
+## specified, the command gives a warning.
+# RUN: cp %t-input2.o %t/dirname
+# RUN: llvm-libtool-darwin -static -o %t.lib \
+# RUN:   %t/dirname/%basename_t.tmp-input2.o %t-input2.o 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=DUPLICATE-INPUT \
+# RUN:     -DFILE=%basename_t.tmp-input2.o \
+# RUN:     -DINPUTA=%t/dirname/%basename_t.tmp-input2.o \
+# RUN:     -DINPUTB=%t-input2.o
+
+# DUPLICATE-INPUT:     warning: file '[[FILE]]' was specified multiple times.
+# DUPLICATE-INPUT-DAG: [[INPUTA]]
+# DUPLICATE-INPUT-DAG: [[INPUTB]]
+
 ## -l option combined with an input file:
 # RUN: llvm-libtool-darwin -static -o %t.lib %t-input1.o -linput2 -L%t/dirname
 # RUN: llvm-ar t %t.lib | \
@@ -69,22 +83,23 @@
 # RUN: llvm-nm --print-armap %t.lib | \
 # RUN:   FileCheck %s --check-prefix=CHECK-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines
 
-## Specify same -l option twice:
-## cctools' libtool raises a warning in this case.
-## The warning is not yet implemented for llvm-libtool-darwin.
-# RUN: llvm-libtool-darwin -static -o %t.lib -l%basename_t.tmp-input2.o -l%basename_t.tmp-input2.o -L%T
-# RUN: llvm-ar t %t.lib | \
-# RUN:   FileCheck %s --check-prefix=DOUBLE-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp
-# RUN: llvm-nm --print-armap %t.lib | \
-# RUN:   FileCheck %s --check-prefix=DOUBLE-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines
-
-# DOUBLE-NAMES:      [[PREFIX]]-input2.o
-# DOUBLE-NAMES-NEXT: [[PREFIX]]-input2.o
+## Specify the same file with a -l option and an input file:
+# RUN: rm -rf %t/copy
+# RUN: mkdir -p %t/copy
+# RUN: cp %t-input1.o %t/copy
+# RUN: llvm-libtool-darwin -static -o %t.lib \
+# RUN:   %t/copy/%basename_t.tmp-input1.o -l%basename_t.tmp-input1.o -L%t/copy 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=DUPLICATE-INPUT -DFILE=%basename_t.tmp-input1.o \
+# RUN:     -DINPUTA=%t/copy/%basename_t.tmp-input1.o \
+# RUN:     -DINPUTB=%t/copy/%basename_t.tmp-input1.o
 
-# DOUBLE-SYMBOLS:      Archive map
-# DOUBLE-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o
-# DOUBLE-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o
-# DOUBLE-SYMBOLS-EMPTY:
+## Specify same -l option twice:
+# RUN: llvm-libtool-darwin -static -o %t.lib -l%basename_t.tmp-input1.o \
+# RUN:  -l%basename_t.tmp-input1.o -L%t/copy 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=DUPLICATE-INPUT \
+# RUN:     -DFILE=%basename_t.tmp-input1.o \
+# RUN:     -DINPUTA=%t/copy/%basename_t.tmp-input1.o \
+# RUN:     -DINPUTB=%t/copy/%basename_t.tmp-input1.o
 
 ## Check that an error is thrown when the input library cannot be found:
 # RUN: not llvm-libtool-darwin -static -o %t.lib -lfile-will-not-exist.o 2>&1 | \

diff  --git a/llvm/test/tools/llvm-libtool-darwin/archive-flattening.test b/llvm/test/tools/llvm-libtool-darwin/archive-flattening.test
index bfe2659e666db..3b133cdffba16 100644
--- a/llvm/test/tools/llvm-libtool-darwin/archive-flattening.test
+++ b/llvm/test/tools/llvm-libtool-darwin/archive-flattening.test
@@ -59,6 +59,17 @@
 # BOTH-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o
 # BOTH-SYMBOLS-EMPTY:
 
+# RUN: llvm-libtool-darwin -static -o %t.lib %t-x86_64.bc %t.correct.ar %t-input1.o  2>&1 | \
+# RUN:   FileCheck %s --check-prefix=DUPLICATE-INPUT -DFILEA=%basename_t.tmp-input1.o \
+# RUN:   -DPATHA=%t-input1.o -DFILEB=%basename_t.tmp-x86_64.bc -DPATHB=%t-x86_64.bc -DPATHCORRECT=%t.correct.ar
+
+# DUPLICATE-INPUT:     warning: file '[[FILEA]]' was specified multiple times.
+# DUPLICATE-INPUT-DAG: [[PATHA]]
+# DUPLICATE-INPUT-DAG: [[PATHCORRECT]]
+# DUPLICATE-INPUT:     file '[[FILEB]]' was specified multiple times.
+# DUPLICATE-INPUT-DAG: [[PATHB]]
+# DUPLICATE-INPUT-DAG: [[PATHCORRECT]]
+
 ## Cannot read archive:
 # RUN: echo '!<arch>' >  %t-invalid-archive.lib
 # RUN: echo 'invalid' >> %t-invalid-archive.lib

diff  --git a/llvm/test/tools/llvm-libtool-darwin/create-static-lib.test b/llvm/test/tools/llvm-libtool-darwin/create-static-lib.test
index 92baea34c7bc2..916f569fa985c 100644
--- a/llvm/test/tools/llvm-libtool-darwin/create-static-lib.test
+++ b/llvm/test/tools/llvm-libtool-darwin/create-static-lib.test
@@ -50,21 +50,19 @@
 # OVERWRITE-SYMBOLS-EMPTY:
 
 ## Duplicate a binary:
-## cctools' libtool raises a warning in this case.
-## The warning is not yet implemented for llvm-libtool-darwin.
 # RUN: llvm-libtool-darwin -static -o %t.lib %t-input1.o %t-input2.o %t-input1.o 2>&1 | \
-# RUN:   FileCheck %s --allow-empty --implicit-check-not={{.}}
-# RUN: llvm-ar t %t.lib | \
-# RUN:   FileCheck %s --check-prefix=DUPLICATE-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp
-# RUN: llvm-nm --print-armap %t.lib | \
-# RUN:   FileCheck %s --check-prefix=DUPLICATE-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines
+# RUN:   FileCheck %s --check-prefix=DUPLICATE-INPUT -DFILE=%basename_t.tmp-input1.o \
+# RUN:     -DINPUTA=%t-input1.o -DINPUTB=%t-input1.o
 
-# DUPLICATE-NAMES:      [[PREFIX]]-input1.o
-# DUPLICATE-NAMES-NEXT: [[PREFIX]]-input2.o
-# DUPLICATE-NAMES-NEXT: [[PREFIX]]-input1.o
+# DUPLICATE-INPUT:     warning: file '[[FILE]]' was specified multiple times.
+# DUPLICATE-INPUT-DAG: [[INPUTA]]
+# DUPLICATE-INPUT-DAG: [[INPUTB]]
 
-# DUPLICATE-SYMBOLS:      Archive map
-# DUPLICATE-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o
-# DUPLICATE-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o
-# DUPLICATE-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o
-# DUPLICATE-SYMBOLS-EMPTY:
+## Make sure we can combine object files with the same name if
+## they are for 
diff erent architectures.
+# RUN: mkdir -p %t/arm64 %t/armv7
+# RUN: llvm-as %S/Inputs/arm64-ios.ll -o %t/arm64/out.bc
+# RUN: llvm-as %S/Inputs/armv7-ios.ll -o %t/armv7/out.bc
+## Command output should be empty.
+# RUN: llvm-libtool-darwin -static %t/arm64/out.bc %t/armv7/out.bc -o %t.lib 2>&1 | \
+# RUN:   FileCheck %s --implicit-check-not=warning: --allow-empty

diff  --git a/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp b/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp
index 82c4688237356..9355d385d6f31 100644
--- a/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp
+++ b/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp
@@ -23,6 +23,7 @@
 #include "llvm/Support/LineIterator.h"
 #include "llvm/Support/VirtualFileSystem.h"
 #include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
 #include "llvm/TextAPI/Architecture.h"
 #include <map>
 #include <type_traits>
@@ -32,8 +33,8 @@ using namespace llvm::object;
 
 static LLVMContext LLVMCtx;
 
-typedef std::map<uint64_t, std::vector<NewArchiveMember>>
-    MembersPerArchitectureMap;
+class NewArchiveMemberList;
+typedef std::map<uint64_t, NewArchiveMemberList> MembersPerArchitectureMap;
 
 cl::OptionCategory LibtoolCategory("llvm-libtool-darwin Options");
 
@@ -212,15 +213,50 @@ struct MembersData {
   // 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;
 
-  static_assert(!std::is_copy_constructible<NewArchiveMember>::value,
-                "MembersPerArchitecture has a dependency on FileBuffers so it "
-                "should not be able to be copied on its own without "
-                "FileBuffers.");
-  static_assert(!std::is_copy_assignable<NewArchiveMember>::value,
-                "MembersPerArchitecture has a dependency on FileBuffers so it "
-                "should not be able to be copied on its own without "
-                "FileBuffers.");
+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<
+          decltype(NewArchiveMemberList::Members)::value_type>::value,
+      "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<
+          decltype(NewArchiveMemberList::Members)::value_type>::value,
+      "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
@@ -231,7 +267,7 @@ class MembersBuilder {
 
   Expected<MembersData> build() {
     for (StringRef FileName : InputFiles)
-      if (Error E = addMember(FileName))
+      if (Error E = AddMember(*this, FileName)())
         return std::move(E);
 
     if (!ArchType.empty()) {
@@ -247,227 +283,239 @@ class MembersBuilder {
   }
 
 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 (C.ArchCPUType != FileCPUType)
-      return false;
-
-    switch (C.ArchCPUType) {
-    case MachO::CPU_TYPE_ARM:
-    case MachO::CPU_TYPE_ARM64_32:
-    case MachO::CPU_TYPE_X86_64:
-      return C.ArchCPUSubtype == FileCPUSubtype;
-
-    case MachO::CPU_TYPE_ARM64:
-      if (C.ArchCPUSubtype == MachO::CPU_SUBTYPE_ARM64_ALL)
-        return FileCPUSubtype == MachO::CPU_SUBTYPE_ARM64_ALL ||
-               FileCPUSubtype == MachO::CPU_SUBTYPE_ARM64_V8;
-      else
-        return C.ArchCPUSubtype == FileCPUSubtype;
-
-    default:
-      return true;
+  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);
+    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 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());
+      // 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 = dyn_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.empty() && !acceptFileArch(FileCPUType, FileCPUSubtype)) {
+        return Error::success();
+      }
 
-    auto *O = dyn_cast<MachOObjectFile>(ObjOrErr->get());
-    uint32_t FileCPUType, FileCPUSubtype;
-    std::tie(FileCPUType, FileCPUSubtype) = MachO::getCPUTypeFromArchitecture(
-        MachO::getArchitectureFromName(O->getArchTriple().getArchName()));
+      if (!NoWarningForNoSymbols && O->symbols().empty())
+        WithColor::warning() << Member.MemberName + " has no symbols\n";
 
-    // If -arch_only is specified then skip this file if it doesn't match
-    // the architecture specified.
-    if (!ArchType.empty() && !acceptFileArch(FileCPUType, FileCPUSubtype)) {
+      uint64_t FileCPUID = getCPUID(FileCPUType, FileCPUSubtype);
+      Builder.Data.MembersPerArchitecture[FileCPUID].push_back(
+          std::move(Member), FileName);
       return Error::success();
     }
 
-    if (!NoWarningForNoSymbols && O->symbols().empty())
-      WithColor::warning() << Member.MemberName + " has no symbols\n";
-
-    uint64_t FileCPUID = getCPUID(FileCPUType, FileCPUSubtype);
-    Data.MembersPerArchitecture[FileCPUID].push_back(std::move(Member));
-    return Error::success();
-  }
+    Error verifyAndAddIRObject(NewArchiveMember Member) {
+      auto MBRef = Member.Buf->getMemBufferRef();
+      Expected<std::unique_ptr<object::IRObjectFile>> IROrErr =
+          object::IRObjectFile::create(MBRef, LLVMCtx);
 
-  Error verifyAndAddIRObject(NewArchiveMember Member) {
-    auto MBRef = Member.Buf->getMemBufferRef();
-    Expected<std::unique_ptr<object::IRObjectFile>> IROrErr =
-        object::IRObjectFile::create(MBRef, LLVMCtx);
+      // Throw error if not a valid IR object file.
+      if (!IROrErr)
+        return createFileError(Member.MemberName, IROrErr.takeError());
 
-    // Throw error if not a valid IR object file.
-    if (!IROrErr)
-      return createFileError(Member.MemberName, IROrErr.takeError());
+      Triple TT = Triple(IROrErr->get()->getTargetTriple());
 
-    Triple TT = Triple(IROrErr->get()->getTargetTriple());
+      Expected<uint32_t> FileCPUTypeOrErr = MachO::getCPUType(TT);
+      if (!FileCPUTypeOrErr)
+        return FileCPUTypeOrErr.takeError();
 
-    Expected<uint32_t> FileCPUTypeOrErr = MachO::getCPUType(TT);
-    if (!FileCPUTypeOrErr)
-      return FileCPUTypeOrErr.takeError();
+      Expected<uint32_t> FileCPUSubTypeOrErr = MachO::getCPUSubType(TT);
+      if (!FileCPUSubTypeOrErr)
+        return FileCPUSubTypeOrErr.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.empty() &&
+          !acceptFileArch(*FileCPUTypeOrErr, *FileCPUSubTypeOrErr)) {
+        return Error::success();
+      }
 
-    // If -arch_only is specified then skip this file if it doesn't match
-    // the architecture specified.
-    if (!ArchType.empty() &&
-        !acceptFileArch(*FileCPUTypeOrErr, *FileCPUSubTypeOrErr)) {
+      uint64_t FileCPUID = getCPUID(*FileCPUTypeOrErr, *FileCPUSubTypeOrErr);
+      Builder.Data.MembersPerArchitecture[FileCPUID].push_back(
+          std::move(Member), FileName);
       return Error::success();
     }
 
-    uint64_t FileCPUID = getCPUID(*FileCPUTypeOrErr, *FileCPUSubTypeOrErr);
-    Data.MembersPerArchitecture[FileCPUID].push_back(std::move(Member));
-    return Error::success();
-  }
-
-  Error addChildMember(const object::Archive::Child &M) {
-    Expected<NewArchiveMember> NewMemberOrErr =
-        NewArchiveMember::getOldMember(M, C.Deterministic);
-    if (!NewMemberOrErr)
-      return NewMemberOrErr.takeError();
-    auto &NewMember = *NewMemberOrErr;
+    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());
+      file_magic Magic = identify_magic(NewMember.Buf->getBuffer());
 
-    if (Magic == file_magic::bitcode)
-      return verifyAndAddIRObject(std::move(NewMember));
+      if (Magic == file_magic::bitcode)
+        return verifyAndAddIRObject(std::move(NewMember));
 
-    return verifyAndAddMachOObject(std::move(NewMember));
-  }
-
-  Error processArchive(object::Archive &Lib, StringRef FileName) {
-    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();
-  }
+      return verifyAndAddMachOObject(std::move(NewMember));
+    }
 
-  Error addArchiveMembers(NewArchiveMember NewMember, StringRef FileName) {
-    Expected<std::unique_ptr<Archive>> LibOrErr =
-        object::Archive::create(NewMember.Buf->getMemBufferRef());
-    if (!LibOrErr)
-      return createFileError(FileName, LibOrErr.takeError());
+    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));
 
-    if (Error E = processArchive(**LibOrErr, FileName))
-      return E;
+      return Error::success();
+    }
 
-    // Update vector FileBuffers with the MemoryBuffers to transfer
-    // ownership.
-    Data.FileBuffers.push_back(std::move(NewMember.Buf));
-    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());
 
-  Error addUniversalMembers(NewArchiveMember NewMember, StringRef FileName) {
-    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;
-      }
+      if (Error E = processArchive(**LibOrErr))
+        return E;
 
-      Expected<std::unique_ptr<IRObjectFile>> IRObjectOrError =
-          O.getAsIRObject(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;
-      }
+      // Update vector FileBuffers with the MemoryBuffers to transfer
+      // ownership.
+      Builder.Data.FileBuffers.push_back(std::move(NewMember.Buf));
+      return Error::success();
+    }
 
-      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, FileName))
-          return E;
-        continue;
+    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(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));
       }
 
-      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();
     }
-
-    // Update vector FileBuffers with the MemoryBuffers to transfer
-    // ownership.
-    Data.FileBuffers.push_back(std::move(NewMember.Buf));
-    return Error::success();
-  }
-
-  Error addMember(StringRef FileName) {
-    Expected<NewArchiveMember> NewMemberOrErr =
-        NewArchiveMember::getFile(FileName, 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), FileName);
-
-    // Flatten universal files.
-    if (Magic == file_magic::macho_universal_binary)
-      return addUniversalMembers(std::move(NewMember), FileName);
-
-    // Bitcode files.
-    if (Magic == file_magic::bitcode)
-      return verifyAndAddIRObject(std::move(NewMember));
-
-    return verifyAndAddMachOObject(std::move(NewMember));
-  }
+  };
 
   MembersData Data;
   const Config &C;
@@ -487,6 +535,41 @@ buildSlices(ArrayRef<OwningBinary<Archive>> OutputBinaries) {
   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();
+    StringMap<std::vector<StringRef>> 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 &MemberToFile : MembersToFiles) {
+      if (MemberToFile.getValue().size() > 1) {
+        ErrorStream << "file '" << MemberToFile.getKey().str()
+                    << "' was specified multiple times.\n";
+
+        for (StringRef OriginalFile : MemberToFile.getValue())
+          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(const Config &C) {
   MembersBuilder Builder(C);
   auto DataOrError = Builder.build();
@@ -495,18 +578,19 @@ static Error createStaticLibrary(const Config &C) {
 
   const auto &NewMembers = DataOrError->MembersPerArchitecture;
 
-  if (NewMembers.size() == 1) {
-    return writeArchive(OutputFile, NewMembers.begin()->second,
+  if (Error E = checkForDuplicates(NewMembers))
+    WithColor::defaultWarningHandler(std::move(E));
+
+  if (NewMembers.size() == 1)
+    return writeArchive(OutputFile, NewMembers.begin()->second.getMembers(),
                         /*WriteSymtab=*/true,
                         /*Kind=*/object::Archive::K_DARWIN, C.Deterministic,
                         /*Thin=*/false);
-  }
 
   SmallVector<OwningBinary<Archive>, 2> OutputBinaries;
-  for (const std::pair<const uint64_t, std::vector<NewArchiveMember>> &M :
-       NewMembers) {
+  for (const std::pair<const uint64_t, NewArchiveMemberList> &M : NewMembers) {
     Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr =
-        writeArchiveToBuffer(M.second,
+        writeArchiveToBuffer(M.second.getMembers(),
                              /*WriteSymtab=*/true,
                              /*Kind=*/object::Archive::K_DARWIN,
                              C.Deterministic,


        


More information about the llvm-commits mailing list