From beb4d8293d5311c4581fd3d914f865e358af53a5 Mon Sep 17 00:00:00 2001 From: Mikhail Glushenkov Date: Mon, 28 Apr 2008 16:44:25 +0000 Subject: [PATCH] Add support for response files to the CommandLine library. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@50355 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/CommandLine.html | 35 ++++++++-- include/llvm/Support/CommandLine.h | 44 +++++++------ lib/Support/CommandLine.cpp | 102 ++++++++++++++++++++++------- 3 files changed, 133 insertions(+), 48 deletions(-) diff --git a/docs/CommandLine.html b/docs/CommandLine.html index 91f4b1535e4..93b5ca1c69e 100644 --- a/docs/CommandLine.html +++ b/docs/CommandLine.html @@ -52,6 +52,7 @@ specified
  • Controlling other formatting options
  • Miscellaneous option modifiers
  • +
  • Response files
  • Top-Level Classes and Functions @@ -1442,6 +1443,29 @@ only makes sense with a cl::list option.
  • + +
    + Response files +
    + +
    + +

    Some systems, such as certain variants of Microsoft Windows and +some older Unices have a relatively low limit on command-line +length. It is therefore customary to use the so-called 'response +files' to circumvent this restriction. These files are mentioned on +the command-line (using the "@file") syntax. The program reads these +files and inserts the contents into argv, thereby working around the +command-line length limits. Response files are enabled by an optional +fourth argument to +cl::ParseEnvironmentOptions +and +cl::ParseCommandLineOptions. +

    + +
    + +
    Top-Level Classes and Functions @@ -1475,7 +1499,8 @@ available.

    The cl::ParseCommandLineOptions function requires two parameters (argc and argv), but may also take an optional third parameter which holds additional extra text to emit when the ---help option is invoked.

    +--help option is invoked, and a fourth boolean parameter that enables +response files.

    @@ -1497,11 +1522,13 @@ like cl::ParseCommandLineOptions does.

    -

    It takes three parameters: the name of the program (since argv may +

    It takes four parameters: the name of the program (since argv may not be available, it can't just look in argv[0]), the name of the -environment variable to examine, and the optional +environment variable to examine, the optional additional extra text to emit when the ---help option is invoked.

    +--help option is invoked, and the boolean +switch that controls whether reponse files +should be read.

    cl::ParseEnvironmentOptions will break the environment variable's value up into words and then process them using diff --git a/include/llvm/Support/CommandLine.h b/include/llvm/Support/CommandLine.h index 0f804395c39..27782c875ff 100644 --- a/include/llvm/Support/CommandLine.h +++ b/include/llvm/Support/CommandLine.h @@ -41,14 +41,16 @@ namespace cl { // ParseCommandLineOptions - Command line option processing entry point. // void ParseCommandLineOptions(int argc, char **argv, - const char *Overview = 0); + const char *Overview = 0, + bool ReadResponseFiles = false); //===----------------------------------------------------------------------===// // ParseEnvironmentOptions - Environment variable option processing alternate // entry point. // void ParseEnvironmentOptions(const char *progName, const char *envvar, - const char *Overview = 0); + const char *Overview = 0, + bool ReadResponseFiles = false); ///===---------------------------------------------------------------------===// /// SetVersionPrinter - Override the default (LLVM specific) version printer @@ -146,10 +148,10 @@ class Option { virtual enum ValueExpected getValueExpectedFlagDefault() const { return ValueOptional; } - + // Out of line virtual function to provide home for the class. virtual void anchor(); - + int NumOccurrences; // The number of times specified int Flags; // Flags for the argument unsigned Position; // Position of last occurrence of the option @@ -213,7 +215,7 @@ public: // addArgument - Register this argument with the commandline system. // void addArgument(); - + Option *getNextRegisteredOption() const { return NextRegistered; } // Return the width of the option tag for printing... @@ -225,7 +227,7 @@ public: virtual void printOptionInfo(unsigned GlobalWidth) const = 0; virtual void getExtraOptionNames(std::vector &OptionNames) {} - + // addOccurrence - Wrapper around handleOccurrence that enforces Flags // bool addOccurrence(unsigned pos, const char *ArgName, @@ -339,7 +341,7 @@ public: }; template -ValuesClass END_WITH_NULL values(const char *Arg, DataType Val, +ValuesClass END_WITH_NULL values(const char *Arg, DataType Val, const char *Desc, ...) { va_list ValueArgs; va_start(ValueArgs, Desc); @@ -389,7 +391,7 @@ struct generic_parser_base { // hasArgStr = O.hasArgStr(); } - + void getExtraOptionNames(std::vector &OptionNames) { // If there has been no argstr specified, that means that we need to add an // argument for every possible option. This ensures that our options are @@ -537,7 +539,7 @@ public: // getValueName - Do not print = at all. virtual const char *getValueName() const { return 0; } - + // An out-of-line virtual method to provide a 'home' for this class. virtual void anchor(); }; @@ -551,7 +553,7 @@ template<> class parser : public basic_parser { public: // parse - Return true on error. - bool parse(Option &O, const char *ArgName, const std::string &Arg, + bool parse(Option &O, const char *ArgName, const std::string &Arg, boolOrDefault &Val); enum ValueExpected getValueExpectedFlagDefault() const { @@ -560,7 +562,7 @@ public: // getValueName - Do not print = at all. virtual const char *getValueName() const { return 0; } - + // An out-of-line virtual method to provide a 'home' for this class. virtual void anchor(); }; @@ -965,7 +967,7 @@ class list : public Option, public list_storage { virtual void getExtraOptionNames(std::vector &OptionNames) { return Parser.getExtraOptionNames(OptionNames); } - + virtual bool handleOccurrence(unsigned pos, const char *ArgName, const std::string &Arg) { typename ParserClass::parser_data_type Val = @@ -1071,7 +1073,7 @@ public: template class bits_storage { unsigned *Location; // Where to store the bits... - + template static unsigned Bit(const T &V) { unsigned BitPos = reinterpret_cast(V); @@ -1096,9 +1098,9 @@ public: "line option with external storage!"); *Location |= Bit(V); } - + unsigned getBits() { return *Location; } - + template bool isSet(const T &V) { return (*Location & Bit(V)) != 0; @@ -1106,13 +1108,13 @@ public: }; -// Define how to hold bits. Since we can inherit from a class, we do so. +// Define how to hold bits. Since we can inherit from a class, we do so. // This makes us exactly compatible with the bits in all cases that it is used. // template class bits_storage { unsigned Bits; // Where to store the bits... - + template static unsigned Bit(const T &V) { unsigned BitPos = reinterpret_cast(V); @@ -1120,15 +1122,15 @@ class bits_storage { "enum exceeds width of bit vector!"); return 1 << BitPos; } - + public: template void addValue(const T &V) { Bits |= Bit(V); } - + unsigned getBits() { return Bits; } - + template bool isSet(const T &V) { return (Bits & Bit(V)) != 0; @@ -1151,7 +1153,7 @@ class bits : public Option, public bits_storage { virtual void getExtraOptionNames(std::vector &OptionNames) { return Parser.getExtraOptionNames(OptionNames); } - + virtual bool handleOccurrence(unsigned pos, const char *ArgName, const std::string &Arg) { typename ParserClass::parser_data_type Val = diff --git a/lib/Support/CommandLine.cpp b/lib/Support/CommandLine.cpp index 24f220d5136..f4594610dff 100644 --- a/lib/Support/CommandLine.cpp +++ b/lib/Support/CommandLine.cpp @@ -17,7 +17,9 @@ //===----------------------------------------------------------------------===// #include "llvm/Config/config.h" +#include "llvm/ADT/OwningPtr.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Streams.h" #include "llvm/System/Path.h" @@ -87,7 +89,7 @@ static Option *RegisteredOptionList = 0; void Option::addArgument() { assert(NextRegistered == 0 && "argument multiply registered!"); - + NextRegistered = RegisteredOptionList; RegisteredOptionList = this; MarkOptionsChanged(); @@ -111,7 +113,7 @@ static void GetOptionInfo(std::vector &PositionalOpts, O->getExtraOptionNames(OptionNames); if (O->ArgStr[0]) OptionNames.push_back(O->ArgStr); - + // Handle named options. for (unsigned i = 0, e = OptionNames.size(); i != e; ++i) { // Add argument to the argument map! @@ -121,9 +123,9 @@ static void GetOptionInfo(std::vector &PositionalOpts, << OptionNames[0] << "' defined more than once!\n"; } } - + OptionNames.clear(); - + // Remember information about positional options. if (O->getFormattingFlag() == cl::Positional) PositionalOpts.push_back(O); @@ -135,10 +137,10 @@ static void GetOptionInfo(std::vector &PositionalOpts, CAOpt = O; } } - + if (CAOpt) PositionalOpts.push_back(CAOpt); - + // Make sure that they are in order of registration not backwards. std::reverse(PositionalOpts.begin(), PositionalOpts.end()); } @@ -150,17 +152,17 @@ static void GetOptionInfo(std::vector &PositionalOpts, static Option *LookupOption(const char *&Arg, const char *&Value, std::map &OptionsMap) { while (*Arg == '-') ++Arg; // Eat leading dashes - + const char *ArgEnd = Arg; while (*ArgEnd && *ArgEnd != '=') ++ArgEnd; // Scan till end of argument name. - + if (*ArgEnd == '=') // If we have an equals sign... Value = ArgEnd+1; // Get the value, not the equals - - + + if (*Arg == 0) return 0; - + // Look up the option. std::map::iterator I = OptionsMap.find(std::string(Arg, ArgEnd)); @@ -309,7 +311,7 @@ static void ParseCStringVector(std::vector &output, /// an environment variable (whose name is given in ENVVAR). /// void cl::ParseEnvironmentOptions(const char *progName, const char *envVar, - const char *Overview) { + const char *Overview, bool ReadResponseFiles) { // Check args. assert(progName && "Program name not specified"); assert(envVar && "Environment variable name missing"); @@ -328,7 +330,7 @@ void cl::ParseEnvironmentOptions(const char *progName, const char *envVar, // and hand it off to ParseCommandLineOptions(). ParseCStringVector(newArgv, envValue); int newArgc = newArgv.size(); - ParseCommandLineOptions(newArgc, &newArgv[0], Overview); + ParseCommandLineOptions(newArgc, &newArgv[0], Overview, ReadResponseFiles); // Free all the strdup()ed strings. for (std::vector::iterator i = newArgv.begin(), e = newArgv.end(); @@ -336,32 +338,78 @@ void cl::ParseEnvironmentOptions(const char *progName, const char *envVar, free (*i); } + +/// ExpandResponseFiles - Copy the contents of argv into newArgv, +/// substituting the contents of the response files for the arguments +/// of type @file. +static void ExpandResponseFiles(int argc, char** argv, + std::vector& newArgv) { + for (int i = 1; i != argc; ++i) { + char* arg = argv[i]; + + if (arg[0] == '@') { + + sys::PathWithStatus respFile(++arg); + + // Check that the response file is not empty (mmap'ing empty + // files can be problematic). + const sys::FileStatus *FileStat = respFile.getFileStatus(); + if (!FileStat) + continue; + if (FileStat->getSize() == 0) + continue; + + // Mmap the response file into memory. + OwningPtr + respFilePtr(MemoryBuffer::getFile(respFile.c_str())); + + if (respFilePtr == 0) + continue; + + ParseCStringVector(newArgv, respFilePtr->getBufferStart()); + } + else { + newArgv.push_back(strdup(arg)); + } + } +} + void cl::ParseCommandLineOptions(int argc, char **argv, - const char *Overview) { + const char *Overview, bool ReadResponseFiles) { // Process all registered options. std::vector PositionalOpts; std::vector SinkOpts; std::map Opts; GetOptionInfo(PositionalOpts, SinkOpts, Opts); - + assert((!Opts.empty() || !PositionalOpts.empty()) && "No options specified!"); + + // Expand response files. + std::vector newArgv; + if (ReadResponseFiles) { + newArgv.push_back(strdup(argv[0])); + ExpandResponseFiles(argc, argv, newArgv); + argv = &newArgv[0]; + argc = newArgv.size(); + } + sys::Path progname(argv[0]); // Copy the program name into ProgName, making sure not to overflow it. std::string ProgName = sys::Path(argv[0]).getLast(); if (ProgName.size() > 79) ProgName.resize(79); strcpy(ProgramName, ProgName.c_str()); - + ProgramOverview = Overview; bool ErrorParsing = false; // Check out the positional arguments to collect information about them. unsigned NumPositionalRequired = 0; - + // Determine whether or not there are an unlimited number of positionals bool HasUnlimitedPositionals = false; - + Option *ConsumeAfterOpt = 0; if (!PositionalOpts.empty()) { if (PositionalOpts[0]->getNumOccurrencesFlag() == cl::ConsumeAfter) { @@ -427,7 +475,7 @@ void cl::ParseCommandLineOptions(int argc, char **argv, GetOptionInfo(PositionalOpts, SinkOpts, Opts); OptionListChanged = false; } - + // Check to see if this is a positional argument. This argument is // considered to be positional if it doesn't start with '-', if it is "-" // itself, or if we have seen "--" already. @@ -567,7 +615,7 @@ void cl::ParseCommandLineOptions(int argc, char **argv, << ": Not enough positional command line arguments specified!\n" << "Must specify at least " << NumPositionalRequired << " positional arguments: See: " << argv[0] << " --help\n"; - + ErrorParsing = true; } else if (!HasUnlimitedPositionals && PositionalVals.size() > PositionalOpts.size()) { @@ -664,6 +712,14 @@ void cl::ParseCommandLineOptions(int argc, char **argv, PositionalOpts.clear(); MoreHelp->clear(); + // Free the memory allocated by ExpandResponseFiles. + if (ReadResponseFiles) { + // Free all the strdup()ed strings. + for (std::vector::iterator i = newArgv.begin(), e = newArgv.end(); + i != e; ++i) + free (*i); + } + // If we had an error processing our arguments, don't let the program execute if (ErrorParsing) exit(1); } @@ -678,7 +734,7 @@ bool Option::error(std::string Message, const char *ArgName) { cerr << HelpStr; // Be nice for positional arguments else cerr << ProgramName << ": for the -" << ArgName; - + cerr << " option: " << Message << "\n"; return true; } @@ -943,7 +999,7 @@ public: std::vector SinkOpts; std::map OptMap; GetOptionInfo(PositionalOpts, SinkOpts, OptMap); - + // Copy Options into a vector so we can sort them as we like... std::vector > Opts; copy(OptMap.begin(), OptMap.end(), std::back_inserter(Opts)); @@ -970,7 +1026,7 @@ public: // Print out the positional options. Option *CAOpt = 0; // The cl::ConsumeAfter option, if it exists... - if (!PositionalOpts.empty() && + if (!PositionalOpts.empty() && PositionalOpts[0]->getNumOccurrencesFlag() == ConsumeAfter) CAOpt = PositionalOpts[0]; -- 2.34.1