llvm-6502/tools/llvm-ar/llvm-ar.cpp
Owen Anderson 0d7c695c74 To simplify the upcoming context-on-type change, switch all command line tools to using the default global context for now.
This will let us to hardwire stuff to the global context in the short term while the API is sorted out.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@75846 91177308-0d34-0410-b5e6-96231b3b80d8
2009-07-15 22:16:10 +00:00

780 lines
27 KiB
C++

//===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Builds up (relatively) standard unix archive files (.a) containing LLVM
// bitcode or other files.
//
//===----------------------------------------------------------------------===//
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Bitcode/Archive.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/System/Signals.h"
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <memory>
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"));
// 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]..."));
// llvm-ar remaining positional arguments.
static cl::list<std::string>
RestOfArgs(cl::Positional, cl::OneOrMore,
cl::desc("[relpos] [count] <archive-file> [members]..."));
// 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.
static cl::extrahelp MoreHelp(
"\nOPERATIONS:\n"
" d[NsS] - delete file(s) from the archive\n"
" m[abiSs] - move file(s) in the archive\n"
" p[kN] - print file(s) found in the archive\n"
" q[ufsS] - quick append file(s) to the archive\n"
" r[abfiuzRsS] - replace or insert file(s) into the archive\n"
" t - display contents of archive\n"
" x[No] - extract file(s) from the archive\n"
"\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"
" [z] - compress files before inserting/extracting\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
};
// 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
bool Compression = false; ///< 'z' 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;
// This variable holds the name of the archive file as given on the
// command line.
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;
// getRelPos - Extract the member filename from the command line for
// the [relpos] argument associated with a, b, and i modifiers
void getRelPos() {
if(RestOfArgs.size() > 0) {
RelPos = RestOfArgs[0];
RestOfArgs.erase(RestOfArgs.begin());
}
else
throw "Expected [relpos] for a, b, or i modifier";
}
// getCount - Extract the [count] argument associated with the N modifier
// from the command line and check its value.
void getCount() {
if(RestOfArgs.size() > 0) {
Count = atoi(RestOfArgs[0].c_str());
RestOfArgs.erase(RestOfArgs.begin());
}
else
throw "Expected [count] value with N modifier";
// Non-positive counts are not allowed
if (Count < 1)
throw "Invalid [count] value (not a positive integer)";
}
// getArchive - Get the archive file name from the command line
void getArchive() {
if(RestOfArgs.size() > 0) {
ArchiveName = RestOfArgs[0];
RestOfArgs.erase(RestOfArgs.begin());
}
else
throw "An archive name must be specified.";
}
// getMembers - Copy over remaining items in RestOfArgs to our Members vector
// This is just for clarity.
void getMembers() {
if(RestOfArgs.size() > 0)
Members = std::vector<std::string>(RestOfArgs);
}
// 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() {
// Keep track of number of operations. We can only specify one
// per execution.
unsigned NumOperations = 0;
// Keep track of the number of positional modifiers (a,b,i). Only
// one can be specified.
unsigned NumPositional = 0;
// Keep track of which operation was requested
ArchiveOperation Operation = NoOperation;
for(unsigned i=0; i<Options.size(); ++i) {
switch(Options[i]) {
case 'd': ++NumOperations; Operation = Delete; break;
case 'm': ++NumOperations; Operation = Move ; break;
case 'p': ++NumOperations; Operation = Print; break;
case 'q': ++NumOperations; Operation = QuickAppend; break;
case 'r': ++NumOperations; Operation = ReplaceOrInsert; break;
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 'u': OnlyUpdate = true; break;
case 'v': Verbose = true; break;
case 'V': Verbose = ReallyVerbose = true; break;
case 'z': Compression = true; break;
case 'a':
getRelPos();
AddAfter = true;
NumPositional++;
break;
case 'b':
getRelPos();
AddBefore = true;
NumPositional++;
break;
case 'i':
getRelPos();
InsertBefore = true;
NumPositional++;
break;
case 'N':
getCount();
UseCount = true;
break;
default:
cl::PrintHelpMessage();
}
}
// At this point, the next thing on the command line must be
// the archive name.
getArchive();
// Everything on the command line at this point is a member.
getMembers();
// Perform various checks on the operation/modifier specification
// to make sure we are dealing with a legal request.
if (NumOperations == 0)
throw "You must specify at least one of the operations";
if (NumOperations > 1)
throw "Only one operation may be specified";
if (NumPositional > 1)
throw "You may only specify one of a, b, and i modifiers";
if (AddAfter || AddBefore || InsertBefore)
if (Operation != Move && Operation != ReplaceOrInsert)
throw "The 'a', 'b' and 'i' modifiers can only be specified with "
"the 'm' or 'r' operations";
if (RecurseDirectories && Operation != ReplaceOrInsert)
throw "The 'R' modifiers is only applicabe to the 'r' operation";
if (OriginalDates && Operation != Extract)
throw "The 'o' modifier is only applicable to the 'x' operation";
if (TruncateNames && Operation!=QuickAppend && Operation!=ReplaceOrInsert)
throw "The 'f' modifier is only applicable to the 'q' and 'r' operations";
if (OnlyUpdate && Operation != ReplaceOrInsert)
throw "The 'u' modifier is only applicable to the 'r' operation";
if (Compression && Operation!=ReplaceOrInsert && Operation!=Extract)
throw "The 'z' modifier is only applicable to the 'r' and 'x' operations";
if (Count > 1 && Members.size() > 1)
throw "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]))
throw std::string("File member name invalid: ") + Members[i];
if (checkExistence) {
if (!aPath.exists())
throw 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)
throw 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() {
std::cout << "\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;
std::cout << " " << std::setw(9) << offset << "\t" << I->first <<"\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)
std::cout << "Printing " << I->getPath().toString() << "\n";
unsigned len = I->getSize();
std::cout.write(data, len);
} else {
countDown--;
}
}
}
return false;
}
// putMode - utility function for printing out the file mode when the 't'
// operation is in verbose mode.
void
printMode(unsigned mode) {
if (mode & 004)
std::cout << "r";
else
std::cout << "-";
if (mode & 002)
std::cout << "w";
else
std::cout << "-";
if (mode & 001)
std::cout << "x";
else
std::cout << "-";
}
// doDisplayTable - 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())
std::cout << "b";
else if (I->isCompressed())
std::cout << "Z";
else
std::cout << " ";
unsigned mode = I->getMode();
printMode((mode >> 6) & 007);
printMode((mode >> 3) & 007);
printMode(mode & 007);
std::cout << " " << std::setw(4) << I->getUser();
std::cout << "/" << std::setw(4) << I->getGroup();
std::cout << " " << std::setw(8) << I->getSize();
std::cout << " " << std::setw(20) <<
I->getModTime().toString().substr(4);
std::cout << " " << I->getPath().toString() << "\n";
} else {
std::cout << I->getPath().toString() << "\n";
}
}
}
if (ReallyVerbose)
printSymbolTable();
return false;
}
// doExtract - Implement the 'x' operation. This function extracts files back to
// the file system, making sure to uncompress any that were compressed
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())) {
// 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);
// Get the data and its length
const char* data = reinterpret_cast<const char*>(I->getData());
unsigned len = I->getSize();
// Write the data.
file.write(data,len);
file.close();
// If we're supposed to retain the original modification times, etc. do so
// now.
if (OriginalDates)
I->getPath().setStatusInfoOnDisk(I->getFileStatus());
}
}
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;
}
}
// We're done editting, reconstruct the archive.
if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,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;
// 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().toString()) {
if (AddAfter) {
moveto_spot = I;
moveto_spot++;
} else {
moveto_spot = I;
}
break;
}
}
}
// 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);
}
}
// We're done editting, reconstruct the archive.
if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg))
return true;
if (ReallyVerbose)
printSymbolTable();
return false;
}
// 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;
// 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;
}
// We're done editting, reconstruct the archive.
if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg))
return true;
if (ReallyVerbose)
printSymbolTable();
return false;
}
// doReplaceOrInsert - Implements the 'r' operation. This function will replace
// any existing files or insert new ones into the archive.
bool
doReplaceOrInsert(std::string* ErrMsg) {
// Build the list of files to be added/replaced.
if (buildPaths(true, ErrMsg))
return true;
if (Paths.empty())
return false;
// 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->toString());
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().toString()) {
found = RI;
break;
}
}
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;
}
} else {
// We purposefully ignore directories.
}
// Remove it from our "to do" list
remaining.erase(found);
}
// Determine if this is the place where we should insert
if ((AddBefore || InsertBefore) && (RelPos == I->getPath().toString()))
insert_spot = I;
else if (AddAfter && (RelPos == I->getPath().toString())) {
insert_spot = I;
insert_spot++;
}
}
// 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;
}
}
// We're done editting, reconstruct the archive.
if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg))
return true;
if (ReallyVerbose)
printSymbolTable();
return false;
}
// main - main program for llvm-ar .. see comments in the code
int main(int argc, char **argv) {
// 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
// like --help and --version.
cl::ParseCommandLineOptions(argc, argv,
"LLVM Archiver (llvm-ar)\n\n"
" This program archives bitcode files into single libraries\n"
);
int exitCode = 0;
// Make sure we don't exit with "unhandled exception".
try {
// 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();
// Check the path name of the archive
sys::Path ArchivePath;
if (!ArchivePath.set(ArchiveName))
throw std::string("Archive name invalid: ") + ArchiveName;
// Create or open the archive object.
if (!ArchivePath.exists()) {
// Produce a warning if we should and we're creating the archive
if (!Create)
errs() << argv[0] << ": creating " << ArchivePath.toString() << "\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 << "': "
<< Error << "!\n";
return 1;
}
}
// Make sure we're not fooling ourselves.
assert(TheArchive && "Unable to instantiate the archive");
// Make sure we clean up the archive even on failure.
std::auto_ptr<Archive> AutoArchive(TheArchive);
// 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;
}
} catch (const char*msg) {
// These errors are usage errors, thrown only by the various checks in the
// code above.
errs() << argv[0] << ": " << msg << "\n\n";
cl::PrintHelpMessage();
exitCode = 1;
} catch (const std::string& msg) {
// These errors are thrown by LLVM libraries (e.g. lib System) and represent
// a more serious error so we bump the exitCode and don't print the usage.
errs() << argv[0] << ": " << msg << "\n";
exitCode = 2;
} catch (...) {
// This really shouldn't happen, but just in case ....
errs() << argv[0] << ": An unexpected unknown exception occurred.\n";
exitCode = 3;
}
// Return result code back to operating system.
return exitCode;
}