From 7c8ab7ddc8944152a1344c43acbb8c57fa2d398a Mon Sep 17 00:00:00 2001 From: Ed Branch Date: Fri, 13 Jan 2017 20:01:17 -0600 Subject: add_custom_{command,target}: Add COMMAND_EXPAND_LISTS option This option allows lists generated by generator expressions to be expanded. Closes: #15935 --- Help/command/add_custom_command.rst | 11 +++++++++- Help/command/add_custom_target.rst | 9 ++++++++ Help/release/dev/expand_custom_commands.rst | 7 +++++++ Source/cmAddCustomCommandCommand.cxx | 9 ++++++-- Source/cmAddCustomTargetCommand.cxx | 13 +++++++++++- Source/cmCustomCommand.cxx | 12 +++++++++++ Source/cmCustomCommand.h | 5 +++++ Source/cmCustomCommandGenerator.cxx | 32 +++++++++++++++++++++-------- Source/cmCustomCommandGenerator.h | 2 ++ Source/cmMakefile.cxx | 22 ++++++++++++-------- Source/cmMakefile.h | 15 +++++++++----- Tests/CustomCommand/CMakeLists.txt | 21 +++++++++++++++++++ Tests/CustomCommand/compare_options.cmake | 14 +++++++++++++ 13 files changed, 146 insertions(+), 26 deletions(-) create mode 100644 Help/release/dev/expand_custom_commands.rst create mode 100644 Tests/CustomCommand/compare_options.cmake diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst index 4ab4298..80e7edf 100644 --- a/Help/command/add_custom_command.rst +++ b/Help/command/add_custom_command.rst @@ -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 `. +``COMMAND_EXPAND_LISTS`` + Lists in ``COMMAND`` arguments will be expanded, including those + created with + :manual:`generator expressions `, + allowing ``COMMAND`` arguments such as + ``${CC} "-I$,;-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 diff --git a/Help/command/add_custom_target.rst b/Help/command/add_custom_target.rst index 6980d61..bd61c8b 100644 --- a/Help/command/add_custom_target.rst +++ b/Help/command/add_custom_target.rst @@ -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 `, + allowing ``COMMAND`` arguments such as + ``${CC} "-I$,;-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 diff --git a/Help/release/dev/expand_custom_commands.rst b/Help/release/dev/expand_custom_commands.rst new file mode 100644 index 0000000..c8cd897 --- /dev/null +++ b/Help/release/dev/expand_custom_commands.rst @@ -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. diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx index 620de31..a100617 100644 --- a/Source/cmAddCustomCommandCommand.cxx +++ b/Source/cmAddCustomCommandCommand.cxx @@ -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 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()) { diff --git a/Source/cmAddCustomTargetCommand.cxx b/Source/cmAddCustomTargetCommand.cxx index 56f33b4..0d01493 100644 --- a/Source/cmAddCustomTargetCommand.cxx +++ b/Source/cmAddCustomTargetCommand.cxx @@ -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 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); diff --git a/Source/cmCustomCommand.cxx b/Source/cmCustomCommand.cxx index 6645b8e..050de17 100644 --- a/Source/cmCustomCommand.cxx +++ b/Source/cmCustomCommand.cxx @@ -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; diff --git a/Source/cmCustomCommand.h b/Source/cmCustomCommand.h index 66f8fa9..73d53ff 100644 --- a/Source/cmCustomCommand.h +++ b/Source/cmCustomCommand.h @@ -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 diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx index 8bd3a89..8f4ff4b 100644 --- a/Source/cmCustomCommandGenerator.cxx +++ b/Source/cmCustomCommandGenerator.cxx @@ -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 cge = this->GE->Parse(*clarg); + std::string parsed_arg = cge->Evaluate(this->LG, this->Config); + if (this->CC.GetCommandExpandLists()) { + std::vector 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 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) { diff --git a/Source/cmCustomCommandGenerator.h b/Source/cmCustomCommandGenerator.h index 8983c54..286aaf3 100644 --- a/Source/cmCustomCommandGenerator.h +++ b/Source/cmCustomCommandGenerator.h @@ -3,6 +3,7 @@ #ifndef cmCustomCommandGenerator_h #define cmCustomCommandGenerator_h +#include "cmCustomCommandLines.h" #include // IWYU pragma: keep #include @@ -22,6 +23,7 @@ class cmCustomCommandGenerator cmGeneratorExpression* GE; mutable bool DependsDone; mutable std::vector Depends; + cmCustomCommandLines CommandLines; const char* GetCrossCompilingEmulator(unsigned int c) const; const char* GetArgv0Location(unsigned int c) const; diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index cfc0495..bfe46ae 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -692,7 +692,7 @@ void cmMakefile::AddCustomCommandToTarget( const std::vector& 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& 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& 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 outputs; outputs.push_back(output); std::vector 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& depends, const cmCustomCommandLines& commandLines, bool escapeOldStyle, - const char* comment, bool uses_terminal) + const char* comment, bool uses_terminal, bool command_expand_lists) { std::vector 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& byproducts, const std::vector& 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. diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 3484e5a..9d9e90a 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -125,7 +125,8 @@ public: const std::vector& 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& outputs, const std::vector& 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& 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& outputs, const std::vector& depends, @@ -182,13 +185,15 @@ public: const std::string& utilityName, bool excludeFromAll, const char* workingDirectory, const std::vector& 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& byproducts, const std::vector& 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. diff --git a/Tests/CustomCommand/CMakeLists.txt b/Tests/CustomCommand/CMakeLists.txt index db57e19..e9a9f52 100644 --- a/Tests/CustomCommand/CMakeLists.txt +++ b/Tests/CustomCommand/CMakeLists.txt @@ -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$,;-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}") diff --git a/Tests/CustomCommand/compare_options.cmake b/Tests/CustomCommand/compare_options.cmake new file mode 100644 index 0000000..a32e579 --- /dev/null +++ b/Tests/CustomCommand/compare_options.cmake @@ -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() -- cgit v0.12