cmake_command: Add command to EVAL a CMake script as a string

This commit is contained in:
Cristian Adam
2020-02-27 20:20:22 +01:00
committed by Brad King
parent c58b9c5ab9
commit 598b676b5e
24 changed files with 183 additions and 37 deletions

View File

@@ -9,6 +9,7 @@ Synopsis
.. parsed-literal::
cmake_command(`INVOKE`_ <command> [<args>...])
cmake_command(`EVAL`_ CODE <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 <code>...)
Evaluates the ``<code>...`` 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)

View File

@@ -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.

View File

@@ -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;
}
/*--------------------------------------------------------------------------*/

View File

@@ -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;
}
/*--------------------------------------------------------------------------*/

View File

@@ -2,11 +2,14 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCMakeCommand.h"
#include <algorithm>
#include <cstddef>
#include "cmExecutionStatus.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmRange.h"
#include "cmStringAlgorithms.h"
bool cmCMakeCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
@@ -19,6 +22,8 @@ bool cmCMakeCommand(std::vector<std::string> 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<std::string> 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;
}

View File

@@ -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;

View File

@@ -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<cmListFileFunction> Functions;
};

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -1,4 +1,4 @@
^CMake Error in CommandEOF.cmake:
^CMake Error at CommandEOF.cmake:1:
Unexpected end of file.
Parse error. Function missing opening "\(".

View File

@@ -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)

View File

@@ -0,0 +1 @@
WORKS!

View File

@@ -0,0 +1 @@
cmake_command(EVAL CODE message(WORKS!))

View File

@@ -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\)

View File

@@ -0,0 +1,5 @@
cmake_command(EVAL CODE
"
message(FATAL_ERROR error!)
"
)

View File

@@ -0,0 +1,2 @@
CMake Error at cmake_command_eval_no_code.cmake:1 \(cmake_command\):
cmake_command called without CODE argument

View File

@@ -0,0 +1 @@
cmake_command(EVAL message "too many parameters")

View File

@@ -0,0 +1,2 @@
CMake Error at cmake_command_eval_no_parameters.cmake:1 \(cmake_command\):
cmake_command called with incorrect number of arguments

View File

@@ -0,0 +1 @@
cmake_command(EVAL)

View File

@@ -0,0 +1,2 @@
cmake_command(EVAL CODE "set(phrase \"WORKS!\")")
message(${phrase})