diff options
author | dim <dim@FreeBSD.org> | 2014-03-21 17:53:59 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2014-03-21 17:53:59 +0000 |
commit | 9cedb8bb69b89b0f0c529937247a6a80cabdbaec (patch) | |
tree | c978f0e9ec1ab92dc8123783f30b08a7fd1e2a39 /contrib/llvm/tools/llvm-ar | |
parent | 03fdc2934eb61c44c049a02b02aa974cfdd8a0eb (diff) | |
download | FreeBSD-src-9cedb8bb69b89b0f0c529937247a6a80cabdbaec.zip FreeBSD-src-9cedb8bb69b89b0f0c529937247a6a80cabdbaec.tar.gz |
MFC 261991:
Upgrade our copy of llvm/clang to 3.4 release. This version supports
all of the features in the current working draft of the upcoming C++
standard, provisionally named C++1y.
The code generator's performance is greatly increased, and the loop
auto-vectorizer is now enabled at -Os and -O2 in addition to -O3. The
PowerPC backend has made several major improvements to code generation
quality and compile time, and the X86, SPARC, ARM32, Aarch64 and SystemZ
backends have all seen major feature work.
Release notes for llvm and clang can be found here:
<http://llvm.org/releases/3.4/docs/ReleaseNotes.html>
<http://llvm.org/releases/3.4/tools/clang/docs/ReleaseNotes.html>
MFC 262121 (by emaste):
Update lldb for clang/llvm 3.4 import
This commit largely restores the lldb source to the upstream r196259
snapshot with the addition of threaded inferior support and a few bug
fixes.
Specific upstream lldb revisions restored include:
SVN git
181387 779e6ac
181703 7bef4e2
182099 b31044e
182650 f2dcf35
182683 0d91b80
183862 15c1774
183929 99447a6
184177 0b2934b
184948 4dc3761
184954 007e7bc
186990 eebd175
Sponsored by: DARPA, AFRL
MFC 262186 (by emaste):
Fix mismerge in r262121
A break statement was lost in the merge. The error had no functional
impact, but restore it to reduce the diff against upstream.
MFC 262303:
Pull in r197521 from upstream clang trunk (by rdivacky):
Use the integrated assembler by default on FreeBSD/ppc and ppc64.
Requested by: jhibbits
MFC 262611:
Pull in r196874 from upstream llvm trunk:
Fix a crash that occurs when PWD is invalid.
MCJIT needs to be able to run in hostile environments, even when PWD
is invalid. There's no need to crash MCJIT in this case.
The obvious fix is to simply leave MCContext's CompilationDir empty
when PWD can't be determined. This way, MCJIT clients,
and other clients that link with LLVM don't need a valid working directory.
If we do want to guarantee valid CompilationDir, that should be done
only for clients of getCompilationDir(). This is as simple as checking
for an empty string.
The only current use of getCompilationDir is EmitGenDwarfInfo, which
won't conceivably run with an invalid working dir. However, in the
purely hypothetically and untestable case that this happens, the
AT_comp_dir will be omitted from the compilation_unit DIE.
This should help fix assertions occurring with ports-mgmt/tinderbox,
when it is using jails, and sometimes invalidates clang's current
working directory.
Reported by: decke
MFC 262809:
Pull in r203007 from upstream clang trunk:
Don't produce an alias between destructors with different calling conventions.
Fixes pr19007.
(Please note that is an LLVM PR identifier, not a FreeBSD one.)
This should fix Firefox and/or libxul crashes (due to problems with
regparm/stdcall calling conventions) on i386.
Reported by: multiple users on freebsd-current
PR: bin/187103
MFC 263048:
Repair recognition of "CC" as an alias for the C++ compiler, since it
was silently broken by upstream for a Windows-specific use-case.
Apparently some versions of CMake still rely on this archaic feature...
Reported by: rakuco
MFC 263049:
Garbage collect the old way of adding the libstdc++ include directories
in clang's InitHeaderSearch.cpp. This has been superseded by David
Chisnall's commit in r255321.
Moreover, if libc++ is used, the libstdc++ include directories should
not be in the search path at all. These directories are now only used
if you pass -stdlib=libstdc++.
Diffstat (limited to 'contrib/llvm/tools/llvm-ar')
-rw-r--r-- | contrib/llvm/tools/llvm-ar/llvm-ar.cpp | 1146 |
1 files changed, 656 insertions, 490 deletions
diff --git a/contrib/llvm/tools/llvm-ar/llvm-ar.cpp b/contrib/llvm/tools/llvm-ar/llvm-ar.cpp index 86eb8e2..d70db72 100644 --- a/contrib/llvm/tools/llvm-ar/llvm-ar.cpp +++ b/contrib/llvm/tools/llvm-ar/llvm-ar.cpp @@ -13,35 +13,63 @@ //===----------------------------------------------------------------------===// #include "llvm/IR/LLVMContext.h" -#include "llvm/Bitcode/Archive.h" #include "llvm/IR/Module.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ObjectFile.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> -#include <fstream> #include <memory> + +#if !defined(_MSC_VER) && !defined(__MINGW32__) +#include <unistd.h> +#else +#include <io.h> +#endif + using namespace llvm; -// Option for compatibility with AIX, not used but must allow it to be present. -static cl::opt<bool> -X32Option ("X32_64", cl::Hidden, - cl::desc("Ignored option for compatibility with AIX")); +// The name this program was invoked as. +static StringRef ToolName; + +static const char *TemporaryOutput; +static int TmpArchiveFD = -1; + +// fail - Show the error message and exit. +LLVM_ATTRIBUTE_NORETURN static void fail(Twine Error) { + outs() << ToolName << ": " << Error << ".\n"; + if (TmpArchiveFD != -1) + close(TmpArchiveFD); + if (TemporaryOutput) + sys::fs::remove(TemporaryOutput); + exit(1); +} + +static void failIfError(error_code EC, Twine Context = "") { + if (!EC) + return; -// llvm-ar operation code and modifier flags. This must come first. -static cl::opt<std::string> -Options(cl::Positional, cl::Required, cl::desc("{operation}[modifiers]...")); + std::string ContextStr = Context.str(); + if (ContextStr == "") + fail(EC.message()); + fail(Context + ": " + EC.message()); +} -// llvm-ar remaining positional arguments. +// llvm-ar/llvm-ranlib remaining positional arguments. static cl::list<std::string> RestOfArgs(cl::Positional, cl::OneOrMore, cl::desc("[relpos] [count] <archive-file> [members]...")); +std::string Options; + // MoreHelp - Provide additional help output explaining the operations and // modifiers of llvm-ar. This object instructs the CommandLine library // to print the text of the constructor when the --help option is given. @@ -57,123 +85,79 @@ static cl::extrahelp MoreHelp( "\nMODIFIERS (operation specific):\n" " [a] - put file(s) after [relpos]\n" " [b] - put file(s) before [relpos] (same as [i])\n" - " [f] - truncate inserted file names\n" " [i] - put file(s) before [relpos] (same as [b])\n" - " [k] - always print bitcode files (default is to skip them)\n" " [N] - use instance [count] of name\n" " [o] - preserve original dates\n" - " [P] - use full path names when matching\n" - " [R] - recurse through directories when inserting\n" " [s] - create an archive index (cf. ranlib)\n" " [S] - do not build a symbol table\n" " [u] - update only files newer than archive contents\n" "\nMODIFIERS (generic):\n" " [c] - do not warn if the library had to be created\n" " [v] - be verbose about actions taken\n" - " [V] - be *really* verbose about actions taken\n" ); // This enumeration delineates the kinds of operations on an archive // that are permitted. enum ArchiveOperation { - NoOperation, ///< An operation hasn't been specified Print, ///< Print the contents of the archive Delete, ///< Delete the specified members Move, ///< Move members to end or as given by {a,b,i} modifiers QuickAppend, ///< Quickly append to end of archive ReplaceOrInsert, ///< Replace or Insert members DisplayTable, ///< Display the table of contents - Extract ///< Extract files back to file system + Extract, ///< Extract files back to file system + CreateSymTab ///< Create a symbol table in an existing archive }; // Modifiers to follow operation to vary behavior -bool AddAfter = false; ///< 'a' modifier -bool AddBefore = false; ///< 'b' modifier -bool Create = false; ///< 'c' modifier -bool TruncateNames = false; ///< 'f' modifier -bool InsertBefore = false; ///< 'i' modifier -bool DontSkipBitcode = false; ///< 'k' modifier -bool UseCount = false; ///< 'N' modifier -bool OriginalDates = false; ///< 'o' modifier -bool FullPath = false; ///< 'P' modifier -bool RecurseDirectories = false; ///< 'R' modifier -bool SymTable = true; ///< 's' & 'S' modifiers -bool OnlyUpdate = false; ///< 'u' modifier -bool Verbose = false; ///< 'v' modifier -bool ReallyVerbose = false; ///< 'V' modifier +static bool AddAfter = false; ///< 'a' modifier +static bool AddBefore = false; ///< 'b' modifier +static bool Create = false; ///< 'c' modifier +static bool OriginalDates = false; ///< 'o' modifier +static bool OnlyUpdate = false; ///< 'u' modifier +static bool Verbose = false; ///< 'v' modifier +static bool Symtab = true; ///< 's' modifier // Relative Positional Argument (for insert/move). This variable holds // the name of the archive member to which the 'a', 'b' or 'i' modifier // refers. Only one of 'a', 'b' or 'i' can be specified so we only need // one variable. -std::string RelPos; - -// Select which of multiple entries in the archive with the same name should be -// used (specified with -N) for the delete and extract operations. -int Count = 1; +static std::string RelPos; // This variable holds the name of the archive file as given on the // command line. -std::string ArchiveName; +static std::string ArchiveName; // This variable holds the list of member files to proecess, as given // on the command line. -std::vector<std::string> Members; - -// This variable holds the (possibly expanded) list of path objects that -// correspond to files we will -std::set<sys::Path> Paths; - -// The Archive object to which all the editing operations will be sent. -Archive* TheArchive = 0; - -// The name this program was invoked as. -static const char *program_name; +static std::vector<std::string> Members; // 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); } // getRelPos - Extract the member filename from the command line for // the [relpos] argument associated with a, b, and i modifiers -void getRelPos() { +static void getRelPos() { if(RestOfArgs.size() == 0) show_help("Expected [relpos] for a, b, or i modifier"); RelPos = RestOfArgs[0]; RestOfArgs.erase(RestOfArgs.begin()); } -// getCount - Extract the [count] argument associated with the N modifier -// from the command line and check its value. -void getCount() { +static void getOptions() { if(RestOfArgs.size() == 0) - show_help("Expected [count] value with N modifier"); - - Count = atoi(RestOfArgs[0].c_str()); + show_help("Expected options"); + Options = RestOfArgs[0]; RestOfArgs.erase(RestOfArgs.begin()); - - // Non-positive counts are not allowed - if (Count < 1) - show_help("Invalid [count] value (not a positive integer)"); } // getArchive - Get the archive file name from the command line -void getArchive() { +static void getArchive() { if(RestOfArgs.size() == 0) show_help("An archive name must be specified"); ArchiveName = RestOfArgs[0]; @@ -182,7 +166,7 @@ void getArchive() { // getMembers - Copy over remaining items in RestOfArgs to our Members vector // This is just for clarity. -void getMembers() { +static void getMembers() { if(RestOfArgs.size() > 0) Members = std::vector<std::string>(RestOfArgs); } @@ -190,7 +174,8 @@ void getMembers() { // parseCommandLine - Parse the command line options as presented and return the // operation specified. Process all modifiers and check to make sure that // constraints on modifier/operation pairs have not been violated. -ArchiveOperation parseCommandLine() { +static ArchiveOperation parseCommandLine() { + getOptions(); // Keep track of number of operations. We can only specify one // per execution. @@ -201,7 +186,9 @@ ArchiveOperation parseCommandLine() { unsigned NumPositional = 0; // Keep track of which operation was requested - ArchiveOperation Operation = NoOperation; + ArchiveOperation Operation; + + bool MaybeJustCreateSymTab = false; for(unsigned i=0; i<Options.size(); ++i) { switch(Options[i]) { @@ -213,17 +200,17 @@ ArchiveOperation parseCommandLine() { case 't': ++NumOperations; Operation = DisplayTable; break; case 'x': ++NumOperations; Operation = Extract; break; case 'c': Create = true; break; - case 'f': TruncateNames = true; break; - case 'k': DontSkipBitcode = true; break; case 'l': /* accepted but unused */ break; case 'o': OriginalDates = true; break; - case 'P': FullPath = true; break; - case 'R': RecurseDirectories = true; break; - case 's': SymTable = true; break; - case 'S': SymTable = false; break; + case 's': + Symtab = true; + MaybeJustCreateSymTab = true; + break; + case 'S': + Symtab = false; + break; case 'u': OnlyUpdate = true; break; case 'v': Verbose = true; break; - case 'V': Verbose = ReallyVerbose = true; break; case 'a': getRelPos(); AddAfter = true; @@ -236,13 +223,9 @@ ArchiveOperation parseCommandLine() { break; case 'i': getRelPos(); - InsertBefore = true; + AddBefore = true; NumPositional++; break; - case 'N': - getCount(); - UseCount = true; - break; default: cl::PrintHelpMessage(); } @@ -255,6 +238,13 @@ ArchiveOperation parseCommandLine() { // Everything on the command line at this point is a member. getMembers(); + if (NumOperations == 0 && MaybeJustCreateSymTab) { + NumOperations = 1; + Operation = CreateSymTab; + if (!Members.empty()) + show_help("The s operation takes only an archive as argument"); + } + // Perform various checks on the operation/modifier specification // to make sure we are dealing with a legal request. if (NumOperations == 0) @@ -263,140 +253,33 @@ ArchiveOperation parseCommandLine() { show_help("Only one operation may be specified"); if (NumPositional > 1) show_help("You may only specify one of a, b, and i modifiers"); - if (AddAfter || AddBefore || InsertBefore) { + if (AddAfter || AddBefore) { if (Operation != Move && Operation != ReplaceOrInsert) show_help("The 'a', 'b' and 'i' modifiers can only be specified with " "the 'm' or 'r' operations"); } - if (RecurseDirectories && Operation != ReplaceOrInsert) - show_help("The 'R' modifiers is only applicabe to the 'r' operation"); if (OriginalDates && Operation != Extract) show_help("The 'o' modifier is only applicable to the 'x' operation"); - if (TruncateNames && Operation!=QuickAppend && Operation!=ReplaceOrInsert) - show_help("The 'f' modifier is only applicable to the 'q' and 'r' " - "operations"); if (OnlyUpdate && Operation != ReplaceOrInsert) show_help("The 'u' modifier is only applicable to the 'r' operation"); - if (Count > 1 && Members.size() > 1) - show_help("Only one member name may be specified with the 'N' modifier"); // Return the parsed operation to the caller return Operation; } -// recurseDirectories - Implements the "R" modifier. This function scans through -// the Paths vector (built by buildPaths, below) and replaces any directories it -// finds with all the files in that directory (recursively). It uses the -// sys::Path::getDirectoryContent method to perform the actual directory scans. -bool -recurseDirectories(const sys::Path& path, - std::set<sys::Path>& result, std::string* ErrMsg) { - result.clear(); - if (RecurseDirectories) { - std::set<sys::Path> content; - if (path.getDirectoryContents(content, ErrMsg)) - return true; - - for (std::set<sys::Path>::iterator I = content.begin(), E = content.end(); - I != E; ++I) { - // Make sure it exists and is a directory - sys::PathWithStatus PwS(*I); - const sys::FileStatus *Status = PwS.getFileStatus(false, ErrMsg); - if (!Status) - return true; - if (Status->isDir) { - std::set<sys::Path> moreResults; - if (recurseDirectories(*I, moreResults, ErrMsg)) - return true; - result.insert(moreResults.begin(), moreResults.end()); - } else { - result.insert(*I); - } - } - } - return false; -} - -// 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') -bool buildPaths(bool checkExistence, std::string* ErrMsg) { - for (unsigned i = 0; i < Members.size(); i++) { - sys::Path aPath; - if (!aPath.set(Members[i])) - fail(std::string("File member name invalid: ") + Members[i]); - if (checkExistence) { - bool Exists; - if (sys::fs::exists(aPath.str(), Exists) || !Exists) - fail(std::string("File does not exist: ") + Members[i]); - std::string Err; - sys::PathWithStatus PwS(aPath); - const sys::FileStatus *si = PwS.getFileStatus(false, &Err); - if (!si) - fail(Err); - if (si->isDir) { - std::set<sys::Path> dirpaths; - if (recurseDirectories(aPath, dirpaths, ErrMsg)) - return true; - Paths.insert(dirpaths.begin(),dirpaths.end()); - } else { - Paths.insert(aPath); - } - } else { - Paths.insert(aPath); - } - } - return false; -} - -// printSymbolTable - print out the archive's symbol table. -void printSymbolTable() { - outs() << "\nArchive Symbol Table:\n"; - const Archive::SymTabType& symtab = TheArchive->getSymbolTable(); - for (Archive::SymTabType::const_iterator I=symtab.begin(), E=symtab.end(); - I != E; ++I ) { - unsigned offset = TheArchive->getFirstFileOffset() + I->second; - outs() << " " << format("%9u", offset) << "\t" << I->first <<"\n"; - } -} +// 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"; -// 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. -bool doPrint(std::string* ErrMsg) { - if (buildPaths(false, ErrMsg)) - return true; - unsigned countDown = Count; - 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 (countDown == 1) { - const char* data = reinterpret_cast<const char*>(I->getData()); - - // Skip things that don't make sense to print - if (I->isLLVMSymbolTable() || I->isSVR4SymbolTable() || - I->isBSD4SymbolTable() || (!DontSkipBitcode && I->isBitcode())) - continue; - - if (Verbose) - outs() << "Printing " << I->getPath().str() << "\n"; - - unsigned len = I->getSize(); - outs().write(data, len); - } else { - countDown--; - } - } - } - return false; + StringRef Data = I->getBuffer(); + outs().write(Data.data(), Data.size()); } // putMode - utility function for printing out the file mode when the 't' // operation is in verbose mode. -void -printMode(unsigned mode) { +static void printMode(unsigned mode) { if (mode & 004) outs() << "r"; else @@ -411,303 +294,586 @@ 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. -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) { - // FIXME: Output should be this format: - // Zrw-r--r-- 500/ 500 525 Nov 8 17:42 2004 Makefile - if (I->isBitcode()) - outs() << "b"; - else - outs() << " "; - unsigned mode = I->getMode(); - printMode((mode >> 6) & 007); - printMode((mode >> 3) & 007); - printMode(mode & 007); - outs() << " " << format("%4u", I->getUser()); - outs() << "/" << format("%4u", I->getGroup()); - outs() << " " << format("%8u", I->getSize()); - outs() << " " << format("%20s", I->getModTime().str().substr(4).c_str()); - outs() << " " << I->getPath().str() << "\n"; - } else { - outs() << I->getPath().str() << "\n"; - } - } +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() << ' '; } - if (ReallyVerbose) - printSymbolTable(); - return false; + outs() << Name << "\n"; } -// doExtract - Implement the 'x' operation. This function extracts files back to -// the file system. -bool -doExtract(std::string* ErrMsg) { - if (buildPaths(false, ErrMsg)) +// Implement the 'x' operation. This function extracts files back to the file +// system. +static void doExtract(StringRef Name, object::Archive::child_iterator I) { + // Retain the original mode. + sys::fs::perms Mode = I->getAccessMode(); + SmallString<128> Storage = Name; + + int FD; + failIfError( + sys::fs::openFileForWrite(Storage.c_str(), FD, sys::fs::F_Binary, Mode), + Storage.c_str()); + + { + raw_fd_ostream file(FD, false); + + // Get the data and its length + StringRef Data = I->getBuffer(); + + // Write the data. + file.write(Data.data(), Data.size()); + } + + // 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"); +} + +static bool shouldCreateArchive(ArchiveOperation Op) { + switch (Op) { + case Print: + case Delete: + case Move: + case DisplayTable: + case Extract: + case CreateSymTab: + return false; + + case QuickAppend: + case ReplaceOrInsert: 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())) { - - // Make sure the intervening directories are created - if (I->hasPath()) { - sys::Path dirs(I->getPath()); - dirs.eraseComponent(); - if (dirs.createDirectoryOnDisk(/*create_parents=*/true, ErrMsg)) - return true; - } + } - // Open up a file stream for writing - std::ios::openmode io_mode = std::ios::out | std::ios::trunc | - std::ios::binary; - std::ofstream file(I->getPath().c_str(), io_mode); + llvm_unreachable("Missing entry in covered switch."); +} - // Get the data and its length - const char* data = reinterpret_cast<const char*>(I->getData()); - unsigned len = I->getSize(); +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)); - // Write the data. - file.write(data,len); - file.close(); + if (!Members.empty() && + std::find(Members.begin(), Members.end(), Name) == Members.end()) + continue; - // If we're supposed to retain the original modification times, etc. do so - // now. - if (OriginalDates) - I->getPath().setStatusInfoOnDisk(I->getFileStatus()); + 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; } } - return false; } -// 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). -bool -doDelete(std::string* ErrMsg) { - if (buildPaths(false, ErrMsg)) - return true; - if (Paths.empty()) - return false; - unsigned countDown = Count; - for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); - I != E; ) { - if (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end()) { - if (countDown == 1) { - Archive::iterator J = I; - ++I; - TheArchive->erase(J); - } else - countDown--; - } else { - ++I; +namespace { +class NewArchiveIterator { + bool IsNewMember; + StringRef Name; + object::Archive::child_iterator OldI; + std::string NewFilename; + +public: + NewArchiveIterator(object::Archive::child_iterator I, StringRef Name); + NewArchiveIterator(std::string *I, StringRef Name); + NewArchiveIterator(); + bool isNewMember() const; + object::Archive::child_iterator getOld() const; + const char *getNew() const; + StringRef getName() const; +}; +} + +NewArchiveIterator::NewArchiveIterator() {} + +NewArchiveIterator::NewArchiveIterator(object::Archive::child_iterator I, + StringRef Name) + : IsNewMember(false), Name(Name), OldI(I) {} + +NewArchiveIterator::NewArchiveIterator(std::string *NewFilename, StringRef Name) + : IsNewMember(true), Name(Name), NewFilename(*NewFilename) {} + +StringRef NewArchiveIterator::getName() const { return Name; } + +bool NewArchiveIterator::isNewMember() const { return IsNewMember; } + +object::Archive::child_iterator NewArchiveIterator::getOld() const { + assert(!IsNewMember); + return OldI; +} + +const char *NewArchiveIterator::getNew() const { + assert(IsNewMember); + return NewFilename.c_str(); +} + +template <typename T> +void addMember(std::vector<NewArchiveIterator> &Members, T I, StringRef Name, + int Pos = -1) { + NewArchiveIterator NI(I, Name); + if (Pos == -1) + Members.push_back(NI); + else + Members[Pos] = NI; +} + +namespace { +class HasName { + StringRef Name; + +public: + HasName(StringRef Name) : Name(Name) {} + bool operator()(StringRef Path) { return Name == sys::path::filename(Path); } +}; +} + +enum InsertAction { + IA_AddOldMember, + IA_AddNewMeber, + IA_Delete, + IA_MoveOldMember, + IA_MoveNewMember +}; + +static InsertAction +computeInsertAction(ArchiveOperation Operation, + object::Archive::child_iterator I, StringRef Name, + std::vector<std::string>::iterator &Pos) { + if (Operation == QuickAppend || Members.empty()) + return IA_AddOldMember; + + std::vector<std::string>::iterator MI = + std::find_if(Members.begin(), Members.end(), HasName(Name)); + + if (MI == Members.end()) + return IA_AddOldMember; + + Pos = MI; + + if (Operation == Delete) + return IA_Delete; + + if (Operation == Move) + return IA_MoveOldMember; + + if (Operation == ReplaceOrInsert) { + StringRef PosName = sys::path::filename(RelPos); + if (!OnlyUpdate) { + if (PosName.empty()) + return IA_AddNewMeber; + return IA_MoveNewMember; } + + // We could try to optimize this to a fstat, but it is not a common + // operation. + sys::fs::file_status Status; + failIfError(sys::fs::status(*MI, Status)); + if (Status.getLastModificationTime() < I->getLastModified()) { + if (PosName.empty()) + return IA_AddOldMember; + return IA_MoveOldMember; + } + + if (PosName.empty()) + return IA_AddNewMeber; + return IA_MoveNewMember; } + llvm_unreachable("No such operation"); +} - // We're done editting, reconstruct the archive. - if (TheArchive->writeToDisk(SymTable,TruncateNames,ErrMsg)) - return true; - if (ReallyVerbose) - printSymbolTable(); - return false; -} - -// 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. -bool -doMove(std::string* ErrMsg) { - if (buildPaths(false, ErrMsg)) - return true; +// 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::vector<NewArchiveIterator> Ret; + std::vector<NewArchiveIterator> Moved; + int InsertPos = -1; + StringRef PosName = sys::path::filename(RelPos); + if (OldArchive) { + for (object::Archive::child_iterator I = OldArchive->begin_children(), + E = OldArchive->end_children(); + I != E; ++I) { + int Pos = Ret.size(); + StringRef Name; + failIfError(I->getName(Name)); + if (Name == PosName) { + assert(AddAfter || AddBefore); + if (AddBefore) + InsertPos = Pos; + else + InsertPos = Pos + 1; + } - // 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 || InsertBefore || 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; - } + std::vector<std::string>::iterator MemberI = Members.end(); + InsertAction Action = computeInsertAction(Operation, I, Name, MemberI); + switch (Action) { + case IA_AddOldMember: + addMember(Ret, I, Name); + break; + case IA_AddNewMeber: + addMember(Ret, &*MemberI, Name); + break; + case IA_Delete: + break; + case IA_MoveOldMember: + addMember(Moved, I, Name); + break; + case IA_MoveNewMember: + addMember(Moved, &*MemberI, Name); break; } + if (MemberI != Members.end()) + Members.erase(MemberI); } } - // Keep a list of the paths remaining to be moved - std::set<sys::Path> 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<sys::Path>::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); - } + if (Operation == Delete) + return Ret; + + if (!RelPos.empty() && InsertPos == -1) + fail("Insertion point not found"); + + if (RelPos.empty()) + InsertPos = Ret.size(); + + assert(unsigned(InsertPos) <= Ret.size()); + Ret.insert(Ret.begin() + InsertPos, Moved.begin(), Moved.end()); + + Ret.insert(Ret.begin() + InsertPos, Members.size(), NewArchiveIterator()); + int Pos = InsertPos; + for (std::vector<std::string>::iterator I = Members.begin(), + E = Members.end(); + I != E; ++I, ++Pos) { + StringRef Name = sys::path::filename(*I); + addMember(Ret, &*I, Name, Pos); } - // We're done editting, reconstruct the archive. - if (TheArchive->writeToDisk(SymTable,TruncateNames,ErrMsg)) - return true; - if (ReallyVerbose) - printSymbolTable(); - return false; + return Ret; } -// doQuickAppend - Implements the 'q' operation. This function just -// indiscriminantly adds the members to the archive and rebuilds it. -bool -doQuickAppend(std::string* ErrMsg) { - // Get the list of paths to append. - if (buildPaths(true, ErrMsg)) - return true; - if (Paths.empty()) - return false; +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 << ' '; +} - // Append them quickly. - for (std::set<sys::Path>::iterator PI = Paths.begin(), PE = Paths.end(); - PI != PE; ++PI) { - if (TheArchive->addFileBefore(*PI,TheArchive->end(),ErrMsg)) - return true; +static void print32BE(raw_fd_ostream &Out, unsigned Val) { + for (int I = 3; I >= 0; --I) { + char V = (Val >> (8 * I)) & 0xff; + Out << V; } +} - // We're done editting, reconstruct the archive. - if (TheArchive->writeToDisk(SymTable,TruncateNames,ErrMsg)) - return true; - if (ReallyVerbose) - printSymbolTable(); - return false; +static void printRestOfMemberHeader(raw_fd_ostream &Out, + const sys::TimeValue &ModTime, unsigned UID, + unsigned GID, unsigned Perms, + unsigned Size) { + printWithSpacePadding(Out, ModTime.toEpochTime(), 12); + printWithSpacePadding(Out, UID, 6); + printWithSpacePadding(Out, GID, 6); + printWithSpacePadding(Out, format("%o", Perms), 8); + printWithSpacePadding(Out, Size, 10); + Out << "`\n"; } -// doReplaceOrInsert - Implements the 'r' operation. This function will replace -// any existing files or insert new ones into the archive. -bool -doReplaceOrInsert(std::string* ErrMsg) { +static void printMemberHeader(raw_fd_ostream &Out, StringRef Name, + const sys::TimeValue &ModTime, unsigned UID, + unsigned GID, unsigned Perms, unsigned Size) { + printWithSpacePadding(Out, Twine(Name) + "/", 16); + printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size); +} - // Build the list of files to be added/replaced. - if (buildPaths(true, ErrMsg)) - return true; - if (Paths.empty()) - return false; +static void printMemberHeader(raw_fd_ostream &Out, unsigned NameOffset, + const sys::TimeValue &ModTime, unsigned UID, + unsigned GID, unsigned Perms, unsigned Size) { + Out << '/'; + printWithSpacePadding(Out, NameOffset, 15); + printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size); +} - // Keep track of the paths that remain to be inserted. - std::set<sys::Path> remaining(Paths); - - // Default the insertion spot to the end of the archive - Archive::iterator insert_spot = TheArchive->end(); - - // 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<sys::Path>::iterator found = remaining.end(); - for (std::set<sys::Path>::iterator RI = remaining.begin(), - RE = remaining.end(); RI != RE; ++RI ) { - std::string compare(RI->str()); - if (TruncateNames && compare.length() > 15) { - const char* nm = compare.c_str(); - unsigned len = compare.length(); - size_t slashpos = compare.rfind('/'); - if (slashpos != std::string::npos) { - nm += slashpos + 1; - len -= slashpos +1; - } - if (len > 15) - len = 15; - compare.assign(nm,len); - } - if (compare == I->getPath().str()) { - found = RI; - break; - } +static void writeStringTable(raw_fd_ostream &Out, + ArrayRef<NewArchiveIterator> Members, + std::vector<unsigned> &StringMapIndexes) { + unsigned StartOffset = 0; + for (ArrayRef<NewArchiveIterator>::iterator I = Members.begin(), + E = Members.end(); + I != E; ++I) { + StringRef Name = I->getName(); + if (Name.size() < 16) + continue; + if (StartOffset == 0) { + printWithSpacePadding(Out, "//", 58); + Out << "`\n"; + StartOffset = Out.tell(); } + StringMapIndexes.push_back(Out.tell() - StartOffset); + Out << Name << "/\n"; + } + if (StartOffset == 0) + return; + if (Out.tell() % 2) + Out << '\n'; + int Pos = Out.tell(); + Out.seek(StartOffset - 12); + printWithSpacePadding(Out, Pos - StartOffset, 10); + Out.seek(Pos); +} - if (found != remaining.end()) { - std::string Err; - sys::PathWithStatus PwS(*found); - const sys::FileStatus *si = PwS.getFileStatus(false, &Err); - if (!si) - return true; - if (!si->isDir) { - if (OnlyUpdate) { - // Replace the item only if it is newer. - if (si->modTime > I->getModTime()) - if (I->replaceWith(*found, ErrMsg)) - return true; - } else { - // Replace the item regardless of time stamp - if (I->replaceWith(*found, ErrMsg)) - return true; - } +static void writeSymbolTable( + raw_fd_ostream &Out, ArrayRef<NewArchiveIterator> Members, + std::vector<std::pair<unsigned, unsigned> > &MemberOffsetRefs) { + unsigned StartOffset = 0; + unsigned MemberNum = 0; + std::vector<StringRef> SymNames; + std::vector<object::ObjectFile *> DeleteIt; + for (ArrayRef<NewArchiveIterator>::iterator I = Members.begin(), + E = Members.end(); + I != E; ++I, ++MemberNum) { + object::ObjectFile *Obj; + if (I->isNewMember()) { + const char *Filename = I->getNew(); + Obj = object::ObjectFile::createObjectFile(Filename); + } else { + object::Archive::child_iterator OldMember = I->getOld(); + OwningPtr<object::Binary> Binary; + error_code EC = OldMember->getAsBinary(Binary); + if (EC) { // FIXME: check only for "not an object file" errors. + Obj = NULL; } else { - // We purposefully ignore directories. + Obj = dyn_cast<object::ObjectFile>(Binary.get()); + if (Obj) + Binary.take(); } - - // Remove it from our "to do" list - remaining.erase(found); + } + if (!Obj) + continue; + DeleteIt.push_back(Obj); + if (!StartOffset) { + printMemberHeader(Out, "", sys::TimeValue::now(), 0, 0, 0, 0); + StartOffset = Out.tell(); + print32BE(Out, 0); } - // Determine if this is the place where we should insert - if ((AddBefore || InsertBefore) && RelPos == I->getPath().str()) - insert_spot = I; - else if (AddAfter && RelPos == I->getPath().str()) { - insert_spot = I; - insert_spot++; + error_code Err; + for (object::symbol_iterator I = Obj->begin_symbols(), + E = Obj->end_symbols(); + I != E; I.increment(Err), failIfError(Err)) { + uint32_t Symflags; + failIfError(I->getFlags(Symflags)); + if (Symflags & object::SymbolRef::SF_FormatSpecific) + continue; + if (!(Symflags & object::SymbolRef::SF_Global)) + continue; + if (Symflags & object::SymbolRef::SF_Undefined) + continue; + StringRef Name; + failIfError(I->getName(Name)); + SymNames.push_back(Name); + MemberOffsetRefs.push_back(std::make_pair(Out.tell(), MemberNum)); + print32BE(Out, 0); } } + for (std::vector<StringRef>::iterator I = SymNames.begin(), + E = SymNames.end(); + I != E; ++I) { + Out << *I; + Out << '\0'; + } + + for (std::vector<object::ObjectFile *>::iterator I = DeleteIt.begin(), + E = DeleteIt.end(); + I != E; ++I) { + object::ObjectFile *O = *I; + delete O; + } + + if (StartOffset == 0) + return; + + if (Out.tell() % 2) + Out << '\0'; + + unsigned Pos = Out.tell(); + Out.seek(StartOffset - 12); + printWithSpacePadding(Out, Pos - StartOffset, 10); + Out.seek(StartOffset); + print32BE(Out, SymNames.size()); + Out.seek(Pos); +} + +static void performWriteOperation(ArchiveOperation Operation, + object::Archive *OldArchive) { + 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::vector<NewArchiveIterator> NewMembers = + computeNewArchiveMembers(Operation, OldArchive); - // 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<sys::Path>::iterator PI = remaining.begin(), - PE = remaining.end(); PI != PE; ++PI) { - if (TheArchive->addFileBefore(*PI,insert_spot, ErrMsg)) - return true; + std::vector<std::pair<unsigned, unsigned> > MemberOffsetRefs; + + if (Symtab) { + writeSymbolTable(Out, NewMembers, MemberOffsetRefs); + } + + std::vector<unsigned> StringMapIndexes; + writeStringTable(Out, NewMembers, StringMapIndexes); + + std::vector<std::pair<unsigned, unsigned> >::iterator MemberRefsI = + MemberOffsetRefs.begin(); + + unsigned MemberNum = 0; + unsigned LongNameMemberNum = 0; + for (std::vector<NewArchiveIterator>::iterator I = NewMembers.begin(), + E = NewMembers.end(); + I != E; ++I, ++MemberNum) { + + unsigned Pos = Out.tell(); + while (MemberRefsI != MemberOffsetRefs.end() && + MemberRefsI->second == MemberNum) { + Out.seek(MemberRefsI->first); + print32BE(Out, Pos); + ++MemberRefsI; } + Out.seek(Pos); + + if (I->isNewMember()) { + const char *FileName = I->getNew(); + + int FD; + failIfError(sys::fs::openFileForRead(FileName, FD), FileName); + + sys::fs::file_status Status; + failIfError(sys::fs::status(FD, Status), FileName); + + // Opening a directory doesn't make sense. Let it failed. + // Linux cannot open directories with open(2), although + // cygwin and *bsd can. + if (Status.type() == sys::fs::file_type::directory_file) + failIfError(error_code(errc::is_a_directory, posix_category()), + FileName); + + OwningPtr<MemoryBuffer> File; + failIfError(MemoryBuffer::getOpenFile(FD, FileName, File, + Status.getSize(), false), + FileName); + + StringRef Name = sys::path::filename(FileName); + if (Name.size() < 16) + printMemberHeader(Out, Name, Status.getLastModificationTime(), + Status.getUser(), Status.getGroup(), + Status.permissions(), Status.getSize()); + else + printMemberHeader(Out, StringMapIndexes[LongNameMemberNum++], + Status.getLastModificationTime(), Status.getUser(), + Status.getGroup(), Status.permissions(), + Status.getSize()); + Out << File->getBuffer(); + } else { + object::Archive::child_iterator OldMember = I->getOld(); + StringRef Name = I->getName(); + + if (Name.size() < 16) + printMemberHeader(Out, Name, OldMember->getLastModified(), + OldMember->getUID(), OldMember->getGID(), + OldMember->getAccessMode(), OldMember->getSize()); + else + printMemberHeader(Out, StringMapIndexes[LongNameMemberNum++], + OldMember->getLastModified(), OldMember->getUID(), + OldMember->getGID(), OldMember->getAccessMode(), + OldMember->getSize()); + Out << OldMember->getBuffer(); + } + + if (Out.tell() % 2) + Out << '\n'; } + Output.keep(); + Out.close(); + sys::fs::rename(TemporaryOutput, ArchiveName); + TemporaryOutput = NULL; +} - // We're done editting, reconstruct the archive. - if (TheArchive->writeToDisk(SymTable,TruncateNames,ErrMsg)) - return true; - if (ReallyVerbose) - printSymbolTable(); - return false; +static void createSymbolTable(object::Archive *OldArchive) { + // When an archive is created or modified, if the s option is given, the + // resulting archive will have a current symbol table. If the S option + // is given, it will have no symbol table. + // In summary, we only need to update the symbol table if we have none. + // This is actually very common because of broken build systems that think + // they have to run ranlib. + if (OldArchive->hasSymbolTable()) + return; + + performWriteOperation(CreateSymTab, OldArchive); } +static void performOperation(ArchiveOperation Operation, + object::Archive *OldArchive) { + switch (Operation) { + case Print: + case DisplayTable: + case Extract: + performReadOperation(Operation, OldArchive); + return; + + case Delete: + case Move: + case QuickAppend: + case ReplaceOrInsert: + performWriteOperation(Operation, OldArchive); + return; + case CreateSymTab: + createSymbolTable(OldArchive); + return; + } + llvm_unreachable("Unknown operation."); +} + +static int ar_main(char **argv); +static int ranlib_main(); + // 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 @@ -717,63 +883,63 @@ int main(int argc, char **argv) { " This program archives bitcode files into single libraries\n" ); - int exitCode = 0; + StringRef Stem = sys::path::stem(ToolName); + if (Stem.find("ar") != StringRef::npos) + return ar_main(argv); + if (Stem.find("ranlib") != StringRef::npos) + return ranlib_main(); + fail("Not ranlib or ar!"); +} +static int performOperation(ArchiveOperation Operation); + +int ranlib_main() { + if (RestOfArgs.size() != 1) + fail(ToolName + "takes just one archive as argument"); + ArchiveName = RestOfArgs[0]; + return performOperation(CreateSymTab); +} + +int ar_main(char **argv) { // 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(); + return performOperation(Operation); +} - // Check the path name of the archive - sys::Path ArchivePath; - if (!ArchivePath.set(ArchiveName)) { - errs() << argv[0] << ": Archive name invalid: " << ArchiveName << "\n"; +static int performOperation(ArchiveOperation Operation) { + // Create or open the archive object. + OwningPtr<MemoryBuffer> Buf; + error_code EC = MemoryBuffer::getFile(ArchiveName, Buf, -1, false); + if (EC && EC != llvm::errc::no_such_file_or_directory) { + errs() << ToolName << ": error opening '" << ArchiveName + << "': " << EC.message() << "!\n"; return 1; } - // Create or open the archive object. - bool Exists; - if (llvm::sys::fs::exists(ArchivePath.str(), Exists) || !Exists) { - // Produce a warning if we should and we're creating the archive - if (!Create) - errs() << argv[0] << ": creating " << ArchivePath.str() << "\n"; - TheArchive = Archive::CreateEmpty(ArchivePath, Context); - TheArchive->writeToDisk(); - } else { - std::string Error; - TheArchive = Archive::OpenAndLoad(ArchivePath, Context, &Error); - if (TheArchive == 0) { - errs() << argv[0] << ": error loading '" << ArchivePath.str() << "': " - << Error << "!\n"; + if (!EC) { + object::Archive Archive(Buf.take(), EC); + + if (EC) { + errs() << ToolName << ": 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; - case NoOperation: - errs() << argv[0] << ": No operation was selected.\n"; - 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() << ToolName << ": creating " << ArchiveName << "\n"; + } } - delete TheArchive; - TheArchive = 0; - - // Return result code back to operating system. - return exitCode; + performOperation(Operation, NULL); + return 0; } |