mirror of
https://github.com/Kitware/CMake.git
synced 2025-12-31 10:50:16 -06:00
file: Add CONFIGURE subcommand
Extend the `file()` command with a new `CONFIGURE` subcommand that behaves the same as `string(CONFIGURE)` except that it writes the resulting output immediately to a file. Fixes: #20388
This commit is contained in:
committed by
Brad King
parent
4a1baca6f7
commit
a6fee09484
@@ -19,6 +19,7 @@ Synopsis
|
||||
file({`WRITE`_ | `APPEND`_} <filename> <content>...)
|
||||
file({`TOUCH`_ | `TOUCH_NOCREATE`_} [<file>...])
|
||||
file(`GENERATE`_ OUTPUT <output-file> [...])
|
||||
file(`CONFIGURE`_ OUTPUT <output-file> CONTENT <content> [...])
|
||||
|
||||
`Filesystem`_
|
||||
file({`GLOB`_ | `GLOB_RECURSE`_} <out-var> [...] [<globbing-expr>...])
|
||||
@@ -484,6 +485,45 @@ generation phase. The output file will not yet have been written when the
|
||||
``file(GENERATE)`` command returns, it is written only after processing all
|
||||
of a project's ``CMakeLists.txt`` files.
|
||||
|
||||
.. _CONFIGURE:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
file(CONFIGURE OUTPUT output-file
|
||||
CONTENT content
|
||||
[ESCAPE_QUOTES] [@ONLY]
|
||||
[NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
|
||||
|
||||
Generate an output file using the input given by ``CONTENT`` and substitute
|
||||
variable values referenced as ``@VAR@`` or ``${VAR}`` contained therein. The
|
||||
substitution rules behave the same as the :command:`configure_file` command.
|
||||
In order to match :command:`configure_file`'s behavior, generator expressions
|
||||
are not supported for both ``OUTPUT`` and ``CONTENT``.
|
||||
|
||||
The arguments are:
|
||||
|
||||
``OUTPUT <output-file>``
|
||||
Specify the output file name to generate. A relative path is treated with
|
||||
respect to the value of :variable:`CMAKE_CURRENT_BINARY_DIR`. See policy
|
||||
:policy:`CMP0070`.
|
||||
``<output-file>`` does not support generator expressions.
|
||||
|
||||
``CONTENT <content>``
|
||||
Use the content given explicitly as input.
|
||||
``<content>`` does not support generator expressions.
|
||||
|
||||
``ESCAPE_QUOTES``
|
||||
Escape any substituted quotes with backslashes (C-style).
|
||||
|
||||
``@ONLY``
|
||||
Restrict variable replacement to references of the form ``@VAR@``.
|
||||
This is useful for configuring scripts that use ``${VAR}`` syntax.
|
||||
|
||||
``NEWLINE_STYLE <style>``
|
||||
Specify the newline style for the output file. Specify
|
||||
``UNIX`` or ``LF`` for ``\n`` newlines, or specify
|
||||
``DOS``, ``WIN32``, or ``CRLF`` for ``\r\n`` newlines.
|
||||
|
||||
Filesystem
|
||||
^^^^^^^^^^
|
||||
|
||||
|
||||
6
Help/release/dev/file_configure.rst
Normal file
6
Help/release/dev/file_configure.rst
Normal file
@@ -0,0 +1,6 @@
|
||||
file_configure
|
||||
--------------
|
||||
|
||||
* The :command:`file(CONFIGURE)` subcommand was created in order replicate the
|
||||
:command:`configure_file` functionality without resorting to a pre-existing
|
||||
file on disk as input. The content is instead passed as a string.
|
||||
@@ -33,12 +33,14 @@
|
||||
#include "cmFileInstaller.h"
|
||||
#include "cmFileLockPool.h"
|
||||
#include "cmFileTimes.h"
|
||||
#include "cmGeneratedFileStream.h"
|
||||
#include "cmGeneratorExpression.h"
|
||||
#include "cmGlobalGenerator.h"
|
||||
#include "cmHexFileConverter.h"
|
||||
#include "cmListFileCache.h"
|
||||
#include "cmMakefile.h"
|
||||
#include "cmMessageType.h"
|
||||
#include "cmNewLineStyle.h"
|
||||
#include "cmPolicies.h"
|
||||
#include "cmRange.h"
|
||||
#include "cmRuntimeDependencyArchive.h"
|
||||
@@ -2776,6 +2778,121 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleConfigureCommand(std::vector<std::string> const& args,
|
||||
cmExecutionStatus& status)
|
||||
{
|
||||
if (args.size() < 5) {
|
||||
status.SetError("Incorrect arguments to CONFIGURE subcommand.");
|
||||
return false;
|
||||
}
|
||||
if (args[1] != "OUTPUT") {
|
||||
status.SetError("Incorrect arguments to CONFIGURE subcommand.");
|
||||
return false;
|
||||
}
|
||||
if (args[3] != "CONTENT") {
|
||||
status.SetError("Incorrect arguments to CONFIGURE subcommand.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string errorMessage;
|
||||
cmNewLineStyle newLineStyle;
|
||||
if (!newLineStyle.ReadFromArguments(args, errorMessage)) {
|
||||
status.SetError(cmStrCat("CONFIGURE ", errorMessage));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool escapeQuotes = false;
|
||||
bool atOnly = false;
|
||||
for (unsigned int i = 5; i < args.size(); ++i) {
|
||||
if (args[i] == "@ONLY") {
|
||||
atOnly = true;
|
||||
} else if (args[i] == "ESCAPE_QUOTES") {
|
||||
escapeQuotes = true;
|
||||
} else if (args[i] == "NEWLINE_STYLE" || args[i] == "LF" ||
|
||||
args[i] == "UNIX" || args[i] == "CRLF" || args[i] == "WIN32" ||
|
||||
args[i] == "DOS") {
|
||||
/* Options handled by NewLineStyle member above. */
|
||||
} else {
|
||||
status.SetError(
|
||||
cmStrCat("CONFIGURE Unrecognized argument \"", args[i], "\""));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for generator expressions
|
||||
const std::string input = args[4];
|
||||
std::string outputFile = args[2];
|
||||
|
||||
std::string::size_type pos = input.find_first_of("<>");
|
||||
if (pos != std::string::npos) {
|
||||
status.SetError(cmStrCat("CONFIGURE called with CONTENT containing a \"",
|
||||
input[pos],
|
||||
"\". This character is not allowed."));
|
||||
return false;
|
||||
}
|
||||
|
||||
pos = outputFile.find_first_of("<>");
|
||||
if (pos != std::string::npos) {
|
||||
status.SetError(cmStrCat("CONFIGURE called with OUTPUT containing a \"",
|
||||
outputFile[pos],
|
||||
"\". This character is not allowed."));
|
||||
return false;
|
||||
}
|
||||
|
||||
cmMakefile& makeFile = status.GetMakefile();
|
||||
if (!makeFile.CanIWriteThisFile(outputFile)) {
|
||||
cmSystemTools::Error("Attempt to write file: " + outputFile +
|
||||
" into a source directory.");
|
||||
return false;
|
||||
}
|
||||
|
||||
cmSystemTools::ConvertToUnixSlashes(outputFile);
|
||||
|
||||
// Re-generate if non-temporary outputs are missing.
|
||||
// when we finalize the configuration we will remove all
|
||||
// output files that now don't exist.
|
||||
makeFile.AddCMakeOutputFile(outputFile);
|
||||
|
||||
// Create output directory
|
||||
const std::string::size_type slashPos = outputFile.rfind('/');
|
||||
if (slashPos != std::string::npos) {
|
||||
const std::string path = outputFile.substr(0, slashPos);
|
||||
cmSystemTools::MakeDirectory(path);
|
||||
}
|
||||
|
||||
std::string newLineCharacters;
|
||||
bool open_with_binary_flag = false;
|
||||
if (newLineStyle.IsValid()) {
|
||||
open_with_binary_flag = true;
|
||||
newLineCharacters = newLineStyle.GetCharacters();
|
||||
}
|
||||
|
||||
cmGeneratedFileStream fout;
|
||||
fout.Open(outputFile, false, open_with_binary_flag);
|
||||
if (!fout) {
|
||||
cmSystemTools::Error("Could not open file for write in copy operation " +
|
||||
outputFile);
|
||||
cmSystemTools::ReportLastSystemError("");
|
||||
return false;
|
||||
}
|
||||
fout.SetCopyIfDifferent(true);
|
||||
|
||||
// copy intput to output and expand variables from input at the same time
|
||||
std::stringstream sin(input, std::ios::in);
|
||||
std::string inLine;
|
||||
std::string outLine;
|
||||
while (cmSystemTools::GetLineFromStream(sin, inLine)) {
|
||||
outLine.clear();
|
||||
makeFile.ConfigureString(inLine, outLine, atOnly, escapeQuotes);
|
||||
fout << outLine << newLineCharacters;
|
||||
}
|
||||
|
||||
// close file before attempting to copy
|
||||
fout.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool cmFileCommand(std::vector<std::string> const& args,
|
||||
@@ -2829,6 +2946,7 @@ bool cmFileCommand(std::vector<std::string> const& args,
|
||||
{ "READ_SYMLINK"_s, HandleReadSymlinkCommand },
|
||||
{ "CREATE_LINK"_s, HandleCreateLinkCommand },
|
||||
{ "GET_RUNTIME_DEPENDENCIES"_s, HandleGetRuntimeDependenciesCommand },
|
||||
{ "CONFIGURE"_s, HandleConfigureCommand },
|
||||
};
|
||||
|
||||
return subcommand(args[0], args, status);
|
||||
|
||||
@@ -448,6 +448,7 @@ if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang"
|
||||
add_RunCMake_test(Framework)
|
||||
endif()
|
||||
|
||||
add_RunCMake_test(File_Configure)
|
||||
add_RunCMake_test(File_Generate)
|
||||
add_RunCMake_test(ExportWithoutLanguage)
|
||||
add_RunCMake_test(target_link_directories)
|
||||
|
||||
1
Tests/RunCMake/File_Configure/BadArg-result.txt
Normal file
1
Tests/RunCMake/File_Configure/BadArg-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
4
Tests/RunCMake/File_Configure/BadArg-stderr.txt
Normal file
4
Tests/RunCMake/File_Configure/BadArg-stderr.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
CMake Error at BadArg.cmake:[0-9]+ \(file\):
|
||||
file Incorrect arguments to CONFIGURE subcommand.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:3 \(include\)
|
||||
1
Tests/RunCMake/File_Configure/BadArg.cmake
Normal file
1
Tests/RunCMake/File_Configure/BadArg.cmake
Normal file
@@ -0,0 +1 @@
|
||||
file(CONFIGURE OUTPUT)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,5 @@
|
||||
CMake Error at BadArgGeneratorExpressionContent.cmake:[0-9]+ \(file\):
|
||||
file CONFIGURE called with CONTENT containing a "<". This character is not
|
||||
allowed.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:3 \(include\)
|
||||
@@ -0,0 +1,4 @@
|
||||
file(CONFIGURE
|
||||
OUTPUT "file.txt"
|
||||
CONTENT "foo-$<CONFIG>"
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,5 @@
|
||||
CMake Error at BadArgGeneratorExpressionOutput.cmake:[0-9]+ \(file\):
|
||||
file CONFIGURE called with OUTPUT containing a "<". This character is not
|
||||
allowed.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:3 \(include\)
|
||||
@@ -0,0 +1,4 @@
|
||||
file(CONFIGURE
|
||||
OUTPUT "file-$<CONFIG>.txt"
|
||||
CONTENT "foo"
|
||||
)
|
||||
3
Tests/RunCMake/File_Configure/CMakeLists.txt
Normal file
3
Tests/RunCMake/File_Configure/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.17)
|
||||
project(${RunCMake_TEST} NONE)
|
||||
include(${RunCMake_TEST}.cmake)
|
||||
1
Tests/RunCMake/File_Configure/DirOutput-stderr.txt
Normal file
1
Tests/RunCMake/File_Configure/DirOutput-stderr.txt
Normal file
@@ -0,0 +1 @@
|
||||
^DirOutput test file$
|
||||
4
Tests/RunCMake/File_Configure/DirOutput.cmake
Normal file
4
Tests/RunCMake/File_Configure/DirOutput.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/DirOutput)
|
||||
configure_file(DirOutput.txt DirOutput)
|
||||
file(READ ${CMAKE_CURRENT_BINARY_DIR}/DirOutput/DirOutput.txt out)
|
||||
message("${out}")
|
||||
1
Tests/RunCMake/File_Configure/DirOutput.txt
Normal file
1
Tests/RunCMake/File_Configure/DirOutput.txt
Normal file
@@ -0,0 +1 @@
|
||||
DirOutput test file
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,5 @@
|
||||
CMake Error at NewLineStyle-NoArg.cmake:[0-9]+ \(file\):
|
||||
file CONFIGURE NEWLINE_STYLE must set a style: LF, CRLF, UNIX, DOS, or
|
||||
WIN32
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:3 \(include\)
|
||||
6
Tests/RunCMake/File_Configure/NewLineStyle-NoArg.cmake
Normal file
6
Tests/RunCMake/File_Configure/NewLineStyle-NoArg.cmake
Normal file
@@ -0,0 +1,6 @@
|
||||
set(file_name ${CMAKE_CURRENT_BINARY_DIR}/NewLineStyle.txt)
|
||||
file(CONFIGURE
|
||||
OUTPUT ${file_name}
|
||||
CONTENT "Data\n"
|
||||
NEWLINE_STYLE
|
||||
)
|
||||
20
Tests/RunCMake/File_Configure/NewLineStyle-ValidArg.cmake
Normal file
20
Tests/RunCMake/File_Configure/NewLineStyle-ValidArg.cmake
Normal file
@@ -0,0 +1,20 @@
|
||||
set(file_name ${CMAKE_CURRENT_BINARY_DIR}/NewLineStyle.txt)
|
||||
|
||||
function(test_eol style in out)
|
||||
file(CONFIGURE
|
||||
OUTPUT ${file_name}
|
||||
CONTENT "@in@"
|
||||
NEWLINE_STYLE ${style}
|
||||
)
|
||||
file(READ ${file_name} new HEX)
|
||||
if(NOT "${new}" STREQUAL "${out}")
|
||||
message(FATAL_ERROR "No ${style} line endings")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
test_eol(DOS "a" "610d0a")
|
||||
test_eol(WIN32 "b" "620d0a")
|
||||
test_eol(CRLF "c" "630d0a")
|
||||
|
||||
test_eol(UNIX "d" "640a")
|
||||
test_eol(LF "e" "650a")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,5 @@
|
||||
CMake Error at NewLineStyle-WrongArg.cmake:[0-9]+ \(file\):
|
||||
file CONFIGURE NEWLINE_STYLE sets an unknown style, only LF, CRLF, UNIX,
|
||||
DOS, and WIN32 are supported
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:3 \(include\)
|
||||
@@ -0,0 +1,6 @@
|
||||
set(file_name ${CMAKE_CURRENT_BINARY_DIR}/NewLineStyle.txt)
|
||||
file(CONFIGURE
|
||||
OUTPUT ${file_name}
|
||||
CONTENT "Data\n"
|
||||
NEWLINE_STYLE FOO
|
||||
)
|
||||
9
Tests/RunCMake/File_Configure/RunCMakeTest.cmake
Normal file
9
Tests/RunCMake/File_Configure/RunCMakeTest.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
include(RunCMake)
|
||||
|
||||
run_cmake(BadArg)
|
||||
run_cmake(BadArgGeneratorExpressionContent)
|
||||
run_cmake(BadArgGeneratorExpressionOutput)
|
||||
run_cmake(DirOutput)
|
||||
run_cmake(NewLineStyle-NoArg)
|
||||
run_cmake(NewLineStyle-ValidArg)
|
||||
run_cmake(NewLineStyle-WrongArg)
|
||||
Reference in New Issue
Block a user