mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-14 11:32:34 +00:00
0b8c9a80f2
into their new header subdirectory: include/llvm/IR. This matches the directory structure of lib, and begins to correct a long standing point of file layout clutter in LLVM. There are still more header files to move here, but I wanted to handle them in separate commits to make tracking what files make sense at each layer easier. The only really questionable files here are the target intrinsic tablegen files. But that's a battle I'd rather not fight today. I've updated both CMake and Makefile build systems (I think, and my tests think, but I may have missed something). I've also re-sorted the includes throughout the project. I'll be committing updates to Clang, DragonEgg, and Polly momentarily. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@171366 91177308-0d34-0410-b5e6-96231b3b80d8
780 lines
26 KiB
C++
780 lines
26 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/IR/LLVMContext.h"
|
|
#include "llvm/Bitcode/Archive.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Format.h"
|
|
#include "llvm/Support/ManagedStatic.h"
|
|
#include "llvm/Support/PrettyStackTrace.h"
|
|
#include "llvm/Support/Signals.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <fstream>
|
|
#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[abfiuRsS] - 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"
|
|
"\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
|
|
|
|
// 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;
|
|
|
|
// 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";
|
|
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() {
|
|
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() {
|
|
if(RestOfArgs.size() == 0)
|
|
show_help("Expected [count] value with N modifier");
|
|
|
|
Count = atoi(RestOfArgs[0].c_str());
|
|
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() {
|
|
if(RestOfArgs.size() == 0)
|
|
show_help("An archive name must be specified");
|
|
ArchiveName = RestOfArgs[0];
|
|
RestOfArgs.erase(RestOfArgs.begin());
|
|
}
|
|
|
|
// 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 '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)
|
|
show_help("You must specify at least one of the operations");
|
|
if (NumOperations > 1)
|
|
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 (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";
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// putMode - utility function for printing out the file mode when the 't'
|
|
// operation is in verbose mode.
|
|
void
|
|
printMode(unsigned mode) {
|
|
if (mode & 004)
|
|
outs() << "r";
|
|
else
|
|
outs() << "-";
|
|
if (mode & 002)
|
|
outs() << "w";
|
|
else
|
|
outs() << "-";
|
|
if (mode & 001)
|
|
outs() << "x";
|
|
else
|
|
outs() << "-";
|
|
}
|
|
|
|
// 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())
|
|
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";
|
|
}
|
|
}
|
|
}
|
|
if (ReallyVerbose)
|
|
printSymbolTable();
|
|
return false;
|
|
}
|
|
|
|
// doExtract - Implement the 'x' operation. This function extracts files back to
|
|
// the file system.
|
|
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,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().str()) {
|
|
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,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,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->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;
|
|
}
|
|
}
|
|
|
|
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().str())
|
|
insert_spot = I;
|
|
else if (AddAfter && RelPos == I->getPath().str()) {
|
|
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,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) {
|
|
program_name = 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
|
|
// 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;
|
|
|
|
// 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)) {
|
|
errs() << argv[0] << ": Archive name invalid: " << ArchiveName << "\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";
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Make sure we're not fooling ourselves.
|
|
assert(TheArchive && "Unable to instantiate the archive");
|
|
|
|
// 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;
|
|
}
|
|
|
|
delete TheArchive;
|
|
TheArchive = 0;
|
|
|
|
// Return result code back to operating system.
|
|
return exitCode;
|
|
}
|