1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-13 17:47:49 +08:00

file(GENERATE): Support new line style

Fixes: #19198
This commit is contained in:
Asit Dhal
2020-12-22 13:29:34 +01:00
parent 93eef92777
commit 255df8622b
18 changed files with 210 additions and 13 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
};

View File

@@ -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)));
}

View File

@@ -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;

View File

@@ -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")

View File

@@ -0,0 +1 @@
1

View File

@@ -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\)

View File

@@ -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
)

View File

@@ -0,0 +1 @@
1

View File

@@ -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\)

View File

@@ -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
)

View File

@@ -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")

View File

@@ -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")

View File

@@ -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)

View File

@@ -0,0 +1,4 @@
file(READ ${generatedFile} actualContent HEX)
if(NOT "${actualContent}" STREQUAL "${expectedContent}")
message(SEND_ERROR "Content mismatch actual: \"${actualContent}\" expected: \"${expectedContent}\"")
endif()