From 598b676b5e77540b366b01b3c10154c2a633d23c Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Thu, 27 Feb 2020 20:20:22 +0100 Subject: cmake_command: Add command to EVAL a CMake script as a string --- Help/command/cmake_command.rst | 54 +++++++++++++++++++++- Help/release/dev/cmake_command-command.rst | 2 +- Source/LexerParser/cmListFileLexer.c | 14 ++---- Source/LexerParser/cmListFileLexer.in.l | 14 ++---- Source/cmCMakeCommand.cxx | 27 +++++++++-- Source/cmListFileCache.cxx | 51 ++++++++++++++++---- Source/cmListFileCache.h | 3 ++ Source/cmMakefile.cxx | 21 +++++++++ Source/cmMakefile.h | 3 ++ Tests/RunCMake/Syntax/CommandEOF-stderr.txt | 2 +- Tests/RunCMake/cmake_command/RunCMakeTest.cmake | 5 ++ .../cmake_command_eval_message-stderr.txt | 1 + .../cmake_command/cmake_command_eval_message.cmake | 1 + ...ake_command_eval_message_fatal_error-result.txt | 1 + ...ake_command_eval_message_fatal_error-stderr.txt | 5 ++ .../cmake_command_eval_message_fatal_error.cmake | 5 ++ .../cmake_command_eval_no_code-result.txt | 1 + .../cmake_command_eval_no_code-stderr.txt | 2 + .../cmake_command/cmake_command_eval_no_code.cmake | 1 + .../cmake_command_eval_no_parameters-result.txt | 1 + .../cmake_command_eval_no_parameters-stderr.txt | 2 + .../cmake_command_eval_no_parameters.cmake | 1 + ...ommand_eval_variable_outside_message-stderr.txt | 1 + ...ake_command_eval_variable_outside_message.cmake | 2 + 24 files changed, 183 insertions(+), 37 deletions(-) create mode 100644 Tests/RunCMake/cmake_command/cmake_command_eval_message-stderr.txt create mode 100644 Tests/RunCMake/cmake_command/cmake_command_eval_message.cmake create mode 100644 Tests/RunCMake/cmake_command/cmake_command_eval_message_fatal_error-result.txt create mode 100644 Tests/RunCMake/cmake_command/cmake_command_eval_message_fatal_error-stderr.txt create mode 100644 Tests/RunCMake/cmake_command/cmake_command_eval_message_fatal_error.cmake create mode 100644 Tests/RunCMake/cmake_command/cmake_command_eval_no_code-result.txt create mode 100644 Tests/RunCMake/cmake_command/cmake_command_eval_no_code-stderr.txt create mode 100644 Tests/RunCMake/cmake_command/cmake_command_eval_no_code.cmake create mode 100644 Tests/RunCMake/cmake_command/cmake_command_eval_no_parameters-result.txt create mode 100644 Tests/RunCMake/cmake_command/cmake_command_eval_no_parameters-stderr.txt create mode 100644 Tests/RunCMake/cmake_command/cmake_command_eval_no_parameters.cmake create mode 100644 Tests/RunCMake/cmake_command/cmake_command_eval_variable_outside_message-stderr.txt create mode 100644 Tests/RunCMake/cmake_command/cmake_command_eval_variable_outside_message.cmake diff --git a/Help/command/cmake_command.rst b/Help/command/cmake_command.rst index 9281647..08b7832 100644 --- a/Help/command/cmake_command.rst +++ b/Help/command/cmake_command.rst @@ -9,6 +9,7 @@ Synopsis .. parsed-literal:: cmake_command(`INVOKE`_ [...]) + cmake_command(`EVAL`_ CODE ...) Introduction ^^^^^^^^^^^^ @@ -16,8 +17,10 @@ Introduction This command will call meta-operations on built-in CMake commands or those created via the :command:`macro` or :command:`function` commands. -Invoking -^^^^^^^^ +``cmake_command`` does not introduce a new variable or policy scope. + +Invoking Commands +^^^^^^^^^^^^^^^^^ .. _INVOKE: @@ -38,3 +41,50 @@ is equivalent to .. code-block:: cmake message(STATUS "Hello World!") + +Evaluating Code +^^^^^^^^^^^^^^^ + +.. _EVAL: + +.. code-block:: cmake + + cmake_command(EVAL CODE ...) + +Evaluates the ``...`` as CMake code. + +For example, the code: + +.. code-block:: cmake + + set(A TRUE) + set(B TRUE) + set(C TRUE) + set(condition "(A AND B) OR C") + + cmake_command(EVAL CODE " + if (${condition}) + message(STATUS TRUE) + else() + message(STATUS FALSE) + endif()" + ) + +is equivalent to + +.. code-block:: cmake + + set(A TRUE) + set(B TRUE) + set(C TRUE) + set(condition "(A AND B) OR C") + + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/eval.cmake " + if (${condition}) + message(STATUS TRUE) + else() + message(STATUS FALSE) + endif()" + ) + + include(${CMAKE_CURRENT_BINARY_DIR}/eval.cmake) diff --git a/Help/release/dev/cmake_command-command.rst b/Help/release/dev/cmake_command-command.rst index ebe75b1..6200ae2 100644 --- a/Help/release/dev/cmake_command-command.rst +++ b/Help/release/dev/cmake_command-command.rst @@ -3,4 +3,4 @@ cmake_command * The :command:`cmake_command()` command was added for meta-operations on scripted or built-in commands, starting with a mode to ``INVOKE`` other - commands. + commands, and ``EVAL CODE`` to inplace evaluate a CMake script. diff --git a/Source/LexerParser/cmListFileLexer.c b/Source/LexerParser/cmListFileLexer.c index 15dcda0..ec7424c 100644 --- a/Source/LexerParser/cmListFileLexer.c +++ b/Source/LexerParser/cmListFileLexer.c @@ -2787,7 +2787,7 @@ int cmListFileLexer_SetString(cmListFileLexer* lexer, const char* text) /*--------------------------------------------------------------------------*/ cmListFileLexer_Token* cmListFileLexer_Scan(cmListFileLexer* lexer) { - if (!lexer->file) { + if (!lexer->file && !lexer->string_buffer) { return 0; } if (cmListFileLexer_yylex(lexer->scanner, lexer)) { @@ -2801,21 +2801,13 @@ cmListFileLexer_Token* cmListFileLexer_Scan(cmListFileLexer* lexer) /*--------------------------------------------------------------------------*/ long cmListFileLexer_GetCurrentLine(cmListFileLexer* lexer) { - if (lexer->file) { - return lexer->line; - } else { - return 0; - } + return lexer->line; } /*--------------------------------------------------------------------------*/ long cmListFileLexer_GetCurrentColumn(cmListFileLexer* lexer) { - if (lexer->file) { - return lexer->column; - } else { - return 0; - } + return lexer->column; } /*--------------------------------------------------------------------------*/ diff --git a/Source/LexerParser/cmListFileLexer.in.l b/Source/LexerParser/cmListFileLexer.in.l index fdf14d2..94cf8a5 100644 --- a/Source/LexerParser/cmListFileLexer.in.l +++ b/Source/LexerParser/cmListFileLexer.in.l @@ -500,7 +500,7 @@ int cmListFileLexer_SetString(cmListFileLexer* lexer, const char* text) /*--------------------------------------------------------------------------*/ cmListFileLexer_Token* cmListFileLexer_Scan(cmListFileLexer* lexer) { - if (!lexer->file) { + if (!lexer->file && !lexer->string_buffer) { return 0; } if (cmListFileLexer_yylex(lexer->scanner, lexer)) { @@ -514,21 +514,13 @@ cmListFileLexer_Token* cmListFileLexer_Scan(cmListFileLexer* lexer) /*--------------------------------------------------------------------------*/ long cmListFileLexer_GetCurrentLine(cmListFileLexer* lexer) { - if (lexer->file) { - return lexer->line; - } else { - return 0; - } + return lexer->line; } /*--------------------------------------------------------------------------*/ long cmListFileLexer_GetCurrentColumn(cmListFileLexer* lexer) { - if (lexer->file) { - return lexer->column; - } else { - return 0; - } + return lexer->column; } /*--------------------------------------------------------------------------*/ diff --git a/Source/cmCMakeCommand.cxx b/Source/cmCMakeCommand.cxx index 5699086..c11a003 100644 --- a/Source/cmCMakeCommand.cxx +++ b/Source/cmCMakeCommand.cxx @@ -2,11 +2,14 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCMakeCommand.h" +#include #include #include "cmExecutionStatus.h" #include "cmListFileCache.h" #include "cmMakefile.h" +#include "cmRange.h" +#include "cmStringAlgorithms.h" bool cmCMakeCommand(std::vector const& args, cmExecutionStatus& status) @@ -19,6 +22,8 @@ bool cmCMakeCommand(std::vector const& args, cmMakefile& makefile = status.GetMakefile(); cmListFileContext context = makefile.GetExecutionContext(); + bool result = false; + if (args[0] == "INVOKE") { if (args.size() == 1) { status.SetError("called with incorrect number of arguments"); @@ -39,9 +44,25 @@ bool cmCMakeCommand(std::vector const& args, func.Arguments.emplace_back(lfarg); } - return makefile.ExecuteCommand(func, status); + result = makefile.ExecuteCommand(func, status); + } else if (args[0] == "EVAL") { + if (args.size() < 2) { + status.SetError("called with incorrect number of arguments"); + return false; + } + + auto code_iter = std::find(args.begin(), args.end(), "CODE"); + if (code_iter == args.end()) { + status.SetError("called without CODE argument"); + return false; + } + + const std::string code = cmJoin(cmMakeRange(++code_iter, args.end()), " "); + result = makefile.ReadListFileAsString( + code, cmStrCat(context.FilePath, ":", context.Line, ":EVAL")); + } else { + status.SetError("called with unknown meta-operation"); } - status.SetError("called with unknown meta-operation"); - return false; + return result; } diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index 47679c9..7ebb02f 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -26,13 +26,15 @@ cmCommandContext::cmCommandName& cmCommandContext::cmCommandName::operator=( struct cmListFileParser { cmListFileParser(cmListFile* lf, cmListFileBacktrace lfbt, - cmMessenger* messenger, const char* filename); + cmMessenger* messenger); ~cmListFileParser(); cmListFileParser(const cmListFileParser&) = delete; cmListFileParser& operator=(const cmListFileParser&) = delete; void IssueFileOpenError(std::string const& text) const; void IssueError(std::string const& text) const; - bool ParseFile(); + bool ParseFile(const char* filename); + bool ParseString(const char* str, const char* virtual_filename); + bool Parse(); bool ParseFunction(const char* name, long line); bool AddArgument(cmListFileLexer_Token* token, cmListFileArgument::Delimiter delim); @@ -51,12 +53,11 @@ struct cmListFileParser }; cmListFileParser::cmListFileParser(cmListFile* lf, cmListFileBacktrace lfbt, - cmMessenger* messenger, - const char* filename) + cmMessenger* messenger) : ListFile(lf) , Backtrace(std::move(lfbt)) , Messenger(messenger) - , FileName(filename) + , FileName(nullptr) , Lexer(cmListFileLexer_New()) { } @@ -83,8 +84,10 @@ void cmListFileParser::IssueError(const std::string& text) const cmSystemTools::SetFatalErrorOccured(); } -bool cmListFileParser::ParseFile() +bool cmListFileParser::ParseFile(const char* filename) { + this->FileName = filename; + // Open the file. cmListFileLexer_BOM bom; if (!cmListFileLexer_SetFileName(this->Lexer, this->FileName, &bom)) { @@ -107,6 +110,24 @@ bool cmListFileParser::ParseFile() return false; } + return Parse(); +} + +bool cmListFileParser::ParseString(const char* str, + const char* virtual_filename) +{ + this->FileName = virtual_filename; + + if (!cmListFileLexer_SetString(this->Lexer, str)) { + this->IssueFileOpenError("cmListFileCache: cannot allocate buffer."); + return false; + } + + return Parse(); +} + +bool cmListFileParser::Parse() +{ // Use a simple recursive-descent parser to process the token // stream. bool haveNewline = true; @@ -155,8 +176,22 @@ bool cmListFile::ParseFile(const char* filename, cmMessenger* messenger, bool parseError = false; { - cmListFileParser parser(this, lfbt, messenger, filename); - parseError = !parser.ParseFile(); + cmListFileParser parser(this, lfbt, messenger); + parseError = !parser.ParseFile(filename); + } + + return !parseError; +} + +bool cmListFile::ParseString(const char* str, const char* virtual_filename, + cmMessenger* messenger, + const cmListFileBacktrace& lfbt) +{ + bool parseError = false; + + { + cmListFileParser parser(this, lfbt, messenger); + parseError = !parser.ParseString(str, virtual_filename); } return !parseError; diff --git a/Source/cmListFileCache.h b/Source/cmListFileCache.h index 9cae827..89902ff 100644 --- a/Source/cmListFileCache.h +++ b/Source/cmListFileCache.h @@ -184,6 +184,9 @@ struct cmListFile bool ParseFile(const char* path, cmMessenger* messenger, cmListFileBacktrace const& lfbt); + bool ParseString(const char* str, const char* virtual_filename, + cmMessenger* messenger, cmListFileBacktrace const& lfbt); + std::vector Functions; }; diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index ed627f8..94d99b7 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -684,6 +684,27 @@ bool cmMakefile::ReadListFile(const std::string& filename) return true; } +bool cmMakefile::ReadListFileAsString(const std::string& content, + const std::string& virtualFileName) +{ + std::string filenametoread = cmSystemTools::CollapseFullPath( + virtualFileName, this->GetCurrentSourceDirectory()); + + ListFileScope scope(this, filenametoread); + + cmListFile listFile; + if (!listFile.ParseString(content.c_str(), virtualFileName.c_str(), + this->GetMessenger(), this->Backtrace)) { + return false; + } + + this->ReadListFile(listFile, filenametoread); + if (cmSystemTools::GetFatalErrorOccured()) { + scope.Quiet(); + } + return true; +} + void cmMakefile::ReadListFile(cmListFile const& listFile, std::string const& filenametoread) { diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index f1a68c2..9c6dca6 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -117,6 +117,9 @@ public: bool ReadListFile(const std::string& filename); + bool ReadListFileAsString(const std::string& content, + const std::string& virtualFileName); + bool ReadDependentFile(const std::string& filename, bool noPolicyScope = true); diff --git a/Tests/RunCMake/Syntax/CommandEOF-stderr.txt b/Tests/RunCMake/Syntax/CommandEOF-stderr.txt index 31cbc08..b9f8fd1 100644 --- a/Tests/RunCMake/Syntax/CommandEOF-stderr.txt +++ b/Tests/RunCMake/Syntax/CommandEOF-stderr.txt @@ -1,4 +1,4 @@ -^CMake Error in CommandEOF.cmake: +^CMake Error at CommandEOF.cmake:1: Unexpected end of file. Parse error. Function missing opening "\(". diff --git a/Tests/RunCMake/cmake_command/RunCMakeTest.cmake b/Tests/RunCMake/cmake_command/RunCMakeTest.cmake index d338cd8..2b6e7a2 100644 --- a/Tests/RunCMake/cmake_command/RunCMakeTest.cmake +++ b/Tests/RunCMake/cmake_command/RunCMakeTest.cmake @@ -6,3 +6,8 @@ run_cmake(cmake_command_invoke_message) run_cmake(cmake_command_invoke_message_fatal_error) run_cmake(cmake_command_invoke_no_parameters) run_cmake(cmake_command_invoke_unknown_function) +run_cmake(cmake_command_eval_message) +run_cmake(cmake_command_eval_message_fatal_error) +run_cmake(cmake_command_eval_no_code) +run_cmake(cmake_command_eval_no_parameters) +run_cmake(cmake_command_eval_variable_outside_message) diff --git a/Tests/RunCMake/cmake_command/cmake_command_eval_message-stderr.txt b/Tests/RunCMake/cmake_command/cmake_command_eval_message-stderr.txt new file mode 100644 index 0000000..cfc8694 --- /dev/null +++ b/Tests/RunCMake/cmake_command/cmake_command_eval_message-stderr.txt @@ -0,0 +1 @@ +WORKS! diff --git a/Tests/RunCMake/cmake_command/cmake_command_eval_message.cmake b/Tests/RunCMake/cmake_command/cmake_command_eval_message.cmake new file mode 100644 index 0000000..9ef5e25 --- /dev/null +++ b/Tests/RunCMake/cmake_command/cmake_command_eval_message.cmake @@ -0,0 +1 @@ +cmake_command(EVAL CODE message(WORKS!)) diff --git a/Tests/RunCMake/cmake_command/cmake_command_eval_message_fatal_error-result.txt b/Tests/RunCMake/cmake_command/cmake_command_eval_message_fatal_error-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/cmake_command/cmake_command_eval_message_fatal_error-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/cmake_command/cmake_command_eval_message_fatal_error-stderr.txt b/Tests/RunCMake/cmake_command/cmake_command_eval_message_fatal_error-stderr.txt new file mode 100644 index 0000000..6a8a124 --- /dev/null +++ b/Tests/RunCMake/cmake_command/cmake_command_eval_message_fatal_error-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at cmake_command_eval_message_fatal_error.cmake:1:EVAL:2 \(message\): + error! +Call Stack \(most recent call first\): + cmake_command_eval_message_fatal_error.cmake:1 \(cmake_command\) + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/cmake_command/cmake_command_eval_message_fatal_error.cmake b/Tests/RunCMake/cmake_command/cmake_command_eval_message_fatal_error.cmake new file mode 100644 index 0000000..22913de --- /dev/null +++ b/Tests/RunCMake/cmake_command/cmake_command_eval_message_fatal_error.cmake @@ -0,0 +1,5 @@ +cmake_command(EVAL CODE +" + message(FATAL_ERROR error!) +" +) diff --git a/Tests/RunCMake/cmake_command/cmake_command_eval_no_code-result.txt b/Tests/RunCMake/cmake_command/cmake_command_eval_no_code-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/cmake_command/cmake_command_eval_no_code-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/cmake_command/cmake_command_eval_no_code-stderr.txt b/Tests/RunCMake/cmake_command/cmake_command_eval_no_code-stderr.txt new file mode 100644 index 0000000..ee53312 --- /dev/null +++ b/Tests/RunCMake/cmake_command/cmake_command_eval_no_code-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at cmake_command_eval_no_code.cmake:1 \(cmake_command\): + cmake_command called without CODE argument diff --git a/Tests/RunCMake/cmake_command/cmake_command_eval_no_code.cmake b/Tests/RunCMake/cmake_command/cmake_command_eval_no_code.cmake new file mode 100644 index 0000000..22e1667 --- /dev/null +++ b/Tests/RunCMake/cmake_command/cmake_command_eval_no_code.cmake @@ -0,0 +1 @@ +cmake_command(EVAL message "too many parameters") diff --git a/Tests/RunCMake/cmake_command/cmake_command_eval_no_parameters-result.txt b/Tests/RunCMake/cmake_command/cmake_command_eval_no_parameters-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/cmake_command/cmake_command_eval_no_parameters-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/cmake_command/cmake_command_eval_no_parameters-stderr.txt b/Tests/RunCMake/cmake_command/cmake_command_eval_no_parameters-stderr.txt new file mode 100644 index 0000000..e9fc317 --- /dev/null +++ b/Tests/RunCMake/cmake_command/cmake_command_eval_no_parameters-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at cmake_command_eval_no_parameters.cmake:1 \(cmake_command\): + cmake_command called with incorrect number of arguments diff --git a/Tests/RunCMake/cmake_command/cmake_command_eval_no_parameters.cmake b/Tests/RunCMake/cmake_command/cmake_command_eval_no_parameters.cmake new file mode 100644 index 0000000..a5ba2c7 --- /dev/null +++ b/Tests/RunCMake/cmake_command/cmake_command_eval_no_parameters.cmake @@ -0,0 +1 @@ +cmake_command(EVAL) diff --git a/Tests/RunCMake/cmake_command/cmake_command_eval_variable_outside_message-stderr.txt b/Tests/RunCMake/cmake_command/cmake_command_eval_variable_outside_message-stderr.txt new file mode 100644 index 0000000..cfc8694 --- /dev/null +++ b/Tests/RunCMake/cmake_command/cmake_command_eval_variable_outside_message-stderr.txt @@ -0,0 +1 @@ +WORKS! diff --git a/Tests/RunCMake/cmake_command/cmake_command_eval_variable_outside_message.cmake b/Tests/RunCMake/cmake_command/cmake_command_eval_variable_outside_message.cmake new file mode 100644 index 0000000..b7a06a5 --- /dev/null +++ b/Tests/RunCMake/cmake_command/cmake_command_eval_variable_outside_message.cmake @@ -0,0 +1,2 @@ +cmake_command(EVAL CODE "set(phrase \"WORKS!\")") +message(${phrase}) -- cgit v0.12