summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Help/command/file.rst40
-rw-r--r--Help/release/dev/file_configure.rst6
-rw-r--r--Source/cmFileCommand.cxx118
-rw-r--r--Tests/RunCMake/CMakeLists.txt1
-rw-r--r--Tests/RunCMake/File_Configure/BadArg-result.txt1
-rw-r--r--Tests/RunCMake/File_Configure/BadArg-stderr.txt4
-rw-r--r--Tests/RunCMake/File_Configure/BadArg.cmake1
-rw-r--r--Tests/RunCMake/File_Configure/BadArgGeneratorExpressionContent-result.txt1
-rw-r--r--Tests/RunCMake/File_Configure/BadArgGeneratorExpressionContent-stderr.txt5
-rw-r--r--Tests/RunCMake/File_Configure/BadArgGeneratorExpressionContent.cmake4
-rw-r--r--Tests/RunCMake/File_Configure/BadArgGeneratorExpressionOutput-result.txt1
-rw-r--r--Tests/RunCMake/File_Configure/BadArgGeneratorExpressionOutput-stderr.txt5
-rw-r--r--Tests/RunCMake/File_Configure/BadArgGeneratorExpressionOutput.cmake4
-rw-r--r--Tests/RunCMake/File_Configure/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/File_Configure/DirOutput-stderr.txt1
-rw-r--r--Tests/RunCMake/File_Configure/DirOutput.cmake4
-rw-r--r--Tests/RunCMake/File_Configure/DirOutput.txt1
-rw-r--r--Tests/RunCMake/File_Configure/NewLineStyle-NoArg-result.txt1
-rw-r--r--Tests/RunCMake/File_Configure/NewLineStyle-NoArg-stderr.txt5
-rw-r--r--Tests/RunCMake/File_Configure/NewLineStyle-NoArg.cmake6
-rw-r--r--Tests/RunCMake/File_Configure/NewLineStyle-ValidArg.cmake20
-rw-r--r--Tests/RunCMake/File_Configure/NewLineStyle-WrongArg-result.txt1
-rw-r--r--Tests/RunCMake/File_Configure/NewLineStyle-WrongArg-stderr.txt5
-rw-r--r--Tests/RunCMake/File_Configure/NewLineStyle-WrongArg.cmake6
-rw-r--r--Tests/RunCMake/File_Configure/RunCMakeTest.cmake9
25 files changed, 253 insertions, 0 deletions
diff --git a/Help/command/file.rst b/Help/command/file.rst
index 5877d4345e..5a479d90de 100644
--- a/Help/command/file.rst
+++ b/Help/command/file.rst
@@ -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
^^^^^^^^^^
diff --git a/Help/release/dev/file_configure.rst b/Help/release/dev/file_configure.rst
new file mode 100644
index 0000000000..35e99c4609
--- /dev/null
+++ b/Help/release/dev/file_configure.rst
@@ -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.
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index 79110ab401..3b532c89ab 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -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);
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 40b5cfacd6..4b5475edb1 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -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)
diff --git a/Tests/RunCMake/File_Configure/BadArg-result.txt b/Tests/RunCMake/File_Configure/BadArg-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/BadArg-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/File_Configure/BadArg-stderr.txt b/Tests/RunCMake/File_Configure/BadArg-stderr.txt
new file mode 100644
index 0000000000..5423a28224
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/BadArg-stderr.txt
@@ -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\)
diff --git a/Tests/RunCMake/File_Configure/BadArg.cmake b/Tests/RunCMake/File_Configure/BadArg.cmake
new file mode 100644
index 0000000000..7c7fcda635
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/BadArg.cmake
@@ -0,0 +1 @@
+file(CONFIGURE OUTPUT)
diff --git a/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionContent-result.txt b/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionContent-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionContent-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionContent-stderr.txt b/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionContent-stderr.txt
new file mode 100644
index 0000000000..acda6541a2
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionContent-stderr.txt
@@ -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\)
diff --git a/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionContent.cmake b/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionContent.cmake
new file mode 100644
index 0000000000..75fe9e5d16
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionContent.cmake
@@ -0,0 +1,4 @@
+file(CONFIGURE
+ OUTPUT "file.txt"
+ CONTENT "foo-$<CONFIG>"
+)
diff --git a/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionOutput-result.txt b/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionOutput-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionOutput-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionOutput-stderr.txt b/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionOutput-stderr.txt
new file mode 100644
index 0000000000..329d956efa
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionOutput-stderr.txt
@@ -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\)
diff --git a/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionOutput.cmake b/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionOutput.cmake
new file mode 100644
index 0000000000..31a79a7e0d
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/BadArgGeneratorExpressionOutput.cmake
@@ -0,0 +1,4 @@
+file(CONFIGURE
+ OUTPUT "file-$<CONFIG>.txt"
+ CONTENT "foo"
+)
diff --git a/Tests/RunCMake/File_Configure/CMakeLists.txt b/Tests/RunCMake/File_Configure/CMakeLists.txt
new file mode 100644
index 0000000000..b7117bd630
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.17)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/File_Configure/DirOutput-stderr.txt b/Tests/RunCMake/File_Configure/DirOutput-stderr.txt
new file mode 100644
index 0000000000..d051f7c20d
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/DirOutput-stderr.txt
@@ -0,0 +1 @@
+^DirOutput test file$
diff --git a/Tests/RunCMake/File_Configure/DirOutput.cmake b/Tests/RunCMake/File_Configure/DirOutput.cmake
new file mode 100644
index 0000000000..aa0fadfe60
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/DirOutput.cmake
@@ -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}")
diff --git a/Tests/RunCMake/File_Configure/DirOutput.txt b/Tests/RunCMake/File_Configure/DirOutput.txt
new file mode 100644
index 0000000000..16388a6b57
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/DirOutput.txt
@@ -0,0 +1 @@
+DirOutput test file
diff --git a/Tests/RunCMake/File_Configure/NewLineStyle-NoArg-result.txt b/Tests/RunCMake/File_Configure/NewLineStyle-NoArg-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/NewLineStyle-NoArg-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/File_Configure/NewLineStyle-NoArg-stderr.txt b/Tests/RunCMake/File_Configure/NewLineStyle-NoArg-stderr.txt
new file mode 100644
index 0000000000..5a8ed2c496
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/NewLineStyle-NoArg-stderr.txt
@@ -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\)
diff --git a/Tests/RunCMake/File_Configure/NewLineStyle-NoArg.cmake b/Tests/RunCMake/File_Configure/NewLineStyle-NoArg.cmake
new file mode 100644
index 0000000000..d6738b4d33
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/NewLineStyle-NoArg.cmake
@@ -0,0 +1,6 @@
+set(file_name ${CMAKE_CURRENT_BINARY_DIR}/NewLineStyle.txt)
+file(CONFIGURE
+ OUTPUT ${file_name}
+ CONTENT "Data\n"
+ NEWLINE_STYLE
+)
diff --git a/Tests/RunCMake/File_Configure/NewLineStyle-ValidArg.cmake b/Tests/RunCMake/File_Configure/NewLineStyle-ValidArg.cmake
new file mode 100644
index 0000000000..e384873663
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/NewLineStyle-ValidArg.cmake
@@ -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")
diff --git a/Tests/RunCMake/File_Configure/NewLineStyle-WrongArg-result.txt b/Tests/RunCMake/File_Configure/NewLineStyle-WrongArg-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/NewLineStyle-WrongArg-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/File_Configure/NewLineStyle-WrongArg-stderr.txt b/Tests/RunCMake/File_Configure/NewLineStyle-WrongArg-stderr.txt
new file mode 100644
index 0000000000..c1bb42c300
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/NewLineStyle-WrongArg-stderr.txt
@@ -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\)
diff --git a/Tests/RunCMake/File_Configure/NewLineStyle-WrongArg.cmake b/Tests/RunCMake/File_Configure/NewLineStyle-WrongArg.cmake
new file mode 100644
index 0000000000..7d38167fb1
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/NewLineStyle-WrongArg.cmake
@@ -0,0 +1,6 @@
+set(file_name ${CMAKE_CURRENT_BINARY_DIR}/NewLineStyle.txt)
+file(CONFIGURE
+ OUTPUT ${file_name}
+ CONTENT "Data\n"
+ NEWLINE_STYLE FOO
+)
diff --git a/Tests/RunCMake/File_Configure/RunCMakeTest.cmake b/Tests/RunCMake/File_Configure/RunCMakeTest.cmake
new file mode 100644
index 0000000000..d09d648b24
--- /dev/null
+++ b/Tests/RunCMake/File_Configure/RunCMakeTest.cmake
@@ -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)