Index: test/tools/llvm-ar/default-add.test =================================================================== --- test/tools/llvm-ar/default-add.test +++ test/tools/llvm-ar/default-add.test @@ -14,5 +14,21 @@ RUN: llvm-ar crs %t.ar %t-macho.o RUN: not grep -q __.SYMDEF %t.ar +RUN: rm -f %t.ar +Test that multiple dashed options works. +RUN: llvm-ar -c -r -s %t.ar %t-macho.o +RUN: grep -q __.SYMDEF %t.ar +Test with duplicated options. +RUN: llvm-ar -c -r -s -c -r -s %t.ar %t-coff.o +RUN: grep -q __.SYMDEF %t.ar + +RUN: rm -f %t.ar +Test with the options in a different order. +RUN: llvm-ar rsc %t.ar %t-macho.o +RUN: grep -q __.SYMDEF %t.ar +Test with duplicated options in a different order. +RUN: llvm-ar -r -s -c -c -s -r %t.ar %t-coff.o +RUN: grep -q __.SYMDEF %t.ar + Ensure that we select the existing format when updating. Index: tools/llvm-ar/llvm-ar.cpp =================================================================== --- tools/llvm-ar/llvm-ar.cpp +++ tools/llvm-ar/llvm-ar.cpp @@ -83,7 +83,7 @@ cl::desc("[relpos] [count] [members]...")); static cl::opt MRI("M", cl::desc("")); -static cl::opt Plugin("plugin", cl::desc("plugin (ignored for compatibility")); +static cl::opt Plugin("plugin", cl::desc("plugin (ignored for compatibility)")); namespace { enum Format { Default, GNU, BSD, DARWIN }; @@ -98,33 +98,46 @@ static std::string Options; -// 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" - " [i] - put file(s) before [relpos] (same as [b])\n" - " [o] - preserve original dates\n" - " [s] - create an archive index (cf. ranlib)\n" - " [S] - do not build a symbol table\n" - " [T] - create a thin archive\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" -); +static const char Description[] = "LLVM Archiver (llvm-ar)\n\n" + " This program archives bitcode files into single libraries\n"; -static const char OptionChars[] = "dmpqrtxabiosSTucv"; +static cl::OptionCategory Operation("Operations"); +static cl::OptionCategory ModifierSpecific("Modifiers (operation specific)"); +static cl::OptionCategory ModifierGeneric("Modifiers (generic)"); + +static const struct { + const char option; + const char* help; + cl::OptionCategory& category; +} ArOptionData[] = { + // Operations. + { 'd', "delete file(s) from the archive", Operation }, + { 'm', "move file(s) in the archive", Operation }, + { 'p', "print file(s) found in the archive", Operation }, + { 'q', "append file(s) to the archive", Operation }, + { 'r', "replace or insert file(s) into the archive", Operation }, + { 't', "display contents of archive", Operation }, + { 'x', "extract file(s) from the archive", Operation }, + + // Modifiers (operation specific). + { 'a', "put file(s) after [relpos]", ModifierSpecific }, + { 'b', "put file(s) before [relpos] (same as [i])", ModifierSpecific }, + { 'i', "put file(s) before [relpos] (same as [b])", ModifierSpecific }, + { 'o', "preserve original dates", ModifierSpecific }, + { 's', "create an archive index (cf. ranlib)", ModifierSpecific }, + { 'S', "do not build a symbol table", ModifierSpecific }, + { 'T', "create a thin archive", ModifierSpecific }, + { 'u', "update only files newer than archive contents", ModifierSpecific }, + + // Modifiers (generic). + { 'c', "do not warn if the library had to be created", ModifierGeneric }, + { 'v', "be verbose about actions taken", ModifierGeneric }, +}; + +static const int ArOptionDataSize = sizeof(ArOptionData)/ + sizeof(ArOptionData[0]); + +static std::vector > > ArOptions; // This enumeration delineates the kinds of operations on an archive // that are permitted. @@ -173,7 +186,46 @@ RestOfArgs.erase(RestOfArgs.begin()); } +static std::string getOptionChars() { + std::string opts; + opts.reserve(ArOptionDataSize); + for (const auto& opt : ArOptionData) { + opts.push_back(opt.option); + } + return opts; +} + +static void getDashedOptions() { + std::vector > OptionPositions; + for (const auto& opt : ArOptions) { + if (*opt) { + OptionPositions.push_back(std::pair( + opt->ArgStr[0], opt->getPosition())); + } + } + struct { + bool operator()(const std::pair& p1, + const std::pair& p2) { + return p1.second < p2.second; + } + } PositionCmp; + std::sort(OptionPositions.begin(), OptionPositions.end(), PositionCmp); + Options.clear(); + Options.reserve(OptionPositions.size()); + for (const auto& pair : OptionPositions) + Options += pair.first; +} + static void getOptions() { + // If there were no dashed options, the next argument should be a + // concatenation of all the options. Otherwise, assume we have all the + // options already. + for (const auto& opt : ArOptions) { + if (*opt) { + getDashedOptions(); + return; + } + } if(RestOfArgs.size() == 0) fail("Expected options"); Options = RestOfArgs[0]; @@ -837,20 +889,6 @@ exit(0); } -static int ar_main() { - // Do our own parsing of the command line because the CommandLine utility - // can't handle the grouped positional parameters without a dash. - ArchiveOperation Operation = parseCommandLine(); - return performOperation(Operation, nullptr); -} - -static int ranlib_main() { - if (RestOfArgs.size() != 1) - fail(ToolName + " takes just one archive as an argument"); - ArchiveName = RestOfArgs[0]; - return performOperation(CreateSymTab, nullptr); -} - int main(int argc, char **argv) { ToolName = argv[0]; // Print a stack trace if we signal out. @@ -870,12 +908,10 @@ Stem.find("lib") != StringRef::npos) return libDriverMain(makeArrayRef(argv, argc)); - SmallVector Argv; - SpecificBumpPtrAllocator ArgAllocator; - failIfError(errorCodeToError(sys::Process::GetArgumentVector( - Argv, makeArrayRef(argv, argc), ArgAllocator))); - - for (unsigned i = 1; i < Argv.size(); i++) { + std::string OptionChars = getOptionChars(); + int OptionsIndex; + int NumOptionsFlags = 0; + for (int i = 1; i < argc; i++) { // If an argument starts with a dash and only contains chars // that belong to the options chars set, remove the dash. // We can't handle it after the command line options parsing @@ -883,26 +919,46 @@ // starting with a dash. // Make sure this doesn't match the actual llvm-ar specific options // that start with a dash. - StringRef S = Argv[i]; + // Also make sure there's only one flagged option string. + StringRef S = argv[i]; if (S.startswith("-") && S.find_first_not_of(OptionChars, 1) == StringRef::npos) { - Argv[i]++; - break; + OptionsIndex = i; + NumOptionsFlags++; } if (S == "--") break; } + if (NumOptionsFlags == 1) + argv[OptionsIndex]++; - // Have the command line options parsed and handle things - // like --help and --version. - cl::ParseCommandLineOptions(Argv.size(), Argv.data(), - "LLVM Archiver (llvm-ar)\n\n" - " This program archives bitcode files into single libraries\n" - ); - - if (Stem.find("ranlib") != StringRef::npos) - return ranlib_main(); - if (Stem.find("ar") != StringRef::npos) - return ar_main(); + SmallVector Argv; + SpecificBumpPtrAllocator ArgAllocator; + failIfError(errorCodeToError(sys::Process::GetArgumentVector( + Argv, makeArrayRef(argv, argc), ArgAllocator))); + + if (Stem.find("ranlib") != StringRef::npos) { + cl::ParseCommandLineOptions(Argv.size(), Argv.data(), Description); + if (RestOfArgs.size() != 1) + fail(ToolName + " takes just one archive as an argument"); + ArchiveName = RestOfArgs[0]; + return performOperation(CreateSymTab, nullptr); + } + if (Stem.find("ar") != StringRef::npos) { + // ar has additional options, so add them now. + ArOptions.reserve(ArOptionDataSize); + for (const auto& option : ArOptionData) { + ArOptions.push_back(std::unique_ptr >( + new cl::opt(StringRef(&option.option, 1), + cl::desc(option.help), cl::init(false), + cl::ZeroOrMore, cl::cat(option.category)))); + } + cl::ParseCommandLineOptions(Argv.size(), Argv.data(), Description); + + // Do our own parsing of the command line because the CommandLine utility + // can't handle the grouped positional parameters without a dash. + ArchiveOperation Operation = parseCommandLine(); + return performOperation(Operation, nullptr); + } fail("Not ranlib, ar, lib or dlltool!"); }