[llvm] r186197 - Change llvm-ar to use lib/Object.

Rafael Espindola rafael.espindola at gmail.com
Fri Jul 12 13:21:40 PDT 2013


Author: rafael
Date: Fri Jul 12 15:21:39 2013
New Revision: 186197

URL: http://llvm.org/viewvc/llvm-project?rev=186197&view=rev
Log:
Change llvm-ar to use lib/Object.

This fixes two bugs is lib/Object that the use in llvm-ar found:
* In OS X created archives, the name can be padded with nulls. Strip them.
* In the constructor, remember the first non special member and use that in
  begin_children. This makes sure we skip all special members, not just the
  first one.

The change to llvm-ar itself consist of
* Using lib/Object for reading archives instead of ArchiveReader.cpp.
* Writing the modified archive directly, instead of creating an in memory
  representation.

The old Archive library was way more general than what is needed, as can
be seen by the diffstat of this patch.

Having llvm-ar using lib/Object now opens the way for creating regular symbol
tables for both native objects and bitcode files so that we can use those
archives for LTO.

Removed:
    llvm/trunk/tools/llvm-ar/Archive.cpp
    llvm/trunk/tools/llvm-ar/ArchiveInternals.h
    llvm/trunk/tools/llvm-ar/ArchiveReader.cpp
    llvm/trunk/tools/llvm-ar/ArchiveWriter.cpp
Modified:
    llvm/trunk/include/llvm/Object/Archive.h
    llvm/trunk/lib/Object/Archive.cpp
    llvm/trunk/test/Object/archive-toc.test
    llvm/trunk/test/Object/directory.ll
    llvm/trunk/tools/llvm-ar/CMakeLists.txt
    llvm/trunk/tools/llvm-ar/llvm-ar.cpp

Modified: llvm/trunk/include/llvm/Object/Archive.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Object/Archive.h?rev=186197&r1=186196&r2=186197&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Object/Archive.h (original)
+++ llvm/trunk/include/llvm/Object/Archive.h Fri Jul 12 15:21:39 2013
@@ -174,7 +174,7 @@ public:
     return Format;
   }
 
-  child_iterator begin_children(bool skip_internal = true) const;
+  child_iterator begin_children(bool SkipInternal = true) const;
   child_iterator end_children() const;
 
   symbol_iterator begin_symbols() const;
@@ -191,6 +191,7 @@ public:
 private:
   child_iterator SymbolTable;
   child_iterator StringTable;
+  child_iterator FirstRegular;
   Kind Format;
 };
 

Modified: llvm/trunk/lib/Object/Archive.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Object/Archive.cpp?rev=186197&r1=186196&r2=186197&view=diff
==============================================================================
--- llvm/trunk/lib/Object/Archive.cpp (original)
+++ llvm/trunk/lib/Object/Archive.cpp Fri Jul 12 15:21:39 2013
@@ -23,20 +23,6 @@ using namespace object;
 
 static const char *Magic = "!<arch>\n";
 
-static bool isInternalMember(const ArchiveMemberHeader &amh) {
-  static const char *const internals[] = {
-    "/",
-    "//"
-  };
-
-  StringRef name = amh.getName();
-  for (std::size_t i = 0; i < sizeof(internals) / sizeof(*internals); ++i) {
-    if (name == internals[i])
-      return true;
-  }
-  return false;
-}
-
 void Archive::anchor() { }
 
 StringRef ArchiveMemberHeader::getName() const {
@@ -93,16 +79,13 @@ unsigned ArchiveMemberHeader::getGID() c
   return Ret;
 }
 
