X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=tools%2Fllvm-ar%2Fllvm-ar.cpp;h=a6611a3e8091c75ef36c11cc3b92f5fe8bf5d466;hb=73b43b9b549a75fb0015c825df68abd95705a67c;hp=7634a1a9e0ab3e12e32e03a695d7dc054c90e93c;hpb=8d2ff1314901dd61f719b7689e4e2355d13c60f5;p=oota-llvm.git diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp index 7634a1a9e0a..a6611a3e809 100644 --- a/tools/llvm-ar/llvm-ar.cpp +++ b/tools/llvm-ar/llvm-ar.cpp @@ -1,370 +1,775 @@ +//===-- 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. +// //===----------------------------------------------------------------------===// -// LLVM 'ar' UTILITY // -// This utility may be invoked in the following manner: -// llvm-ar archivename files.. +// Builds up (relatively) standard unix archive files (.a) containing LLVM +// bitcode or other files. // //===----------------------------------------------------------------------===// -#include "Support/CommandLine.h" -#include "llvm/Bytecode/Reader.h" -#include "llvm/Module.h" -#include +#include "llvm/Module.h" +#include "llvm/Bitcode/Archive.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/System/Signals.h" #include -#include -#include -#include -#include -#include -#include -#include -#include - - -using std::string; -using std::vector; -using std::cout; - - -#define ARFMAG "\n" /* header trailer string */ -#define ARMAG "!\n" /* magic string */ -#define SARMAG 8 /* length of magic string */ - -namespace { - - // Each file member is preceded by a file member header. Which is - // of the following format: - // - // char ar_name[16] - '/' terminated file member name. - // If the file name does not fit, a dummy name is used. - // char ar_date[12] - file date in decimal - // char ar_uid[6] - User id of file owner in decimal. - // char ar_gid[6] - Group ID file belongs to in decimal. - // char ar_mode[8] - File mode in octal. - // char ar_size[10] - Size of file in decimal. - // char ar_fmag[2] - Trailer of header file, a newline. - struct ar_hdr { - char name[16]; - char date[12]; - char uid[6]; - char gid[6]; - char mode[8]; - char size[10]; - char fmag[2]; - void init() { - memset(name,' ',16); - memset(date,' ',12); - memset(uid,' ',6); - memset(gid,' ',6); - memset(mode,' ',8); - memset(size,' ',10); - memset(fmag,' ',2); - } - }; +#include +#include +#include +using namespace llvm; + +// Option for compatibility with AIX, not used but must allow it to be present. +static cl::opt +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 +Options(cl::Positional, cl::Required, cl::desc("{operation}[modifiers]...")); + +// llvm-ar remaining positional arguments. +static cl::list +RestOfArgs(cl::Positional, cl::OneOrMore, + cl::desc("[relpos] [count] [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 Members; + +// This variable holds the (possibly expanded) list of path objects that +// correspond to files we will +std::set 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"; } -//Option to generate symbol table or not -//running llvm-ar -s is the same as ranlib -cl::opt SymbolTable ("s", cl::desc("Generate an archive symbol table")); - -//Archive name -cl::opt Archive (cl::Positional, cl::desc(""), - cl::Required); - -//For now we require one or more member files, this should change so -//we can just run llvm-ar -s on an archive to generate the symbol -//table -cl::list Members(cl::ConsumeAfter, cl::desc("...")); +// 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)"; +} -static inline bool Error(std::string *ErrorStr, const char *Message) { - if (ErrorStr) *ErrorStr = Message; - return true; +// 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(RestOfArgs); +} -// WriteSymbolTable - Writes symbol table to ArchiveFile, return false -// on errors. Also returns by reference size of symbol table. -// -// Overview of method: -// 1) Generate the header for the symbol table. This is a normal -// archive member header, but it has a zero length name. -// 2) For each archive member file, stat the file and parse the bytecode -// Store cummulative offset (file size + header size). -// 3) Loop over all the symbols for the current member file, -// add offset entry to offset vector, and add symbol name to its vector. -// Note: The symbol name vector is a vector of chars to speed up calculating -// the total size of the symbol table. -// 4) Update offset vector once we know the total size of symbol table. This is -// because the symbol table appears before all archive member file contents. -// We add the size of magic string, and size of symbol table to each offset. -// 5) If the new updated offset it not even, we add 1 byte to offset because -// a newline will be inserted when writing member files. This adjustment is -// cummulative (ie. each time we have an odd offset we add 1 to total adjustment). -// 6) Lastly, write symbol table to file. -// -bool WriteSymbolTable(std::ofstream &ArchiveFile) { - - //Create header for symbol table. This is essentially an empty header with the - //name set to a '/' to indicate its a symbol table. - ar_hdr Hdr; - Hdr.init(); - - //Name of symbol table is '/' - Hdr.name[0] = '/'; - Hdr.name[1] = '\0'; - - //Set the header trailer to a newline - memcpy(Hdr.fmag,ARFMAG,sizeof(ARFMAG)); - - - //Write header to archive file - ArchiveFile.write((char*)&Hdr, sizeof(Hdr)); - - - unsigned memoff = 0; //Keep Track of total size of files added to archive - vector offsets; //Vector of offsets into archive file - vector names; //Vector of characters that are the symbol names. - - //Loop over archive member files, parse bytecode, and generate symbol table. - for(unsigned i=0; i 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; +} - //Size of file - unsigned Length = StatBuf.st_size; - - //Read in file into a buffer. - unsigned char *buf = (unsigned char*)mmap(0, Length,PROT_READ, - MAP_PRIVATE, FD, 0); - - //Check if mmap failed. - if (buf == (unsigned char*)MAP_FAILED) { - std::cerr << "Error mmapping file!\n"; - return false; +// 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& result, std::string* ErrMsg) { + result.clear(); + if (RecurseDirectories) { + std::set content; + if (path.getDirectoryContents(content, ErrMsg)) + return true; + + for (std::set::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 moreResults; + if (recurseDirectories(*I, moreResults, ErrMsg)) + return true; + result.insert(moreResults.begin(), moreResults.end()); + } else { + result.insert(*I); + } } - - //Parse the bytecode file and get all the symbols. - string ErrorStr; - Module *M = ParseBytecodeBuffer(buf,Length,Members[i],&ErrorStr); - - //Check for errors parsing bytecode. - //if(ErrorStr) { - //std::cerr << "Error Parsing Bytecode\n"; - //return false; - //} - - //Loop over function names and global vars and add to symbol maps - for(Module::iterator I = M->begin(), E=M->end(); I != E; ++I) { - - //get function name - string NM = ((Function*)I)->getName(); - - //Loop over the characters in the name and add to symbol name vector - for(unsigned i=0; iisDir) { + std::set dirpaths; + if (recurseDirectories(aPath, dirpaths, ErrMsg)) + return true; + Paths.insert(dirpaths.begin(),dirpaths.end()); + } else { + Paths.insert(aPath); + } + } else { + Paths.insert(aPath); } + } + return false; +} - memoff += Length + sizeof(Hdr); +// 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"; } +} - //Determine how large our symbol table is. - unsigned symbolTableSize = sizeof(Hdr) + 4 + 4*(offsets.size()) + names.size(); - cout << "Symbol Table Size: " << symbolTableSize << "\n"; - - //Number of symbols should be in network byte order as well - char num[4]; - unsigned temp = offsets.size(); - num[0] = (temp >> 24) & 255; - num[1] = (temp >> 16) & 255; - num[2] = (temp >> 8) & 255; - num[3] = temp & 255; - - //Write number of symbols to archive file - ArchiveFile.write(num,4); - - //Adjustment to offset to start files on even byte boundaries - unsigned adjust = 0; - - //Update offsets write symbol tabel to archive. - for(unsigned i=0; ibegin(), 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(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--; + } } - - cout << "Offset: " << offsets[i] << "\n"; - output[0] = (offsets[i] >> 24) & 255; - output[1] = (offsets[i] >> 16) & 255; - output[2] = (offsets[i] >> 8) & 255; - output[3] = offsets[i] & 255; - ArchiveFile.write(output,4); } + 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 << "-"; +} - //Write out symbol name vector. - for(unsigned i=0; ibegin(), 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; +} - return true; +// 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(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; } -// AddMemberToArchive - Writes member file to archive. Returns false on errors. -// -// Overview of method: -// 1) Open file, and stat it. -// 2) Fill out header using stat information. If name is longer then 15 -// characters, use "dummy" name. -// 3) Write header and file contents to disk. -// 4) Keep track of total offset into file, and insert a newline if it is odd. -// -bool AddMemberToArchive(string Member, std::ofstream &ArchiveFile) { - - ar_hdr Hdr; //Header for archive member file. - - //stat the file to get info - struct stat StatBuf; - if (stat(Member.c_str(), &StatBuf) == -1 || StatBuf.st_size == 0) - cout << "ERROR\n"; - - //fill in header - - //set name to white spaces - memset(Hdr.name,' ', sizeof(Hdr.name)); - - //check the size of the name, if less than 15, we can copy it directly - //otherwise we give it a dummy name for now - if(Member.length() < 16) - memcpy(Hdr.name,Member.c_str(),Member.length()); - else - memcpy(Hdr.name, "Dummy", 5); - - //terminate name with forward slash - Hdr.name[15] = '/'; - - //file member size in decimal - unsigned Length = StatBuf.st_size; - sprintf(Hdr.size,"%d", Length); - cout << "Size: " << Length << "\n"; - - //file member user id in decimal - sprintf(Hdr.uid, "%d", StatBuf.st_uid); - - //file member group id in decimal - sprintf(Hdr.gid, "%d", StatBuf.st_gid); - - //file member date in decimal - sprintf(Hdr.date,"%d", (int)StatBuf.st_mtime); - - //file member mode in OCTAL - sprintf(Hdr.mode,"%d", StatBuf.st_mode); - - //add our header trailer - memcpy(Hdr.fmag,ARFMAG,sizeof(ARFMAG)); - - //write header to archive file - ArchiveFile.write((char*)&Hdr, sizeof(Hdr)); - - //open Member file for reading and copy to buffer - int FD = open(Member.c_str(),O_RDONLY); - if (FD == -1) { - std::cerr << "Error opening file!\n"; +// 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; + } } - unsigned char *buf = (unsigned char*)mmap(0, Length,PROT_READ, - MAP_PRIVATE, FD, 0); - - //check if mmap failed - if (buf == (unsigned char*)MAP_FAILED) { - std::cerr << "Error mmapping file!\n"; - return false; + // 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; + } + } } - //write to archive file - ArchiveFile.write((char*)buf,Length); - - // Unmmap the memberfile - munmap((char*)buf, Length); + // Keep a list of the paths remaining to be moved + std::set 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::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); + } + } - return true; + // 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; -// CreateArchive - Generates archive with or without symbol table. -// -void CreateArchive() { - - //Create archive file for output. - std::ofstream ArchiveFile(Archive.c_str()); - - //Check for errors opening or creating archive file. - if(!ArchiveFile.is_open() || ArchiveFile.bad() ) { - std::cerr << "Error opening Archive File\n"; - exit(1); + // Append them quickly. + for (std::set::iterator PI = Paths.begin(), PE = Paths.end(); + PI != PE; ++PI) { + if (TheArchive->addFileBefore(*PI,TheArchive->end(),ErrMsg)) + return true; } - //Write magic string to archive. - ArchiveFile << ARMAG; + // 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; - //If the '-s' option was specified, generate symbol table. - if(SymbolTable) { - cout << "Symbol Table Start: " << ArchiveFile.tellp() << "\n"; - if(!WriteSymbolTable(ArchiveFile)) { - std::cerr << "Error creating symbol table. Exiting program."; - exit(1); + // Keep track of the paths that remain to be inserted. + std::set 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::iterator found = remaining.end(); + for (std::set::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; + } } - cout << "Symbol Table End: " << ArchiveFile.tellp() << "\n"; - } - //Loop over all member files, and add to the archive. - for(unsigned i=0; iisDir) { + 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); + } - if(AddMemberToArchive(Members[i],ArchiveFile) != true) { - std::cerr << "Error adding file to archive. Exiting program."; - exit(1); + // 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::iterator PI = remaining.begin(), + PE = remaining.end(); PI != PE; ++PI) { + if (TheArchive->addFileBefore(*PI,insert_spot, ErrMsg)) + return true; } - cout << "Member File End: " << ArchiveFile.tellp() << "\n"; } - - //Close archive file. - ArchiveFile.close(); -} + // 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) { + llvm_shutdown_obj X; // 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" + ); + + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + + 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) + std::cerr << argv[0] << ": creating " << ArchivePath.toString() << "\n"; + TheArchive = Archive::CreateEmpty(ArchivePath); + TheArchive->writeToDisk(); + } else { + std::string Error; + TheArchive = Archive::OpenAndLoad(ArchivePath, &Error); + if (TheArchive == 0) { + std::cerr << argv[0] << ": error loading '" << ArchivePath << "': " + << Error << "!\n"; + return 1; + } + } - //Parse Command line options - cl::ParseCommandLineOptions(argc, argv, " llvm-ar\n"); - - //Create archive! - CreateArchive(); + // 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 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: + std::cerr << argv[0] << ": No operation was selected.\n"; + break; + } + if (haveError) { + std::cerr << 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. + std::cerr << 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. + std::cerr << argv[0] << ": " << msg << "\n"; + exitCode = 2; + } catch (...) { + // This really shouldn't happen, but just in case .... + std::cerr << argv[0] << ": An unexpected unknown exception occurred.\n"; + exitCode = 3; + } - return 0; + // Return result code back to operating system. + return exitCode; }