From 946adadd404a160b708e96e668be9b84c949de69 Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Fri, 2 Oct 2020 12:11:19 -0400 Subject: cmGccDepfileReader: Rework helper code Fix some of the semantics of the depfile, add error handling, and refactor cmGccDepfileLexerHelper. --- Source/LexerParser/cmGccDepfileLexer.cxx | 2 +- Source/LexerParser/cmGccDepfileLexer.in.l | 2 +- Source/cmGccDepfileLexerHelper.cxx | 33 ++++++++++++++-------- Source/cmGccDepfileLexerHelper.h | 3 +- Source/cmGccDepfileReader.cxx | 9 +++--- Source/cmGccDepfileReader.h | 4 ++- Source/cmQtAutoMocUic.cxx | 8 +++--- Tests/CMakeLib/testGccDepfileReader.cxx | 21 ++++++++++---- Tests/CMakeLib/testGccDepfileReader_data/deps4.d | 1 + Tests/CMakeLib/testGccDepfileReader_data/deps5.d | 0 Tests/CMakeLib/testGccDepfileReader_data/deps5.txt | 2 ++ Tests/CMakeLib/testGccDepfileReader_data/deps7.d | 6 ++++ Tests/CMakeLib/testGccDepfileReader_data/deps7.txt | 10 +++++++ 13 files changed, 73 insertions(+), 28 deletions(-) create mode 100644 Tests/CMakeLib/testGccDepfileReader_data/deps4.d create mode 100644 Tests/CMakeLib/testGccDepfileReader_data/deps5.d create mode 100644 Tests/CMakeLib/testGccDepfileReader_data/deps5.txt create mode 100644 Tests/CMakeLib/testGccDepfileReader_data/deps7.d create mode 100644 Tests/CMakeLib/testGccDepfileReader_data/deps7.txt diff --git a/Source/LexerParser/cmGccDepfileLexer.cxx b/Source/LexerParser/cmGccDepfileLexer.cxx index a98969d..3630f4e 100644 --- a/Source/LexerParser/cmGccDepfileLexer.cxx +++ b/Source/LexerParser/cmGccDepfileLexer.cxx @@ -994,7 +994,7 @@ case 5: YY_RULE_SETUP { // A line continuation ends the current file name. - yyextra->newDependency(); + yyextra->newRuleOrDependency(); } YY_BREAK case 6: diff --git a/Source/LexerParser/cmGccDepfileLexer.in.l b/Source/LexerParser/cmGccDepfileLexer.in.l index 08f8577..c83cb75 100644 --- a/Source/LexerParser/cmGccDepfileLexer.in.l +++ b/Source/LexerParser/cmGccDepfileLexer.in.l @@ -42,7 +42,7 @@ NEWLINE \r?\n } {WSPACE}*\\{NEWLINE} { // A line continuation ends the current file name. - yyextra->newDependency(); + yyextra->newRuleOrDependency(); } {NEWLINE} { // A newline ends the current file name and the current rule. diff --git a/Source/cmGccDepfileLexerHelper.cxx b/Source/cmGccDepfileLexerHelper.cxx index 957896f..c782bcd 100644 --- a/Source/cmGccDepfileLexerHelper.cxx +++ b/Source/cmGccDepfileLexerHelper.cxx @@ -27,23 +27,30 @@ bool cmGccDepfileLexerHelper::readFile(const char* filePath) if (!file) { return false; } - newEntry(); + this->newEntry(); yyscan_t scanner; cmGccDepfile_yylex_init(&scanner); cmGccDepfile_yyset_extra(this, scanner); cmGccDepfile_yyrestart(file, scanner); cmGccDepfile_yylex(scanner); cmGccDepfile_yylex_destroy(scanner); - sanitizeContent(); + this->sanitizeContent(); fclose(file); - return true; + return this->HelperState != State::Failed; } void cmGccDepfileLexerHelper::newEntry() { + if (this->HelperState == State::Rule && !this->Content.empty()) { + if (!this->Content.back().rules.empty() && + !this->Content.back().rules.back().empty()) { + this->HelperState = State::Failed; + } + return; + } this->HelperState = State::Rule; this->Content.emplace_back(); - newRule(); + this->newRule(); } void cmGccDepfileLexerHelper::newRule() @@ -56,20 +63,22 @@ void cmGccDepfileLexerHelper::newRule() void cmGccDepfileLexerHelper::newDependency() { - // printf("NEW DEP\n"); + if (this->HelperState == State::Failed) { + return; + } this->HelperState = State::Dependency; - if (this->Content.back().paths.empty() || - !this->Content.back().paths.back().empty()) { - this->Content.back().paths.emplace_back(); + auto& entry = this->Content.back(); + if (entry.paths.empty() || !entry.paths.back().empty()) { + entry.paths.emplace_back(); } } void cmGccDepfileLexerHelper::newRuleOrDependency() { if (this->HelperState == State::Rule) { - newRule(); - } else { - newDependency(); + this->newRule(); + } else if (this->HelperState == State::Dependency) { + this->newDependency(); } } @@ -93,6 +102,8 @@ void cmGccDepfileLexerHelper::addToCurrentPath(const char* s) } dst = &dep->paths.back(); } break; + case State::Failed: + return; } dst->append(s); } diff --git a/Source/cmGccDepfileLexerHelper.h b/Source/cmGccDepfileLexerHelper.h index 07ca61d..91132f5 100644 --- a/Source/cmGccDepfileLexerHelper.h +++ b/Source/cmGccDepfileLexerHelper.h @@ -29,7 +29,8 @@ private: enum class State { Rule, - Dependency + Dependency, + Failed, }; State HelperState = State::Rule; }; diff --git a/Source/cmGccDepfileReader.cxx b/Source/cmGccDepfileReader.cxx index 9d70ede..eb3511a 100644 --- a/Source/cmGccDepfileReader.cxx +++ b/Source/cmGccDepfileReader.cxx @@ -5,14 +5,15 @@ #include #include +#include + #include "cmGccDepfileLexerHelper.h" -cmGccDepfileContent cmReadGccDepfile(const char* filePath) +cm::optional cmReadGccDepfile(const char* filePath) { - cmGccDepfileContent result; cmGccDepfileLexerHelper helper; if (helper.readFile(filePath)) { - result = std::move(helper).extractContent(); + return cm::make_optional(std::move(helper).extractContent()); } - return result; + return cm::nullopt; } diff --git a/Source/cmGccDepfileReader.h b/Source/cmGccDepfileReader.h index 395dd77..59ed7fd 100644 --- a/Source/cmGccDepfileReader.h +++ b/Source/cmGccDepfileReader.h @@ -2,6 +2,8 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #pragma once +#include + #include "cmGccDepfileReaderTypes.h" -cmGccDepfileContent cmReadGccDepfile(const char* filePath); +cm::optional cmReadGccDepfile(const char* filePath); diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index 9cb172b..b27bb88 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -26,7 +27,6 @@ #include "cmCryptoHash.h" #include "cmFileTime.h" #include "cmGccDepfileReader.h" -#include "cmGccDepfileReaderTypes.h" #include "cmGeneratedFileStream.h" #include "cmQtAutoGen.h" #include "cmQtAutoGenerator.h" @@ -2841,14 +2841,14 @@ bool cmQtAutoMocUicT::CreateDirectories() std::vector cmQtAutoMocUicT::dependenciesFromDepFile( const char* filePath) { - cmGccDepfileContent content = cmReadGccDepfile(filePath); - if (content.empty()) { + auto const content = cmReadGccDepfile(filePath); + if (!content || content->empty()) { return {}; } // Moc outputs a depfile with exactly one rule. // Discard the rule and return the dependencies. - return content.front().paths; + return content->front().paths; } void cmQtAutoMocUicT::Abort(bool error) diff --git a/Tests/CMakeLib/testGccDepfileReader.cxx b/Tests/CMakeLib/testGccDepfileReader.cxx index e79f047..d46e8f3 100644 --- a/Tests/CMakeLib/testGccDepfileReader.cxx +++ b/Tests/CMakeLib/testGccDepfileReader.cxx @@ -5,6 +5,8 @@ #include #include +#include + #include "cmsys/FStream.hxx" #include "cmGccDepfileReader.h" @@ -112,17 +114,26 @@ int testGccDepfileReader(int argc, char* argv[]) std::string dataDirPath = argv[1]; dataDirPath += "/testGccDepfileReader_data"; - const int numberOfTestFiles = 3; + const int numberOfTestFiles = 7; // 6th file doesn't exist for (int i = 1; i <= numberOfTestFiles; ++i) { const std::string base = dataDirPath + "/deps" + std::to_string(i); const std::string depfile = base + ".d"; const std::string plainDepfile = base + ".txt"; std::cout << "Comparing " << base << " with " << plainDepfile << std::endl; const auto actual = cmReadGccDepfile(depfile.c_str()); - const auto expected = readPlainDepfile(plainDepfile.c_str()); - if (!compare(actual, expected)) { - dump("actual", actual); - dump("expected", expected); + if (cmSystemTools::FileExists(plainDepfile)) { + if (!actual) { + std::cerr << "Reading " << depfile << " should have succeeded\n"; + return 1; + } + const auto expected = readPlainDepfile(plainDepfile.c_str()); + if (!compare(*actual, expected)) { + dump("actual", *actual); + dump("expected", expected); + return 1; + } + } else if (actual) { + std::cerr << "Reading " << depfile << " should have failed\n"; return 1; } } diff --git a/Tests/CMakeLib/testGccDepfileReader_data/deps4.d b/Tests/CMakeLib/testGccDepfileReader_data/deps4.d new file mode 100644 index 0000000..9977a28 --- /dev/null +++ b/Tests/CMakeLib/testGccDepfileReader_data/deps4.d @@ -0,0 +1 @@ +invalid diff --git a/Tests/CMakeLib/testGccDepfileReader_data/deps5.d b/Tests/CMakeLib/testGccDepfileReader_data/deps5.d new file mode 100644 index 0000000..e69de29 diff --git a/Tests/CMakeLib/testGccDepfileReader_data/deps5.txt b/Tests/CMakeLib/testGccDepfileReader_data/deps5.txt new file mode 100644 index 0000000..6c4a75b --- /dev/null +++ b/Tests/CMakeLib/testGccDepfileReader_data/deps5.txt @@ -0,0 +1,2 @@ +--RULES-- +--DEPENDENCIES-- diff --git a/Tests/CMakeLib/testGccDepfileReader_data/deps7.d b/Tests/CMakeLib/testGccDepfileReader_data/deps7.d new file mode 100644 index 0000000..92280cf --- /dev/null +++ b/Tests/CMakeLib/testGccDepfileReader_data/deps7.d @@ -0,0 +1,6 @@ +out1 \ + out2: \ + in1 \ + in2 + +out3: in3 diff --git a/Tests/CMakeLib/testGccDepfileReader_data/deps7.txt b/Tests/CMakeLib/testGccDepfileReader_data/deps7.txt new file mode 100644 index 0000000..86b6600 --- /dev/null +++ b/Tests/CMakeLib/testGccDepfileReader_data/deps7.txt @@ -0,0 +1,10 @@ +--RULES-- +out1 +out2 +--DEPENDENCIES-- +in1 +in2 +--RULES-- +out3 +--DEPENDENCIES-- +in3 -- cgit v0.12 From b2c14bc7749c21b8a994ce1d9f4e736f712481b9 Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Fri, 2 Oct 2020 14:25:30 -0400 Subject: cmake -E: Add cmake_transform_depfile internal command --- Source/CMakeLists.txt | 2 + Source/cmTransformDepfile.cxx | 114 +++++++++++++++++++++ Source/cmTransformDepfile.h | 14 +++ Source/cmcmd.cxx | 18 ++++ Tests/RunCMake/CMakeLists.txt | 1 + Tests/RunCMake/TransformDepfile/RunCMakeTest.cmake | 21 ++++ Tests/RunCMake/TransformDepfile/deps-unix.d | 6 ++ Tests/RunCMake/TransformDepfile/deps-unix.d.txt | 8 ++ Tests/RunCMake/TransformDepfile/deps-unix.tlog.txt | 6 ++ Tests/RunCMake/TransformDepfile/deps-windows.d | 6 ++ Tests/RunCMake/TransformDepfile/deps-windows.d.txt | 8 ++ .../TransformDepfile/deps-windows.tlog.txt | 6 ++ Tests/RunCMake/TransformDepfile/empty.d | 0 Tests/RunCMake/TransformDepfile/empty.d.txt | 0 Tests/RunCMake/TransformDepfile/empty.tlog.txt | 0 Tests/RunCMake/TransformDepfile/gccdepfile.cmake | 16 +++ .../TransformDepfile/invalid-gcc-result.txt | 1 + .../TransformDepfile/invalid-tlog-result.txt | 1 + Tests/RunCMake/TransformDepfile/invalid.d | 1 + Tests/RunCMake/TransformDepfile/noexist.d.txt | 0 Tests/RunCMake/TransformDepfile/noexist.tlog.txt | 0 Tests/RunCMake/TransformDepfile/vstlog.cmake | 16 +++ bootstrap | 4 + 23 files changed, 249 insertions(+) create mode 100644 Source/cmTransformDepfile.cxx create mode 100644 Source/cmTransformDepfile.h create mode 100644 Tests/RunCMake/TransformDepfile/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/TransformDepfile/deps-unix.d create mode 100644 Tests/RunCMake/TransformDepfile/deps-unix.d.txt create mode 100644 Tests/RunCMake/TransformDepfile/deps-unix.tlog.txt create mode 100644 Tests/RunCMake/TransformDepfile/deps-windows.d create mode 100644 Tests/RunCMake/TransformDepfile/deps-windows.d.txt create mode 100644 Tests/RunCMake/TransformDepfile/deps-windows.tlog.txt create mode 100644 Tests/RunCMake/TransformDepfile/empty.d create mode 100644 Tests/RunCMake/TransformDepfile/empty.d.txt create mode 100644 Tests/RunCMake/TransformDepfile/empty.tlog.txt create mode 100644 Tests/RunCMake/TransformDepfile/gccdepfile.cmake create mode 100644 Tests/RunCMake/TransformDepfile/invalid-gcc-result.txt create mode 100644 Tests/RunCMake/TransformDepfile/invalid-tlog-result.txt create mode 100644 Tests/RunCMake/TransformDepfile/invalid.d create mode 100644 Tests/RunCMake/TransformDepfile/noexist.d.txt create mode 100644 Tests/RunCMake/TransformDepfile/noexist.tlog.txt create mode 100644 Tests/RunCMake/TransformDepfile/vstlog.cmake diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 1e16e17..2946022 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -439,6 +439,8 @@ set(SRCS cmTest.h cmTestGenerator.cxx cmTestGenerator.h + cmTransformDepfile.cxx + cmTransformDepfile.h cmUuid.cxx cmUVHandlePtr.cxx cmUVHandlePtr.h diff --git a/Source/cmTransformDepfile.cxx b/Source/cmTransformDepfile.cxx new file mode 100644 index 0000000..e1f8753 --- /dev/null +++ b/Source/cmTransformDepfile.cxx @@ -0,0 +1,114 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmTransformDepfile.h" + +#include +#include +#include +#include + +#include + +#include "cmsys/FStream.hxx" + +#include "cmGccDepfileReader.h" +#include "cmGccDepfileReaderTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +namespace { +void WriteFilenameGcc(cmsys::ofstream& fout, const std::string& filename) +{ + for (auto c : filename) { + switch (c) { + case ' ': + fout << "\\ "; + break; + case '\\': + fout << "\\\\"; + break; + default: + fout << c; + break; + } + } +} + +void WriteGccDepfile(cmsys::ofstream& fout, const cmGccDepfileContent& content) +{ + for (auto const& dep : content) { + bool first = true; + for (auto const& rule : dep.rules) { + if (!first) { + fout << " \\\n "; + } + first = false; + WriteFilenameGcc(fout, rule); + } + fout << ':'; + for (auto const& path : dep.paths) { + fout << " \\\n " << path; + } + fout << '\n'; + } +} + +void WriteVsTlog(cmsys::ofstream& fout, const cmGccDepfileContent& content) +{ + for (auto const& dep : content) { + fout << '^'; + bool first = true; + for (auto const& rule : dep.rules) { + if (!first) { + fout << '|'; + } + first = false; + fout << cmSystemTools::ConvertToOutputPath(rule); + } + fout << "\r\n"; + for (auto const& path : dep.paths) { + fout << cmSystemTools::ConvertToOutputPath(path) << "\r\n"; + } + } +} +} + +bool cmTransformDepfile(cmDepfileFormat format, const std::string& prefix, + const std::string& infile, const std::string& outfile) +{ + cmGccDepfileContent content; + if (cmSystemTools::FileExists(infile)) { + auto result = cmReadGccDepfile(infile.c_str()); + if (!result) { + return false; + } + content = *std::move(result); + } + + for (auto& dep : content) { + for (auto& rule : dep.rules) { + if (!cmSystemTools::FileIsFullPath(rule)) { + rule = cmStrCat(prefix, rule); + } + } + for (auto& path : dep.paths) { + if (!cmSystemTools::FileIsFullPath(path)) { + path = cmStrCat(prefix, path); + } + } + } + + cmsys::ofstream fout(outfile.c_str()); + if (!fout) { + return false; + } + switch (format) { + case cmDepfileFormat::GccDepfile: + WriteGccDepfile(fout, content); + break; + case cmDepfileFormat::VsTlog: + WriteVsTlog(fout, content); + break; + } + return true; +} diff --git a/Source/cmTransformDepfile.h b/Source/cmTransformDepfile.h new file mode 100644 index 0000000..792c1aa --- /dev/null +++ b/Source/cmTransformDepfile.h @@ -0,0 +1,14 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include + +enum class cmDepfileFormat +{ + GccDepfile, + VsTlog, +}; + +bool cmTransformDepfile(cmDepfileFormat format, const std::string& prefix, + const std::string& infile, const std::string& outfile); diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 15d2fd1..0bdc6fa 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -19,6 +19,7 @@ #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmTransformDepfile.h" #include "cmUVProcessChain.h" #include "cmUtils.hxx" #include "cmVersion.h" @@ -1426,6 +1427,23 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args) return cmcmd::WindowsCEEnvironment("9.0", args[2]); } #endif + + // Internal depfile transformation + if (args[1] == "cmake_transform_depfile" && args.size() == 6) { + auto format = cmDepfileFormat::GccDepfile; + if (args[2] == "gccdepfile") { + format = cmDepfileFormat::GccDepfile; + } else if (args[2] == "vstlog") { + format = cmDepfileFormat::VsTlog; + } else { + return 1; + } + std::string prefix = args[3]; + if (prefix == "./") { + prefix.clear(); + } + return cmTransformDepfile(format, prefix, args[4], args[5]) ? 0 : 1; + } } ::CMakeCommandUsage(args[0].c_str()); diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index a8261c6..59ca3b1 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -771,6 +771,7 @@ add_RunCMake_test(PrecompileHeaders -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} add_RunCMake_test("UnityBuild") add_RunCMake_test(CMakePresets) +add_RunCMake_test(TransformDepfile) if(WIN32) add_RunCMake_test(Win32GenEx) diff --git a/Tests/RunCMake/TransformDepfile/RunCMakeTest.cmake b/Tests/RunCMake/TransformDepfile/RunCMakeTest.cmake new file mode 100644 index 0000000..cb75eb0 --- /dev/null +++ b/Tests/RunCMake/TransformDepfile/RunCMakeTest.cmake @@ -0,0 +1,21 @@ +include(RunCMake) + +function(run_transform_depfile name) + set(RunCMake-check-file gccdepfile.cmake) + run_cmake_command(${name}-gcc + ${CMAKE_COMMAND} -E cmake_transform_depfile gccdepfile ../ ${CMAKE_CURRENT_LIST_DIR}/${name}.d out.d + ) + set(RunCMake-check-file vstlog.cmake) + run_cmake_command(${name}-tlog + ${CMAKE_COMMAND} -E cmake_transform_depfile vstlog ../ ${CMAKE_CURRENT_LIST_DIR}/${name}.d out.tlog + ) +endfunction() + +if(WIN32) + run_transform_depfile(deps-windows) +else() + run_transform_depfile(deps-unix) +endif() +run_transform_depfile(noexist) +run_transform_depfile(empty) +run_transform_depfile(invalid) diff --git a/Tests/RunCMake/TransformDepfile/deps-unix.d b/Tests/RunCMake/TransformDepfile/deps-unix.d new file mode 100644 index 0000000..5da5be8 --- /dev/null +++ b/Tests/RunCMake/TransformDepfile/deps-unix.d @@ -0,0 +1,6 @@ +out1 /home/build/out2: in1 /home/build/in2 + +out3 \ + /home/build/out4: \ + in3 \ + /home/build/in4 diff --git a/Tests/RunCMake/TransformDepfile/deps-unix.d.txt b/Tests/RunCMake/TransformDepfile/deps-unix.d.txt new file mode 100644 index 0000000..58770f2 --- /dev/null +++ b/Tests/RunCMake/TransformDepfile/deps-unix.d.txt @@ -0,0 +1,8 @@ +../out1 \ + /home/build/out2: \ + ../in1 \ + /home/build/in2 +../out3 \ + /home/build/out4: \ + ../in3 \ + /home/build/in4 diff --git a/Tests/RunCMake/TransformDepfile/deps-unix.tlog.txt b/Tests/RunCMake/TransformDepfile/deps-unix.tlog.txt new file mode 100644 index 0000000..2a26edf --- /dev/null +++ b/Tests/RunCMake/TransformDepfile/deps-unix.tlog.txt @@ -0,0 +1,6 @@ +^../out1|/home/build/out2 +../in1 +/home/build/in2 +^../out3|/home/build/out4 +../in3 +/home/build/in4 diff --git a/Tests/RunCMake/TransformDepfile/deps-windows.d b/Tests/RunCMake/TransformDepfile/deps-windows.d new file mode 100644 index 0000000..c926670 --- /dev/null +++ b/Tests/RunCMake/TransformDepfile/deps-windows.d @@ -0,0 +1,6 @@ +out1 C:/build/out2: in1 C:/build/in2 + +out3 \ + C:/build/out4: \ + in3 \ + C:/build/in4 diff --git a/Tests/RunCMake/TransformDepfile/deps-windows.d.txt b/Tests/RunCMake/TransformDepfile/deps-windows.d.txt new file mode 100644 index 0000000..47b3ebf --- /dev/null +++ b/Tests/RunCMake/TransformDepfile/deps-windows.d.txt @@ -0,0 +1,8 @@ +../out1 \ + C:/build/out2: \ + ../in1 \ + C:/build/in2 +../out3 \ + C:/build/out4: \ + ../in3 \ + C:/build/in4 diff --git a/Tests/RunCMake/TransformDepfile/deps-windows.tlog.txt b/Tests/RunCMake/TransformDepfile/deps-windows.tlog.txt new file mode 100644 index 0000000..1e6024d --- /dev/null +++ b/Tests/RunCMake/TransformDepfile/deps-windows.tlog.txt @@ -0,0 +1,6 @@ +^..\out1|C:\build\out2 +..\in1 +C:\build\in2 +^..\out3|C:\build\out4 +..\in3 +C:\build\in4 diff --git a/Tests/RunCMake/TransformDepfile/empty.d b/Tests/RunCMake/TransformDepfile/empty.d new file mode 100644 index 0000000..e69de29 diff --git a/Tests/RunCMake/TransformDepfile/empty.d.txt b/Tests/RunCMake/TransformDepfile/empty.d.txt new file mode 100644 index 0000000..e69de29 diff --git a/Tests/RunCMake/TransformDepfile/empty.tlog.txt b/Tests/RunCMake/TransformDepfile/empty.tlog.txt new file mode 100644 index 0000000..e69de29 diff --git a/Tests/RunCMake/TransformDepfile/gccdepfile.cmake b/Tests/RunCMake/TransformDepfile/gccdepfile.cmake new file mode 100644 index 0000000..be1e210 --- /dev/null +++ b/Tests/RunCMake/TransformDepfile/gccdepfile.cmake @@ -0,0 +1,16 @@ +if(EXISTS "${RunCMake_SOURCE_DIR}/${name}.d.txt") + file(READ "${RunCMake_SOURCE_DIR}/${name}.d.txt" expected_contents) + + if(EXISTS "${RunCMake_TEST_BINARY_DIR}/out.d") + file(READ "${RunCMake_TEST_BINARY_DIR}/out.d" actual_contents) + if(NOT actual_contents STREQUAL expected_contents) + string(REPLACE "\n" "\n " p_expected_contents "${expected_contents}") + string(REPLACE "\n" "\n " p_actual_contents "${actual_contents}") + string(APPEND RunCMake_TEST_FAILED "Expected contents of ${RunCMake_TEST_BINARY_DIR}/out.d:\n ${p_expected_contents}\nActual contents:\n ${p_actual_contents}") + endif() + else() + string(APPEND RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/out.d should exist\n") + endif() +elseif(EXISTS "${RunCMake_TEST_BINARY_DIR}/out.d") + string(APPEND RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/out.d should not exist\n") +endif() diff --git a/Tests/RunCMake/TransformDepfile/invalid-gcc-result.txt b/Tests/RunCMake/TransformDepfile/invalid-gcc-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/TransformDepfile/invalid-gcc-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/TransformDepfile/invalid-tlog-result.txt b/Tests/RunCMake/TransformDepfile/invalid-tlog-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/TransformDepfile/invalid-tlog-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/TransformDepfile/invalid.d b/Tests/RunCMake/TransformDepfile/invalid.d new file mode 100644 index 0000000..9977a28 --- /dev/null +++ b/Tests/RunCMake/TransformDepfile/invalid.d @@ -0,0 +1 @@ +invalid diff --git a/Tests/RunCMake/TransformDepfile/noexist.d.txt b/Tests/RunCMake/TransformDepfile/noexist.d.txt new file mode 100644 index 0000000..e69de29 diff --git a/Tests/RunCMake/TransformDepfile/noexist.tlog.txt b/Tests/RunCMake/TransformDepfile/noexist.tlog.txt new file mode 100644 index 0000000..e69de29 diff --git a/Tests/RunCMake/TransformDepfile/vstlog.cmake b/Tests/RunCMake/TransformDepfile/vstlog.cmake new file mode 100644 index 0000000..afa78d0 --- /dev/null +++ b/Tests/RunCMake/TransformDepfile/vstlog.cmake @@ -0,0 +1,16 @@ +if(EXISTS "${RunCMake_SOURCE_DIR}/${name}.tlog.txt") + file(READ "${RunCMake_SOURCE_DIR}/${name}.tlog.txt" expected_contents) + + if(EXISTS "${RunCMake_TEST_BINARY_DIR}/out.tlog") + file(READ "${RunCMake_TEST_BINARY_DIR}/out.tlog" actual_contents) + if(NOT actual_contents STREQUAL expected_contents) + string(REPLACE "\n" "\n " p_expected_contents "${expected_contents}") + string(REPLACE "\n" "\n " p_actual_contents "${actual_contents}") + string(APPEND RunCMake_TEST_FAILED "Expected contents of ${RunCMake_TEST_BINARY_DIR}/out.tlog:\n ${p_expected_contents}\nActual contents:\n ${p_actual_contents}") + endif() + else() + string(APPEND RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/out.tlog should exist\n") + endif() +elseif(EXISTS "${RunCMake_TEST_BINARY_DIR}/out.tlog") + string(APPEND RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/out.tlog should not exist\n") +endif() diff --git a/bootstrap b/bootstrap index feccde9..9126ae6 100755 --- a/bootstrap +++ b/bootstrap @@ -412,6 +412,8 @@ CMAKE_CXX_SOURCES="\ cmProjectCommand \ cmPropertyDefinition \ cmPropertyMap \ + cmGccDepfileLexerHelper \ + cmGccDepfileReader \ cmReturnCommand \ cmRulePlaceholderExpander \ cmRuntimeDependencyArchive \ @@ -452,6 +454,7 @@ CMAKE_CXX_SOURCES="\ cmTest \ cmTestGenerator \ cmTimestamp \ + cmTransformDepfile \ cmTryCompileCommand \ cmTryRunCommand \ cmUnsetCommand \ @@ -491,6 +494,7 @@ LexerParser_CXX_SOURCES="\ cmCommandArgumentParser \ cmExprLexer \ cmExprParser \ + cmGccDepfileLexer \ " LexerParser_C_SOURCES="\ -- cgit v0.12 From 596439b1bbac44b4218f65615d93e9fe9bd43922 Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Mon, 5 Oct 2020 12:15:17 -0400 Subject: cmCustomCommandGenerator: Add option to transform depfile --- Source/cmCustomCommandGenerator.cxx | 73 ++++++++++++++++++++++++++++++++++++- Source/cmCustomCommandGenerator.h | 4 +- Source/cmGlobalGenerator.h | 6 +++ bootstrap | 67 +++++++++++++++++----------------- 4 files changed, 113 insertions(+), 37 deletions(-) diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx index 60504ba..d8307f6 100644 --- a/Source/cmCustomCommandGenerator.cxx +++ b/Source/cmCustomCommandGenerator.cxx @@ -6,18 +6,22 @@ #include #include +#include #include +#include "cmCryptoHash.h" #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmProperty.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmTransformDepfile.h" namespace { void AppendPaths(const std::vector& inputs, @@ -42,7 +46,8 @@ void AppendPaths(const std::vector& inputs, cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc, std::string config, - cmLocalGenerator* lg) + cmLocalGenerator* lg, + bool transformDepfile) : CC(cc) , Config(std::move(config)) , LG(lg) @@ -75,6 +80,36 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc, this->CommandLines.push_back(std::move(argv)); } + if (transformDepfile && !this->CommandLines.empty() && + !cc.GetDepfile().empty() && + this->LG->GetGlobalGenerator()->DepfileFormat()) { + cmCustomCommandLine argv; + argv.push_back(cmSystemTools::GetCMakeCommand()); + argv.emplace_back("-E"); + argv.emplace_back("cmake_transform_depfile"); + switch (*this->LG->GetGlobalGenerator()->DepfileFormat()) { + case cmDepfileFormat::GccDepfile: + argv.emplace_back("gccdepfile"); + break; + case cmDepfileFormat::VsTlog: + argv.emplace_back("vstlog"); + break; + } + if (this->LG->GetCurrentBinaryDirectory() == + this->LG->GetBinaryDirectory()) { + argv.emplace_back("./"); + } else { + argv.push_back(cmStrCat(this->LG->MaybeConvertToRelativePath( + this->LG->GetBinaryDirectory(), + this->LG->GetCurrentBinaryDirectory()), + '/')); + } + argv.push_back(this->GetFullDepfile()); + argv.push_back(this->GetInternalDepfile()); + + this->CommandLines.push_back(std::move(argv)); + } + AppendPaths(cc.GetByproducts(), ge, this->LG, this->Config, this->Byproducts); AppendPaths(cc.GetDepends(), ge, this->LG, this->Config, this->Depends); @@ -97,7 +132,7 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc, unsigned int cmCustomCommandGenerator::GetNumberOfCommands() const { - return static_cast(this->CC.GetCommandLines().size()); + return static_cast(this->CommandLines.size()); } void cmCustomCommandGenerator::FillEmulatorsWithArguments() @@ -234,6 +269,40 @@ void cmCustomCommandGenerator::AppendArguments(unsigned int c, } } +std::string cmCustomCommandGenerator::GetFullDepfile() const +{ + std::string depfile = this->CC.GetDepfile(); + if (depfile.empty()) { + return ""; + } + + if (!cmSystemTools::FileIsFullPath(depfile)) { + depfile = cmStrCat(this->LG->GetCurrentBinaryDirectory(), '/', depfile); + } + return cmSystemTools::CollapseFullPath(depfile); +} + +std::string cmCustomCommandGenerator::GetInternalDepfile() const +{ + std::string depfile = this->GetFullDepfile(); + if (depfile.empty()) { + return ""; + } + + cmCryptoHash hash(cmCryptoHash::AlgoSHA256); + std::string extension; + switch (*this->LG->GetGlobalGenerator()->DepfileFormat()) { + case cmDepfileFormat::GccDepfile: + extension = ".d"; + break; + case cmDepfileFormat::VsTlog: + extension = ".tlog"; + break; + } + return cmStrCat(this->LG->GetBinaryDirectory(), "/CMakeFiles/d/", + hash.HashString(depfile), extension); +} + const char* cmCustomCommandGenerator::GetComment() const { return this->CC.GetComment(); diff --git a/Source/cmCustomCommandGenerator.h b/Source/cmCustomCommandGenerator.h index 412eba8..8b5259d 100644 --- a/Source/cmCustomCommandGenerator.h +++ b/Source/cmCustomCommandGenerator.h @@ -31,7 +31,7 @@ class cmCustomCommandGenerator public: cmCustomCommandGenerator(cmCustomCommand const& cc, std::string config, - cmLocalGenerator* lg); + cmLocalGenerator* lg, bool transformDepfile = true); cmCustomCommandGenerator(const cmCustomCommandGenerator&) = delete; cmCustomCommandGenerator& operator=(const cmCustomCommandGenerator&) = delete; @@ -45,4 +45,6 @@ public: std::vector const& GetByproducts() const; std::vector const& GetDepends() const; bool HasOnlyEmptyCommandLines() const; + std::string GetFullDepfile() const; + std::string GetInternalDepfile() const; }; diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index b532a43..de5eba7 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -14,6 +14,7 @@ #include #include +#include #include #include "cm_codecvt.hxx" @@ -26,6 +27,7 @@ #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetDepend.h" +#include "cmTransformDepfile.h" #if !defined(CMAKE_BOOTSTRAP) # include @@ -452,6 +454,10 @@ public: virtual bool ShouldStripResourcePath(cmMakefile*) const; virtual bool SupportsCustomCommandDepfile() const { return false; } + virtual cm::optional DepfileFormat() const + { + return cm::nullopt; + } std::string GetSharedLibFlagsForLanguage(std::string const& lang) const; diff --git a/bootstrap b/bootstrap index 9126ae6..b36c162 100755 --- a/bootstrap +++ b/bootstrap @@ -307,6 +307,7 @@ CMAKE_CXX_SOURCES="\ cmContinueCommand \ cmCoreTryCompile \ cmCreateTestSourceList \ + cmCryptoHash \ cmCustomCommand \ cmCustomCommandGenerator \ cmCustomCommandLines \ @@ -539,6 +540,18 @@ KWSYS_FILES="\ SystemTools.hxx \ Terminal.h" +LIBRHASH_C_SOURCES="\ + librhash/algorithms.c \ + librhash/byte_order.c \ + librhash/hex.c \ + librhash/md5.c \ + librhash/rhash.c \ + librhash/sha1.c \ + librhash/sha256.c \ + librhash/sha3.c \ + librhash/sha512.c \ + " + if ${cmake_system_mingw}; then LIBUV_C_SOURCES="\ src/fs-poll.c \ @@ -1016,7 +1029,6 @@ cmake_ld_flags=${LDFLAGS} # Add generator-specific files if test "${cmake_bootstrap_generator}" = "Ninja"; then CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES} \ - cmCryptoHash \ cmFortranParserImpl \ cmGlobalNinjaGenerator \ cmLocalNinjaGenerator \ @@ -1037,18 +1049,6 @@ if test "${cmake_bootstrap_generator}" = "Ninja"; then src/lib_json/json_value.cpp \ src/lib_json/json_writer.cpp \ " - - LIBRHASH_C_SOURCES="\ - librhash/algorithms.c \ - librhash/byte_order.c \ - librhash/hex.c \ - librhash/md5.c \ - librhash/rhash.c \ - librhash/sha1.c \ - librhash/sha256.c \ - librhash/sha3.c \ - librhash/sha512.c \ - " else CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES} \ cmDepends \ @@ -1062,7 +1062,6 @@ else " JSONCPP_CXX_SOURCES= - LIBRHASH_C_SOURCES= fi # Add Cygwin-specific flags @@ -1632,17 +1631,17 @@ if test "x${bootstrap_system_libuv}" = "x"; then objs="${objs} uv-`cmake_obj ${a}`" done fi +if test "x${bootstrap_system_librhash}" = "x"; then + for a in ${LIBRHASH_C_SOURCES}; do + objs="${objs} rhash-`cmake_obj ${a}`" + done +fi if test "${cmake_bootstrap_generator}" = "Ninja"; then if test "x${bootstrap_system_jsoncpp}" = "x"; then for a in ${JSONCPP_CXX_SOURCES}; do objs="${objs} jsoncpp-`cmake_obj ${a}`" done fi - if test "x${bootstrap_system_librhash}" = "x"; then - for a in ${LIBRHASH_C_SOURCES}; do - objs="${objs} rhash-`cmake_obj ${a}`" - done - fi fi libs="" @@ -1702,6 +1701,15 @@ else libs="${libs} -luv" fi +if test "x${bootstrap_system_librhash}" != "x"; then + if test `which pkg-config`; then + use_librhash_flags="`pkg-config --cflags librhash`" + cmake_c_flags="${cmake_c_flags} ${use_librhash_flags}" + cmake_cxx_flags="${cmake_cxx_flags} ${use_librhash_flags}" + fi + libs="${libs} -lrhash" +fi + if test "${cmake_bootstrap_generator}" = "Ninja"; then jsoncpp_cxx_flags= if test "x${bootstrap_system_jsoncpp}" = "x"; then @@ -1713,15 +1721,6 @@ if test "${cmake_bootstrap_generator}" = "Ninja"; then fi libs="${libs} -ljsoncpp" fi - - if test "x${bootstrap_system_librhash}" != "x"; then - if test `which pkg-config`; then - use_librhash_flags="`pkg-config --cflags librhash`" - cmake_c_flags="${cmake_c_flags} ${use_librhash_flags}" - cmake_cxx_flags="${cmake_cxx_flags} ${use_librhash_flags}" - fi - libs="${libs} -lrhash" - fi fi if test "x${cmake_ansi_cxx_flags}" != "x"; then @@ -1845,6 +1844,12 @@ if test "x${bootstrap_system_libuv}" = "x"; then write_source_rule "c" "uv-`cmake_obj ${a}`" "${src}" "${uv_c_flags}" done fi +if test "x${bootstrap_system_librhash}" = "x"; then + for a in ${LIBRHASH_C_SOURCES}; do + src=`cmake_escape_artifact "${cmake_source_dir}/Utilities/cmlibrhash/${a}"` + write_source_rule "c" "rhash-`cmake_obj ${a}`" "${src}" "" + done +fi if test "${cmake_bootstrap_generator}" = "Ninja"; then if test "x${bootstrap_system_jsoncpp}" = "x"; then for a in ${JSONCPP_CXX_SOURCES}; do @@ -1852,12 +1857,6 @@ if test "${cmake_bootstrap_generator}" = "Ninja"; then write_source_rule "cxx" "jsoncpp-`cmake_obj ${a}`" "${src}" "${jsoncpp_cxx_flags}" done fi - if test "x${bootstrap_system_librhash}" = "x"; then - for a in ${LIBRHASH_C_SOURCES}; do - src=`cmake_escape_artifact "${cmake_source_dir}/Utilities/cmlibrhash/${a}"` - write_source_rule "c" "rhash-`cmake_obj ${a}`" "${src}" "" - done - fi fi if test "${cmake_bootstrap_generator}" = "Ninja"; then echo " -- cgit v0.12 From 146e1e6ba14d964bf120635aca953cc76e5b2282 Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Fri, 2 Oct 2020 17:35:40 -0400 Subject: Ninja: Transform DEPFILEs with policy CMP0116 Fixes: #21267 --- Help/command/add_custom_command.rst | 5 +++ Help/manual/cmake-policies.7.rst | 1 + Help/policy/CMP0116.rst | 38 +++++++++++++++++ Help/release/dev/ninja-depfile-transformation.rst | 5 +++ Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst | 2 + Source/cmGlobalNinjaGenerator.h | 5 +++ Source/cmLocalNinjaGenerator.cxx | 45 +++++++++++++++++++- Source/cmPolicies.h | 5 ++- Tests/RunCMake/CMP0116/CMP0116-NEW-NOWARN.cmake | 3 ++ Tests/RunCMake/CMP0116/CMP0116-NEW-WARN.cmake | 3 ++ Tests/RunCMake/CMP0116/CMP0116-OLD-NOWARN.cmake | 3 ++ Tests/RunCMake/CMP0116/CMP0116-OLD-WARN.cmake | 3 ++ .../CMP0116/CMP0116-WARN-NOWARN-stderr.txt | 7 ++++ Tests/RunCMake/CMP0116/CMP0116-WARN-NOWARN.cmake | 3 ++ .../RunCMake/CMP0116/CMP0116-WARN-WARN-stderr.txt | 16 +++++++ Tests/RunCMake/CMP0116/CMP0116-WARN-WARN.cmake | 3 ++ Tests/RunCMake/CMP0116/CMakeLists.txt | 3 ++ Tests/RunCMake/CMP0116/Common.cmake | 8 ++++ Tests/RunCMake/CMP0116/RunCMakeTest.cmake | 49 ++++++++++++++++++++++ Tests/RunCMake/CMP0116/Subdirectory/CMakeLists.txt | 6 +++ Tests/RunCMake/CMP0116/WriteDepfile.cmake | 3 ++ Tests/RunCMake/CMP0116/check.cmake | 18 ++++++++ Tests/RunCMake/CMakeLists.txt | 3 ++ 23 files changed, 234 insertions(+), 3 deletions(-) create mode 100644 Help/policy/CMP0116.rst create mode 100644 Help/release/dev/ninja-depfile-transformation.rst create mode 100644 Tests/RunCMake/CMP0116/CMP0116-NEW-NOWARN.cmake create mode 100644 Tests/RunCMake/CMP0116/CMP0116-NEW-WARN.cmake create mode 100644 Tests/RunCMake/CMP0116/CMP0116-OLD-NOWARN.cmake create mode 100644 Tests/RunCMake/CMP0116/CMP0116-OLD-WARN.cmake create mode 100644 Tests/RunCMake/CMP0116/CMP0116-WARN-NOWARN-stderr.txt create mode 100644 Tests/RunCMake/CMP0116/CMP0116-WARN-NOWARN.cmake create mode 100644 Tests/RunCMake/CMP0116/CMP0116-WARN-WARN-stderr.txt create mode 100644 Tests/RunCMake/CMP0116/CMP0116-WARN-WARN.cmake create mode 100644 Tests/RunCMake/CMP0116/CMakeLists.txt create mode 100644 Tests/RunCMake/CMP0116/Common.cmake create mode 100644 Tests/RunCMake/CMP0116/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/CMP0116/Subdirectory/CMakeLists.txt create mode 100644 Tests/RunCMake/CMP0116/WriteDepfile.cmake create mode 100644 Tests/RunCMake/CMP0116/check.cmake diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst index 231f9da..a28b0b1 100644 --- a/Help/command/add_custom_command.rst +++ b/Help/command/add_custom_command.rst @@ -239,6 +239,11 @@ The options are: command itself. Using ``DEPFILE`` with other generators than Ninja is an error. + If the ``DEPFILE`` argument is relative, it should be relative to + :variable:`CMAKE_CURRENT_BINARY_DIR`, and any relative paths inside the + ``DEPFILE`` should also be relative to :variable:`CMAKE_CURRENT_BINARY_DIR` + (see policy :policy:`CMP0116`.) + Build Events ^^^^^^^^^^^^ diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 0836883..886fe80 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -57,6 +57,7 @@ Policies Introduced by CMake 3.20 .. toctree:: :maxdepth: 1 + CMP0116: Ninja generators transform DEPFILEs from add_custom_command(). CMP0115: Source file extensions must be explicit. Policies Introduced by CMake 3.19 diff --git a/Help/policy/CMP0116.rst b/Help/policy/CMP0116.rst new file mode 100644 index 0000000..25a14c4 --- /dev/null +++ b/Help/policy/CMP0116.rst @@ -0,0 +1,38 @@ +CMP0116 +------- + +.. versionadded:: 3.20 + +Ninja generators transform ``DEPFILE`` s from :command:`add_custom_command`. + +In CMake 3.19 and below, files given to the ``DEPFILE`` argument of +:command:`add_custom_command` were passed directly to Ninja's ``depfile`` +variable without any path resolution. This meant that if +:command:`add_custom_command` was called from a subdirectory (created by +:command:`add_subdirectory`), the ``DEPFILE`` argument would have to be either +an absolute path or a path relative to :variable:`CMAKE_BINARY_DIR`, rather +than :variable:`CMAKE_CURRENT_BINARY_DIR`. In addition, no transformation was +done on the file listed in ``DEPFILE``, which meant that the paths within the +``DEPFILE`` had the same restrictions. + +Starting with CMake 3.20, the ``DEPFILE`` argument is relative to +:variable:`CMAKE_CURRENT_BINARY_DIR` (unless it is absolute), and the paths in +the ``DEPFILE`` are also relative to :variable:`CMAKE_CURRENT_BINARY_DIR`. +CMake automatically transforms the paths in the ``DEPFILE`` (unless they are +absolute) after the custom command is run. The file listed in ``DEPFILE`` is +not modified in any way. Instead, CMake writes the transformation to its own +internal file, and passes this internal file to Ninja's ``depfile`` variable. +This transformation happens regardless of whether or not ``DEPFILE`` is +relative, and regardless of whether or not :command:`add_custom_command` is +called from a subdirectory. + +The ``OLD`` behavior for this policy is to pass the ``DEPFILE`` to Ninja +unaltered. The ``NEW`` behavior for this policy is to transform the ``DEPFILE`` +after running the custom command. + +This policy was introduced in CMake version 3.20. Unlike most policies, +CMake version |release| does *not* warn by default when this policy is not set +(unless ``DEPFILE`` is used in a subdirectory) and simply uses ``OLD`` +behavior. See documentation of the +:variable:`CMAKE_POLICY_WARNING_CMP0116 >` +variable to control the warning. diff --git a/Help/release/dev/ninja-depfile-transformation.rst b/Help/release/dev/ninja-depfile-transformation.rst new file mode 100644 index 0000000..edf7f58 --- /dev/null +++ b/Help/release/dev/ninja-depfile-transformation.rst @@ -0,0 +1,5 @@ +ninja-depfile-transformation +---------------------------- + +* Ninja generators now transform ``DEPFILE`` s from + :command:`add_custom_command`. See policy :policy:`CMP0116` for details. diff --git a/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst b/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst index d35595a..9f68741 100644 --- a/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst +++ b/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst @@ -27,6 +27,8 @@ warn by default: policy :policy:`CMP0102`. * ``CMAKE_POLICY_WARNING_CMP0112`` controls the warning for policy :policy:`CMP0112`. +* ``CMAKE_POLICY_WARNING_CMP0116`` controls the warning for + policy :policy:`CMP0116`. This variable should not be set by a project in CMake code. Project developers running CMake may set this variable in their cache to diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 884a711..ec001d3 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -24,6 +24,7 @@ #include "cmNinjaTypes.h" #include "cmPolicies.h" #include "cmStringAlgorithms.h" +#include "cmTransformDepfile.h" class cmCustomCommand; class cmGeneratorTarget; @@ -211,6 +212,10 @@ public: const char* GetCleanTargetName() const override { return "clean"; } bool SupportsCustomCommandDepfile() const override { return true; } + cm::optional DepfileFormat() const override + { + return cmDepfileFormat::GccDepfile; + } virtual cmGeneratedFileStream* GetImplFileStream( const std::string& /*config*/) const diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index ad782ee..ce85320 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -22,7 +22,9 @@ #include "cmGlobalNinjaGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmNinjaTargetGenerator.h" +#include "cmPolicies.h" #include "cmProperty.h" #include "cmRulePlaceholderExpander.h" #include "cmSourceFile.h" @@ -573,7 +575,20 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( return; } - cmCustomCommandGenerator ccg(*cc, config, this); + bool transformDepfile = false; + auto cmp0116 = this->GetPolicyStatus(cmPolicies::CMP0116); + switch (cmp0116) { + case cmPolicies::OLD: + case cmPolicies::WARN: + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + transformDepfile = true; + break; + } + + cmCustomCommandGenerator ccg(*cc, config, this, transformDepfile); const std::vector& outputs = ccg.GetOutputs(); const std::vector& byproducts = ccg.GetByproducts(); @@ -623,10 +638,36 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( cmCryptoHash hash(cmCryptoHash::AlgoSHA256); customStep += hash.HashString(ninjaOutputs[0]).substr(0, 7); + std::string depfile = cc->GetDepfile(); + if (!depfile.empty()) { + switch (cmp0116) { + case cmPolicies::WARN: + if (this->GetCurrentBinaryDirectory() != + this->GetBinaryDirectory() || + this->Makefile->PolicyOptionalWarningEnabled( + "CMAKE_POLICY_WARNING_CMP0116")) { + this->GetCMakeInstance()->IssueMessage( + MessageType::AUTHOR_WARNING, + cmPolicies::GetPolicyWarning(cmPolicies::CMP0116), + cc->GetBacktrace()); + } + CM_FALLTHROUGH; + case cmPolicies::OLD: + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + cmSystemTools::MakeDirectory( + cmStrCat(this->GetBinaryDirectory(), "/CMakeFiles/d")); + depfile = ccg.GetInternalDepfile(); + break; + } + } + gg->WriteCustomCommandBuild( this->BuildCommandLine(cmdLines, customStep), this->ConstructComment(ccg), "Custom command for " + ninjaOutputs[0], - cc->GetDepfile(), cc->GetJobPool(), cc->GetUsesTerminal(), + depfile, cc->GetJobPool(), cc->GetUsesTerminal(), /*restat*/ !symbolic || !byproducts.empty(), ninjaOutputs, config, ninjaDeps, orderOnlyDeps); } diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index f9f7d0e..99bac98 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -342,7 +342,10 @@ class cmMakefile; "ExternalProject step targets fully adopt their steps.", 3, 19, 0, \ cmPolicies::WARN) \ SELECT(POLICY, CMP0115, "Source file extensions must be explicit.", 3, 20, \ - 0, cmPolicies::WARN) + 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0116, \ + "Ninja generators transform DEPFILEs from add_custom_command().", 3, \ + 20, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ diff --git a/Tests/RunCMake/CMP0116/CMP0116-NEW-NOWARN.cmake b/Tests/RunCMake/CMP0116/CMP0116-NEW-NOWARN.cmake new file mode 100644 index 0000000..f92fac6 --- /dev/null +++ b/Tests/RunCMake/CMP0116/CMP0116-NEW-NOWARN.cmake @@ -0,0 +1,3 @@ +set(depdir) + +include(Common.cmake) diff --git a/Tests/RunCMake/CMP0116/CMP0116-NEW-WARN.cmake b/Tests/RunCMake/CMP0116/CMP0116-NEW-WARN.cmake new file mode 100644 index 0000000..f92fac6 --- /dev/null +++ b/Tests/RunCMake/CMP0116/CMP0116-NEW-WARN.cmake @@ -0,0 +1,3 @@ +set(depdir) + +include(Common.cmake) diff --git a/Tests/RunCMake/CMP0116/CMP0116-OLD-NOWARN.cmake b/Tests/RunCMake/CMP0116/CMP0116-OLD-NOWARN.cmake new file mode 100644 index 0000000..665f485 --- /dev/null +++ b/Tests/RunCMake/CMP0116/CMP0116-OLD-NOWARN.cmake @@ -0,0 +1,3 @@ +set(depdir Subdirectory/) + +include(Common.cmake) diff --git a/Tests/RunCMake/CMP0116/CMP0116-OLD-WARN.cmake b/Tests/RunCMake/CMP0116/CMP0116-OLD-WARN.cmake new file mode 100644 index 0000000..665f485 --- /dev/null +++ b/Tests/RunCMake/CMP0116/CMP0116-OLD-WARN.cmake @@ -0,0 +1,3 @@ +set(depdir Subdirectory/) + +include(Common.cmake) diff --git a/Tests/RunCMake/CMP0116/CMP0116-WARN-NOWARN-stderr.txt b/Tests/RunCMake/CMP0116/CMP0116-WARN-NOWARN-stderr.txt new file mode 100644 index 0000000..843ff1c --- /dev/null +++ b/Tests/RunCMake/CMP0116/CMP0116-WARN-NOWARN-stderr.txt @@ -0,0 +1,7 @@ +^(CMake Warning \(dev\) at Subdirectory/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + Policy CMP0116 is not set: Ninja generators transform DEPFILEs from + add_custom_command\(\)\. Run "cmake --help-policy CMP0116" for policy + details\. Use the cmake_policy command to set the policy and suppress this + warning\. +This warning is for project developers\. Use -Wno-dev to suppress it\. +*)+$ diff --git a/Tests/RunCMake/CMP0116/CMP0116-WARN-NOWARN.cmake b/Tests/RunCMake/CMP0116/CMP0116-WARN-NOWARN.cmake new file mode 100644 index 0000000..665f485 --- /dev/null +++ b/Tests/RunCMake/CMP0116/CMP0116-WARN-NOWARN.cmake @@ -0,0 +1,3 @@ +set(depdir Subdirectory/) + +include(Common.cmake) diff --git a/Tests/RunCMake/CMP0116/CMP0116-WARN-WARN-stderr.txt b/Tests/RunCMake/CMP0116/CMP0116-WARN-WARN-stderr.txt new file mode 100644 index 0000000..e29af91 --- /dev/null +++ b/Tests/RunCMake/CMP0116/CMP0116-WARN-WARN-stderr.txt @@ -0,0 +1,16 @@ +^(CMake Warning \(dev\) at Common\.cmake:[0-9]+ \(add_custom_command\): + Policy CMP0116 is not set: Ninja generators transform DEPFILEs from + add_custom_command\(\)\. Run "cmake --help-policy CMP0116" for policy + details\. Use the cmake_policy command to set the policy and suppress this + warning\. +Call Stack \(most recent call first\): + CMP0116-WARN-WARN\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. ++)+(CMake Warning \(dev\) at Subdirectory/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + Policy CMP0116 is not set: Ninja generators transform DEPFILEs from + add_custom_command\(\)\. Run "cmake --help-policy CMP0116" for policy + details\. Use the cmake_policy command to set the policy and suppress this + warning\. +This warning is for project developers\. Use -Wno-dev to suppress it\. +*)+$ diff --git a/Tests/RunCMake/CMP0116/CMP0116-WARN-WARN.cmake b/Tests/RunCMake/CMP0116/CMP0116-WARN-WARN.cmake new file mode 100644 index 0000000..665f485 --- /dev/null +++ b/Tests/RunCMake/CMP0116/CMP0116-WARN-WARN.cmake @@ -0,0 +1,3 @@ +set(depdir Subdirectory/) + +include(Common.cmake) diff --git a/Tests/RunCMake/CMP0116/CMakeLists.txt b/Tests/RunCMake/CMP0116/CMakeLists.txt new file mode 100644 index 0000000..b646c4a --- /dev/null +++ b/Tests/RunCMake/CMP0116/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.18) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMP0116/Common.cmake b/Tests/RunCMake/CMP0116/Common.cmake new file mode 100644 index 0000000..472b162 --- /dev/null +++ b/Tests/RunCMake/CMP0116/Common.cmake @@ -0,0 +1,8 @@ +add_custom_command( + OUTPUT top.txt + COMMAND ${CMAKE_COMMAND} -DOUTFILE=top.txt -DINFILE=topdep.txt -DDEPFILE=top.txt.d -DSTAMPFILE=topstamp.txt -DDEPDIR= -P ${CMAKE_SOURCE_DIR}/WriteDepfile.cmake + DEPFILE top.txt.d + ) +add_custom_target(top ALL DEPENDS top.txt) + +add_subdirectory(Subdirectory) diff --git a/Tests/RunCMake/CMP0116/RunCMakeTest.cmake b/Tests/RunCMake/CMP0116/RunCMakeTest.cmake new file mode 100644 index 0000000..8a83cc1 --- /dev/null +++ b/Tests/RunCMake/CMP0116/RunCMakeTest.cmake @@ -0,0 +1,49 @@ +include(RunCMake) + +function(run_cmp0116 status warn) + if(warn) + set(name CMP0116-${status}-WARN) + else() + set(name CMP0116-${status}-NOWARN) + endif() + set(RunCMake_TEST_OPTIONS + -DCMAKE_POLICY_WARNING_CMP0116:BOOL=${warn} + ) + if(NOT status STREQUAL "WARN") + list(APPEND RunCMake_TEST_OPTIONS + -DCMAKE_POLICY_DEFAULT_CMP0116:STRING=${status} + ) + endif() + + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build) + run_cmake(${name}) + unset(RunCMake_TEST_OPTIONS) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake-check-file check.cmake) + + file(TOUCH "${RunCMake_TEST_BINARY_DIR}/topdep.txt") + file(TOUCH "${RunCMake_TEST_BINARY_DIR}/Subdirectory/subdep.txt") + set(cmp0116_step 1) + run_cmake_command(${name}-build1 ${CMAKE_COMMAND} --build . --config Debug) + file(REMOVE "${RunCMake_TEST_BINARY_DIR}/topstamp.txt") + file(REMOVE "${RunCMake_TEST_BINARY_DIR}/Subdirectory/substamp.txt") + execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1.25) + + file(TOUCH "${RunCMake_TEST_BINARY_DIR}/topdep.txt") + file(TOUCH "${RunCMake_TEST_BINARY_DIR}/Subdirectory/subdep.txt") + set(cmp0116_step 2) + run_cmake_command(${name}-build2 ${CMAKE_COMMAND} --build . --config Debug) + file(REMOVE "${RunCMake_TEST_BINARY_DIR}/topstamp.txt") + file(REMOVE "${RunCMake_TEST_BINARY_DIR}/Subdirectory/substamp.txt") + execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1.25) + + set(cmp0116_step 3) + run_cmake_command(${name}-build3 ${CMAKE_COMMAND} --build . --config Debug) +endfunction() + +run_cmp0116(WARN OFF) +run_cmp0116(OLD OFF) +run_cmp0116(NEW OFF) +run_cmp0116(WARN ON) +run_cmp0116(OLD ON) +run_cmp0116(NEW ON) diff --git a/Tests/RunCMake/CMP0116/Subdirectory/CMakeLists.txt b/Tests/RunCMake/CMP0116/Subdirectory/CMakeLists.txt new file mode 100644 index 0000000..f0f60b2 --- /dev/null +++ b/Tests/RunCMake/CMP0116/Subdirectory/CMakeLists.txt @@ -0,0 +1,6 @@ +add_custom_command( + OUTPUT sub.txt + COMMAND ${CMAKE_COMMAND} -DOUTFILE=sub.txt -DINFILE=subdep.txt -DDEPFILE=sub.txt.d -DSTAMPFILE=substamp.txt -DDEPDIR=${depdir} -P ${CMAKE_SOURCE_DIR}/WriteDepfile.cmake + DEPFILE ${depdir}sub.txt.d + ) +add_custom_target(sub ALL DEPENDS sub.txt) diff --git a/Tests/RunCMake/CMP0116/WriteDepfile.cmake b/Tests/RunCMake/CMP0116/WriteDepfile.cmake new file mode 100644 index 0000000..1a74d2a --- /dev/null +++ b/Tests/RunCMake/CMP0116/WriteDepfile.cmake @@ -0,0 +1,3 @@ +file(TOUCH "${OUTFILE}") +file(TOUCH "${STAMPFILE}") +file(WRITE "${DEPFILE}" "${DEPDIR}${OUTFILE}: ${DEPDIR}${INFILE}\n") diff --git a/Tests/RunCMake/CMP0116/check.cmake b/Tests/RunCMake/CMP0116/check.cmake new file mode 100644 index 0000000..1b16748 --- /dev/null +++ b/Tests/RunCMake/CMP0116/check.cmake @@ -0,0 +1,18 @@ +function(check_exists file) + if(NOT EXISTS "${file}") + string(APPEND RunCMake_TEST_FAILED "${file} does not exist\n") + endif() + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) +endfunction() + +function(check_not_exists file) + if(EXISTS "${file}") + string(APPEND RunCMake_TEST_FAILED "${file} exists\n") + endif() + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) +endfunction() + +if(cmp0116_step EQUAL 3) + check_not_exists("${RunCMake_TEST_BINARY_DIR}/topstamp.txt") + check_not_exists("${RunCMake_TEST_BINARY_DIR}/Subdirectory/substamp.txt") +endif() diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 59ca3b1..6072711 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -126,6 +126,9 @@ endif() add_RunCMake_test(CMP0106) add_RunCMake_test(CMP0111) add_RunCMake_test(CMP0115) +if(CMAKE_GENERATOR MATCHES "Ninja") + add_RunCMake_test(CMP0116) +endif() # The test for Policy 65 requires the use of the # CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode -- cgit v0.12