1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-14 02:08:27 +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> <INPUT input-file|CONTENT content>
[CONDITION expression] [TARGET target] [CONDITION expression] [TARGET target]
[FILE_PERMISSIONS <permissions>...] [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 Generate an output file for each build configuration supported by the current
:manual:`CMake Generator <cmake-generators(7)>`. Evaluate :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. Transfer the file permissions of the original file to the generated file.
This option expects INPUT option. 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 Exactly one ``CONTENT`` or ``INPUT`` option must be given. A specific
``OUTPUT`` file may be named by at most one invocation of ``file(GENERATE)``. ``OUTPUT`` file may be named by at most one invocation of ``file(GENERATE)``.
Generated files are modified and their timestamp updated on subsequent cmake 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& targetName,
const std::string& outputExpr, const std::string& outputExpr,
const std::string& condition, bool inputIsContent, 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(); cmListFileBacktrace lfbt = status.GetMakefile().GetBacktrace();
@@ -2304,7 +2305,7 @@ void AddEvaluationFile(const std::string& inputName,
status.GetMakefile().AddEvaluationFile( status.GetMakefile().AddEvaluationFile(
inputName, targetName, std::move(outputCge), std::move(conditionCge), inputName, targetName, std::move(outputCge), std::move(conditionCge),
permissions, inputIsContent); newLineCharacter, permissions, inputIsContent);
} }
bool HandleGenerateCommand(std::vector<std::string> const& args, 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 Content;
std::string Condition; std::string Condition;
std::string Target; std::string Target;
std::string NewLineStyle;
bool NoSourcePermissions = false; bool NoSourcePermissions = false;
bool UseSourcePermissions = false; bool UseSourcePermissions = false;
std::vector<std::string> FilePermissions; std::vector<std::string> FilePermissions;
@@ -2336,7 +2338,8 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
.Bind("TARGET"_s, &Arguments::Target) .Bind("TARGET"_s, &Arguments::Target)
.Bind("NO_SOURCE_PERMISSIONS"_s, &Arguments::NoSourcePermissions) .Bind("NO_SOURCE_PERMISSIONS"_s, &Arguments::NoSourcePermissions)
.Bind("USE_SOURCE_PERMISSIONS"_s, &Arguments::UseSourcePermissions) .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> unparsedArguments;
std::vector<std::string> keywordsMissingValues; std::vector<std::string> keywordsMissingValues;
@@ -2400,6 +2403,18 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
status.SetError("Unknown argument to GENERATE subcommand."); 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; std::string input = arguments.Input;
if (inputIsContent) { if (inputIsContent) {
input = arguments.Content; input = arguments.Content;
@@ -2463,7 +2478,8 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
} }
AddEvaluationFile(input, arguments.Target, arguments.Output, AddEvaluationFile(input, arguments.Target, arguments.Output,
arguments.Condition, inputIsContent, permisiions, status); arguments.Condition, inputIsContent,
newLineStyle.GetCharacters(), permisiions, status);
return true; return true;
} }

View File

