add_custom_{command,target}: Add COMMAND_EXPAND_LISTS option

This option allows lists generated by generator expressions to be expanded.

Closes: #15935
This commit is contained in:
Ed Branch 2017-01-13 20:01:17 -06:00
parent eee51e24d2
commit 7c8ab7ddc8
13 changed files with 146 additions and 26 deletions

View File

@ -21,7 +21,8 @@ The first signature is for adding a custom command to produce an output::
[WORKING_DIRECTORY dir]
[COMMENT comment]
[DEPFILE depfile]
[VERBATIM] [APPEND] [USES_TERMINAL])
[VERBATIM] [APPEND] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS])
This defines a command to generate specified ``OUTPUT`` file(s).
A target created in the same directory (``CMakeLists.txt`` file)
@ -122,6 +123,14 @@ The options are:
Arguments to ``DEPENDS`` may use
:manual:`generator expressions <cmake-generator-expressions(7)>`.
``COMMAND_EXPAND_LISTS``
Lists in ``COMMAND`` arguments will be expanded, including those
created with
:manual:`generator expressions <cmake-generator-expressions(7)>`,
allowing ``COMMAND`` arguments such as
``${CC} "-I$<JOIN:$<TARGET_PROPERTY:foo,INCLUDE_DIRECTORIES>,;-I>" foo.cc``
to be properly expanded.
``IMPLICIT_DEPENDS``
Request scanning of implicit dependencies of an input file.
The language given specifies the programming language whose

View File

@ -12,6 +12,7 @@ Add a target with no output so it will always be built.
[WORKING_DIRECTORY dir]
[COMMENT comment]
[VERBATIM] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS]
[SOURCES src1 [src2...]])
Adds a target with the given name that executes the given commands.
@ -88,6 +89,14 @@ The options are:
Use the :command:`add_dependencies` command to add dependencies
on other targets.
``COMMAND_EXPAND_LISTS``
Lists in ``COMMAND`` arguments will be expanded, including those
created with
:manual:`generator expressions <cmake-generator-expressions(7)>`,
allowing ``COMMAND`` arguments such as
``${CC} "-I$<JOIN:$<TARGET_PROPERTY:foo,INCLUDE_DIRECTORIES>,;-I>" foo.cc``
to be properly expanded.
``SOURCES``
Specify additional source files to be included in the custom target.
Specified source files will be added to IDE project files for

View File

@ -0,0 +1,7 @@
expand_custom_commands
----------------------
* The commands :command:`add_custom_command` and :command:`add_custom_target`
learned the option ``COMMAND_EXPAND_LISTS`` which causes lists in the
``COMMAND`` argument to be expanded, including lists created by generator
expressions.

View File

