diff options
author | Leander Beernaert <leander.beernaert@qt.io> | 2020-03-06 12:41:07 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2020-03-09 15:54:02 (GMT) |
commit | a6fee09484dd467028021e2c54e9791f1b6a0cd1 (patch) | |
tree | 1bda1bde6e6693c668a406781d23abfb57571dd2 | |
parent | 4a1baca6f79557c61aafd99ae3abed533afa11a2 (diff) | |
download | CMake-a6fee09484dd467028021e2c54e9791f1b6a0cd1.zip CMake-a6fee09484dd467028021e2c54e9791f1b6a0cd1.tar.gz CMake-a6fee09484dd467028021e2c54e9791f1b6a0cd1.tar.bz2 |
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
25 files changed, 253 insertions, 0 deletions
diff --git a/Help/command/file.rst b/Help/command/file.rst index 5877d43..5a479d9 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 0000000..35e99c4 --- /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 79110ab..3b532c8 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 40b5cfa..4b5475e 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 0000000..d00491f --- /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 0000000..5423a28 --- /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 0000000..7c7fcda --- /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 0000000..d00491f --- /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 0000000..acda654 --- /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 0000000..75fe9e5 --- /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 0000000..d00491f --- /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 0000000..329d956 --- /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 0000000..31a79a7 --- /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 0000000..b7117bd --- /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 0000000..d051f7c --- /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 0000000..aa0fadf --- /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 0000000..16388a6 --- /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 0000000..d00491f --- /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 0000000..5a8ed2c --- /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 0000000..d6738b4 --- /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 0000000..e384873 --- /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 0000000..d00491f --- /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 0000000..c1bb42c --- /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 0000000..7d38167 --- /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 0000000..d09d648 --- /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) |