summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Help/command/file.rst10
-rw-r--r--Help/release/dev/file-generate-new-line-style.rst5
-rw-r--r--Source/cmFileCommand.cxx24
-rw-r--r--Source/cmGeneratorExpressionEvaluationFile.cxx31
-rw-r--r--Source/cmGeneratorExpressionEvaluationFile.h3
-rw-r--r--Source/cmMakefile.cxx6
-rw-r--r--Source/cmMakefile.h3
-rw-r--r--Tests/RunCMake/File_Generate/NewLineStyle-Default.cmake35
-rw-r--r--Tests/RunCMake/File_Generate/NewLineStyle-InvalidArg-result.txt1
-rw-r--r--Tests/RunCMake/File_Generate/NewLineStyle-InvalidArg-stderr.txt5
-rw-r--r--Tests/RunCMake/File_Generate/NewLineStyle-InvalidArg.cmake7
-rw-r--r--Tests/RunCMake/File_Generate/NewLineStyle-NoArg-result.txt1
-rw-r--r--Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt4
-rw-r--r--Tests/RunCMake/File_Generate/NewLineStyle-NoArg.cmake7
-rw-r--r--Tests/RunCMake/File_Generate/NewLineStyle-Unix.cmake33
-rw-r--r--Tests/RunCMake/File_Generate/NewLineStyle-Win32.cmake33
-rw-r--r--Tests/RunCMake/File_Generate/RunCMakeTest.cmake11
-rw-r--r--Tests/RunCMake/File_Generate/VerifyContent.cmake4
18 files changed, 210 insertions, 13 deletions
diff --git a/Help/command/file.rst b/Help/command/file.rst
index c64b13f4b7..3db605d6a8 100644
--- a/Help/command/file.rst
+++ b/Help/command/file.rst
@@ -481,7 +481,8 @@ modified.
<INPUT input-file|CONTENT content>
[CONDITION expression] [TARGET target]
[FILE_PERMISSIONS <permissions>...]
- [NO_SOURCE_PERMISSIONS] [USE_SOURCE_PERMISSIONS])
+ [NO_SOURCE_PERMISSIONS] [USE_SOURCE_PERMISSIONS]
+ [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
Generate an output file for each build configuration supported by the current
:manual:`CMake Generator <cmake-generators(7)>`. Evaluate
@@ -533,6 +534,13 @@ from the input content to produce the output content. The options are:
Transfer the file permissions of the original file to the generated file.
This option expects INPUT option.
+``NEWLINE_STYLE <style>``
+ .. versionadded:: 3.20
+
+ Specify the newline style for the generated file. Specify
+ ``UNIX`` or ``LF`` for ``\n`` newlines, or specify
+ ``DOS``, ``WIN32``, or ``CRLF`` for ``\r\n`` newlines.
+
Exactly one ``CONTENT`` or ``INPUT`` option must be given. A specific
``OUTPUT`` file may be named by at most one invocation of ``file(GENERATE)``.
Generated files are modified and their timestamp updated on subsequent cmake
diff --git a/Help/release/dev/file-generate-new-line-style.rst b/Help/release/dev/file-generate-new-line-style.rst
new file mode 100644
index 0000000000..b629b461db
--- /dev/null
+++ b/Help/release/dev/file-generate-new-line-style.rst
@@ -0,0 +1,5 @@
+file-generate-new-line-style
+----------------------------
+
+* The :command:`file(GENERATE)` command gained ``NEWLINE_STYLE`` option to
+ support newline style of the generated file.
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index 9377baab5e..5eff78913d 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -2290,7 +2290,8 @@ void AddEvaluationFile(const std::string& inputName,
const std::string& targetName,
const std::string& outputExpr,
const std::string& condition, bool inputIsContent,
- mode_t permissions, cmExecutionStatus& status)
+ const std::string& newLineCharacter, mode_t permissions,
+ cmExecutionStatus& status)
{
cmListFileBacktrace lfbt = status.GetMakefile().GetBacktrace();
@@ -2304,7 +2305,7 @@ void AddEvaluationFile(const std::string& inputName,
status.GetMakefile().AddEvaluationFile(
inputName, targetName, std::move(outputCge), std::move(conditionCge),
- permissions, inputIsContent);
+ newLineCharacter, permissions, inputIsContent);
}
bool HandleGenerateCommand(std::vector<std::string> const& args,
@@ -2322,6 +2323,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
std::string Content;
std::string Condition;
std::string Target;
+ std::string NewLineStyle;
bool NoSourcePermissions = false;
bool UseSourcePermissions = false;
std::vector<std::string> FilePermissions;
@@ -2336,7 +2338,8 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
.Bind("TARGET"_s, &Arguments::Target)
.Bind("NO_SOURCE_PERMISSIONS"_s, &Arguments::NoSourcePermissions)
.Bind("USE_SOURCE_PERMISSIONS"_s, &Arguments::UseSourcePermissions)
- .Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions);
+ .Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions)
+ .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle);
std::vector<std::string> unparsedArguments;
std::vector<std::string> keywordsMissingValues;
@@ -2400,6 +2403,18 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
status.SetError("Unknown argument to GENERATE subcommand.");
}
+ const bool newLineStyleSpecified =
+ std::find(parsedKeywords.begin(), parsedKeywords.end(),
+ "NEWLINE_STYLE"_s) != parsedKeywords.end();
+ cmNewLineStyle newLineStyle;
+ if (newLineStyleSpecified) {
+ std::string errorMessage;
+ if (!newLineStyle.ReadFromArguments(args, errorMessage)) {
+ status.SetError(cmStrCat("GENERATE ", errorMessage));
+ return false;
+ }
+ }
+
std::string input = arguments.Input;
if (inputIsContent) {
input = arguments.Content;
@@ -2463,7 +2478,8 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
}
AddEvaluationFile(input, arguments.Target, arguments.Output,
- arguments.Condition, inputIsContent, permisiions, status);
+ arguments.Condition, inputIsContent,
+ newLineStyle.GetCharacters(), permisiions, status);
return true;
}
diff --git a/Source/cmGeneratorExpressionEvaluationFile.cxx b/Source/cmGeneratorExpressionEvaluationFile.cxx
index ec44df3041..9fae15ae8e 100644
--- a/Source/cmGeneratorExpressionEvaluationFile.cxx
+++ b/Source/cmGeneratorExpressionEvaluationFile.cxx
@@ -21,13 +21,14 @@ cmGeneratorExpressionEvaluationFile::cmGeneratorExpressionEvaluationFile(
std::string input, std::string target,
std::unique_ptr<cmCompiledGeneratorExpression> outputFileExpr,
std::unique_ptr<cmCompiledGeneratorExpression> condition,
- bool inputIsContent, mode_t permissions,
+ bool inputIsContent, std::string newLineCharacter, mode_t permissions,
cmPolicies::PolicyStatus policyStatusCMP0070)
: Input(std::move(input))
, Target(std::move(target))
, OutputFileExpr(std::move(outputFileExpr))
, Condition(std::move(condition))
, InputIsContent(inputIsContent)
+ , NewLineCharacter(std::move(newLineCharacter))
, PolicyStatusCMP0070(policyStatusCMP0070)
, Permissions(permissions)
{
@@ -82,9 +83,33 @@ void cmGeneratorExpressionEvaluationFile::Generate(
this->Files.push_back(outputFileName);
outputFiles[outputFileName] = outputContent;
- cmGeneratedFileStream fout(outputFileName);
+ bool openWithBinaryFlag = false;
+ if (!this->NewLineCharacter.empty()) {
+ openWithBinaryFlag = true;
+ }
+ cmGeneratedFileStream fout;
+ fout.Open(outputFileName, false, openWithBinaryFlag);
+ if (!fout) {
+ lg->IssueMessage(MessageType::FATAL_ERROR,
+ "Could not open file for write in copy operation " +
+ outputFileName);
+ return;
+ }
fout.SetCopyIfDifferent(true);
- fout << outputContent;
+ std::istringstream iss(outputContent);
+ std::string line;
+ bool hasNewLine = false;
+ while (cmSystemTools::GetLineFromStream(iss, line, &hasNewLine)) {
+ fout << line;
+ if (!this->NewLineCharacter.empty()) {
+ fout << this->NewLineCharacter;
+ } else if (hasNewLine) {
+ // if new line character is not specified, the file will be opened in
+ // text mode. So, "\n" will be translated to the correct newline
+ // ending based on the platform.
+ fout << "\n";
+ }
+ }
if (fout.Close() && perm) {
cmSystemTools::SetPermissions(outputFileName.c_str(), perm);
}
diff --git a/Source/cmGeneratorExpressionEvaluationFile.h b/Source/cmGeneratorExpressionEvaluationFile.h
index 9a870ac0ae..0eb78acdc3 100644
--- a/Source/cmGeneratorExpressionEvaluationFile.h
+++ b/Source/cmGeneratorExpressionEvaluationFile.h
@@ -24,7 +24,7 @@ public:
std::string input, std::string target,
std::unique_ptr<cmCompiledGeneratorExpression> outputFileExpr,
std::unique_ptr<cmCompiledGeneratorExpression> condition,
- bool inputIsContent, mode_t permissions,
+ bool inputIsContent, std::string newLineCharacter, mode_t permissions,
cmPolicies::PolicyStatus policyStatusCMP0070);
void Generate(cmLocalGenerator* lg);
@@ -58,6 +58,7 @@ private:
const std::unique_ptr<cmCompiledGeneratorExpression> Condition;
std::vector<std::string> Files;
const bool InputIsContent;
+ const std::string NewLineCharacter;
cmPolicies::PolicyStatus PolicyStatusCMP0070;
mode_t Permissions;
};
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index f479af1054..e4859864c6 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -865,13 +865,13 @@ void cmMakefile::EnforceDirectoryLevelRules() const
void cmMakefile::AddEvaluationFile(
const std::string& inputFile, const std::string& targetName,
std::unique_ptr<cmCompiledGeneratorExpression> outputName,
- std::unique_ptr<cmCompiledGeneratorExpression> condition, mode_t permissions,
- bool inputIsContent)
+ std::unique_ptr<cmCompiledGeneratorExpression> condition,
+ const std::string& newLineCharacter, mode_t permissions, bool inputIsContent)
{
this->EvaluationFiles.push_back(
cm::make_unique<cmGeneratorExpressionEvaluationFile>(
inputFile, targetName, std::move(outputName), std::move(condition),
- inputIsContent, permissions,
+ inputIsContent, newLineCharacter, permissions,
this->GetPolicyStatus(cmPolicies::CMP0070)));
}
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index 60b66a2fad..6341abcb86 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -899,7 +899,8 @@ public:
const std::string& inputFile, const std::string& targetName,
std::unique_ptr<cmCompiledGeneratorExpression> outputName,
std::unique_ptr<cmCompiledGeneratorExpression> condition,
- mode_t permissions, bool inputIsContent);
+ const std::string& newLineCharacter, mode_t permissions,
+ bool inputIsContent);
const std::vector<std::unique_ptr<cmGeneratorExpressionEvaluationFile>>&
GetEvaluationFiles() const;
diff --git a/Tests/RunCMake/File_Generate/NewLineStyle-Default.cmake b/Tests/RunCMake/File_Generate/NewLineStyle-Default.cmake
new file mode 100644
index 0000000000..9df8ffe3a9
--- /dev/null
+++ b/Tests/RunCMake/File_Generate/NewLineStyle-Default.cmake
@@ -0,0 +1,35 @@
+function(generate_from_file in out)
+ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/file_ip.txt "${in}")
+ file(GENERATE
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/file_op.txt
+ INPUT ${CMAKE_CURRENT_BINARY_DIR}/file_ip.txt
+ )
+
+ add_custom_target(verifyContentFromFile ALL
+ COMMAND ${CMAKE_COMMAND}
+ -DgeneratedFile=${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/file_op.txt
+ -DexpectedContent=${out}
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/VerifyContent.cmake"
+ )
+endfunction()
+
+function(generate_from_content in out)
+ file(GENERATE
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/content_op.txt
+ CONTENT ${in}
+ )
+
+ add_custom_target(verifyContentFromContent ALL
+ COMMAND ${CMAKE_COMMAND}
+ -DgeneratedFile=${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/content_op.txt
+ -DexpectedContent=${out}
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/VerifyContent.cmake"
+ )
+endfunction()
+
+if (WIN32)
+ generate_from_file("a" "610d0a") # 62->b, 0d0a->\r\n
+elseif(UNIX)
+ generate_from_file("a" "610a") # 62->b, 0a->\n
+endif()
+generate_from_content("a" "61")
diff --git a/Tests/RunCMake/File_Generate/NewLineStyle-InvalidArg-result.txt b/Tests/RunCMake/File_Generate/NewLineStyle-InvalidArg-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/File_Generate/NewLineStyle-InvalidArg-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/File_Generate/NewLineStyle-InvalidArg-stderr.txt b/Tests/RunCMake/File_Generate/NewLineStyle-InvalidArg-stderr.txt
new file mode 100644
index 0000000000..44e32d0280
--- /dev/null
+++ b/Tests/RunCMake/File_Generate/NewLineStyle-InvalidArg-stderr.txt
@@ -0,0 +1,5 @@
+CMake Error at NewLineStyle-InvalidArg.cmake:[0-9]+ \(file\):
+ file GENERATE NEWLINE_STYLE sets an unknown style, only LF, CRLF, UNIX,
+ DOS, and WIN32 are supported
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/File_Generate/NewLineStyle-InvalidArg.cmake b/Tests/RunCMake/File_Generate/NewLineStyle-InvalidArg.cmake
new file mode 100644
index 0000000000..578cf21629
--- /dev/null
+++ b/Tests/RunCMake/File_Generate/NewLineStyle-InvalidArg.cmake
@@ -0,0 +1,7 @@
+file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp")
+
+file(GENERATE
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/main.cpp"
+ CONTENT "int main() { return 0; }\n"
+ NEWLINE_STYLE FOO
+ )
diff --git a/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-result.txt b/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt b/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt
new file mode 100644
index 0000000000..bc71f2f599
--- /dev/null
+++ b/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at NewLineStyle-NoArg.cmake:[0-9]+ \(file\):
+ file Incorrect arguments to GENERATE subcommand.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/File_Generate/NewLineStyle-NoArg.cmake b/Tests/RunCMake/File_Generate/NewLineStyle-NoArg.cmake
new file mode 100644
index 0000000000..9bd2ffa44e
--- /dev/null
+++ b/Tests/RunCMake/File_Generate/NewLineStyle-NoArg.cmake
@@ -0,0 +1,7 @@
+file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp")
+
+file(GENERATE
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/main.cpp"
+ CONTENT "int main() { return 0; }\n"
+ NEWLINE_STYLE
+ )
diff --git a/Tests/RunCMake/File_Generate/NewLineStyle-Unix.cmake b/Tests/RunCMake/File_Generate/NewLineStyle-Unix.cmake
new file mode 100644
index 0000000000..7c26217ca1
--- /dev/null
+++ b/Tests/RunCMake/File_Generate/NewLineStyle-Unix.cmake
@@ -0,0 +1,33 @@
+function(generate_from_file in out)
+ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/file_ip.txt "${in}")
+ file(GENERATE
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/file_op.txt
+ INPUT ${CMAKE_CURRENT_BINARY_DIR}/file_ip.txt
+ NEWLINE_STYLE UNIX
+ )
+
+ add_custom_target(verifyContentFromFile ALL
+ COMMAND ${CMAKE_COMMAND}
+ -DgeneratedFile=${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/file_op.txt
+ -DexpectedContent=${out}
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/VerifyContent.cmake"
+ )
+endfunction()
+
+function(generate_from_content in out)
+ file(GENERATE
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/content_op.txt
+ CONTENT ${in}
+ NEWLINE_STYLE UNIX
+ )
+
+ add_custom_target(verifyContentFromContent ALL
+ COMMAND ${CMAKE_COMMAND}
+ -DgeneratedFile=${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/content_op.txt
+ -DexpectedContent=${out}
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/VerifyContent.cmake"
+ )
+endfunction()
+
+generate_from_file("a" "610a") # 62->b, 0a->\n
+generate_from_content("a" "610a")
diff --git a/Tests/RunCMake/File_Generate/NewLineStyle-Win32.cmake b/Tests/RunCMake/File_Generate/NewLineStyle-Win32.cmake
new file mode 100644
index 0000000000..394ef7585a
--- /dev/null
+++ b/Tests/RunCMake/File_Generate/NewLineStyle-Win32.cmake
@@ -0,0 +1,33 @@
+function(generate_from_file in out)
+ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/file_ip.txt "${in}")
+ file(GENERATE
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/file_op.txt
+ INPUT ${CMAKE_CURRENT_BINARY_DIR}/file_ip.txt
+ NEWLINE_STYLE WIN32
+ )
+
+ add_custom_target(verifyContentFromFile ALL
+ COMMAND ${CMAKE_COMMAND}
+ -DgeneratedFile=${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/file_op.txt
+ -DexpectedContent=${out}
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/VerifyContent.cmake"
+ )
+endfunction()
+
+function(generate_from_content in out)
+ file(GENERATE
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/content_op.txt
+ CONTENT ${in}
+ NEWLINE_STYLE WIN32
+ )
+
+ add_custom_target(verifyContentFromContent ALL
+ COMMAND ${CMAKE_COMMAND}
+ -DgeneratedFile=${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/content_op.txt
+ -DexpectedContent=${out}
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/VerifyContent.cmake"
+ )
+endfunction()
+
+generate_from_file("a" "610d0a") # 62->b, 0d0a->\r\n
+generate_from_content("a" "610d0a")
diff --git a/Tests/RunCMake/File_Generate/RunCMakeTest.cmake b/Tests/RunCMake/File_Generate/RunCMakeTest.cmake
index 51491af5f0..be3bf04dd9 100644
--- a/Tests/RunCMake/File_Generate/RunCMakeTest.cmake
+++ b/Tests/RunCMake/File_Generate/RunCMakeTest.cmake
@@ -135,6 +135,11 @@ function(run_cmake_and_verify_after_build case)
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
set(RunCMake_TEST_NO_CLEAN 1)
+ if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+ set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug)
+ else()
+ set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+ endif()
run_cmake(${case})
run_cmake_command("${case}-build" ${CMAKE_COMMAND} --build .)
unset(RunCMake_TEST_NO_CLEAN)
@@ -144,3 +149,9 @@ endfunction()
run_cmake_and_verify_after_build(NoSourcePermissions)
run_cmake_and_verify_after_build(UseSourcePermissions)
run_cmake_and_verify_after_build(CustomFilePermissions)
+
+run_cmake(NewLineStyle-NoArg)
+run_cmake(NewLineStyle-InvalidArg)
+run_cmake_and_verify_after_build(NewLineStyle-Default)
+run_cmake_and_verify_after_build(NewLineStyle-Unix)
+run_cmake_and_verify_after_build(NewLineStyle-Win32)
diff --git a/Tests/RunCMake/File_Generate/VerifyContent.cmake b/Tests/RunCMake/File_Generate/VerifyContent.cmake
new file mode 100644
index 0000000000..8563708fcd
--- /dev/null
+++ b/Tests/RunCMake/File_Generate/VerifyContent.cmake
@@ -0,0 +1,4 @@
+file(READ ${generatedFile} actualContent HEX)
+if(NOT "${actualContent}" STREQUAL "${expectedContent}")
+ message(SEND_ERROR "Content mismatch actual: \"${actualContent}\" expected: \"${expectedContent}\"")
+endif()