Skip to content

Add argument parser utility

wahikihiki requested to merge wahikihiki/cmake:argument-parser into master

There are currently four different ways to parse arguments for cmake functions implemented in C++:

  1. Most commands have their own parsing logic as a huge switch-case statement inside a for-loop. They keep track of the current state by an enum called Doing. Adding a new argument requires four changes: A variable for the result, a new enum value, code to set the doing state, code to set the variable.

  2. There is a family of utility classes around cmCommandArgumentsHelper. It is not straight forward to use them and they are used only in three commands (file, install, export). Adding a new argument requires two changes: Adding a cmCAXXX instance, wiring the cmCAXXX to the argument it may follow. The actual result is then accessed through the cmCAXXX instance.

  3. Classes derived from cmCTestHandlerCommand use the parsing logic from that base class. Adding a new argument requires three changes: Adding an enum for the parser state (Doing), adding an enum for the variable index, storing the name of the argument at the right index in an array (Arguments). The actual result may be accessed at the right index in another array (Values). Alternatively, one can override two virtual functions: Both are called for each argument, but one is intended to change the parsing state while the other is intended to handle the argument based on the state.

  4. The C++ implementation of cmake_parse_arguments contains code to parse arguments. It is written in a way that it cannot be used for cmake functions that are written in C++.


In this MR, I propose a new class cmArgumentParser that is intended to succeed all the other ways mentioned above. It is based on the parsing code extracted from 4, but also supports "lists of lists" (as a preparation for #18932). With this class, adding support for a new argument requires two changes: A variable for the result, registering the variable to the parser with a name.

Example code:

cmArgumentParser parser;

bool Option;
std::string String;
std::vector<std::string> List;
std::vector<std::vector<std::string>> Multi;

parser.Bind("OPTION", &Option);
parser.Bind("STRING", &String);
parser.Bind("LIST", &List);
parser.Bind("MULTI", &Multi);

std::vector<std::string> UnparsedArguments;
std::set<std::string> KeywordsMissingValue;
parser.parse(args, &UnparsedArguments, &KeywordsMissingValue);

In order to make sure that this new approach supports current use cases and that this does not end up as https://xkcd.com/927/, this MR also replaces all uses of the approaches 2 and 4.

Edited by Kyle Edwards

Merge request reports