summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2022-07-06 15:30:22 (GMT)
committerBrad King <brad.king@kitware.com>2022-07-07 13:48:58 (GMT)
commite6d1e29ffa6bd3141a769d1281f3407ed0774139 (patch)
tree667e168e1643d72da6c88433c3c71a26c4fdcb0e
parent4c50f639c7e67098eba14bc41869dd5c354d0da6 (diff)
downloadCMake-e6d1e29ffa6bd3141a769d1281f3407ed0774139.zip
CMake-e6d1e29ffa6bd3141a769d1281f3407ed0774139.tar.gz
CMake-e6d1e29ffa6bd3141a769d1281f3407ed0774139.tar.bz2
cmArgumentParser: Model maybe-empty and non-empty lists with wrapper types
Previously bindings to `std::vector<std::string>` required at least one value. Some clients have been filtering `keywordsMissingValue` to support keywords followed by empty lists. Instead, require clients to specify whether a keyword's list can be empty as part of the binding type.
-rw-r--r--Source/CTest/cmCTestCoverageCommand.h3
-rw-r--r--Source/CTest/cmCTestSubmitCommand.h7
-rw-r--r--Source/CTest/cmCTestUploadCommand.h3
-rw-r--r--Source/cmArgumentParser.cxx11
-rw-r--r--Source/cmArgumentParser.h5
-rw-r--r--Source/cmArgumentParserTypes.h19
-rw-r--r--Source/cmCMakeLanguageCommand.cxx3
-rw-r--r--Source/cmDefinePropertyCommand.cxx5
-rw-r--r--Source/cmExportCommand.cxx3
-rw-r--r--Source/cmFileCommand.cxx72
-rw-r--r--Source/cmInstallCommand.cxx60
-rw-r--r--Source/cmInstallCommandArguments.h5
-rw-r--r--Source/cmParseArgumentsCommand.cxx4
-rw-r--r--Source/cmTargetSourcesCommand.cxx5
-rw-r--r--Tests/CMakeLib/testArgumentParser.cxx17
15 files changed, 128 insertions, 94 deletions
diff --git a/Source/CTest/cmCTestCoverageCommand.h b/Source/CTest/cmCTestCoverageCommand.h
index dc8f71e..55c68b2 100644
--- a/Source/CTest/cmCTestCoverageCommand.h
+++ b/Source/CTest/cmCTestCoverageCommand.h
@@ -11,6 +11,7 @@
#include <cm/memory>
#include <cm/optional>
+#include "cmArgumentParserTypes.h" // IWYU pragma: keep
#include "cmCTestHandlerCommand.h"
#include "cmCommand.h"
@@ -44,5 +45,5 @@ protected:
void BindArguments() override;
cmCTestGenericHandler* InitializeHandler() override;
- cm::optional<std::vector<std::string>> Labels;
+ cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Labels;
};
diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h
index 903bb64..b67f182 100644
--- a/Source/CTest/cmCTestSubmitCommand.h
+++ b/Source/CTest/cmCTestSubmitCommand.h
@@ -10,6 +10,7 @@
#include <cm/optional>
+#include "cmArgumentParserTypes.h"
#include "cmCTestHandlerCommand.h"
class cmCommand;
@@ -50,7 +51,7 @@ protected:
std::string RetryDelay;
std::string SubmitURL;
- cm::optional<std::vector<std::string>> Files;
- std::vector<std::string> HttpHeaders;
- cm::optional<std::vector<std::string>> Parts;
+ cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Files;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> HttpHeaders;
+ cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Parts;
};
diff --git a/Source/CTest/cmCTestUploadCommand.h b/Source/CTest/cmCTestUploadCommand.h
index 0dad2bf..a9d1dd2 100644
--- a/Source/CTest/cmCTestUploadCommand.h
+++ b/Source/CTest/cmCTestUploadCommand.h
@@ -10,6 +10,7 @@
#include <cm/memory>
+#include "cmArgumentParserTypes.h"
#include "cmCTestHandlerCommand.h"
#include "cmCommand.h"
@@ -45,5 +46,5 @@ protected:
void CheckArguments() override;
cmCTestGenericHandler* InitializeHandler() override;
- std::vector<std::string> Files;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Files;
};
diff --git a/Source/cmArgumentParser.cxx b/Source/cmArgumentParser.cxx
index 1a05dbc..1bb9051 100644
--- a/Source/cmArgumentParser.cxx
+++ b/Source/cmArgumentParser.cxx
@@ -4,6 +4,8 @@
#include <algorithm>
+#include "cmArgumentParserTypes.h"
+
namespace ArgumentParser {
auto ActionMap::Emplace(cm::string_view name, Action action)
@@ -44,7 +46,14 @@ void Instance::Bind(std::string& val)
this->ExpectValue = true;
}
-void Instance::Bind(std::vector<std::string>& val)
+void Instance::Bind(MaybeEmpty<std::vector<std::string>>& val)
+{
+ this->CurrentString = nullptr;
+ this->CurrentList = &val;
+ this->ExpectValue = false;
+}
+
+void Instance::Bind(NonEmpty<std::vector<std::string>>& val)
{
this->CurrentString = nullptr;
this->CurrentList = &val;
diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h
index 8be5a9c..2f0a76d 100644
--- a/Source/cmArgumentParser.h
+++ b/Source/cmArgumentParser.h
@@ -14,6 +14,8 @@
#include <cm/string_view>
#include <cmext/string_view>
+#include "cmArgumentParserTypes.h" // IWYU pragma: keep
+
namespace ArgumentParser {
class Instance;
@@ -37,7 +39,8 @@ public:
void Bind(bool& val);
void Bind(std::string& val);
- void Bind(std::vector<std::string>& val);
+ void Bind(MaybeEmpty<std::vector<std::string>>& val);
+ void Bind(NonEmpty<std::vector<std::string>>& val);
void Bind(std::vector<std::vector<std::string>>& val);
// cm::optional<> records the presence the keyword to which it binds.
diff --git a/Source/cmArgumentParserTypes.h b/Source/cmArgumentParserTypes.h
new file mode 100644
index 0000000..7d4ce6f
--- /dev/null
+++ b/Source/cmArgumentParserTypes.h
@@ -0,0 +1,19 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+namespace ArgumentParser {
+
+template <typename T>
+struct MaybeEmpty : public T
+{
+};
+
+template <typename T>
+struct NonEmpty : public T
+{
+};
+
+} // namespace ArgumentParser
diff --git a/Source/cmCMakeLanguageCommand.cxx b/Source/cmCMakeLanguageCommand.cxx
index 287f45b..76561c3 100644
--- a/Source/cmCMakeLanguageCommand.cxx
+++ b/Source/cmCMakeLanguageCommand.cxx
@@ -14,6 +14,7 @@
#include <cmext/string_view>
#include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
#include "cmDependencyProvider.h"
#include "cmExecutionStatus.h"
#include "cmGlobalGenerator.h"
@@ -237,7 +238,7 @@ bool cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER(
struct SetProviderArgs
{
std::string Command;
- std::vector<std::string> Methods;
+ ArgumentParser::NonEmpty<std::vector<std::string>> Methods;
};
auto const ArgsParser =
diff --git a/Source/cmDefinePropertyCommand.cxx b/Source/cmDefinePropertyCommand.cxx
index faefcb8..31ee665 100644
--- a/Source/cmDefinePropertyCommand.cxx
+++ b/Source/cmDefinePropertyCommand.cxx
@@ -8,6 +8,7 @@
#include <cmext/string_view>
#include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
#include "cmExecutionStatus.h"
#include "cmMakefile.h"
#include "cmProperty.h"
@@ -51,8 +52,8 @@ bool cmDefinePropertyCommand(std::vector<std::string> const& args,
// Parse remaining arguments.
bool inherited = false;
std::string PropertyName;
- std::vector<std::string> BriefDocs;
- std::vector<std::string> FullDocs;
+ ArgumentParser::NonEmpty<std::vector<std::string>> BriefDocs;
+ ArgumentParser::NonEmpty<std::vector<std::string>> FullDocs;
std::string initializeFromVariable;
cmArgumentParser<void> parser;
diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx
index 4071f99..a58f2b7 100644
--- a/Source/cmExportCommand.cxx
+++ b/Source/cmExportCommand.cxx
@@ -13,6 +13,7 @@
#include "cmsys/RegularExpression.hxx"
#include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
#include "cmExecutionStatus.h"
#include "cmExperimental.h"
#include "cmExportBuildAndroidMKGenerator.h"
@@ -58,7 +59,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
struct Arguments
{
std::string ExportSetName;
- cm::optional<std::vector<std::string>> Targets;
+ cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Targets;
std::string Namespace;
std::string Filename;
std::string AndroidMKFile;
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index 45ec343..22ab5f7 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -30,6 +30,7 @@
#include "cmAlgorithms.h"
#include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
#include "cmCMakePath.h"
#include "cmCryptoHash.h"
#include "cmELF.h"
@@ -2505,7 +2506,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
cm::optional<std::string> NewLineStyle;
bool NoSourcePermissions = false;
bool UseSourcePermissions = false;
- std::vector<std::string> FilePermissions;
+ ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions;
};
static auto const parser =
@@ -3052,17 +3053,18 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
std::string ConflictingDependenciesPrefix;
std::string RPathPrefix;
std::string BundleExecutable;
- std::vector<std::string> Executables;
- std::vector<std::string> Libraries;
- std::vector<std::string> Directories;
- std::vector<std::string> Modules;
- std::vector<std::string> PreIncludeRegexes;
- std::vector<std::string> PreExcludeRegexes;
- std::vector<std::string> PostIncludeRegexes;
- std::vector<std::string> PostExcludeRegexes;
- std::vector<std::string> PostIncludeFiles;
- std::vector<std::string> PostExcludeFiles;
- std::vector<std::string> PostExcludeFilesStrict;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Executables;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Libraries;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Modules;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> PreIncludeRegexes;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> PreExcludeRegexes;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeRegexes;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeRegexes;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeFiles;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeFiles;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>>
+ PostExcludeFilesStrict;
};
static auto const parser =
@@ -3098,25 +3100,10 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
return false;
}
- // Arguments that are allowed to be empty lists. Keep entries sorted!
- static const std::vector<cm::string_view> LIST_ARGS = {
- "DIRECTORIES"_s,
- "EXECUTABLES"_s,
- "LIBRARIES"_s,
- "MODULES"_s,
- "POST_EXCLUDE_FILES"_s,
- "POST_EXCLUDE_FILES_STRICT"_s,
- "POST_EXCLUDE_REGEXES"_s,
- "POST_INCLUDE_FILES"_s,
- "POST_INCLUDE_REGEXES"_s,
- "PRE_EXCLUDE_REGEXES"_s,
- "PRE_INCLUDE_REGEXES"_s,
- };
- auto kwbegin = keywordsMissingValues.cbegin();
- auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS);
- if (kwend != kwbegin) {
- status.SetError(cmStrCat("Keywords missing values:\n ",
- cmJoin(cmMakeRange(kwbegin, kwend), "\n ")));
+ if (!keywordsMissingValues.empty()) {
+ status.SetError(
+ cmStrCat("Keywords missing values:\n ",
+ cmJoin(cmMakeRange(keywordsMissingValues), "\n ")));
cmSystemTools::SetFatalErrorOccurred();
return false;
}
@@ -3362,7 +3349,8 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
std::string CompressionLevel;
std::string MTime;
bool Verbose = false;
- std::vector<std::string> Paths;
+ // "PATHS" requires at least one value, but use a custom check below.
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Paths;
};
static auto const parser =
@@ -3393,7 +3381,6 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
// value, but it has long been accidentally accepted without
// one and treated as if an empty value were given.
// Fixing this would require a policy.
- "PATHS"_s, // "PATHS" is here only so we can issue a custom error below.
};
auto kwbegin = keywordsMissingValues.cbegin();
auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS);
@@ -3496,7 +3483,7 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
bool Verbose = false;
bool ListOnly = false;
std::string Destination;
- std::vector<std::string> Patterns;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Patterns;
bool Touch = false;
};
@@ -3520,13 +3507,10 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
return false;
}
- // Arguments that are allowed to be empty lists. Keep entries sorted!
- static const std::vector<cm::string_view> LIST_ARGS = { "PATTERNS"_s };
- auto kwbegin = keywordsMissingValues.cbegin();
- auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS);
- if (kwend != kwbegin) {
- status.SetError(cmStrCat("Keywords missing values:\n ",
- cmJoin(cmMakeRange(kwbegin, kwend), "\n ")));
+ if (!keywordsMissingValues.empty()) {
+ status.SetError(
+ cmStrCat("Keywords missing values:\n ",
+ cmJoin(cmMakeRange(keywordsMissingValues), "\n ")));
cmSystemTools::SetFatalErrorOccurred();
return false;
}
@@ -3620,9 +3604,9 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
struct Arguments
{
- std::vector<std::string> Permissions;
- std::vector<std::string> FilePermissions;
- std::vector<std::string> DirectoryPermissions;
+ ArgumentParser::NonEmpty<std::vector<std::string>> Permissions;
+ ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions;
+ ArgumentParser::NonEmpty<std::vector<std::string>> DirectoryPermissions;
};
static auto const parser =
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index 35ddf34..b1044a8 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -19,6 +19,7 @@
#include "cmsys/Glob.hxx"
#include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
#include "cmExecutionStatus.h"
#include "cmExperimental.h"
#include "cmExportSet.h"
@@ -57,13 +58,13 @@ namespace {
struct RuntimeDependenciesArgs
{
- std::vector<std::string> Directories;
- std::vector<std::string> PreIncludeRegexes;
- std::vector<std::string> PreExcludeRegexes;
- std::vector<std::string> PostIncludeRegexes;
- std::vector<std::string> PostExcludeRegexes;
- std::vector<std::string> PostIncludeFiles;
- std::vector<std::string> PostExcludeFiles;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> PreIncludeRegexes;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> PreExcludeRegexes;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeRegexes;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeRegexes;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeFiles;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeFiles;
};
auto const RuntimeDependenciesArgHelper =
@@ -406,18 +407,18 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
struct ArgVectors
{
- std::vector<std::string> Archive;
- std::vector<std::string> Library;
- std::vector<std::string> Runtime;
- std::vector<std::string> Object;
- std::vector<std::string> Framework;
- std::vector<std::string> Bundle;
- std::vector<std::string> Includes;
- std::vector<std::string> PrivateHeader;
- std::vector<std::string> PublicHeader;
- std::vector<std::string> Resource;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Archive;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Library;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Object;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Bundle;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Includes;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> PrivateHeader;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> PublicHeader;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Resource;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> CxxModulesBmi;
std::vector<std::vector<std::string>> FileSets;
- std::vector<std::string> CxxModulesBmi;
};
static auto const argHelper =
@@ -441,9 +442,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
// now parse the generic args (i.e. the ones not specialized on LIBRARY/
// ARCHIVE, RUNTIME etc. (see above)
// These generic args also contain the targets and the export stuff
- std::vector<std::string> targetList;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> targetList;
std::string exports;
- cm::optional<std::vector<std::string>> runtimeDependenciesArgVector;
+ cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>>
+ runtimeDependenciesArgVector;
std::string runtimeDependencySetArg;
std::vector<std::string> unknownArgs;
cmInstallCommandArguments genericArgs(helper.DefaultComponentName);
@@ -1251,10 +1253,10 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args,
struct ArgVectors
{
- std::vector<std::string> Library;
- std::vector<std::string> Runtime;
- std::vector<std::string> Framework;
- std::vector<std::string> Bundle;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Library;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Bundle;
};
static auto const argHelper = cmArgumentParser<ArgVectors>{}
@@ -1268,7 +1270,7 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args,
// now parse the generic args (i.e. the ones not specialized on LIBRARY,
// RUNTIME etc. (see above)
- std::vector<std::string> targetList;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> targetList;
std::string runtimeDependencySetArg;
std::vector<std::string> unknownArgs;
cmInstallCommandArguments genericArgs(helper.DefaultComponentName);
@@ -1509,7 +1511,7 @@ bool HandleFilesMode(std::vector<std::string> const& args,
// This is the FILES mode.
bool programs = (args[0] == "PROGRAMS");
cmInstallCommandArguments ica(helper.DefaultComponentName);
- std::vector<std::string> files;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> files;
ica.Bind(programs ? "PROGRAMS"_s : "FILES"_s, files);
std::vector<std::string> unknownArgs;
ica.Parse(args, &unknownArgs);
@@ -2140,9 +2142,9 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args,
struct ArgVectors
{
- std::vector<std::string> Library;
- std::vector<std::string> Runtime;
- std::vector<std::string> Framework;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Library;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework;
};
static auto const argHelper = cmArgumentParser<ArgVectors>{}
diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h
index 79bd945..6e46aac 100644
--- a/Source/cmInstallCommandArguments.h
+++ b/Source/cmInstallCommandArguments.h
@@ -8,6 +8,7 @@
#include <vector>
#include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
class cmInstallCommandArguments : public cmArgumentParser<void>
{
@@ -44,8 +45,8 @@ private:
std::string NamelinkComponent;
bool ExcludeFromAll = false;
std::string Rename;
- std::vector<std::string> Permissions;
- std::vector<std::string> Configurations;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Permissions;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Configurations;
bool Optional = false;
bool NamelinkOnly = false;
bool NamelinkSkip = false;
diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx
index 5ea35e8..31538b6 100644
--- a/Source/cmParseArgumentsCommand.cxx
+++ b/Source/cmParseArgumentsCommand.cxx
@@ -10,6 +10,7 @@
#include <cm/string_view>
#include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
#include "cmExecutionStatus.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
@@ -41,7 +42,8 @@ namespace {
using options_map = std::map<std::string, bool>;
using single_map = std::map<std::string, std::string>;
-using multi_map = std::map<std::string, std::vector<std::string>>;
+using multi_map =
+ std::map<std::string, ArgumentParser::NonEmpty<std::vector<std::string>>>;
using options_set = std::set<cm::string_view>;
struct UserArgumentParser : public cmArgumentParser<void>
diff --git a/Source/cmTargetSourcesCommand.cxx b/Source/cmTargetSourcesCommand.cxx
index 74945fa..e2b0213 100644
--- a/Source/cmTargetSourcesCommand.cxx
+++ b/Source/cmTargetSourcesCommand.cxx
@@ -9,6 +9,7 @@
#include <cmext/string_view>
#include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
#include "cmExperimental.h"
#include "cmFileSet.h"
#include "cmGeneratorExpression.h"
@@ -28,8 +29,8 @@ struct FileSetArgs
{
std::string Type;
std::string FileSet;
- std::vector<std::string> BaseDirs;
- std::vector<std::string> Files;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> BaseDirs;
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> Files;
};
auto const FileSetArgsParser = cmArgumentParser<FileSetArgs>()
diff --git a/Tests/CMakeLib/testArgumentParser.cxx b/Tests/CMakeLib/testArgumentParser.cxx
index 52c5861..85650ed 100644
--- a/Tests/CMakeLib/testArgumentParser.cxx
+++ b/Tests/CMakeLib/testArgumentParser.cxx
@@ -11,6 +11,7 @@
#include <cmext/string_view>
#include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
namespace {
@@ -23,11 +24,12 @@ struct Result
cm::optional<std::string> String2;
cm::optional<std::string> String3;
- std::vector<std::string> List1;
- std::vector<std::string> List2;
- cm::optional<std::vector<std::string>> List3;
- cm::optional<std::vector<std::string>> List4;
- cm::optional<std::vector<std::string>> List5;
+ ArgumentParser::NonEmpty<std::vector<std::string>> List1;
+ ArgumentParser::NonEmpty<std::vector<std::string>> List2;
+ cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> List3;
+ cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> List4;
+ cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> List5;
+ cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> List6;
std::vector<std::vector<std::string>> Multi1;
std::vector<std::vector<std::string>> Multi2;
@@ -48,6 +50,7 @@ std::initializer_list<cm::string_view> const args = {
"LIST_3", "foo", // ... with continuation
"LIST_4", // list arg missing values, presence captured
// "LIST_5", // list arg that is not present
+ "LIST_6", // list arg allowed to be empty
"MULTI_2", // multi list with 0 lists
"MULTI_3", "foo", "bar", // multi list with first list with two elems
"MULTI_3", "bar", "foo", // multi list with second list with two elems
@@ -88,6 +91,8 @@ bool verifyResult(Result const& result,
ASSERT_TRUE(result.List4);
ASSERT_TRUE(result.List4->empty());
ASSERT_TRUE(!result.List5);
+ ASSERT_TRUE(result.List6);
+ ASSERT_TRUE(result.List6->empty());
ASSERT_TRUE(result.Multi1.empty());
ASSERT_TRUE(result.Multi2.size() == 1);
@@ -122,6 +127,7 @@ bool testArgumentParserDynamic()
.Bind("LIST_3"_s, result.List3)
.Bind("LIST_4"_s, result.List4)
.Bind("LIST_5"_s, result.List5)
+ .Bind("LIST_6"_s, result.List6)
.Bind("MULTI_1"_s, result.Multi1)
.Bind("MULTI_2"_s, result.Multi2)
.Bind("MULTI_3"_s, result.Multi3)
@@ -145,6 +151,7 @@ bool testArgumentParserStatic()
.Bind("LIST_3"_s, &Result::List3)
.Bind("LIST_4"_s, &Result::List4)
.Bind("LIST_5"_s, &Result::List5)
+ .Bind("LIST_6"_s, &Result::List6)
.Bind("MULTI_1"_s, &Result::Multi1)
.Bind("MULTI_2"_s, &Result::Multi2)
.Bind("MULTI_3"_s, &Result::Multi3)