mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-14 02:08:27 +08:00
@@ -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
|
||||
|
5
Help/release/dev/file-generate-new-line-style.rst
Normal file
5
Help/release/dev/file-generate-new-line-style.rst
Normal 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.
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -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)));
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
35
Tests/RunCMake/File_Generate/NewLineStyle-Default.cmake
Normal file
35
Tests/RunCMake/File_Generate/NewLineStyle-Default.cmake
Normal 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")
|
@@ -0,0 +1 @@
|
||||
1
|
@@ -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\)
|
@@ -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
|
||||
)
|
@@ -0,0 +1 @@
|
||||
1
|
@@ -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\)
|
7
Tests/RunCMake/File_Generate/NewLineStyle-NoArg.cmake
Normal file
7
Tests/RunCMake/File_Generate/NewLineStyle-NoArg.cmake
Normal 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
|
||||
)
|
33
Tests/RunCMake/File_Generate/NewLineStyle-Unix.cmake
Normal file
33
Tests/RunCMake/File_Generate/NewLineStyle-Unix.cmake
Normal 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")
|
33
Tests/RunCMake/File_Generate/NewLineStyle-Win32.cmake
Normal file
33
Tests/RunCMake/File_Generate/NewLineStyle-Win32.cmake
Normal 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")
|
@@ -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)
|
||||
|
4
Tests/RunCMake/File_Generate/VerifyContent.cmake
Normal file
4
Tests/RunCMake/File_Generate/VerifyContent.cmake
Normal 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()
|
Reference in New Issue
Block a user