//===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===// // // The LLVM Compiler Infrastructure // // This file was developed by the Reid Spencer based on the original design by // Tanya Lattner and is distributed by the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Builds up (relatively) standard unix archive files (.a) containing LLVM // bytecode or other files. // //===----------------------------------------------------------------------===// #include "llvm/Module.h" #include "llvm/Bytecode/Archive.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compressor.h" #include "llvm/Support/FileUtilities.h" #include "llvm/System/Signals.h" #include #include #include using namespace llvm; // Option for compatibility with ASIX, not used but must allow it to be present. cl::opt X32Option ("X32_64", cl::desc("Ignored option for compatibility with AIX")); // llvm-ar operation code and modifier flags. This must come first cl::opt Options(cl::Positional, cl::Required, cl::desc("{operation}[modifiers]...")); // llvm-ar remaining positional arguments cl::list RestOfArgs(cl::Positional, cl::OneOrMore, cl::desc("[relpos] [count] [members]...")); // 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 DontSkipBytecode = 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 sys::Path::Vector Paths; // The Archive object to which all the editing operations will be sent. Archive* TheArchive = 0; // printMoreHelp - Provide additional help output explaining the operations and // modifiers of llvm-ar. This function is called by the CommandLine library // when the --help option is given because we set the global cl::MoreHelp // variable to the address of this function. void printMoreHelp() { std::cout << "\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"; std::cout << "\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 bytecode 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"; std::cout << "\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"; } // printUse - Print out our usage information. This is used in cases where the // user has made a mistake on the command line syntax. void printUse() { std::cout << "OVERVIEW: LLVM Archiver (llvm-ar)\n\n" << " This program archives bytecode files into single libraries\n\n" << "USAGE: llvm-ar [-X32_64] [-]{operation}[modifiers]... " << "[relpos] [count] archive-file [files..]\n"; printMoreHelp(); 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) { 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(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 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. sys::Path::Vector recurseDirectories(const sys::Path& path) { assert(path.isDirectory() && "Oops, can't recurse a file"); sys::Path::Vector result; if (RecurseDirectories) { sys::Path::Vector content; path.getDirectoryContents(content); for (sys::Path::Vector::iterator I = content.begin(), E = content.end(); I != E; ++I) { if (I->isDirectory()) { sys::Path::Vector moreResults = recurseDirectories(*I); result.insert(result.begin(), moreResults.begin(), moreResults.end()); } else { result.push_back(*I); } } } return result; } // 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') void buildPaths(bool checkExistence = true) { for (unsigned i = 0; i < Members.size(); i++) { sys::Path aPath; if (!aPath.setFile(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]; sys::Path::StatusInfo si; aPath.getStatusInfo(si); if (si.isDir) { sys::Path::Vector dirpaths = recurseDirectories(aPath); Paths.insert(Paths.end(),dirpaths.begin(),dirpaths.end()); } else { Paths.push_back(aPath); } } else { Paths.push_back(aPath); } } } // 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 bytecode files unless the 'k' modifier was // given. void doPrint() { buildPaths(false); 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(I->getData()); // Skip things that don't make sense to print if (I->isLLVMSymbolTable() || I->isForeignSymbolTable() || (!DontSkipBytecode && (I->isBytecode() || I->isCompressedBytecode()))) continue; if (Verbose) std::cout << "Printing " << I->getPath().get() << "\n"; if (I->isCompressedBytecode()) Compressor::decompressToFile(data+4,I->getSize()-4,std::cout); else if (I->isCompressed()) { Compressor::decompressToFile(data,I->getSize(),std::cout); } else { unsigned len = I->getSize(); std::cout.write(data, len); } } else { countDown--; } } } } // putMode - utility function for printing out the file mode when the 't' // operation is in verbose mode. void putMode(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. void doDisplayTable() { buildPaths(false); 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->isBytecode()) std::cout << "b"; else if (I->isCompressedBytecode()) std::cout << "B"; else if (I->isForeignSymbolTable()) std::cout << "s"; else if (I->isLLVMSymbolTable()) std::cout << "S"; else if (I->isCompressed()) std::cout << "Z"; else std::cout << " "; unsigned mode = I->getMode(); putMode((mode >> 6) & 007); putMode((mode >> 3) & 007); putMode(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().get() << "\n"; } else { std::cout << I->getPath().get() << "\n"; } } } if (ReallyVerbose) { 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"; } } } // doExtract - Implement the 'x' operation. This function extracts files back to // the file system, making sure to uncompress any that were compressed. void doExtract() { buildPaths(false); 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())) { // Make sure the intervening directories are created if (I->hasPath()) { sys::Path dirs(I->getPath()); dirs.elideFile(); dirs.createDirectory(/*create_parents=*/true); } // Open up a file stream for writing std::ofstream file(I->getPath().c_str()); // Get the data and its length const char* data = reinterpret_cast(I->getData()); unsigned len = I->getSize(); // Write the data, making sure to uncompress things first if (I->isCompressed()) { Compressor::decompressToFile(data,len,file); } else { file.write(data,len); } file.close(); // If we're supposed to retain the original modification times, etc. do so // now. if (OriginalDates) I->getPath().setStatusInfo(I->getStatusInfo()); } } } // 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). void doDelete() { buildPaths(false); if (Paths.empty()) return; 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->remove(J); } else countDown--; } else { ++I; } } // We're done editting, reconstruct the archive. TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ReallyVerbose); } // 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. void doMove() { buildPaths(false); // 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().get()) { if (AddAfter) { moveto_spot = I; moveto_spot++; } else { moveto_spot = I; } break; } } } // Keep a list of the paths remaining to be moved sys::Path::Vector 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 ) { sys::Path::Vector::iterator found = std::find(remaining.begin(),remaining.end(),I->getPath()); if (found != remaining.end()) { if (I != moveto_spot) TheArchive->moveMemberBefore(I,moveto_spot); remaining.erase(found); } } // We're done editting, reconstruct the archive. TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ReallyVerbose); } // doQuickAppend - Implements the 'q' operation. This function just // indiscriminantly adds the members to the archive and rebuilds it. void doQuickAppend() { // Get the list of paths to append. buildPaths(true); if (Paths.empty()) return; // Append them quickly. for (sys::Path::Vector::iterator PI = Paths.begin(), PE = Paths.end(); PI != PE; ++PI) { TheArchive->addFileBefore(*PI,TheArchive->end()); } // We're done editting, reconstruct the archive. TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ReallyVerbose); } // doReplaceOrInsert - Implements the 'r' operation. This function will replace // any existing files or insert new ones into the archive. void doReplaceOrInsert() { // Build the list of files to be added/replaced. buildPaths(true); if (Paths.empty()) return; // Keep track of the paths that remain to be inserted. sys::Path::Vector 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. sys::Path::Vector::iterator found = std::find(remaining.begin(),remaining.end(), I->getPath()); if (found != remaining.end()) { if (OnlyUpdate) { // Replace the item only if it is newer. sys::Path::StatusInfo si; found->getStatusInfo(si); if (si.modTime > I->getModTime()) I->replaceWith(*found); } else { // Replace the item regardless of time stamp I->replaceWith(*found); } // 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().get())) insert_spot = I; else if (AddAfter && (RelPos == I->getPath().get())) { 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 (sys::Path::Vector::iterator PI = remaining.begin(), PE = remaining.end(); PI != PE; ++PI) { TheArchive->addFileBefore(*PI,insert_spot); } } // We're done editting, reconstruct the archive. TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ReallyVerbose); } // main - main program for llvm-ar .. see comments in the code int main(int argc, char **argv) { // Ensure we initialize the global MoreHelp to tell the command line utility // that we have a MoreHelp function. This function is called to print more // help if the --help option is given on the command line cl::MoreHelp = printMoreHelp; // 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 bytecode 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.setFile(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.get() << "\n"; TheArchive = Archive::CreateEmpty(ArchivePath); } else { TheArchive = Archive::OpenAndLoad(ArchivePath); } // Make sure we're not fooling ourselves. assert(TheArchive && "Unable to instantiate the archive"); // Perform the operation switch (Operation) { case Print: doPrint(); break; case Delete: doDelete(); break; case Move: doMove(); break; case QuickAppend: /* FALL THROUGH */ case ReplaceOrInsert: doReplaceOrInsert(); break; case DisplayTable: doDisplayTable(); break; case Extract: doExtract(); break; case NoOperation: std::cerr << argv[0] << ": No operation was selected.\n"; break; } // Close up shop delete TheArchive; } 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"; printUse(); 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 nexpected unknown exception occurred.\n"; exitCode = 3; } // Return result code back to operating system. return exitCode; }