@@ -21,13 +21,14 @@ cmGeneratorExpressionEvaluationFile::cmGeneratorExpressionEvaluationFile(
std::string input, std::string target, std::string input, std::string target,
std::unique_ptr<cmCompiledGeneratorExpression> outputFileExpr, std::unique_ptr<cmCompiledGeneratorExpression> outputFileExpr,
std::unique_ptr<cmCompiledGeneratorExpression> condition, std::unique_ptr<cmCompiledGeneratorExpression> condition,
bool inputIsContent, mode_t permissions, bool inputIsContent, std::string newLineCharacter, mode_t permissions,
cmPolicies::PolicyStatus policyStatusCMP0070) cmPolicies::PolicyStatus policyStatusCMP0070)
: Input(std::move(input)) : Input(std::move(input))
, Target(std::move(target)) , Target(std::move(target))
, OutputFileExpr(std::move(outputFileExpr)) , OutputFileExpr(std::move(outputFileExpr))
, Condition(std::move(condition)) , Condition(std::move(condition))
, InputIsContent(inputIsContent) , InputIsContent(inputIsContent)
, NewLineCharacter(std::move(newLineCharacter))
, PolicyStatusCMP0070(policyStatusCMP0070) , PolicyStatusCMP0070(policyStatusCMP0070)
, Permissions(permissions) , Permissions(permissions)
{ {
@@ -82,9 +83,33 @@ void cmGeneratorExpressionEvaluationFile::Generate(
this->Files.push_back(outputFileName); this->Files.push_back(outputFileName);
outputFiles[outputFileName] = outputContent; 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.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) { if (fout.Close() && perm) {
cmSystemTools::SetPermissions(outputFileName.c_str(), perm); cmSystemTools::SetPermissions(outputFileName.c_str(), perm);
} }

View File

@@ -24,7 +24,7 @@ public:
std::string input, std::string target, std::string input, std::string target,
std::unique_ptr<cmCompiledGeneratorExpression> outputFileExpr, std::unique_ptr<cmCompiledGeneratorExpression> outputFileExpr,
std::unique_ptr<cmCompiledGeneratorExpression> condition, std::unique_ptr<cmCompiledGeneratorExpression> condition,
bool inputIsContent, mode_t permissions, bool inputIsContent, std::string newLineCharacter, mode_t permissions,
cmPolicies::PolicyStatus policyStatusCMP0070); cmPolicies::PolicyStatus policyStatusCMP0070);
void Generate(cmLocalGenerator* lg); void Generate(cmLocalGenerator* lg);
@@ -58,6 +58,7 @@ private:
const std::unique_ptr<cmCompiledGeneratorExpression> Condition; const std::unique_ptr<cmCompiledGeneratorExpression> Condition;
std::vector<std::string> Files; std::vector<std::string> Files;
const bool InputIsContent; const bool InputIsContent;
const std::string NewLineCharacter;
cmPolicies::PolicyStatus PolicyStatusCMP0070; cmPolicies::PolicyStatus PolicyStatusCMP0070;
mode_t Permissions; mode_t Permissions;
}; };

View File

@@ -865,13 +865,13 @@ void cmMakefile::EnforceDirectoryLevelRules() const
void cmMakefile::AddEvaluationFile( void cmMakefile::AddEvaluationFile(
const std::string& inputFile, const std::string& targetName, const std::string& inputFile, const std::string& targetName,
std::unique_ptr<cmCompiledGeneratorExpression> outputName, std::unique_ptr<cmCompiledGeneratorExpression> outputName,
std::unique_ptr<cmCompiledGeneratorExpression> condition, mode_t permissions, std::unique_ptr<cmCompiledGeneratorExpression> condition,
bool inputIsContent) const std::string& newLineCharacter, mode_t permissions, bool inputIsContent)
{ {
this->EvaluationFiles.push_back( this->EvaluationFiles.push_back(
cm::make_unique<cmGeneratorExpressionEvaluationFile>( cm::make_unique<cmGeneratorExpressionEvaluationFile>(
inputFile, targetName, std::move(outputName), std::move(condition), inputFile, targetName, std::move(outputName), std::move(condition),
inputIsContent, permissions, inputIsContent, newLineCharacter, permissions,
this->GetPolicyStatus(cmPolicies::CMP0070))); this->GetPolicyStatus(cmPolicies::CMP0070)));
} }

View File

@@ -899,7 +899,8 @@ public:
const std::string& inputFile, const std::string& targetName, const std::string& inputFile, const std::string& targetName,
std::unique_ptr<cmCompiledGeneratorExpression> outputName, std::unique_ptr<cmCompiledGeneratorExpression> outputName,
std::unique_ptr<cmCompiledGeneratorExpression> condition, 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>>& const std::vector<std::unique_ptr<cmGeneratorExpressionEvaluationFile>>&
GetEvaluationFiles() const; 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(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
set(RunCMake_TEST_NO_CLEAN 1) 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(${case})
run_cmake_command("${case}-build" ${CMAKE_COMMAND} --build .) run_cmake_command("${case}-build" ${CMAKE_COMMAND} --build .)
unset(RunCMake_TEST_NO_CLEAN) unset(RunCMake_TEST_NO_CLEAN)
@@ -144,3 +149,9 @@ endfunction()
run_cmake_and_verify_after_build(NoSourcePermissions) run_cmake_and_verify_after_build(NoSourcePermissions)
run_cmake_and_verify_after_build(UseSourcePermissions) run_cmake_and_verify_after_build(UseSourcePermissions)
run_cmake_and_verify_after_build(CustomFilePermissions) 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()