install: Parse SCRIPT and CODE arguments with cmArgumentParser

This commit is contained in:
Taylor Sasser
2026-09-06 04:45:37 -04:00
parent 026c254a8e
commit 8997bb6431
6 changed files with 119 additions and 64 deletions
+59 -64
View File
@@ -316,89 +316,84 @@ void CheckAbsoluteDestination(Helper& helper, std::string const& destination)
bool HandleScriptMode(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
Helper helper(status);
struct Arguments : ArgumentParser::ParseResult
{
struct Script
{
std::string Value;
bool IsCode;
};
std::string component = helper.DefaultComponentName;
int componentCount = 0;
bool doingScript = false;
bool doingCode = false;
bool excludeFromAll = false;
bool allComponents = false;
ArgumentParser::Continue AddScript(cm::string_view keyword,
cm::string_view value)
{
this->Scripts.push_back({ std::string(value), keyword == "CODE"_s });
return ArgumentParser::Continue::No;
}
// Scan the args once for COMPONENT. Only allow one.
//
for (size_t i = 0; i < args.size(); ++i) {
if (args[i] == "COMPONENT" && i + 1 < args.size()) {
++componentCount;
++i;
component = args[i];
}
if (args[i] == "EXCLUDE_FROM_ALL") {
excludeFromAll = true;
} else if (args[i] == "ALL_COMPONENTS") {
allComponents = true;
ArgumentParser::Continue AddComponent(cm::string_view value)
{
this->Components.emplace_back(value);
return ArgumentParser::Continue::No;
}
std::vector<Script> Scripts;
std::vector<std::string> Components;
bool ExcludeFromAll = false;
bool AllComponents = false;
};
static auto const parser =
cmArgumentParser<Arguments>{}
.Bind("SCRIPT"_s, &Arguments::AddScript)
.Bind("CODE"_s, &Arguments::AddScript)
.Bind("COMPONENT"_s, &Arguments::AddComponent)
.Bind("EXCLUDE_FROM_ALL"_s, &Arguments::ExcludeFromAll)
.Bind("ALL_COMPONENTS"_s, &Arguments::AllComponents);
std::vector<std::string> unknownArgs;
Arguments arguments = parser.Parse(args, &unknownArgs);
if (!arguments.Check(args[0], &unknownArgs, status)) {
return false;
}
if (componentCount > 1) {
if (arguments.Components.size() > 1) {
status.SetError("given more than one COMPONENT for the SCRIPT or CODE "
"signature of the INSTALL command. "
"Use multiple INSTALL commands with one COMPONENT each.");
return false;
}
if (allComponents && componentCount == 1) {
if (arguments.AllComponents && !arguments.Components.empty()) {
status.SetError("ALL_COMPONENTS and COMPONENT are mutually exclusive");
return false;
}
// Scan the args again, this time adding install generators each time we
// encounter a SCRIPT or CODE arg:
//
for (std::string const& arg : args) {
if (arg == "SCRIPT") {
doingScript = true;
doingCode = false;
} else if (arg == "CODE") {
doingScript = false;
doingCode = true;
} else if (arg == "COMPONENT") {
doingScript = false;
doingCode = false;
} else if (doingScript) {
doingScript = false;
std::string script = arg;
if (!cmHasLiteralPrefix(script, "$<INSTALL_PREFIX>")) {
if (!cmSystemTools::FileIsFullPath(script)) {
script =
cmStrCat(helper.Makefile->GetCurrentSourceDirectory(), '/', arg);
}
if (cmSystemTools::FileIsDirectory(script)) {
status.SetError("given a directory as value of SCRIPT argument.");
return false;
}
Helper helper(status);
std::string const component = arguments.Components.empty()
? helper.DefaultComponentName
: arguments.Components.front();
for (Arguments::Script& script : arguments.Scripts) {
if (!script.IsCode &&
!cmHasLiteralPrefix(script.Value, "$<INSTALL_PREFIX>")) {
if (!cmSystemTools::FileIsFullPath(script.Value)) {
script.Value = cmStrCat(helper.Makefile->GetCurrentSourceDirectory(),
'/', script.Value);
}
if (cmSystemTools::FileIsDirectory(script.Value)) {
status.SetError("given a directory as value of SCRIPT argument.");
return false;
}
helper.Makefile->AddInstallGenerator(
cm::make_unique<cmInstallScriptGenerator>(
script, false, component, excludeFromAll, allComponents,
helper.Makefile->GetBacktrace()));
} else if (doingCode) {
doingCode = false;
std::string const& code = arg;
helper.Makefile->AddInstallGenerator(
cm::make_unique<cmInstallScriptGenerator>(
code, true, component, excludeFromAll, allComponents,
helper.Makefile->GetBacktrace()));
}
}
if (doingScript) {
status.SetError("given no value for SCRIPT argument.");
return false;
}
if (doingCode) {
status.SetError("given no value for CODE argument.");
return false;
for (Arguments::Script& script : arguments.Scripts) {
helper.Makefile->AddInstallGenerator(
cm::make_unique<cmInstallScriptGenerator>(
std::move(script.Value), script.IsCode, component,
arguments.ExcludeFromAll, arguments.AllComponents,
helper.Makefile->GetBacktrace()));
}
// Tell the global generator about any installation component names
@@ -76,6 +76,7 @@ if (CMake_TEST_install_VARIANT STREQUAL "Files")
run_cmake(SkipInstallRulesWarning)
run_cmake(SkipInstallRulesNoWarning1)
run_cmake(SkipInstallRulesNoWarning2)
run_cmake(SCRIPT-CODE-arguments)
run_cmake(DIRECTORY-DIRECTORY-bad)
run_cmake(DIRECTORY-DESTINATION-bad)
run_cmake(FILES-DESTINATION-bad)
@@ -0,0 +1 @@
1
@@ -0,0 +1,50 @@
CMake Error at SCRIPT-CODE-arguments\.cmake:[0-9]+ \(install\):
install SCRIPT given invalid argument:
SCRIPT: missing required value
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at SCRIPT-CODE-arguments\.cmake:[0-9]+ \(install\):
install CODE given invalid argument:
CODE: missing required value
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at SCRIPT-CODE-arguments\.cmake:[0-9]+ \(install\):
install CODE given invalid argument:
CODE: missing required value
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at SCRIPT-CODE-arguments\.cmake:[0-9]+ \(install\):
install CODE given invalid argument:
COMPONENT: missing required value
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at SCRIPT-CODE-arguments\.cmake:[0-9]+ \(install\):
install CODE given unknown argument: "UNKNOWN"\.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at SCRIPT-CODE-arguments\.cmake:[0-9]+ \(install\):
install given more than one COMPONENT for the SCRIPT or CODE signature of
the INSTALL command\. Use multiple INSTALL commands with one COMPONENT
each\.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at SCRIPT-CODE-arguments\.cmake:[0-9]+ \(install\):
install ALL_COMPONENTS and COMPONENT are mutually exclusive
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
@@ -0,0 +1,7 @@
install(SCRIPT)
install(CODE)
install(CODE SCRIPT install_script.cmake)
install(CODE "" COMPONENT)
install(CODE "" UNKNOWN)
install(CODE "" COMPONENT "" COMPONENT "")
install(CODE "" COMPONENT "" ALL_COMPONENTS)
+1
View File
@@ -4,6 +4,7 @@ install(
)
install(
SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/install_script.cmake"
CODE ""
CODE "write_empty_file(empty2.txt)"
SCRIPT "$<INSTALL_PREFIX>/empty3.cmake"
CODE [[include($<INSTALL_PREFIX>/empty4.cmake)]]