-static const ArchiveMemberHeader *toHeader(const char *base) {
-  return reinterpret_cast<const ArchiveMemberHeader *>(base);
-}
-
 Archive::Child::Child(const Archive *Parent, const char *Start)
     : Parent(Parent) {
   if (!Start)
     return;
 
-  const ArchiveMemberHeader *Header = toHeader(Start);
+  const ArchiveMemberHeader *Header =
+      reinterpret_cast<const ArchiveMemberHeader *>(Start);
   Data = StringRef(Start, sizeof(ArchiveMemberHeader) + Header->getSize());
 
   // Setup StartOfFile and PaddingBytes.
@@ -173,7 +156,8 @@ error_code Archive::Child::getName(Strin
     uint64_t name_size;
     if (name.substr(3).rtrim(" ").getAsInteger(10, name_size))
       llvm_unreachable("Long name length is not an ingeter");
-    Result = Data.substr(sizeof(ArchiveMemberHeader), name_size);
+    Result = Data.substr(sizeof(ArchiveMemberHeader), name_size)
+        .rtrim(StringRef("\0", 1));
     return object_error::success;
   }
   // It's a simple name.
@@ -252,6 +236,8 @@ Archive::Archive(MemoryBuffer *source, e
   if (Name == "__.SYMDEF") {
     Format = K_BSD;
     SymbolTable = i;
+    ++i;
+    FirstRegular = i;
     ec = object_error::success;
     return;
   }
@@ -262,8 +248,11 @@ Archive::Archive(MemoryBuffer *source, e
     ec = i->getName(Name);
     if (ec)
       return;
-    if (Name == StringRef("__.SYMDEF SORTED\0\0\0", 20))
+    if (Name == "__.SYMDEF SORTED") {
       SymbolTable = i;
+      ++i;
+    }
+    FirstRegular = i;
     return;
   }
 
@@ -281,12 +270,15 @@ Archive::Archive(MemoryBuffer *source, e
   if (Name == "//") {
     Format = K_GNU;
     StringTable = i;
+    ++i;
+    FirstRegular = i;
     ec = object_error::success;
     return;
   }
 
   if (Name[0] != '/') {
     Format = K_GNU;
+    FirstRegular = i;
     ec = object_error::success;
     return;
   }
@@ -301,26 +293,31 @@ Archive::Archive(MemoryBuffer *source, e
 
   ++i;
   if (i == e) {
+    FirstRegular = i;
     ec = object_error::success;
     return;
   }
 
   Name = i->getRawName();
 
-  if (Name == "//")
+  if (Name == "//") {
     StringTable = i;
+    ++i;
+  }
 
+  FirstRegular = i;
   ec = object_error::success;
 }
 
-Archive::child_iterator Archive::begin_children(bool skip_internal) const {
+Archive::child_iterator Archive::begin_children(bool SkipInternal) const {
   if (Data->getBufferSize() == 8) // empty archive.
     return end_children();
+
+  if (SkipInternal)
+    return FirstRegular;
+
   const char *Loc = Data->getBufferStart() + strlen(Magic);
   Child c(this, Loc);
-  // Skip internals at the beginning of an archive.
-  if (skip_internal && isInternalMember(*toHeader(Loc)))
-    return c.getNext();
   return c;
 }
 

Modified: llvm/trunk/test/Object/archive-toc.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Object/archive-toc.test?rev=186197&r1=186196&r2=186197&view=diff
==============================================================================
--- llvm/trunk/test/Object/archive-toc.test (original)
+++ llvm/trunk/test/Object/archive-toc.test Fri Jul 12 15:21:39 2013
@@ -10,6 +10,7 @@ GNU-NEXT: rw-r--r-- 500/500   2280 Nov 1
 Test reading an archive createdy by Mac OS X ar
 RUN: env TZ=GMT llvm-ar tv %p/Inputs/MacOSX.a | FileCheck %s --check-prefix=OSX -strict-whitespace
 
+OSX-NOT: __.SYMDEF
 OSX:      rw-r--r-- 501/501      8 Nov 19 02:57 2004 evenlen
 OSX-NEXT: rw-r--r-- 501/501      8 Nov 19 02:57 2004 oddlen
 OSX-NEXT: rw-r--r-- 502/502   1465 Feb  4 06:59 2010 very_long_bytecode_file_name.bc

Modified: llvm/trunk/test/Object/directory.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Object/directory.ll?rev=186197&r1=186196&r2=186197&view=diff
==============================================================================
--- llvm/trunk/test/Object/directory.ll (original)
+++ llvm/trunk/test/Object/directory.ll Fri Jul 12 15:21:39 2013
@@ -1,12 +1,13 @@
+;RUN: rm -f %T/test.a
 ;RUN: not llvm-ar r %T/test.a . 2>&1 | FileCheck %s
-;CHECK: . Is a directory
+;CHECK: .: Is a directory
 
 ;RUN: rm -f %T/test.a
 ;RUN: touch %T/a-very-long-file-name
 ;RUN: llvm-ar r %T/test.a %s %T/a-very-long-file-name
 ;RUN: llvm-ar r %T/test.a %T/a-very-long-file-name
-;RUN: llvm-ar t %T/test.a | sort | FileCheck -check-prefix=MEMBERS %s
+;RUN: llvm-ar t %T/test.a | FileCheck -check-prefix=MEMBERS %s
 ;MEMBERS-NOT: /
-;MEMBERS: a-very-long-file-name
 ;MEMBERS: directory.ll
+;MEMBERS: a-very-long-file-name
 ;MEMBERS-NOT: a-very-long-file-name

Removed: llvm/trunk/tools/llvm-ar/Archive.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-ar/Archive.cpp?rev=186196&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-ar/Archive.cpp (original)
+++ llvm/trunk/tools/llvm-ar/Archive.cpp (removed)
@@ -1,171 +0,0 @@
-//===-- Archive.cpp - Generic LLVM archive functions ------------*- C++ -*-===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file contains the implementation of the Archive and ArchiveMember
-// classes that is common to both reading and writing archives..
-//
-//===----------------------------------------------------------------------===//
-
-#include "Archive.h"
-#include "ArchiveInternals.h"
-#include "llvm/Bitcode/ReaderWriter.h"
-#include "llvm/IR/Module.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/Process.h"
-#include "llvm/Support/system_error.h"
-#include <cstring>
-#include <memory>
-using namespace llvm;
-
-// getMemberSize - compute the actual physical size of the file member as seen
-// on disk. This isn't the size of member's payload. Use getSize() for that.
-unsigned
-ArchiveMember::getMemberSize() const {
-  // Basically its the file size plus the header size
-  unsigned result = Size + sizeof(ArchiveMemberHeader);
-
-  // If it has a long filename, include the name length
-  if (hasLongFilename())
-    result += path.length() + 1;
-
-  // If its now odd lengthed, include the padding byte
-  if (result % 2 != 0 )
-    result++;
-
-  return result;
-}
-
-// This default constructor is only use by the ilist when it creates its
-// sentry node. We give it specific static values to make it stand out a bit.
-ArchiveMember::ArchiveMember()
-  : parent(0), path("--invalid--"), flags(0), data(0)
-{
-  User = sys::Process::GetCurrentUserId();
-  Group = sys::Process::GetCurrentGroupId();
-  Mode = 0777;
-  Size = 0;
-  ModTime = sys::TimeValue::now();
-}
-
-// This is the constructor that the Archive class uses when it is building or
-// reading an archive. It just defaults a few things and ensures the parent is
-// set for the iplist. The Archive class fills in the ArchiveMember's data.
-// This is required because correctly setting the data may depend on other
-// things in the Archive.
-ArchiveMember::ArchiveMember(Archive* PAR)
-  : parent(PAR), path(), flags(0), data(0)
-{
-}
-
-// This method allows an ArchiveMember to be replaced with the data for a
-// different file, presumably as an update to the member. It also makes sure
-// the flags are reset correctly.
-bool ArchiveMember::replaceWith(StringRef newFile, std::string* ErrMsg) {
-  bool Exists;
-  if (sys::fs::exists(newFile.str(), Exists) || !Exists) {
-    if (ErrMsg)
-      *ErrMsg = "Can not replace an archive member with a non-existent file";
-    return true;
-  }
-
-  data = 0;
-  path = newFile.str();
-
-  // SVR4 symbol tables have an empty name
-  if (path == ARFILE_SVR4_SYMTAB_NAME)
-    flags |= SVR4SymbolTableFlag;
-  else
-    flags &= ~SVR4SymbolTableFlag;
-
-  // BSD4.4 symbol tables have a special name
-  if (path == ARFILE_BSD4_SYMTAB_NAME)
-    flags |= BSD4SymbolTableFlag;
-  else
-    flags &= ~BSD4SymbolTableFlag;
-
-  // String table name
-  if (path == ARFILE_STRTAB_NAME)
-    flags |= StringTableFlag;
-  else
-    flags &= ~StringTableFlag;
-
-  // If it has a slash or its over 15 chars then its a long filename format
-  if (path.length() > 15)
-    flags |= HasLongFilenameFlag;
-  else
-    flags &= ~HasLongFilenameFlag;
-
-  // Get the signature and status info
-  const char* signature = (const char*) data;
-  SmallString<4> magic;
-  if (!signature) {
-    sys::fs::get_magic(path, magic.capacity(), magic);
-    signature = magic.c_str();
-
-    sys::fs::file_status Status;
-    error_code EC = sys::fs::status(path, Status);
-    if (EC)
-      return true;
-
-    User = Status.getUser();
-    Group = Status.getGroup();
-    Mode = Status.permissions();
-    ModTime = Status.getLastModificationTime();
-
-    // FIXME: On posix this is a second stat.
-    EC = sys::fs::file_size(path, Size);
-    if (EC)
-      return true;
-  }
-
-  return false;
-}
-
-// Archive constructor - this is the only constructor that gets used for the
-// Archive class. Everything else (default,copy) is deprecated. This just
-// initializes and maps the file into memory, if requested.
-Archive::Archive(StringRef filename, LLVMContext &C)
-    : archPath(filename), members(), mapfile(0), base(0), strtab(),
-      firstFileOffset(0), modules(), Context(C) {}
-
-bool
-Archive::mapToMemory(std::string* ErrMsg) {
-  OwningPtr<MemoryBuffer> File;
-  if (error_code ec = MemoryBuffer::getFile(archPath.c_str(), File)) {
-    if (ErrMsg)
-      *ErrMsg = ec.message();
-    return true;
-  }
-  mapfile = File.take();
-  base = mapfile->getBufferStart();
-  return false;
-}
-
-void Archive::cleanUpMemory() {
-  // Shutdown the file mapping
-  delete mapfile;
-  mapfile = 0;
-  base = 0;
-
-  firstFileOffset = 0;
-
-  // Delete any Modules and ArchiveMember's we've allocated as a result of
-  // symbol table searches.
-  for (ModuleMap::iterator I=modules.begin(), E=modules.end(); I != E; ++I ) {
-    delete I->second.first;
-    delete I->second.second;
-  }
-}
-
-// Archive destructor - just clean up memory
-Archive::~Archive() {
-  cleanUpMemory();
-}
-

Removed: llvm/trunk/tools/llvm-ar/ArchiveInternals.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-ar/ArchiveInternals.h?rev=186196&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-ar/ArchiveInternals.h (original)
+++ llvm/trunk/tools/llvm-ar/ArchiveInternals.h (removed)
@@ -1,75 +0,0 @@
-//===-- lib/Archive/ArchiveInternals.h -------------------------*- C++ -*-===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Internal implementation header for LLVM Archive files.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef TOOLS_LLVM_AR_ARCHIVEINTERNALS_H
-#define TOOLS_LLVM_AR_ARCHIVEINTERNALS_H
-
-#include "Archive.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/TimeValue.h"
-#include <cstring>
-
-#define ARFILE_MAGIC "!<arch>\n"                   ///< magic string
-#define ARFILE_MAGIC_LEN (sizeof(ARFILE_MAGIC)-1)  ///< length of magic string
-#define ARFILE_SVR4_SYMTAB_NAME "/               " ///< SVR4 symtab entry name
-#define ARFILE_BSD4_SYMTAB_NAME "__.SYMDEF SORTED" ///< BSD4 symtab entry name
-#define ARFILE_STRTAB_NAME      "//              " ///< Name of string table
-#define ARFILE_PAD "\n"                            ///< inter-file align padding
-#define ARFILE_MEMBER_MAGIC "`\n"                  ///< fmag field magic #
-
-namespace llvm {
-
-  class LLVMContext;
-
-  /// The ArchiveMemberHeader structure is used internally for bitcode
-  /// archives.
-  /// The header precedes each file member in the archive. This structure is
-  /// defined using character arrays for direct and correct interpretation
-  /// regardless of the endianess of the machine that produced it.
-  /// @brief Archive File Member Header
-  class ArchiveMemberHeader {
-    /// @name Data
-    /// @{
-    public:
-      char name[16];  ///< Name of the file member.
-      char date[12];  ///< File date, decimal seconds since Epoch
-      char uid[6];    ///< user id in ASCII decimal
-      char gid[6];    ///< group id in ASCII decimal
-      char mode[8];   ///< file mode in ASCII octal
-      char size[10];  ///< file size in ASCII decimal
-      char fmag[2];   ///< Always contains ARFILE_MAGIC_TERMINATOR
-
-    /// @}
-    /// @name Methods
-    /// @{
-    public:
-    void init() {
-      memset(name,' ',16);
-      memset(date,' ',12);
-      memset(uid,' ',6);
-      memset(gid,' ',6);
-      memset(mode,' ',8);
-      memset(size,' ',10);
-      fmag[0] = '`';
-      fmag[1] = '\n';
-    }
-
-    bool checkSignature() const {
-      return 0 == memcmp(fmag, ARFILE_MEMBER_MAGIC,2);
-    }
-  };
-}
-
-#endif
-
-// vim: sw=2 ai

Removed: llvm/trunk/tools/llvm-ar/ArchiveReader.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-ar/ArchiveReader.cpp?rev=186196&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-ar/ArchiveReader.cpp (original)
+++ llvm/trunk/tools/llvm-ar/ArchiveReader.cpp (removed)
@@ -1,317 +0,0 @@
-//===-- ArchiveReader.cpp - Read LLVM archive files -------------*- C++ -*-===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Builds up standard unix archive files (.a) containing LLVM bitcode.
-//
-//===----------------------------------------------------------------------===//
-
-#include "Archive.h"
-#include "ArchiveInternals.h"
-#include "llvm/ADT/OwningPtr.h"
-#include "llvm/ADT/SmallPtrSet.h"
-#include "llvm/Bitcode/ReaderWriter.h"
-#include "llvm/IR/Module.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include <cstdio>
-#include <cstdlib>
-using namespace llvm;
-
-/// Read a variable-bit-rate encoded unsigned integer
-static inline unsigned readInteger(const char*&At, const char*End) {
-  unsigned Shift = 0;
-  unsigned Result = 0;
-
-  do {
-    if (At == End)
-      return Result;
-    Result |= (unsigned)((*At++) & 0x7F) << Shift;
-    Shift += 7;
-  } while (At[-1] & 0x80);
-  return Result;
-}
-
-// This member parses an ArchiveMemberHeader that is presumed to be pointed to
-// by At. The At pointer is updated to the byte just after the header, which
-// can be variable in size.
-ArchiveMember*
-Archive::parseMemberHeader(const char*& At, const char* End, std::string* error)
-{
-  if (At + sizeof(ArchiveMemberHeader) >= End) {
-    if (error)
-      *error = "Unexpected end of file";
-    return 0;
-  }
-
-  // Cast archive member header
-  const ArchiveMemberHeader* Hdr = (const ArchiveMemberHeader*)At;
-  At += sizeof(ArchiveMemberHeader);
-
-  int flags = 0;
-  int MemberSize = atoi(Hdr->size);
-  assert(MemberSize >= 0);
-
-  // Check the size of the member for sanity
-  if (At + MemberSize > End) {
-    if (error)
-      *error = "invalid member length in archive file";
-    return 0;
-  }
-
-  // Check the member signature
-  if (!Hdr->checkSignature()) {
-    if (error)
-      *error = "invalid file member signature";
-    return 0;
-  }
-
-  // Convert and check the member name
-  // The empty name ( '/' and 15 blanks) is for a foreign (non-LLVM) symbol
-  // table. The special name "//" and 14 blanks is for a string table, used
-  // for long file names. This library doesn't generate either of those but
-  // it will accept them. If the name starts with #1/ and the remainder is
-  // digits, then those digits specify the length of the name that is
-  // stored immediately following the header. Anything else is a regular, short
-  // filename that is terminated with a '/' and blanks.
-
-  std::string pathname;
-  switch (Hdr->name[0]) {
-    case '#':
-      if (Hdr->name[1] == '1' && Hdr->name[2] == '/') {
-        if (isdigit(Hdr->name[3])) {
-          unsigned len = atoi(&Hdr->name[3]);
-          const char *nulp = (const char *)memchr(At, '\0', len);
-          pathname.assign(At, nulp != 0 ? (uintptr_t)(nulp - At) : len);
-          At += len;
-          MemberSize -= len;
-          flags |= ArchiveMember::HasLongFilenameFlag;
-        } else {
-          if (error)
-            *error = "invalid long filename";
-          return 0;
-        }
-      }
-      break;
-    case '/':
-      if (Hdr->name[1]== '/') {
-        if (0 == memcmp(Hdr->name, ARFILE_STRTAB_NAME, 16)) {
-          pathname.assign(ARFILE_STRTAB_NAME);
-          flags |= ArchiveMember::StringTableFlag;
-        } else {
-          if (error)
-            *error = "invalid string table name";
-          return 0;
-        }
-      } else if (Hdr->name[1] == ' ') {
-        if (0 == memcmp(Hdr->name, ARFILE_SVR4_SYMTAB_NAME, 16)) {
-          pathname.assign(ARFILE_SVR4_SYMTAB_NAME);
-          flags |= ArchiveMember::SVR4SymbolTableFlag;
-        } else {
-          if (error)
-            *error = "invalid SVR4 symbol table name";
-          return 0;
-        }
-      } else if (isdigit(Hdr->name[1])) {
-        unsigned index = atoi(&Hdr->name[1]);
-        if (index < strtab.length()) {
-          const char* namep = strtab.c_str() + index;
-          const char* endp = strtab.c_str() + strtab.length();
-          const char* p = namep;
-          const char* last_p = p;
-          while (p < endp) {
-            if (*p == '\n' && *last_p == '/') {
-              pathname.assign(namep, last_p - namep);
-              flags |= ArchiveMember::HasLongFilenameFlag;
-              break;
-            }
-            last_p = p;
-            p++;
-          }
-          if (p >= endp) {
-            if (error)
-              *error = "missing name terminator in string table";
-            return 0;
-          }
-        } else {
-          if (error)
-            *error = "name index beyond string table";
-          return 0;
-        }
-      }
-      break;
-    case '_':
-      if (Hdr->name[1] == '_' &&
-          (0 == memcmp(Hdr->name, ARFILE_BSD4_SYMTAB_NAME, 16))) {
-        pathname.assign(ARFILE_BSD4_SYMTAB_NAME);
-        flags |= ArchiveMember::BSD4SymbolTableFlag;
-        break;
-      }
-      /* FALL THROUGH */
-
-    default:
-      const char* slash = (const char*) memchr(Hdr->name, '/', 16);
-      if (slash == 0)
-        slash = Hdr->name + 16;
-      pathname.assign(Hdr->name, slash - Hdr->name);
-      break;
-  }
-
-  // Instantiate the ArchiveMember to be filled
-  ArchiveMember* member = new ArchiveMember(this);
-
-  // Fill in fields of the ArchiveMember
-  member->parent = this;
-  member->path = pathname;
-  member->Size = MemberSize;
-  member->ModTime.fromEpochTime(atoi(Hdr->date));
-  unsigned int mode;
-  sscanf(Hdr->mode, "%o", &mode);
-  member->Mode = mode;
-  member->User = atoi(Hdr->uid);
-  member->Group = atoi(Hdr->gid);
-  member->flags = flags;
-  member->data = At;
-
-  return member;
-}
-
-bool
-Archive::checkSignature(std::string* error) {
-  // Check the magic string at file's header
-  if (mapfile->getBufferSize() < 8 || memcmp(base, ARFILE_MAGIC, 8)) {
-    if (error)
-      *error = "invalid signature for an archive file";
-    return false;
-  }
-  return true;
-}
-
-// This function loads the entire archive and fully populates its ilist with
-// the members of the archive file. This is typically used in preparation for
-// editing the contents of the archive.
-bool
-Archive::loadArchive(std::string* error) {
-
-  // Set up parsing
-  members.clear();
-  const char *At = base;
-  const char *End = mapfile->getBufferEnd();
-
-  if (!checkSignature(error))
-    return false;
-
-  At += 8;  // Skip the magic string.
-
-  bool foundFirstFile = false;
-  while (At < End) {
-    // parse the member header
-    const char* Save = At;
-    OwningPtr<ArchiveMember> mbr(parseMemberHeader(At, End, error));
-    if (!mbr)
-      return false;
-
-    // check if this is the foreign symbol table
-    if (mbr->isSVR4SymbolTable() || mbr->isBSD4SymbolTable()) {
-      At += mbr->getSize();
-      if ((intptr_t(At) & 1) == 1)
-        At++;
-    } else if (mbr->isStringTable()) {
-      // Simply suck the entire string table into a string
-      // variable. This will be used to get the names of the
-      // members that use the "/ddd" format for their names
-      // (SVR4 style long names).
-      strtab.assign(At, mbr->getSize());
-      At += mbr->getSize();
-      if ((intptr_t(At) & 1) == 1)
-        At++;
-    } else {
-      // This is just a regular file. If its the first one, save its offset.
-      // Otherwise just push it on the list and move on to the next file.
-      if (!foundFirstFile) {
-        firstFileOffset = Save - base;
-        foundFirstFile = true;
-      }
-      At += mbr->getSize();
-      members.push_back(mbr.take());
-      if ((intptr_t(At) & 1) == 1)
-        At++;
-    }
-  }
-  return true;
-}
-
-// Open and completely load the archive file.
-Archive*
-Archive::OpenAndLoad(StringRef File, LLVMContext& C,
-                     std::string* ErrorMessage) {
-  OwningPtr<Archive> result(new Archive(File, C));
-  if (result->mapToMemory(ErrorMessage))
-    return NULL;
-  if (!result->loadArchive(ErrorMessage))
-    return NULL;
-  return result.take();
-}
-
-// Load just the symbol table from the archive file
-bool
-Archive::loadSymbolTable(std::string* ErrorMsg) {
-
-  // Set up parsing
-  members.clear();
-  const char *At = base;
-  const char *End = mapfile->getBufferEnd();
-
-  // Make sure we're dealing with an archive
-  if (!checkSignature(ErrorMsg))
-    return false;
-
-  At += 8; // Skip signature
-
-  // Parse the first file member header
-  const char* FirstFile = At;
-  OwningPtr<ArchiveMember> mbr(parseMemberHeader(At, End, ErrorMsg));
-  if (!mbr)
-    return false;
-
-  if (mbr->isSVR4SymbolTable() || mbr->isBSD4SymbolTable()) {
-    // Skip the foreign symbol table, we don't do anything with it
-    At += mbr->getSize();
-    if ((intptr_t(At) & 1) == 1)
-      At++;
-
-    // Read the next one
-    FirstFile = At;
-    mbr.reset(parseMemberHeader(At, End, ErrorMsg));
-    if (!mbr)
-      return false;
-  }
-
-  if (mbr->isStringTable()) {
-    // Process the string table entry
-    strtab.assign((const char*)mbr->getData(), mbr->getSize());
-    At += mbr->getSize();
-    if ((intptr_t(At) & 1) == 1)
-      At++;
-
-    // Get the next one
-    FirstFile = At;
-    mbr.reset(parseMemberHeader(At, End, ErrorMsg));
-    if (!mbr)
-      return false;
-  }
-
-  // There's no symbol table in the file. We have to rebuild it from scratch
-  // because the intent of this method is to get the symbol table loaded so
-  // it can be searched efficiently.
-  // Add the member to the members list
-  members.push_back(mbr.take());
-
-  firstFileOffset = FirstFile - base;
-  return true;
-}

Removed: llvm/trunk/tools/llvm-ar/ArchiveWriter.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-ar/ArchiveWriter.cpp?rev=186196&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-ar/ArchiveWriter.cpp (original)
+++ llvm/trunk/tools/llvm-ar/ArchiveWriter.cpp (removed)
@@ -1,288 +0,0 @@
-//===-- ArchiveWriter.cpp - Write LLVM archive files ----------------------===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Builds up an LLVM archive file (.a) containing LLVM bitcode.
-//
-//===----------------------------------------------------------------------===//
-
-#include "Archive.h"
-#include "ArchiveInternals.h"
-#include "llvm/ADT/OwningPtr.h"
-#include "llvm/Bitcode/ReaderWriter.h"
-#include "llvm/IR/Module.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/Process.h"
-#include "llvm/Support/Signals.h"
-#include "llvm/Support/system_error.h"
-#include <fstream>
-#include <iomanip>
-#include <ostream>
-using namespace llvm;
-
-// Write an integer using variable bit rate encoding. This saves a few bytes
-// per entry in the symbol table.
-static inline void writeInteger(unsigned num, std::ofstream& ARFile) {
-  while (1) {
-    if (num < 0x80) { // done?
-      ARFile << (unsigned char)num;
-      return;
-    }
-
-    // Nope, we are bigger than a character, output the next 7 bits and set the
-    // high bit to say that there is more coming...
-    ARFile << (unsigned char)(0x80 | ((unsigned char)num & 0x7F));
-    num >>= 7;  // Shift out 7 bits now...
-  }
-}
-
-// Compute how many bytes are taken by a given VBR encoded value. This is needed
-// to pre-compute the size of the symbol table.
-static inline unsigned numVbrBytes(unsigned num) {
-
-  // Note that the following nested ifs are somewhat equivalent to a binary
-  // search. We split it in half by comparing against 2^14 first. This allows
-  // most reasonable values to be done in 2 comparisons instead of 1 for
-  // small ones and four for large ones. We expect this to access file offsets
-  // in the 2^10 to 2^24 range and symbol lengths in the 2^0 to 2^8 range,
-  // so this approach is reasonable.
-  if (num < 1<<14) {
-    if (num < 1<<7)
-      return 1;
-    else
-      return 2;
-  }
-  if (num < 1<<21)
-    return 3;
-
-  if (num < 1<<28)
-    return 4;
-  return 5; // anything >= 2^28 takes 5 bytes
-}
-
-// Create an empty archive.
-Archive* Archive::CreateEmpty(StringRef FilePath, LLVMContext& C) {
-  Archive* result = new Archive(FilePath, C);
-  return result;
-}
-
-// Fill the ArchiveMemberHeader with the information from a member. If
-// TruncateNames is true, names are flattened to 15 chars or less. The sz field
-// is provided here instead of coming from the mbr because the member might be
-// stored compressed and the compressed size is not the ArchiveMember's size.
-// Furthermore compressed files have negative size fields to identify them as
-// compressed.
-bool
-Archive::fillHeader(const ArchiveMember &mbr, ArchiveMemberHeader& hdr,
-                    int sz) const {
-
-  // Set the permissions mode, uid and gid
-  hdr.init();
-  char buffer[32];
-  sprintf(buffer, "%-8o", mbr.getMode());
-  memcpy(hdr.mode,buffer,8);
-  sprintf(buffer,  "%-6u", mbr.getUser());
-  memcpy(hdr.uid,buffer,6);
-  sprintf(buffer,  "%-6u", mbr.getGroup());
-  memcpy(hdr.gid,buffer,6);
-
-  // Set the last modification date
-  uint64_t secondsSinceEpoch = mbr.getModTime().toEpochTime();
-  sprintf(buffer,"%-12u", unsigned(secondsSinceEpoch));
-  memcpy(hdr.date,buffer,12);
-
-  std::string mbrPath = sys::path::filename(mbr.getPath());
-
-  // Set the name field in one of its various flavors.
-  bool writeLongName = false;
-  if (mbr.isStringTable()) {
-    memcpy(hdr.name,ARFILE_STRTAB_NAME,16);
-  } else if (mbr.isSVR4SymbolTable()) {
-    memcpy(hdr.name,ARFILE_SVR4_SYMTAB_NAME,16);
-  } else if (mbr.isBSD4SymbolTable()) {
-    memcpy(hdr.name,ARFILE_BSD4_SYMTAB_NAME,16);
-  } else if (mbrPath.length() < 16 && mbrPath.find('/') == std::string::npos) {
-    memcpy(hdr.name,mbrPath.c_str(),mbrPath.length());
-    hdr.name[mbrPath.length()] = '/';
-  } else {
-    std::string nm = "#1/";
-    nm += utostr(mbrPath.length());
-    memcpy(hdr.name,nm.data(),nm.length());
-    if (sz < 0)
-      sz -= mbrPath.length();
-    else
-      sz += mbrPath.length();
-    writeLongName = true;
-  }
-
-  // Set the size field
-  if (sz < 0) {
-    buffer[0] = '-';
-    sprintf(&buffer[1],"%-9u",(unsigned)-sz);
-  } else {
-    sprintf(buffer, "%-10u", (unsigned)sz);
-  }
-  memcpy(hdr.size,buffer,10);
-
-  return writeLongName;
-}
-
-// Insert a file into the archive before some other member. This also takes care
-// of extracting the necessary flags and information from the file.
-bool Archive::addFileBefore(StringRef filePath, iterator where,
-                            std::string *ErrMsg) {
-  if (!sys::fs::exists(filePath)) {
-    if (ErrMsg)
-      *ErrMsg = "Can not add a non-existent file to archive";
-    return true;
-  }
-
-  ArchiveMember* mbr = new ArchiveMember(this);
-
-  mbr->data = 0;
-  mbr->path = filePath;
-  sys::fs::file_status Status;
-  error_code EC = sys::fs::status(filePath, Status);
-  if (EC) {
-    delete mbr;
-    return true;
-  }
-  mbr->User = Status.getUser();
-  mbr->Group = Status.getGroup();
-  mbr->Mode = Status.permissions();
-  mbr->ModTime = Status.getLastModificationTime();
-  // FIXME: On posix this is a second stat.
-  EC =  sys::fs::file_size(filePath, mbr->Size);
-  if (EC) {
-    delete mbr;
-    return true;
-  }
-
-  unsigned flags = 0;
-  if (sys::path::filename(filePath).size() > 15)
-    flags |= ArchiveMember::HasLongFilenameFlag;
-
-  sys::fs::file_magic type;
-  if (sys::fs::identify_magic(mbr->path, type))
-    type = sys::fs::file_magic::unknown;
-  mbr->flags = flags;
-  members.insert(where,mbr);
-  return false;
-}
-
-// Write one member out to the file.
-bool
-Archive::writeMember(
-  const ArchiveMember& member,
-  raw_fd_ostream& ARFile,
-  std::string* ErrMsg
-) {
-
-  uint64_t filepos = ARFile.tell();
-  filepos -= 8;
-
-  // Get the data and its size either from the
-  // member's in-memory data or directly from the file.
-  size_t fSize = member.getSize();
-  const char *data = (const char*)member.getData();
-  MemoryBuffer *mFile = 0;
-  if (!data) {
-    OwningPtr<MemoryBuffer> File;
-    if (error_code ec = MemoryBuffer::getFile(member.getPath(), File)) {
-      if (ErrMsg)
-        *ErrMsg = ec.message();
-      return true;
-    }
-    mFile = File.take();
-    data = mFile->getBufferStart();
-    fSize = mFile->getBufferSize();
-  }
-
-  int hdrSize = fSize;
-
-  // Compute the fields of the header
-  ArchiveMemberHeader Hdr;
-  bool writeLongName = fillHeader(member,Hdr,hdrSize);
-
-  // Write header to archive file
-  ARFile.write((char*)&Hdr, sizeof(Hdr));
-
-  // Write the long filename if its long
-  if (writeLongName) {
-    StringRef Name = sys::path::filename(member.getPath());
-    ARFile.write(Name.data(), Name.size());
-  }
-
-  // Write the (possibly compressed) member's content to the file.
-  ARFile.write(data,fSize);
-
-  // Make sure the member is an even length
-  if ((ARFile.tell() & 1) == 1)
-    ARFile << ARFILE_PAD;
-
-  // Close the mapped file if it was opened
-  delete mFile;
-  return false;
-}
-
-// Write the entire archive to the file specified when the archive was created.
-// This writes to a temporary file first. Options are for creating a symbol
-// table, flattening the file names (no directories, 15 chars max) and
-// compressing each archive member.
-bool Archive::writeToDisk(std::string *ErrMsg) {
-  // Make sure they haven't opened up the file, not loaded it,
-  // but are now trying to write it which would wipe out the file.
-  if (members.empty() && mapfile && mapfile->getBufferSize() > 8) {
-    if (ErrMsg)
-      *ErrMsg = "Can't write an archive not opened for writing";
-    return true;
-  }
-
-  // Create a temporary file to store the archive in
-  int TmpArchiveFD;
-  SmallString<128> TmpArchive;
-  error_code EC = sys::fs::createUniqueFile(
-      archPath + ".temp-archive-%%%%%%%.a", TmpArchiveFD, TmpArchive);
-  if (EC)
-    return true;
-
-  // Make sure the temporary gets removed if we crash
-  sys::RemoveFileOnSignal(TmpArchive);
-
-  // Create archive file for output.
-  raw_fd_ostream ArchiveFile(TmpArchiveFD, true);
-
-  // Write magic string to archive.
-  ArchiveFile << ARFILE_MAGIC;
-
-  // Loop over all member files, and write them out. Note that this also
-  // builds the symbol table, symTab.
-  for (MembersList::iterator I = begin(), E = end(); I != E; ++I) {
-    if (writeMember(*I, ArchiveFile, ErrMsg)) {
-      sys::fs::remove(Twine(TmpArchive));
-      ArchiveFile.close();
-      return true;
-    }
-  }
-
-  // Close archive file.
-  ArchiveFile.close();
-
-  // Before we replace the actual archive, we need to forget all the
-  // members, since they point to data in that old archive. We need to do
-  // this because we cannot replace an open file on Windows.
-  cleanUpMemory();
-
-  if (sys::fs::rename(Twine(TmpArchive), archPath)) {
-    *ErrMsg = EC.message();
-    return true;
-  }
-
-  return false;
-}

Modified: llvm/trunk/tools/llvm-ar/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-ar/CMakeLists.txt?rev=186197&r1=186196&r2=186197&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-ar/CMakeLists.txt (original)
+++ llvm/trunk/tools/llvm-ar/CMakeLists.txt Fri Jul 12 15:21:39 2013
@@ -1,10 +1,7 @@
-set(LLVM_LINK_COMPONENTS support bitreader)
+set(LLVM_LINK_COMPONENTS support object bitreader)
 
 add_llvm_tool(llvm-ar
   llvm-ar.cpp
-  ArchiveWriter.cpp
-  ArchiveReader.cpp
-  Archive.cpp
   )
 
 # TODO: Support check-local.

Modified: llvm/trunk/tools/llvm-ar/llvm-ar.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-ar/llvm-ar.cpp?rev=186197&r1=186196&r2=186197&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-ar/llvm-ar.cpp (original)
+++ llvm/trunk/tools/llvm-ar/llvm-ar.cpp Fri Jul 12 15:21:39 2013
@@ -12,15 +12,17 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "Archive.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Module.h"
+#include "llvm/Object/Archive.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/PrettyStackTrace.h"
 #include "llvm/Support/Signals.h"
+#include "llvm/Support/ToolOutputFile.h"
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <cstdlib>
@@ -35,6 +37,29 @@
 
 using namespace llvm;
 
+// The name this program was invoked as.
+static StringRef ToolName;
+
+static const char *TemporaryOutput;
+
+// fail - Show the error message and exit.
+LLVM_ATTRIBUTE_NORETURN static void fail(Twine Error) {
+  outs() << ToolName << ": " << Error << ".\n";
+  if (TemporaryOutput)
+    sys::fs::remove(TemporaryOutput);
+  exit(1);
+}
+
+static void failIfError(error_code EC, Twine Context = "") {
+  if (!EC)
+    return;
+
+  std::string ContextStr = Context.str();
+  if (ContextStr == "")
+    fail(EC.message());
+  fail(Context + ": " + EC.message());
+}
+
 // Option for compatibility with AIX, not used but must allow it to be present.
 static cl::opt<bool>
 X32Option ("X32_64", cl::Hidden,
@@ -109,32 +134,11 @@ static std::string ArchiveName;
 // on the command line.
 static std::vector<std::string> Members;
 
-// This variable holds the (possibly expanded) list of path objects that
-// correspond to files we will
-static std::set<std::string> Paths;
-
-// The Archive object to which all the editing operations will be sent.
-static Archive *TheArchive = 0;
-
-// The name this program was invoked as.
-static const char *program_name;
-
 // show_help - Show the error message, the help message and exit.
 LLVM_ATTRIBUTE_NORETURN static void
 show_help(const std::string &msg) {
-  errs() << program_name << ": " << msg << "\n\n";
+  errs() << ToolName << ": " << msg << "\n\n";
   cl::PrintHelpMessage();
-  if (TheArchive)
-    delete TheArchive;
-  std::exit(1);
-}
-
-// fail - Show the error message and exit.
-LLVM_ATTRIBUTE_NORETURN static void
-fail(const std::string &msg) {
-  errs() << program_name << ": " << msg << "\n\n";
-  if (TheArchive)
-    delete TheArchive;
   std::exit(1);
 }
 
@@ -243,53 +247,14 @@ static ArchiveOperation parseCommandLine
   return Operation;
 }
 
-// buildPaths - Convert the strings in the Members vector to sys::Path objects
-// and make sure they are valid and exist exist. This check is only needed for
-// the operations that add/replace files to the archive ('q' and 'r')
-static bool buildPaths(bool checkExistence, std::string *ErrMsg) {
-  for (unsigned i = 0; i < Members.size(); i++) {
-    std::string aPath = Members[i];
-    if (checkExistence) {
-      bool IsDirectory;
-      error_code EC = sys::fs::is_directory(aPath, IsDirectory);
-      if (EC)
-        fail(aPath + ": " + EC.message());
-      if (IsDirectory)
-        fail(aPath + " Is a directory");
+// Implements the 'p' operation. This function traverses the archive
+// looking for members that match the path list.
+static void doPrint(StringRef Name, object::Archive::child_iterator I) {
+  if (Verbose)
+    outs() << "Printing " << Name << "\n";
 
-      Paths.insert(aPath);
-    } else {
-      Paths.insert(aPath);
-    }
-  }
-  return false;
-}
-
-// doPrint - Implements the 'p' operation. This function traverses the archive
-// looking for members that match the path list. It is careful to uncompress
-// things that should be and to skip bitcode files unless the 'k' modifier was
-// given.
-static bool doPrint(std::string *ErrMsg) {
-  if (buildPaths(false, ErrMsg))
-    return true;
-  for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end();
-       I != E; ++I ) {
-    if (Paths.empty() ||
-        (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) {
-      const char *data = reinterpret_cast<const char *>(I->getData());
-
-      // Skip things that don't make sense to print
-      if (I->isSVR4SymbolTable() || I->isBSD4SymbolTable())
-        continue;
-
-      if (Verbose)
-        outs() << "Printing " << I->getPath().str() << "\n";
-
-      unsigned len = I->getSize();
-      outs().write(data, len);
-    }
-  }
-  return false;
+  StringRef Data = I->getBuffer();
+  outs().write(Data.data(), Data.size());
 }
 
 // putMode - utility function for printing out the file mode when the 't'
@@ -309,291 +274,356 @@ static void printMode(unsigned mode) {
     outs() << "-";
 }
 
-// doDisplayTable - Implement the 't' operation. This function prints out just
+// Implement the 't' operation. This function prints out just
 // the file names of each of the members. However, if verbose mode is requested
 // ('v' modifier) then the file type, permission mode, user, group, size, and
 // modification time are also printed.
-static bool doDisplayTable(std::string *ErrMsg) {
-  if (buildPaths(false, ErrMsg))
-    return true;
-  for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end();
-       I != E; ++I ) {
-    if (Paths.empty() ||
-        (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) {
-      if (Verbose) {
-        unsigned mode = I->getMode();
-        printMode((mode >> 6) & 007);
-        printMode((mode >> 3) & 007);
-        printMode(mode & 007);
-        outs() << ' ' << I->getUser();
-        outs() << "/" << I->getGroup();
-        outs() << ' ' << format("%6llu", I->getSize());
-        sys::TimeValue ModTime = I->getModTime();
-        outs() << " " << ModTime.str();
-        outs() << " " << I->getPath().str() << "\n";
-      } else {
-        outs() << I->getPath().str() << "\n";
-      }
-    }
-  }
-  return false;
-}
-
-// doExtract - Implement the 'x' operation. This function extracts files back to
-// the file system.
-static bool doExtract(std::string *ErrMsg) {
-  if (buildPaths(false, ErrMsg))
-    return true;
-  for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end();
-       I != E; ++I ) {
-    if (Paths.empty() ||
-        (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) {
-
-      // Open up a file stream for writing
-      int OpenFlags = O_TRUNC | O_WRONLY | O_CREAT;
+static void doDisplayTable(StringRef Name, object::Archive::child_iterator I) {
+  if (Verbose) {
+    sys::fs::perms Mode = I->getAccessMode();
+    printMode((Mode >> 6) & 007);
+    printMode((Mode >> 3) & 007);
+    printMode(Mode & 007);
+    outs() << ' ' << I->getUID();
+    outs() << '/' << I->getGID();
+    outs() << ' ' << format("%6llu", I->getSize());
+    outs() << ' ' << I->getLastModified().str();
+    outs() << ' ';
+  }
+  outs() << Name << "\n";
+}
+
+// Implement the 'x' operation. This function extracts files back to the file
+// system.
+static void doExtract(StringRef Name, object::Archive::child_iterator I) {
+  // Open up a file stream for writing
+  // FIXME: we should abstract this, O_BINARY in particular.
+  int OpenFlags = O_TRUNC | O_WRONLY | O_CREAT;
 #ifdef O_BINARY
-      OpenFlags |= O_BINARY;
+  OpenFlags |= O_BINARY;
 #endif
 
-      // Retain the original mode.
-      sys::fs::perms Mode = sys::fs::perms(I->getMode());
+  // Retain the original mode.
+  sys::fs::perms Mode = I->getAccessMode();
 
-      int FD = open(I->getPath().str().c_str(), OpenFlags, Mode);
-      if (FD < 0)
-        return true;
-
-      {
-        raw_fd_ostream file(FD, false);
-
-        // Get the data and its length
-        const char* data = reinterpret_cast<const char*>(I->getData());
-        unsigned len = I->getSize();
+  int FD = open(Name.str().c_str(), OpenFlags, Mode);
+  if (FD < 0)
+    fail("Could not open output file");
 
-        // Write the data.
-        file.write(data, len);
-      }
+  {
+    raw_fd_ostream file(FD, false);
 
-      // If we're supposed to retain the original modification times, etc. do so
-      // now.
-      if (OriginalDates) {
-        error_code EC =
-            sys::fs::setLastModificationAndAccessTime(FD, I->getModTime());
-        if (EC)
-          fail(EC.message());
-      }
-      if (close(FD))
-        return true;
-    }
+    // Get the data and its length
+    StringRef Data = I->getBuffer();
+
+    // Write the data.
+    file.write(Data.data(), Data.size());
   }
-  return false;
+
+  // If we're supposed to retain the original modification times, etc. do so
+  // now.
+  if (OriginalDates)
+    failIfError(
+        sys::fs::setLastModificationAndAccessTime(FD, I->getLastModified()));
+
+  if (close(FD))
+    fail("Could not close the file");
 }
 
-// doDelete - Implement the delete operation. This function deletes zero or more
-// members from the archive. Note that if the count is specified, there should
-// be no more than one path in the Paths list or else this algorithm breaks.
-// That check is enforced in parseCommandLine (above).
-static bool doDelete(std::string *ErrMsg) {
-  if (buildPaths(false, ErrMsg))
-    return true;
-  if (Paths.empty())
+static bool shouldCreateArchive(ArchiveOperation Op) {
+  switch (Op) {
+  case Print:
+  case Delete:
+  case Move:
+  case DisplayTable:
+  case Extract:
     return false;
-  for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end();
-       I != E; ) {
-    if (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end()) {
-      Archive::iterator J = I;
-      ++I;
-      TheArchive->erase(J);
-    } else {
-      ++I;
-    }
-  }
 
-  // We're done editting, reconstruct the archive.
-  if (TheArchive->writeToDisk(ErrMsg))
+  case QuickAppend:
+  case ReplaceOrInsert:
     return true;
-  return false;
+  }
+
+  llvm_unreachable("Missing entry in covered switch.");
 }
 
-// doMore - Implement the move operation. This function re-arranges just the
-// order of the archive members so that when the archive is written the move
-// of the members is accomplished. Note the use of the RelPos variable to
-// determine where the items should be moved to.
-static bool doMove(std::string *ErrMsg) {
-  if (buildPaths(false, ErrMsg))
-    return true;
+static void performReadOperation(ArchiveOperation Operation,
+                                 object::Archive *OldArchive) {
+  for (object::Archive::child_iterator I = OldArchive->begin_children(),
+                                       E = OldArchive->end_children();
+       I != E; ++I) {
+    StringRef Name;
+    failIfError(I->getName(Name));
+
+    if (!Members.empty() &&
+        std::find(Members.begin(), Members.end(), Name) == Members.end())
+      continue;
 
-  // By default and convention the place to move members to is the end of the
-  // archive.
-  Archive::iterator moveto_spot = TheArchive->end();
-
-  // However, if the relative positioning modifiers were used, we need to scan
-  // the archive to find the member in question. If we don't find it, its no
-  // crime, we just move to the end.
-  if (AddBefore || AddAfter) {
-    for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end();
-         I != E; ++I ) {
-      if (RelPos == I->getPath().str()) {
-        if (AddAfter) {
-          moveto_spot = I;
-          moveto_spot++;
-        } else {
-          moveto_spot = I;
-        }
-        break;
-      }
+    switch (Operation) {
+    default:
+      llvm_unreachable("Not a read operation");
+    case Print:
+      doPrint(Name, I);
+      break;
+    case DisplayTable:
+      doDisplayTable(Name, I);
+      break;
+    case Extract:
+      doExtract(Name, I);
+      break;
     }
   }
+}
 
-  // Keep a list of the paths remaining to be moved
-  std::set<std::string> remaining(Paths);
-
-  // Scan the archive again, this time looking for the members to move to the
-  // moveto_spot.
-  for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end();
-       I != E && !remaining.empty(); ++I ) {
-    std::set<std::string>::iterator found =
-      std::find(remaining.begin(),remaining.end(), I->getPath());
-    if (found != remaining.end()) {
-      if (I != moveto_spot)
-        TheArchive->splice(moveto_spot,*TheArchive,I);
-      remaining.erase(found);
-    }
-  }
+namespace {
+class NewArchiveIterator {
+  bool IsNewMember;
+  SmallString<16> MemberName;
+  union {
+    object::Archive::child_iterator OldI;
+    std::vector<std::string>::const_iterator NewI;
+  };
+
+public:
+  NewArchiveIterator(object::Archive::child_iterator I, Twine Name);
+  NewArchiveIterator(std::vector<std::string>::const_iterator I, Twine Name);
+  bool isNewMember() const;
+  object::Archive::child_iterator getOld() const;
+  StringRef getNew() const;
+  StringRef getMemberName() const { return MemberName; }
+};
+}
 
-  // We're done editting, reconstruct the archive.
-  if (TheArchive->writeToDisk(ErrMsg))
-    return true;
-  return false;
+NewArchiveIterator::NewArchiveIterator(object::Archive::child_iterator I,
+                                       Twine Name)
+    : IsNewMember(false), OldI(I) {
+  Name.toVector(MemberName);
 }
 
-// doQuickAppend - Implements the 'q' operation. This function just
-// indiscriminantly adds the members to the archive and rebuilds it.
-static bool doQuickAppend(std::string *ErrMsg) {
-  // Get the list of paths to append.
-  if (buildPaths(true, ErrMsg))
-    return true;
-  if (Paths.empty())
-    return false;
+NewArchiveIterator::NewArchiveIterator(
+    std::vector<std::string>::const_iterator I, Twine Name)
+    : IsNewMember(true), NewI(I) {
+  Name.toVector(MemberName);
+}
 
-  // Append them quickly.
-  for (std::set<std::string>::iterator PI = Paths.begin(), PE = Paths.end();
-       PI != PE; ++PI) {
-    if (TheArchive->addFileBefore(*PI, TheArchive->end(), ErrMsg))
-      return true;
-  }
+bool NewArchiveIterator::isNewMember() const { return IsNewMember; }
 
-  // We're done editting, reconstruct the archive.
-  if (TheArchive->writeToDisk(ErrMsg))
-    return true;
-  return false;
+object::Archive::child_iterator NewArchiveIterator::getOld() const {
+  assert(!IsNewMember);
+  return OldI;
 }
 
-// doReplaceOrInsert - Implements the 'r' operation. This function will replace
-// any existing files or insert new ones into the archive.
-static bool doReplaceOrInsert(std::string *ErrMsg) {
+StringRef NewArchiveIterator::getNew() const {
+  assert(IsNewMember);
+  return *NewI;
+}
 
-  // Build the list of files to be added/replaced.
-  if (buildPaths(true, ErrMsg))
-    return true;
-  if (Paths.empty())
-    return false;
+template <typename T>
+void addMember(std::vector<NewArchiveIterator> &Members,
+               std::string &StringTable, T I, StringRef Name) {
+  if (Name.size() < 15) {
+    NewArchiveIterator NI(I, Twine(Name) + "/");
+    Members.push_back(NI);
+  } else {
+    int MapIndex = StringTable.size();
+    NewArchiveIterator NI(I, Twine("/") + Twine(MapIndex));
+    Members.push_back(NI);
+    StringTable += Name;
+    StringTable += "/\n";
+  }
+}
 
-  // Keep track of the paths that remain to be inserted.
-  std::set<std::string> remaining(Paths);
+namespace {
+class HasName {
+  StringRef Name;
 
-  // Default the insertion spot to the end of the archive
-  Archive::iterator insert_spot = TheArchive->end();
+public:
+  HasName(StringRef Name) : Name(Name) {}
+  bool operator()(StringRef Path) { return Name == sys::path::filename(Path); }
+};
+}
 
-  // Iterate over the archive contents
-  for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end();
-       I != E && !remaining.empty(); ++I ) {
-
-    // Determine if this archive member matches one of the paths we're trying
-    // to replace.
-
-    std::set<std::string>::iterator found = remaining.end();
-    for (std::set<std::string>::iterator RI = remaining.begin(),
-         RE = remaining.end(); RI != RE; ++RI ) {
-      std::string compare(sys::path::filename(*RI));
-      if (compare == I->getPath().str()) {
-        found = RI;
-        break;
+// We have to walk this twice and computing it is not trivial, so creating an
+// explicit std::vector is actually fairly efficient.
+static std::vector<NewArchiveIterator>
+computeNewArchiveMembers(ArchiveOperation Operation,
+                         object::Archive *OldArchive,
+                         std::string &StringTable) {
+  std::vector<NewArchiveIterator> Ret;
+  std::vector<NewArchiveIterator> Moved;
+  int InsertPos = -1;
+  StringRef PosName = sys::path::filename(RelPos);
+  if (OldArchive) {
+    int Pos = 0;
+    for (object::Archive::child_iterator I = OldArchive->begin_children(),
+                                         E = OldArchive->end_children();
+         I != E; ++I, ++Pos) {
+      StringRef Name;
+      failIfError(I->getName(Name));
+      if (Name == PosName) {
+        assert(AddAfter || AddBefore);
+        if (AddBefore)
+          InsertPos = Pos;
+        else
+          InsertPos = Pos + 1;
+      }
+      if (Operation != QuickAppend && !Members.empty()) {
+        std::vector<std::string>::iterator MI =
+            std::find_if(Members.begin(), Members.end(), HasName(Name));
+        if (MI != Members.end()) {
+          if (Operation == Move) {
+            addMember(Moved, StringTable, I, Name);
+            continue;
+          }
+          if (Operation != ReplaceOrInsert || !OnlyUpdate)
+            continue;
+          // Ignore if the file if it is older than the member.
+          sys::fs::file_status Status;
+          failIfError(sys::fs::status(*MI, Status));
+          if (Status.getLastModificationTime() < I->getLastModified())
+            Members.erase(MI);
+          else
+            continue;
+        }
       }
+      addMember(Ret, StringTable, I, Name);
     }
+  }
 
-    if (found != remaining.end()) {
+  if (Operation == Delete)
+    return Ret;
+
+  if (Operation == Move) {
+    if (RelPos.empty()) {
+      Ret.insert(Ret.end(), Moved.begin(), Moved.end());
+      return Ret;
+    }
+    if (InsertPos == -1)
+      fail("Insertion point not found");
+    assert(unsigned(InsertPos) <= Ret.size());
+    Ret.insert(Ret.begin() + InsertPos, Moved.begin(), Moved.end());
+    return Ret;
+  }
+
+  for (std::vector<std::string>::iterator I = Members.begin(),
+                                          E = Members.end();
+       I != E; ++I) {
+    StringRef Name = sys::path::filename(*I);
+    addMember(Ret, StringTable, I, Name);
+  }
+
+  return Ret;
+}
+
+template <typename T>
+static void printWithSpacePadding(raw_ostream &OS, T Data, unsigned Size) {
+  uint64_t OldPos = OS.tell();
+  OS << Data;
+  unsigned SizeSoFar = OS.tell() - OldPos;
+  assert(Size >= SizeSoFar && "Data doesn't fit in Size");
+  unsigned Remaining = Size - SizeSoFar;
+  for (unsigned I = 0; I < Remaining; ++I)
+    OS << ' ';
+}
+
+static void performWriteOperation(ArchiveOperation Operation,
+                                  object::Archive *OldArchive) {
+  int TmpArchiveFD;
+  SmallString<128> TmpArchive;
+  failIfError(sys::fs::createUniqueFile(ArchiveName + ".temp-archive-%%%%%%%.a",
+                                        TmpArchiveFD, TmpArchive));
+
+  TemporaryOutput = TmpArchive.c_str();
+  tool_output_file Output(TemporaryOutput, TmpArchiveFD);
+  raw_fd_ostream &Out = Output.os();
+  Out << "!<arch>\n";
+
+  std::string StringTable;
+  std::vector<NewArchiveIterator> NewMembers =
+      computeNewArchiveMembers(Operation, OldArchive, StringTable);
+  if (!StringTable.empty()) {
+    if (StringTable.size() % 2)
+      StringTable += '\n';
+    printWithSpacePadding(Out, "//", 48);
+    printWithSpacePadding(Out, StringTable.size(), 10);
+    Out << "`\n";
+    Out << StringTable;
+  }
+
+  for (std::vector<NewArchiveIterator>::iterator I = NewMembers.begin(),
+                                                 E = NewMembers.end();
+       I != E; ++I) {
+    StringRef Name = I->getMemberName();
+    printWithSpacePadding(Out, Name, 16);
+
+    if (I->isNewMember()) {
+      // FIXME: we do a stat + open. We should do a open + fstat.
+      StringRef FileName = I->getNew();
       sys::fs::file_status Status;
-      error_code EC = sys::fs::status(*found, Status);
-      if (EC)
-        return true;
-      if (!sys::fs::is_directory(Status)) {
-        if (OnlyUpdate) {
-          // Replace the item only if it is newer.
-          if (Status.getLastModificationTime() > I->getModTime())
-            if (I->replaceWith(*found, ErrMsg))
-              return true;
-        } else {
-          // Replace the item regardless of time stamp
-          if (I->replaceWith(*found, ErrMsg))
-            return true;
-        }
-      } else {
-        // We purposefully ignore directories.
-      }
+      failIfError(sys::fs::status(FileName, Status), FileName);
 
-      // Remove it from our "to do" list
-      remaining.erase(found);
-    }
+      uint64_t secondsSinceEpoch =
+          Status.getLastModificationTime().toEpochTime();
+      printWithSpacePadding(Out, secondsSinceEpoch, 12);
+
+      printWithSpacePadding(Out, Status.getUser(), 6);
+      printWithSpacePadding(Out, Status.getGroup(), 6);
+      printWithSpacePadding(Out, format("%o", Status.permissions()), 8);
+      printWithSpacePadding(Out, Status.getSize(), 10);
+      Out << "`\n";
+
+      OwningPtr<MemoryBuffer> File;
+      failIfError(MemoryBuffer::getFile(FileName, File), FileName);
+      Out << File->getBuffer();
+    } else {
+      object::Archive::child_iterator OldMember = I->getOld();
 
-    // Determine if this is the place where we should insert
-    if (AddBefore && RelPos == I->getPath().str())
-      insert_spot = I;
-    else if (AddAfter && RelPos == I->getPath().str()) {
-      insert_spot = I;
-      insert_spot++;
-    }
-  }
+      uint64_t secondsSinceEpoch = OldMember->getLastModified().toEpochTime();
+      printWithSpacePadding(Out, secondsSinceEpoch, 12);
 
-  // If we didn't replace all the members, some will remain and need to be
-  // inserted at the previously computed insert-spot.
-  if (!remaining.empty()) {
-    for (std::set<std::string>::iterator PI = remaining.begin(),
-         PE = remaining.end(); PI != PE; ++PI) {
-      if (TheArchive->addFileBefore(*PI, insert_spot, ErrMsg))
-        return true;
+      printWithSpacePadding(Out, OldMember->getUID(), 6);
+      printWithSpacePadding(Out, OldMember->getGID(), 6);
+      printWithSpacePadding(Out, format("%o", OldMember->getAccessMode()), 8);
+      printWithSpacePadding(Out, OldMember->getSize(), 10);
+      Out << "`\n";
+
+      Out << OldMember->getBuffer();
     }
-  }
 
-  // We're done editting, reconstruct the archive.
-  if (TheArchive->writeToDisk(ErrMsg))
-    return true;
-  return false;
+    if (Out.tell() % 2)
+      Out << '\n';
+  }
+  Output.keep();
+  Out.close();
+  sys::fs::rename(TemporaryOutput, ArchiveName);
+  TemporaryOutput = NULL;
 }
 
-static bool shouldCreateArchive(ArchiveOperation Op) {
-  switch (Op) {
+static void performOperation(ArchiveOperation Operation,
+                             object::Archive *OldArchive) {
+  switch (Operation) {
   case Print:
-  case Delete:
-  case Move:
   case DisplayTable:
   case Extract:
-    return false;
+    performReadOperation(Operation, OldArchive);
+    return;
 
+  case Delete:
+  case Move:
   case QuickAppend:
   case ReplaceOrInsert:
-    return true;
+    performWriteOperation(Operation, OldArchive);
+    return;
   }
-
-  llvm_unreachable("Missing entry in covered switch.");
+  llvm_unreachable("Unknown operation.");
 }
 
 // main - main program for llvm-ar .. see comments in the code
 int main(int argc, char **argv) {
-  program_name = argv[0];
+  ToolName = argv[0];
   // Print a stack trace if we signal out.
   sys::PrintStackTraceOnErrorSignal();
   PrettyStackTraceProgram X(argc, argv);
-  LLVMContext &Context = getGlobalContext();
   llvm_shutdown_obj Y;  // Call llvm_shutdown() on exit.
 
   // Have the command line options parsed and handle things
@@ -603,54 +633,42 @@ int main(int argc, char **argv) {
     "  This program archives bitcode files into single libraries\n"
   );
 
-  int exitCode = 0;
-
   // Do our own parsing of the command line because the CommandLine utility
   // can't handle the grouped positional parameters without a dash.
   ArchiveOperation Operation = parseCommandLine();
 
   // Create or open the archive object.
-  if (shouldCreateArchive(Operation) && !llvm::sys::fs::exists(ArchiveName)) {
-    // Produce a warning if we should and we're creating the archive
-    if (!Create)
-      errs() << argv[0] << ": creating " << ArchiveName << "\n";
-    TheArchive = Archive::CreateEmpty(ArchiveName, Context);
-    TheArchive->writeToDisk();
+  OwningPtr<MemoryBuffer> Buf;
+  error_code EC = MemoryBuffer::getFile(ArchiveName, Buf, -1, false);
+  if (EC && EC != llvm::errc::no_such_file_or_directory) {
+    errs() << argv[0] << ": error opening '" << ArchiveName
+           << "': " << EC.message() << "!\n";
+    return 1;
   }
 
-  if (!TheArchive) {
-    std::string Error;
-    TheArchive = Archive::OpenAndLoad(ArchiveName, Context, &Error);
-    if (TheArchive == 0) {
-      errs() << argv[0] << ": error loading '" << ArchiveName << "': "
-             << Error << "!\n";
+  if (!EC) {
+    object::Archive Archive(Buf.take(), EC);
+
+    if (EC) {
+      errs() << argv[0] << ": error loading '" << ArchiveName
+             << "': " << EC.message() << "!\n";
       return 1;
     }
+    performOperation(Operation, &Archive);
+    return 0;
   }
 
-  // Make sure we're not fooling ourselves.
-  assert(TheArchive && "Unable to instantiate the archive");
+  assert(EC == llvm::errc::no_such_file_or_directory);
 
-  // Perform the operation
-  std::string ErrMsg;
-  bool haveError = false;
-  switch (Operation) {
-    case Print:           haveError = doPrint(&ErrMsg); break;
-    case Delete:          haveError = doDelete(&ErrMsg); break;
-    case Move:            haveError = doMove(&ErrMsg); break;
-    case QuickAppend:     haveError = doQuickAppend(&ErrMsg); break;
-    case ReplaceOrInsert: haveError = doReplaceOrInsert(&ErrMsg); break;
-    case DisplayTable:    haveError = doDisplayTable(&ErrMsg); break;
-    case Extract:         haveError = doExtract(&ErrMsg); break;
-  }
-  if (haveError) {
-    errs() << argv[0] << ": " << ErrMsg << "\n";
-    return 1;
+  if (!shouldCreateArchive(Operation)) {
+    failIfError(EC, Twine("error loading '") + ArchiveName + "'");
+  } else {
+    if (!Create) {
+      // Produce a warning if we should and we're creating the archive
+      errs() << argv[0] << ": creating " << ArchiveName << "\n";
+    }
   }
 
-  delete TheArchive;
-  TheArchive = 0;
-
-  // Return result code back to operating system.
-  return exitCode;
+  performOperation(Operation, NULL);
+  return 0;
 }





More information about the llvm-commits mailing list