mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-02 11:49:55 -06:00
cmake_command: Add command to EVAL a CMake script as a string
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
^CMake Error in CommandEOF.cmake:
|
||||
^CMake Error at CommandEOF.cmake:1:
|
||||
Unexpected end of file.
|
||||
|
||||
Parse error. Function missing opening "\(".
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
WORKS!
|
||||
@@ -0,0 +1 @@
|
||||
cmake_command(EVAL CODE message(WORKS!))
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -0,0 +1,5 @@
|
||||
cmake_command(EVAL CODE
|
||||
"
|
||||
message(FATAL_ERROR error!)
|
||||
"
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,2 @@
|
||||
CMake Error at cmake_command_eval_no_code.cmake:1 \(cmake_command\):
|
||||
cmake_command called without CODE argument
|
||||
@@ -0,0 +1 @@
|
||||
cmake_command(EVAL message "too many parameters")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,2 @@
|
||||
CMake Error at cmake_command_eval_no_parameters.cmake:1 \(cmake_command\):
|
||||
cmake_command called with incorrect number of arguments
|
||||
@@ -0,0 +1 @@
|
||||
cmake_command(EVAL)
|
||||
@@ -0,0 +1 @@
|
||||
WORKS!
|
||||
@@ -0,0 +1,2 @@
|
||||
cmake_command(EVAL CODE "set(phrase \"WORKS!\")")
|
||||
message(${phrase})
|
||||
Reference in New Issue
Block a user