@ -36,6 +36,7 @@ bool cmAddCustomCommandCommand::InitialPass(
bool verbatim = false;
bool append = false;
bool uses_terminal = false;
bool command_expand_lists = false;
std::string implicit_depends_lang;
cmCustomCommand::ImplicitDependsList implicit_depends;
@ -92,6 +93,8 @@ bool cmAddCustomCommandCommand::InitialPass(
append = true;
} else if (copy == "USES_TERMINAL") {
uses_terminal = true;
} else if (copy == "COMMAND_EXPAND_LISTS") {
command_expand_lists = true;
} else if (copy == "TARGET") {
doing = doing_target;
} else if (copy == "ARGS") {
@ -281,12 +284,14 @@ bool cmAddCustomCommandCommand::InitialPass(
std::vector<std::string> no_depends;
this->Makefile->AddCustomCommandToTarget(
target, byproducts, no_depends, commandLines, cctype, comment,
working.c_str(), escapeOldStyle, uses_terminal, depfile);
working.c_str(), escapeOldStyle, uses_terminal, depfile,
command_expand_lists);
} else if (target.empty()) {
// Target is empty, use the output.
this->Makefile->AddCustomCommandToOutput(
output, byproducts, depends, main_dependency, commandLines, comment,
working.c_str(), false, escapeOldStyle, uses_terminal, depfile);
working.c_str(), false, escapeOldStyle, uses_terminal,
command_expand_lists, depfile);
// Add implicit dependency scanning requests if any were given.
if (!implicit_depends.empty()) {

View File

@ -47,6 +47,7 @@ bool cmAddCustomTargetCommand::InitialPass(
std::string working_directory;
bool verbatim = false;
bool uses_terminal = false;
bool command_expand_lists = false;
std::string comment_buffer;
const char* comment = CM_NULLPTR;
std::vector<std::string> sources;
@ -90,6 +91,9 @@ bool cmAddCustomTargetCommand::InitialPass(
} else if (copy == "USES_TERMINAL") {
doing = doing_nothing;
uses_terminal = true;
} else if (copy == "COMMAND_EXPAND_LISTS") {
doing = doing_nothing;
command_expand_lists = true;
} else if (copy == "COMMENT") {
doing = doing_comment;
} else if (copy == "COMMAND") {
@ -221,12 +225,19 @@ bool cmAddCustomTargetCommand::InitialPass(
"USES_TERMINAL may not be specified without any COMMAND");
return true;
}
if (commandLines.empty() && command_expand_lists) {
this->Makefile->IssueMessage(
cmake::FATAL_ERROR,
"COMMAND_EXPAND_LISTS may not be specified without any COMMAND");
return true;
}
// Add the utility target to the makefile.
bool escapeOldStyle = !verbatim;
cmTarget* target = this->Makefile->AddUtilityCommand(
targetName, excludeFromAll, working_directory.c_str(), byproducts, depends,
commandLines, escapeOldStyle, comment, uses_terminal);
commandLines, escapeOldStyle, comment, uses_terminal,
command_expand_lists);
// Add additional user-specified source files to the target.
target->AddSources(sources);

View File

@ -13,6 +13,7 @@ cmCustomCommand::cmCustomCommand()
this->EscapeOldStyle = true;
this->EscapeAllowMakeVars = false;
this->UsesTerminal = false;
this->CommandExpandLists = false;
}
cmCustomCommand::cmCustomCommand(cmMakefile const* mf,
@ -32,6 +33,7 @@ cmCustomCommand::cmCustomCommand(cmMakefile const* mf,
, HaveComment(comment != CM_NULLPTR)
, EscapeAllowMakeVars(false)
, EscapeOldStyle(true)
, CommandExpandLists(false)
{
if (mf) {
this->Backtrace = mf->GetBacktrace();
@ -127,6 +129,16 @@ void cmCustomCommand::SetUsesTerminal(bool b)
this->UsesTerminal = b;
}
bool cmCustomCommand::GetCommandExpandLists() const
{
return this->CommandExpandLists;
}
void cmCustomCommand::SetCommandExpandLists(bool b)
{
this->CommandExpandLists = b;
}
const std::string& cmCustomCommand::GetDepfile() const
{
return this->Depfile;

View File

@ -85,6 +85,10 @@ public:
bool GetUsesTerminal() const;
void SetUsesTerminal(bool b);
/** Set/Get whether lists in command lines should be expanded. */
bool GetCommandExpandLists() const;
void SetCommandExpandLists(bool b);
/** Set/Get the depfile (used by the Ninja generator) */
const std::string& GetDepfile() const;
void SetDepfile(const std::string& depfile);
@ -103,6 +107,7 @@ private:
bool EscapeAllowMakeVars;
bool EscapeOldStyle;
bool UsesTerminal;
bool CommandExpandLists;
};
#endif

View File

@ -26,6 +26,24 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc,
, GE(new cmGeneratorExpression(cc.GetBacktrace()))
, DependsDone(false)
{
const cmCustomCommandLines& cmdlines = this->CC.GetCommandLines();
for (cmCustomCommandLines::const_iterator cmdline = cmdlines.begin();
cmdline != cmdlines.end(); ++cmdline) {
cmCustomCommandLine argv;
for (cmCustomCommandLine::const_iterator clarg = cmdline->begin();
clarg != cmdline->end(); ++clarg) {
CM_AUTO_PTR<cmCompiledGeneratorExpression> cge = this->GE->Parse(*clarg);
std::string parsed_arg = cge->Evaluate(this->LG, this->Config);
if (this->CC.GetCommandExpandLists()) {
std::vector<std::string> ExpandedArg;
cmSystemTools::ExpandListArgument(parsed_arg, ExpandedArg);
argv.insert(argv.end(), ExpandedArg.begin(), ExpandedArg.end());
} else {
argv.push_back(parsed_arg);
}
}
this->CommandLines.push_back(argv);
}
}
cmCustomCommandGenerator::~cmCustomCommandGenerator()
@ -44,7 +62,7 @@ const char* cmCustomCommandGenerator::GetCrossCompilingEmulator(
if (!this->LG->GetMakefile()->IsOn("CMAKE_CROSSCOMPILING")) {
return CM_NULLPTR;
}
std::string const& argv0 = this->CC.GetCommandLines()[c][0];
std::string const& argv0 = this->CommandLines[c][0];
cmGeneratorTarget* target = this->LG->FindGeneratorTargetToUse(argv0);
if (target && target->GetType() == cmStateEnums::EXECUTABLE &&
!target->IsImported()) {
@ -55,7 +73,7 @@ const char* cmCustomCommandGenerator::GetCrossCompilingEmulator(
const char* cmCustomCommandGenerator::GetArgv0Location(unsigned int c) const
{
std::string const& argv0 = this->CC.GetCommandLines()[c][0];
std::string const& argv0 = this->CommandLines[c][0];
cmGeneratorTarget* target = this->LG->FindGeneratorTargetToUse(argv0);
if (target && target->GetType() == cmStateEnums::EXECUTABLE &&
(target->IsImported() ||
@ -75,11 +93,7 @@ std::string cmCustomCommandGenerator::GetCommand(unsigned int c) const
return std::string(location);
}
std::string const& argv0 = this->CC.GetCommandLines()[c][0];
CM_AUTO_PTR<cmCompiledGeneratorExpression> cge = this->GE->Parse(argv0);
std::string exe = cge->Evaluate(this->LG, this->Config);
return exe;
return this->CommandLines[c][0];
}
std::string escapeForShellOldStyle(const std::string& str)
@ -114,7 +128,7 @@ void cmCustomCommandGenerator::AppendArguments(unsigned int c,
if (this->GetCrossCompilingEmulator(c) != CM_NULLPTR) {
offset = 0;
}
cmCustomCommandLine const& commandLine = this->CC.GetCommandLines()[c];
cmCustomCommandLine const& commandLine = this->CommandLines[c];
for (unsigned int j = offset; j < commandLine.size(); ++j) {
std::string arg;
if (const char* location =
@ -123,7 +137,7 @@ void cmCustomCommandGenerator::AppendArguments(unsigned int c,
// so transform the latter now.
arg = location;
} else {
arg = this->GE->Parse(commandLine[j])->Evaluate(this->LG, this->Config);
arg = commandLine[j];
}
cmd += " ";
if (this->OldStyle) {

View File

@ -3,6 +3,7 @@
#ifndef cmCustomCommandGenerator_h
#define cmCustomCommandGenerator_h
#include "cmCustomCommandLines.h"
#include <cmConfigure.h> // IWYU pragma: keep
#include <string>
@ -22,6 +23,7 @@ class cmCustomCommandGenerator
cmGeneratorExpression* GE;
mutable bool DependsDone;
mutable std::vector<std::string> Depends;
cmCustomCommandLines CommandLines;
const char* GetCrossCompilingEmulator(unsigned int c) const;
const char* GetArgv0Location(unsigned int c) const;

View File

@ -692,7 +692,7 @@ void cmMakefile::AddCustomCommandToTarget(
const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type,
const char* comment, const char* workingDir, bool escapeOldStyle,
bool uses_terminal, const std::string& depfile)
bool uses_terminal, const std::string& depfile, bool command_expand_lists)
{
// Find the target to which to add the custom command.
cmTargets::iterator ti = this->Targets.find(target);
@ -764,6 +764,7 @@ void cmMakefile::AddCustomCommandToTarget(
cc.SetEscapeOldStyle(escapeOldStyle);
cc.SetEscapeAllowMakeVars(true);
cc.SetUsesTerminal(uses_terminal);
cc.SetCommandExpandLists(command_expand_lists);
cc.SetDepfile(depfile);
switch (type) {
case cmTarget::PRE_BUILD:
@ -784,7 +785,7 @@ cmSourceFile* cmMakefile::AddCustomCommandToOutput(
const std::vector<std::string>& depends, const std::string& main_dependency,
const cmCustomCommandLines& commandLines, const char* comment,
const char* workingDir, bool replace, bool escapeOldStyle,
bool uses_terminal, const std::string& depfile)
bool uses_terminal, bool command_expand_lists, const std::string& depfile)
{
// Make sure there is at least one output.
if (outputs.empty()) {
@ -878,6 +879,7 @@ cmSourceFile* cmMakefile::AddCustomCommandToOutput(
cc->SetEscapeOldStyle(escapeOldStyle);
cc->SetEscapeAllowMakeVars(true);
cc->SetUsesTerminal(uses_terminal);
cc->SetCommandExpandLists(command_expand_lists);
cc->SetDepfile(depfile);
file->SetCustomCommand(cc);
this->UpdateOutputToSourceMap(outputs, file);
@ -916,14 +918,16 @@ cmSourceFile* cmMakefile::AddCustomCommandToOutput(
const std::string& output, const std::vector<std::string>& depends,
const std::string& main_dependency, const cmCustomCommandLines& commandLines,
const char* comment, const char* workingDir, bool replace,
bool escapeOldStyle, bool uses_terminal, const std::string& depfile)
bool escapeOldStyle, bool uses_terminal, bool command_expand_lists,
const std::string& depfile)
{
std::vector<std::string> outputs;
outputs.push_back(output);
std::vector<std::string> no_byproducts;
return this->AddCustomCommandToOutput(
outputs, no_byproducts, depends, main_dependency, commandLines, comment,
workingDir, replace, escapeOldStyle, uses_terminal, depfile);
workingDir, replace, escapeOldStyle, uses_terminal, command_expand_lists,
depfile);
}
void cmMakefile::AddCustomCommandOldStyle(
@ -1018,12 +1022,13 @@ cmTarget* cmMakefile::AddUtilityCommand(
const std::string& utilityName, bool excludeFromAll,
const char* workingDirectory, const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines, bool escapeOldStyle,
const char* comment, bool uses_terminal)
const char* comment, bool uses_terminal, bool command_expand_lists)
{
std::vector<std::string> no_byproducts;
return this->AddUtilityCommand(utilityName, excludeFromAll, workingDirectory,
no_byproducts, depends, commandLines,
escapeOldStyle, comment, uses_terminal);
escapeOldStyle, comment, uses_terminal,
command_expand_lists);
}
cmTarget* cmMakefile::AddUtilityCommand(
@ -1031,7 +1036,7 @@ cmTarget* cmMakefile::AddUtilityCommand(
const char* workingDirectory, const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines, bool escapeOldStyle,
const char* comment, bool uses_terminal)
const char* comment, bool uses_terminal, bool command_expand_lists)
{
// Create a target instance for this utility.
cmTarget* target = this->AddNewTarget(cmStateEnums::UTILITY, utilityName);
@ -1055,7 +1060,8 @@ cmTarget* cmMakefile::AddUtilityCommand(
bool no_replace = false;
this->AddCustomCommandToOutput(
forced, byproducts, depends, no_main_dependency, commandLines, comment,
workingDirectory, no_replace, escapeOldStyle, uses_terminal);
workingDirectory, no_replace, escapeOldStyle, uses_terminal,
command_expand_lists);
cmSourceFile* sf = target->AddSourceCMP0049(force);
// The output is not actually created so mark it symbolic.

View File

@ -125,7 +125,8 @@ public:
const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type,
const char* comment, const char* workingDir, bool escapeOldStyle = true,
bool uses_terminal = false, const std::string& depfile = "");
bool uses_terminal = false, const std::string& depfile = "",
bool command_expand_lists = false);
cmSourceFile* AddCustomCommandToOutput(
const std::vector<std::string>& outputs,
const std::vector<std::string>& byproducts,
@ -133,13 +134,15 @@ public:
const std::string& main_dependency,
const cmCustomCommandLines& commandLines, const char* comment,
const char* workingDir, bool replace = false, bool escapeOldStyle = true,
bool uses_terminal = false, const std::string& depfile = "");
bool uses_terminal = false, bool command_expand_lists = false,
const std::string& depfile = "");
cmSourceFile* AddCustomCommandToOutput(
const std::string& output, const std::vector<std::string>& depends,
const std::string& main_dependency,
const cmCustomCommandLines& commandLines, const char* comment,
const char* workingDir, bool replace = false, bool escapeOldStyle = true,
bool uses_terminal = false, const std::string& depfile = "");
bool uses_terminal = false, bool command_expand_lists = false,
const std::string& depfile = "");
void AddCustomCommandOldStyle(const std::string& target,
const std::vector<std::string>& outputs,
const std::vector<std::string>& depends,
@ -182,13 +185,15 @@ public:
const std::string& utilityName, bool excludeFromAll,
const char* workingDirectory, const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines, bool escapeOldStyle = true,
const char* comment = CM_NULLPTR, bool uses_terminal = false);
const char* comment = CM_NULLPTR, bool uses_terminal = false,
bool command_expand_lists = false);
cmTarget* AddUtilityCommand(
const std::string& utilityName, bool excludeFromAll,
const char* workingDirectory, const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines, bool escapeOldStyle = true,
const char* comment = CM_NULLPTR, bool uses_terminal = false);
const char* comment = CM_NULLPTR, bool uses_terminal = false,
bool command_expand_lists = false);
/**
* Add a subdirectory to the build.

View File

@ -513,3 +513,24 @@ add_custom_target(UseConsoleTarget ALL
VERBATIM
USES_TERMINAL
)
# Test COMMAND_EXPAND_LISTS
set(cmp_args "1ARG=COMMAND_EXPAND_LISTS" "2ARG=test" "3ARG=outfile"
"4ARG=content")
set(AARGS "")
foreach(arg IN LISTS cmp_args)
list(APPEND AARGS "-DA${arg}")
endforeach()
set(gen_file "expand_custom_command.phony")
add_custom_command(
OUTPUT "${gen_file}"
COMMAND ${CMAKE_COMMAND} ${AARGS}
"-DB$<JOIN:$<TARGET_PROPERTY:command_expand_lists,CMPARGS>,;-DB>"
"-P" "${CMAKE_CURRENT_SOURCE_DIR}/compare_options.cmake"
COMMAND_EXPAND_LISTS
VERBATIM
)
set_property(SOURCE "${gen_file}" PROPERTY SYMBOLIC ON)
add_custom_target(command_expand_lists ALL DEPENDS "${gen_file}")
set_property(TARGET command_expand_lists PROPERTY CMPARGS "${cmp_args}")

View File

@ -0,0 +1,14 @@
set(range 1 2 3 4 5 6 7 8 9 10)
set(aargs "")
set(bargs "")
foreach(n IN LISTS range)
set(aval "${A${n}ARG}")
set(bval "${B${n}ARG}")
if(aval OR bval)
list(APPEND aargs "\"${aval}\"")
list(APPEND bargs "\"${bval}\"")
endif()
endforeach()
if(NOT "${aargs}" STREQUAL "${bargs}")
message(FATAL_ERROR "COMPARE_OPTIONS: \n\t${aargs} != \n\t${bargs}")
endif()