Browse Source

Merge topic 'makefiles-custom_command-depfile'

3eacf857e3 Tests: Add case for DEPFILE in add_custom_command
cfd8a5ac1f Makefiles: Add support of DEPFILE for add_custom_command
a526f71266 cmGccDepfileReader: Add new function ensuring paths are valid

Acked-by: Kitware Robot <[email protected]>
Merge-request: !5617
Marc Chevrier 5 years ago
parent
commit
f5cdcd0606
29 changed files with 475 additions and 94 deletions
  1. 18 3
      Help/command/add_custom_command.rst
  2. 5 0
      Help/release/dev/makefile-depfile.rst
  3. 6 0
      Source/cmAddCustomCommandCommand.cxx
  4. 86 47
      Source/cmDependsCompiler.cxx
  5. 34 0
      Source/cmGccDepfileReader.cxx
  6. 8 0
      Source/cmGccDepfileReader.h
  7. 6 0
      Source/cmGlobalUnixMakefileGenerator3.h
  8. 27 12
      Source/cmLocalUnixMakefileGenerator3.cxx
  9. 19 0
      Source/cmMakefileTargetGenerator.cxx
  10. 36 3
      Source/cmMakefileUtilityTargetGenerator.cxx
  11. 1 15
      Source/cmTransformDepfile.cxx
  12. 2 0
      Source/cmcmd.cxx
  13. 0 0
      Tests/RunCMake/BuildDepends/CustomCommandDependencies-BadArgs-result.txt
  14. 4 0
      Tests/RunCMake/BuildDepends/CustomCommandDependencies-BadArgs-stderr.txt
  15. 10 0
      Tests/RunCMake/BuildDepends/CustomCommandDependencies-BadArgs.cmake
  16. 73 0
      Tests/RunCMake/BuildDepends/CustomCommandDependencies.cmake
  17. 3 0
      Tests/RunCMake/BuildDepends/CustomCommandDependencies.step1.cmake
  18. 3 0
      Tests/RunCMake/BuildDepends/CustomCommandDependencies.step2.cmake
  19. 61 0
      Tests/RunCMake/BuildDepends/CustomCommandDepfile.cmake
  20. 10 0
      Tests/RunCMake/BuildDepends/CustomCommandDepfile.step1.cmake
  21. 6 0
      Tests/RunCMake/BuildDepends/CustomCommandDepfile.step2.cmake
  22. 1 0
      Tests/RunCMake/BuildDepends/CustomCommandDepfile.step3.cmake
  23. 22 0
      Tests/RunCMake/BuildDepends/DepfileSubdir/CMakeLists.txt
  24. 6 0
      Tests/RunCMake/BuildDepends/GenerateDepFile.cmake
  25. 20 0
      Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
  26. 8 0
      Tests/RunCMake/BuildDepends/WriteDepfile.cmake
  27. 0 5
      Tests/RunCMake/Make/CustomCommandDepfile-ERROR-stderr.txt
  28. 0 8
      Tests/RunCMake/Make/CustomCommandDepfile-ERROR.cmake
  29. 0 1
      Tests/RunCMake/Make/RunCMakeTest.cmake

+ 18 - 3
Help/command/add_custom_command.rst

@@ -201,6 +201,10 @@ The options are:
   Note that the ``IMPLICIT_DEPENDS`` option is currently supported
   only for Makefile generators and will be ignored by other generators.
 
+  .. note::
+
+    This option cannot be specified at the same time as ``DEPFILE`` option.
+
 ``JOB_POOL``
   .. versionadded:: 3.15
 
@@ -263,15 +267,26 @@ The options are:
 ``DEPFILE``
   .. versionadded:: 3.7
 
-  Specify a ``.d`` depfile for the :generator:`Ninja` generator.
+  Specify a ``.d`` depfile for the :generator:`Ninja` generator and
+  :ref:`Makefile Generators`.
   A ``.d`` file holds dependencies usually emitted by the custom
   command itself.
-  Using ``DEPFILE`` with other generators than Ninja is an error.
+  Using ``DEPFILE`` with other generators than :generator:`Ninja` or
+  :ref:`Makefile Generators` is an error.
+
+  .. versionadded:: 3.20
+    Added the support of :ref:`Makefile Generators`.
 
   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`.)
+  (see policy :policy:`CMP0116`. This policy is always ``NEW`` for
+  :ref:`Makefile Generators`).
+
+  .. note::
+
+    For :ref:`Makefile Generators`, this option cannot be specified at the
+    same time as ``IMPLICIT_DEPENDS`` option.
 
 Examples: Generating Files
 ^^^^^^^^^^^^^^^^^^^^^^^^^^

+ 5 - 0
Help/release/dev/makefile-depfile.rst

@@ -0,0 +1,5 @@
+makefile-depfile
+----------------
+
+* The :command:`add_custom_command` command gained ``DEPFILE`` support on
+  :ref:`Makefile Generators`.

+ 6 - 0
Source/cmAddCustomCommandCommand.cxx

@@ -296,6 +296,12 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
     status.SetError("given APPEND option with no OUTPUT.");
     return false;
   }
+  if (!implicit_depends.empty() && !depfile.empty() &&
+      mf.GetGlobalGenerator()->GetName() != "Ninja") {
+    // Makefiles generators does not support both at the same time
+    status.SetError("IMPLICIT_DEPENDS and DEPFILE can not both be specified.");
+    return false;
+  }
 
   // Check for an append request.
   if (append) {

+ 86 - 47
Source/cmDependsCompiler.cxx

@@ -19,6 +19,7 @@
 
 #include "cmFileTime.h"
 #include "cmGccDepfileReader.h"
+#include "cmGccDepfileReaderTypes.h"
 #include "cmGlobalUnixMakefileGenerator3.h"
 #include "cmLocalUnixMakefileGenerator3.h"
 #include "cmStringAlgorithms.h"
@@ -86,7 +87,7 @@ bool cmDependsCompiler::CheckDependencies(
     if (!forceReadDeps) {
       depFileTime.Load(depFile);
     }
-    if (forceReadDeps || depFileTime.Newer(internalDepFileTime)) {
+    if (forceReadDeps || depFileTime.Compare(internalDepFileTime) >= 0) {
       status = false;
       if (this->Verbose) {
         cmSystemTools::Stdout(cmStrCat("Dependencies file \"", depFile,
@@ -95,59 +96,92 @@ bool cmDependsCompiler::CheckDependencies(
       }
 
       std::vector<std::string> depends;
-      if (format == "msvc"_s) {
-        cmsys::ifstream fin(depFile.c_str());
-        if (!fin) {
-          continue;
+      if (format == "custom"_s) {
+        std::string prefix;
+        if (this->LocalGenerator->GetCurrentBinaryDirectory() !=
+            this->LocalGenerator->GetBinaryDirectory()) {
+          prefix =
+            cmStrCat(this->LocalGenerator->MaybeConvertToRelativePath(
+                       this->LocalGenerator->GetBinaryDirectory(),
+                       this->LocalGenerator->GetCurrentBinaryDirectory()),
+                     '/');
         }
 
-        std::string line;
-        if (!isValidPath) {
-          // insert source as first dependency
-          depends.push_back(source);
-        }
-        while (cmSystemTools::GetLineFromStream(fin, line)) {
-          depends.emplace_back(std::move(line));
-        }
-      } else {
-        auto deps = cmReadGccDepfile(depFile.c_str());
+        auto deps = cmReadGccDepfile(depFile.c_str(), prefix);
         if (!deps) {
           continue;
         }
 
-        // dependencies generated by the compiler contains only one target
-        depends = std::move(deps->front().paths);
-        if (depends.empty()) {
-          // unexpectedly empty, ignore it and continue
-          continue;
-        }
-
-        // depending of the effective format of the dependencies file generated
-        // by the compiler, the target can be wrongly identified as a
-        // dependency so remove it from the list
-        if (depends.front() == target) {
-          depends.erase(depends.begin());
+        for (auto& entry : *deps) {
+          depends = std::move(entry.paths);
+          if (isValidPath) {
+            cm::erase_if(depends, isValidPath);
+          }
+          // copy depends for each target, except first one, which can be
+          // moved
+          for (auto index = entry.rules.size() - 1; index > 0; --index) {
+            dependencies[entry.rules[index]] = depends;
+          }
+          dependencies[entry.rules.front()] = std::move(depends);
         }
+      } else {
+        if (format == "msvc"_s) {
+          cmsys::ifstream fin(depFile.c_str());
+          if (!fin) {
+            continue;
+          }
 
-        // ensure source file is the first dependency
-        if (depends.front() != source) {
-          cm::erase(depends, source);
+          std::string line;
           if (!isValidPath) {
-            depends.insert(depends.begin(), source);
+            // insert source as first dependency
+            depends.push_back(source);
+          }
+          while (cmSystemTools::GetLineFromStream(fin, line)) {
+            depends.emplace_back(std::move(line));
+          }
+        } else if (format == "gcc"_s) {
+          auto deps = cmReadGccDepfile(depFile.c_str());
+          if (!deps) {
+            continue;
           }
-        } else if (isValidPath) {
-          // remove first dependency because it must not be filtered out
-          depends.erase(depends.begin());
+
+          // dependencies generated by the compiler contains only one target
+          depends = std::move(deps->front().paths);
+          if (depends.empty()) {
+            // unexpectedly empty, ignore it and continue
+            continue;
+          }
+
+          // depending of the effective format of the dependencies file
+          // generated by the compiler, the target can be wrongly identified
+          // as a dependency so remove it from the list
+          if (depends.front() == target) {
+            depends.erase(depends.begin());
+          }
+
+          // ensure source file is the first dependency
+          if (depends.front() != source) {
+            cm::erase(depends, source);
+            if (!isValidPath) {
+              depends.insert(depends.begin(), source);
+            }
+          } else if (isValidPath) {
+            // remove first dependency because it must not be filtered out
+            depends.erase(depends.begin());
+          }
+        } else {
+          // unknown format, ignore it
+          continue;
         }
-      }
 
-      if (isValidPath) {
-        cm::erase_if(depends, isValidPath);
-        // insert source as first dependency
-        depends.insert(depends.begin(), source);
-      }
+        if (isValidPath) {
+          cm::erase_if(depends, isValidPath);
+          // insert source as first dependency
+          depends.insert(depends.begin(), source);
+        }
 
-      dependencies[target] = std::move(depends);
+        dependencies[target] = std::move(depends);
+      }
     }
   }
 
@@ -168,6 +202,8 @@ void cmDependsCompiler::WriteDependencies(
 
   // external dependencies file
   for (auto& node : makeDependencies) {
+    auto target = LocalGenerator->ConvertToMakefilePath(
+      this->LocalGenerator->MaybeConvertToRelativePath(binDir, node.first));
     auto& deps = node.second;
     std::transform(
       deps.cbegin(), deps.cend(), deps.begin(),
@@ -176,13 +212,16 @@ void cmDependsCompiler::WriteDependencies(
           this->LocalGenerator->MaybeConvertToRelativePath(binDir, dep));
       });
 
-    makeDepends << this->LocalGenerator->ConvertToMakefilePath(node.first)
-                << ": " << deps.front();
-    // first dependency is the source, remove it because should not be declared
-    // as phony target
-    deps.erase(deps.begin());
+    bool first_dep = true;
+    makeDepends << target << ": ";
     for (const auto& dep : deps) {
-      makeDepends << ' ' << lineContinue << "  " << dep;
+      if (first_dep) {
+        first_dep = false;
+        makeDepends << dep;
+      } else {
+        makeDepends << ' ' << lineContinue << "  " << dep;
+      }
+
       phonyTargets.emplace(dep.data(), dep.length());
     }
     makeDepends << std::endl << std::endl;

+ 34 - 0
Source/cmGccDepfileReader.cxx

@@ -4,10 +4,13 @@
 
 #include <type_traits>
 #include <utility>
+#include <vector>
 
 #include <cm/optional>
 
 #include "cmGccDepfileLexerHelper.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 
 cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath)
 {
@@ -17,3 +20,34 @@ cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath)
   }
   return cm::nullopt;
 }
+
+cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath,
+                                                   const std::string& prefix)
+{
+  auto deps = cmReadGccDepfile(filePath);
+
+  if (prefix.empty() || !deps) {
+    return deps;
+  }
+
+  for (auto& dep : *deps) {
+    for (auto& rule : dep.rules) {
+      if (!cmSystemTools::FileIsFullPath(rule)) {
+        rule = cmStrCat(prefix, rule);
+      }
+      if (cmSystemTools::FileIsFullPath(rule)) {
+        rule = cmSystemTools::CollapseFullPath(rule);
+      }
+    }
+    for (auto& path : dep.paths) {
+      if (!cmSystemTools::FileIsFullPath(path)) {
+        path = cmStrCat(prefix, path);
+      }
+      if (cmSystemTools::FileIsFullPath(path)) {
+        path = cmSystemTools::CollapseFullPath(path);
+      }
+    }
+  }
+
+  return deps;
+}

+ 8 - 0
Source/cmGccDepfileReader.h

@@ -2,8 +2,16 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #pragma once
 
+#include <string>
+
 #include <cm/optional>
 
 #include "cmGccDepfileReaderTypes.h"
 
 cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath);
+
+/*
+ * Read dependencies file and append prefix to all relative paths
+ */
+cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath,
+                                                   const std::string& prefix);

+ 6 - 0
Source/cmGlobalUnixMakefileGenerator3.h

@@ -93,6 +93,12 @@ public:
    */
   static bool SupportsPlatform() { return false; }
 
+  /**
+   * Utilized to determine if this generator
+   * supports DEPFILE option.
+   */
+  bool SupportsCustomCommandDepfile() const override { return true; }
+
   /** Get the documentation entry for this generator.  */
   static void GetDocumentation(cmDocumentationEntry& entry);
 

+ 27 - 12
Source/cmLocalUnixMakefileGenerator3.cxx

@@ -13,6 +13,7 @@
 #include <cm/string_view>
 #include <cm/vector>
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 #include "cmsys/FStream.hxx"
 #include "cmsys/Terminal.h"
@@ -1435,7 +1436,7 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies(
     this->Makefile->GetSafeDefinition("CMAKE_DEPENDS_DEPENDENCY_FILES");
   if (!depends.empty()) {
     // dependencies are managed by compiler
-    auto depFiles = cmExpandedList(depends);
+    auto depFiles = cmExpandedList(depends, true);
     std::string const internalDepFile =
       targetDir + "/compiler_depend.internal";
     std::string const depFile = targetDir + "/compiler_depend.make";
@@ -1998,18 +1999,32 @@ void cmLocalUnixMakefileGenerator3::WriteDependLanguageInfo(
   cmakefileStream << "\n# The set of dependency files which are needed:\n";
   cmakefileStream << "set(CMAKE_DEPENDS_DEPENDENCY_FILES\n";
   for (auto const& compilerLang : compilerLangs) {
-    auto depFormat = this->Makefile->GetSafeDefinition(
-      cmStrCat("CMAKE_", compilerLang.first, "_DEPFILE_FORMAT"));
     auto const& compilerPairs = compilerLang.second;
-    for (auto const& compilerPair : compilerPairs) {
-      for (auto const& src : compilerPair.second) {
-        cmakefileStream << "  \"" << src << "\" \""
-                        << this->MaybeConvertToRelativePath(
-                             this->GetBinaryDirectory(), compilerPair.first)
-                        << "\" \"" << depFormat << "\" \""
-                        << this->MaybeConvertToRelativePath(
-                             this->GetBinaryDirectory(), compilerPair.first)
-                        << ".d\"\n";
+    if (compilerLang.first == "CUSTOM"_s) {
+      for (auto const& compilerPair : compilerPairs) {
+        for (auto const& src : compilerPair.second) {
+          cmakefileStream << R"(  "" ")"
+                          << this->MaybeConvertToRelativePath(
+                               this->GetBinaryDirectory(), compilerPair.first)
+                          << R"(" "custom" ")"
+                          << this->MaybeConvertToRelativePath(
+                               this->GetBinaryDirectory(), src)
+                          << "\"\n";
+        }
+      }
+    } else {
+      auto depFormat = this->Makefile->GetSafeDefinition(
+        cmStrCat("CMAKE_", compilerLang.first, "_DEPFILE_FORMAT"));
+      for (auto const& compilerPair : compilerPairs) {
+        for (auto const& src : compilerPair.second) {
+          cmakefileStream << "  \"" << src << "\" \""
+                          << this->MaybeConvertToRelativePath(
+                               this->GetBinaryDirectory(), compilerPair.first)
+                          << "\" \"" << depFormat << "\" \""
+                          << this->MaybeConvertToRelativePath(
+                               this->GetBinaryDirectory(), compilerPair.first)
+                          << ".d\"\n";
+        }
       }
     }
   }

+ 19 - 0
Source/cmMakefileTargetGenerator.cxx

@@ -1617,6 +1617,16 @@ void cmMakefileTargetGenerator::GenerateCustomRuleFile(
   std::vector<std::string> depends;
   this->LocalGenerator->AppendCustomDepend(depends, ccg);
 
+  if (!ccg.GetCC().GetDepfile().empty()) {
+    // Add dependency over timestamp file for dependencies management
+    auto dependTimestamp = cmSystemTools::ConvertToOutputPath(
+      this->LocalGenerator->MaybeConvertToRelativePath(
+        this->LocalGenerator->GetBinaryDirectory(),
+        cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts")));
+
+    depends.push_back(dependTimestamp);
+  }
+
   // Write the rule.
   const std::vector<std::string>& outputs = ccg.GetOutputs();
   bool symbolic = this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs,
@@ -1653,6 +1663,15 @@ void cmMakefileTargetGenerator::GenerateCustomRuleFile(
                                              objFullPath, srcFullPath);
   }
 
+  // Setup implicit depend for depfile if any
+  if (!ccg.GetCC().GetDepfile().empty()) {
+    std::string objFullPath = cmSystemTools::CollapseFullPath(
+      outputs[0], this->LocalGenerator->GetCurrentBinaryDirectory());
+    this->LocalGenerator->AddImplicitDepends(
+      this->GeneratorTarget, "CUSTOM", objFullPath, ccg.GetFullDepfile(),
+      cmDependencyScannerKind::Compiler);
+  }
+
   this->CustomCommandOutputs.insert(outputs.begin(), outputs.end());
 }
 

+ 36 - 3
Source/cmMakefileUtilityTargetGenerator.cxx

@@ -15,6 +15,7 @@
 #include "cmLocalUnixMakefileGenerator3.h"
 #include "cmMakefile.h"
 #include "cmOSXBundleGenerator.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmMakefileUtilityTargetGenerator::cmMakefileUtilityTargetGenerator(
@@ -36,10 +37,42 @@ void cmMakefileUtilityTargetGenerator::WriteRuleFiles()
   *this->BuildFileStream << "# Utility rule file for "
                          << this->GeneratorTarget->GetName() << ".\n\n";
 
+  const char* root = (this->Makefile->IsOn("CMAKE_MAKE_INCLUDE_FROM_ROOT")
+                        ? "$(CMAKE_BINARY_DIR)/"
+                        : "");
+
+  // Include the dependencies for the target.
+  std::string dependFile =
+    cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.make");
+  *this->BuildFileStream
+    << "# Include any custom commands dependencies for this target.\n"
+    << this->GlobalGenerator->IncludeDirective << " " << root
+    << cmSystemTools::ConvertToOutputPath(
+         this->LocalGenerator->MaybeConvertToRelativePath(
+           this->LocalGenerator->GetBinaryDirectory(), dependFile))
+    << "\n\n";
+  if (!cmSystemTools::FileExists(dependFile)) {
+    // Write an empty dependency file.
+    cmGeneratedFileStream depFileStream(
+      dependFile, false, this->GlobalGenerator->GetMakefileEncoding());
+    depFileStream << "# Empty custom commands generated dependencies file for "
+                  << this->GeneratorTarget->GetName() << ".\n"
+                  << "# This may be replaced when dependencies are built.\n";
+  }
+
+  std::string dependTimestamp =
+    cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts");
+  if (!cmSystemTools::FileExists(dependTimestamp)) {
+    // Write a dependency timestamp file.
+    cmGeneratedFileStream depFileStream(
+      dependTimestamp, false, this->GlobalGenerator->GetMakefileEncoding());
+    depFileStream << "# CMAKE generated file: DO NOT EDIT!\n"
+                  << "# Timestamp file for custom commands dependencies "
+                     "management for "
+                  << this->GeneratorTarget->GetName() << ".\n";
+  }
+
   if (!this->NoRuleMessages) {
-    const char* root = (this->Makefile->IsOn("CMAKE_MAKE_INCLUDE_FROM_ROOT")
-                          ? "$(CMAKE_BINARY_DIR)/"
-                          : "");
     // Include the progress variables for the target.
     *this->BuildFileStream
       << "# Include the progress variables for this target.\n"

+ 1 - 15
Source/cmTransformDepfile.cxx

@@ -13,7 +13,6 @@
 
 #include "cmGccDepfileReader.h"
 #include "cmGccDepfileReaderTypes.h"
-#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 namespace {
@@ -79,26 +78,13 @@ bool cmTransformDepfile(cmDepfileFormat format, const std::string& prefix,
 {
   cmGccDepfileContent content;
   if (cmSystemTools::FileExists(infile)) {
-    auto result = cmReadGccDepfile(infile.c_str());
+    auto result = cmReadGccDepfile(infile.c_str(), prefix);
     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;

+ 2 - 0
Source/cmcmd.cxx

@@ -1272,6 +1272,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
         cmStateSnapshot snapshot = cm.GetCurrentSnapshot();
         snapshot.GetDirectory().SetCurrentBinary(startOutDir);
         snapshot.GetDirectory().SetCurrentSource(startDir);
+        snapshot.GetDirectory().SetRelativePathTopSource(homeDir.c_str());
+        snapshot.GetDirectory().SetRelativePathTopBinary(homeOutDir.c_str());
         cmMakefile mf(cm.GetGlobalGenerator(), snapshot);
         auto lgd = cm.GetGlobalGenerator()->CreateLocalGenerator(&mf);
 

+ 0 - 0
Tests/RunCMake/Make/CustomCommandDepfile-ERROR-result.txt → Tests/RunCMake/BuildDepends/CustomCommandDependencies-BadArgs-result.txt


+ 4 - 0
Tests/RunCMake/BuildDepends/CustomCommandDependencies-BadArgs-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at CustomCommandDependencies-BadArgs.cmake:[0-9]+ \(add_custom_command\):
+  add_custom_command IMPLICIT_DEPENDS and DEPFILE can not both be specified.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 10 - 0
Tests/RunCMake/BuildDepends/CustomCommandDependencies-BadArgs.cmake

@@ -0,0 +1,10 @@
+enable_language(C)
+
+add_custom_command(OUTPUT main.c
+  DEPFILE main.c.d
+  IMPLICIT_DEPENDS C main.c.in
+  COMMAND "${CMAKE_COMMAND}" -DINFILE=main.c.in -DOUTFILE=main.c -DDEPFILE=main.c.d
+  -P "${CMAKE_CURRENT_SOURCE_DIR}/GenerateDepFile.cmake"
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
+
+add_custom_target(mainc ALL DEPENDS main.c)

+ 73 - 0
Tests/RunCMake/BuildDepends/CustomCommandDependencies.cmake

@@ -0,0 +1,73 @@
+enable_language(C)
+
+add_custom_command(OUTPUT main.c
+  DEPFILE main.c.d
+  COMMAND "${CMAKE_COMMAND}" -DINFILE=main.c.in -DOUTFILE=main.c -DDEPFILE=main.c.d
+  -P "${CMAKE_CURRENT_SOURCE_DIR}/GenerateDepFile.cmake"
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
+
+add_custom_target(mainc ALL DEPENDS main.c)
+
+add_executable(main ${CMAKE_CURRENT_BINARY_DIR}/main.c)
+
+file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake CONTENT "
+cmake_minimum_required(VERSION 3.19)
+set(check_pairs
+  \"$<TARGET_FILE:main>|${CMAKE_CURRENT_BINARY_DIR}/main.c.in\"
+  \"$<TARGET_FILE:main>|${CMAKE_CURRENT_BINARY_DIR}/main.c\"
+  )
+set(check_exes
+  \"$<TARGET_FILE:main>\"
+  )
+
+if (check_step EQUAL 2)
+  include(\"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Makefile.cmake\")
+  if (NOT CMAKE_DEPEND_INFO_FILES)
+    set(RunCMake_TEST_FAILED \"Variable CMAKE_DEPEND_INFO_FILES not found.\")
+  else()
+    foreach(DEPEND_INFO_FILE IN LISTS CMAKE_DEPEND_INFO_FILES)
+      include(\"${CMAKE_CURRENT_BINARY_DIR}/\${DEPEND_INFO_FILE}\")
+      if (NOT CMAKE_DEPENDS_DEPENDENCY_FILES)
+        set(RunCMake_TEST_FAILED \"Variable CMAKE_DEPENDS_DEPENDENCY_FILES not found.\")
+      else()
+        list(LENGTH CMAKE_DEPENDS_DEPENDENCY_FILES DEPENDENCY_FILES_SIZE)
+          math(EXPR STOP_INDEX \"\${DEPENDENCY_FILES_SIZE} - 1\")
+        foreach(INDEX RANGE 0 \${STOP_INDEX} 4)
+          math(EXPR OBJECT_INDEX \"\${INDEX} + 1\")
+          math(EXPR FORMAT_INDEX \"\${INDEX} + 2\")
+          math(EXPR DEP_INDEX \"\${INDEX} + 3\")
+          list(GET CMAKE_DEPENDS_DEPENDENCY_FILES \${OBJECT_INDEX} OBJECT_FILE)
+          list(GET CMAKE_DEPENDS_DEPENDENCY_FILES \${FORMAT_INDEX} DEP_FORMAT)
+          list(GET CMAKE_DEPENDS_DEPENDENCY_FILES \${DEP_INDEX} DEP_FILE)
+          if (NOT EXISTS \"${CMAKE_CURRENT_BINARY_DIR}/\${DEP_FILE}\")
+            set(RunCMake_TEST_FAILED \"File \${DEP_FILE} not found.\")
+          else()
+            cmake_path(APPEND TARGET_DEP_FILE \"${CMAKE_CURRENT_BINARY_DIR}\" \"\${DEPEND_INFO_FILE}\")
+            cmake_path(REPLACE_FILENAME TARGET_DEP_FILE \"compiler_depend.make\")
+            file(READ \"\${TARGET_DEP_FILE}\" DEPENDS_CONTENT)
+            if (WIN32)
+              string (REPLACE \"\\\\\" \"/\" DEPENDS_CONTENT \"\${DEPENDS_CONTENT}\")
+              string (TOLOWER \"\${DEPENDS_CONTENT}\" DEPENDS_CONTENT)
+              string (TOLOWER \"\${OBJECT_FILE}\" OBJECT_FILE)
+            else()
+              string(REPLACE \"\\\\ \" \" \" DEPENDS_CONTENT \"\${DEPENDS_CONTENT}\")
+            endif()
+            if(DEPEND_INFO_FILE MATCHES \"main\\\\.dir\")
+              if (DEP_FORMAT STREQUAL \"gcc\" AND NOT DEPENDS_CONTENT MATCHES \"\${OBJECT_FILE} *:.+main.c\")
+                set(RunCMake_TEST_FAILED \"Dependency file '\${TARGET_DEP_FILE}' badly generated:\\n\${DEPENDS_CONTENT}\")
+              endif()
+              if (DEP_FORMAT STREQUAL \"custom\" AND NOT DEPENDS_CONTENT MATCHES \"\${OBJECT_FILE} *:.+main.c.in\")
+                set(RunCMake_TEST_FAILED \"Dependency file '\${TARGET_DEP_FILE}' badly generated:\\n\${DEPENDS_CONTENT}\")
+              endif()
+            else()
+              if (NOT DEPENDS_CONTENT MATCHES \"\${OBJECT_FILE} *:.+main.c.in\")
+                set(RunCMake_TEST_FAILED \"Dependency file '\${TARGET_DEP_FILE}' badly generated:\\n\${DEPENDS_CONTENT}\")
+              endif()
+            endif()
+          endif()
+        endforeach()
+      endif()
+    endforeach()
+  endif()
+endif()
+")

+ 3 - 0
Tests/RunCMake/BuildDepends/CustomCommandDependencies.step1.cmake

@@ -0,0 +1,3 @@
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/main.c.in" [[
+int main(void) { return 1; }
+]])

+ 3 - 0
Tests/RunCMake/BuildDepends/CustomCommandDependencies.step2.cmake

@@ -0,0 +1,3 @@
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/main.c.in" [[
+int main(void) { return 2; }
+]])

+ 61 - 0
Tests/RunCMake/BuildDepends/CustomCommandDepfile.cmake

@@ -0,0 +1,61 @@
+cmake_policy(SET CMP0116 NEW)
+enable_language(C)
+
+add_custom_command(
+  OUTPUT topcc.c
+  DEPFILE topcc.c.d
+  COMMAND ${CMAKE_COMMAND} -DOUTFILE=topcc.c -DINFILE=topccdep.txt -DDEPFILE=topcc.c.d -P "${CMAKE_CURRENT_LIST_DIR}/WriteDepfile.cmake"
+  )
+add_custom_target(topcc ALL DEPENDS topcc.c)
+
+add_custom_command(
+  OUTPUT topexe.c
+  DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/topexe.c.d
+  COMMAND ${CMAKE_COMMAND} -DOUTFILE=topexe.c "-DINFILE=${CMAKE_CURRENT_BINARY_DIR}/topexedep.txt" -DDEPFILE=topexe.c.d -P "${CMAKE_CURRENT_LIST_DIR}/WriteDepfile.cmake"
+  )
+add_executable(topexe "${CMAKE_CURRENT_BINARY_DIR}/topexe.c")
+
+add_custom_command(
+  OUTPUT toplib.c
+  DEPFILE toplib.c.d
+  COMMAND ${CMAKE_COMMAND} -DOUTFILE=toplib.c -DINFILE=toplibdep.txt -DDEPFILE=toplib.c.d -P "${CMAKE_CURRENT_LIST_DIR}/WriteDepfile.cmake"
+  )
+add_library(toplib STATIC toplib.c)
+
+add_subdirectory(DepfileSubdir)
+
+file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake CONTENT "
+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()
+
+set(check_pairs
+  \"${CMAKE_BINARY_DIR}/topcc.c|${CMAKE_BINARY_DIR}/topccdep.txt\"
+  \"$<TARGET_FILE:topexe>|${CMAKE_BINARY_DIR}/topexedep.txt\"
+  \"$<TARGET_FILE:toplib>|${CMAKE_BINARY_DIR}/toplibdep.txt\"
+  \"${CMAKE_BINARY_DIR}/DepfileSubdir/subcc.c|${CMAKE_BINARY_DIR}/DepfileSubdir/subccdep.txt\"
+  \"$<TARGET_FILE:subexe>|${CMAKE_BINARY_DIR}/DepfileSubdir/subexedep.txt\"
+  \"$<TARGET_FILE:sublib>|${CMAKE_BINARY_DIR}/DepfileSubdir/sublibdep.txt\"
+  )
+
+if(check_step EQUAL 3)
+  list(APPEND check_pairs
+    \"${CMAKE_BINARY_DIR}/step3.timestamp|${CMAKE_BINARY_DIR}/topcc.c\"
+    \"${CMAKE_BINARY_DIR}/step3.timestamp|$<TARGET_FILE:topexe>\"
+    \"${CMAKE_BINARY_DIR}/step3.timestamp|$<TARGET_FILE:toplib>\"
+    \"${CMAKE_BINARY_DIR}/step3.timestamp|${CMAKE_BINARY_DIR}/DepfileSubdir/subcc.c\"
+    \"${CMAKE_BINARY_DIR}/step3.timestamp|$<TARGET_FILE:subexe>\"
+    \"${CMAKE_BINARY_DIR}/step3.timestamp|$<TARGET_FILE:sublib>\"
+    )
+endif()
+")

+ 10 - 0
Tests/RunCMake/BuildDepends/CustomCommandDepfile.step1.cmake

@@ -0,0 +1,10 @@
+file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/DepfileSubdir")
+file(REMOVE "${RunCMake_TEST_BINARY_DIR}/../sublib.c")
+file(REMOVE "${RunCMake_TEST_BINARY_DIR}/step3.timestamp")
+
+file(TOUCH "${RunCMake_TEST_BINARY_DIR}/topccdep.txt")
+file(TOUCH "${RunCMake_TEST_BINARY_DIR}/topexedep.txt")
+file(TOUCH "${RunCMake_TEST_BINARY_DIR}/toplibdep.txt")
+file(TOUCH "${RunCMake_TEST_BINARY_DIR}/DepfileSubdir/subccdep.txt")
+file(TOUCH "${RunCMake_TEST_BINARY_DIR}/DepfileSubdir/subexedep.txt")
+file(TOUCH "${RunCMake_TEST_BINARY_DIR}/DepfileSubdir/sublibdep.txt")

+ 6 - 0
Tests/RunCMake/BuildDepends/CustomCommandDepfile.step2.cmake

@@ -0,0 +1,6 @@
+file(TOUCH "${RunCMake_TEST_BINARY_DIR}/topccdep.txt")
+file(TOUCH "${RunCMake_TEST_BINARY_DIR}/topexedep.txt")
+file(TOUCH "${RunCMake_TEST_BINARY_DIR}/toplibdep.txt")
+file(TOUCH "${RunCMake_TEST_BINARY_DIR}/DepfileSubdir/subccdep.txt")
+file(TOUCH "${RunCMake_TEST_BINARY_DIR}/DepfileSubdir/subexedep.txt")
+file(TOUCH "${RunCMake_TEST_BINARY_DIR}/DepfileSubdir/sublibdep.txt")

+ 1 - 0
Tests/RunCMake/BuildDepends/CustomCommandDepfile.step3.cmake

@@ -0,0 +1 @@
+file(TOUCH "${RunCMake_TEST_BINARY_DIR}/step3.timestamp")

+ 22 - 0
Tests/RunCMake/BuildDepends/DepfileSubdir/CMakeLists.txt

@@ -0,0 +1,22 @@
+cmake_policy(SET CMP0116 NEW)
+
+add_custom_command(
+  OUTPUT subcc.c
+  DEPFILE subcc.c.d
+  COMMAND ${CMAKE_COMMAND} -DOUTFILE=subcc.c -DINFILE=subccdep.txt -DDEPFILE=subcc.c.d -P "${CMAKE_CURRENT_LIST_DIR}/../WriteDepfile.cmake"
+  )
+add_custom_target(subcc ALL DEPENDS subcc.c)
+
+add_custom_command(
+  OUTPUT subexe.c
+  DEPFILE subexe.c.d
+  COMMAND ${CMAKE_COMMAND} -DOUTFILE=subexe.c -DINFILE=subexedep.txt -DDEPFILE=subexe.c.d -P "${CMAKE_CURRENT_LIST_DIR}/../WriteDepfile.cmake"
+  )
+add_executable(subexe subexe.c)
+
+add_custom_command(
+  OUTPUT ${CMAKE_BINARY_DIR}/../sublib.c
+  DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/sublib.c.d
+  COMMAND ${CMAKE_COMMAND} -DOUTFILE=${CMAKE_BINARY_DIR}/../sublib.c "-DINFILE=${CMAKE_CURRENT_BINARY_DIR}/sublibdep.txt" -DDEPFILE=sublib.c.d -P "${CMAKE_CURRENT_LIST_DIR}/../WriteDepfile.cmake"
+  )
+add_library(sublib STATIC "${CMAKE_BINARY_DIR}/../sublib.c")

+ 6 - 0
Tests/RunCMake/BuildDepends/GenerateDepFile.cmake

@@ -0,0 +1,6 @@
+file(READ "${INFILE}" INCONTENT)
+file(WRITE "${OUTFILE}" "${INCONTENT}")
+
+string(REPLACE [[ ]] [[\ ]] OUTFILE "${OUTFILE}")
+string(REPLACE [[ ]] [[\ ]] INFILE "${INFILE}")
+file(WRITE "${DEPFILE}" "${OUTFILE}: ${INFILE}\n")

+ 20 - 0
Tests/RunCMake/BuildDepends/RunCMakeTest.cmake

@@ -30,8 +30,17 @@ function(run_BuildDepends CASE)
   include(${RunCMake_SOURCE_DIR}/${CASE}.step2.cmake OPTIONAL)
   set(check_step 2)
   run_cmake_command(${CASE}-build2 ${CMAKE_COMMAND} --build . --config Debug)
+  if(run_BuildDepends_skip_step_3)
+    return()
+  endif()
+  execute_process(COMMAND ${CMAKE_COMMAND} -E sleep ${fs_delay}) # handle 1s resolution
+  include(${RunCMake_SOURCE_DIR}/${CASE}.step3.cmake OPTIONAL)
+  set(check_step 3)
+  run_cmake_command(${CASE}-build3 ${CMAKE_COMMAND} --build . --config Debug)
 endfunction()
 
+set(run_BuildDepends_skip_step_3 1)
+
 run_BuildDepends(C-Exe)
 if(NOT RunCMake_GENERATOR STREQUAL "Xcode")
   if(RunCMake_GENERATOR MATCHES "Visual Studio 10" OR
@@ -122,4 +131,15 @@ if ((RunCMake_GENERATOR STREQUAL "Unix Makefiles"
       AND MSVC_VERSION GREATER 1300
       AND CMAKE_C_COMPILER_ID STREQUAL "MSVC"))
   run_BuildDepends(CompilerDependencies)
+  run_BuildDepends(CustomCommandDependencies)
+endif()
+
+if (RunCMake_GENERATOR MATCHES "Makefiles")
+  run_cmake(CustomCommandDependencies-BadArgs)
+endif()
+
+if(RunCMake_GENERATOR MATCHES "Make|Ninja")
+  unset(run_BuildDepends_skip_step_3)
+  run_BuildDepends(CustomCommandDepfile)
+  set(run_BuildDepends_skip_step_3 1)
 endif()

+ 8 - 0
Tests/RunCMake/BuildDepends/WriteDepfile.cmake

@@ -0,0 +1,8 @@
+file(WRITE "${OUTFILE}" [[int main(void)
+{
+  return 0;
+}
+]])
+string(REPLACE [[ ]] [[\ ]] OUTFILE "${OUTFILE}")
+string(REPLACE [[ ]] [[\ ]] INFILE "${INFILE}")
+file(WRITE "${DEPFILE}" "${OUTFILE}: ${INFILE}\n")

+ 0 - 5
Tests/RunCMake/Make/CustomCommandDepfile-ERROR-stderr.txt

@@ -1,5 +0,0 @@
-^CMake Error at CustomCommandDepfile-ERROR.cmake:1 \(add_custom_command\):
-  add_custom_command Option DEPFILE not supported by [^
-]+
-Call Stack \(most recent call first\):
-  CMakeLists.txt:3 \(include\)$

+ 0 - 8
Tests/RunCMake/Make/CustomCommandDepfile-ERROR.cmake

@@ -1,8 +0,0 @@
-add_custom_command(
-  OUTPUT hello.copy.c
-  COMMAND "${CMAKE_COMMAND}" -E copy
-          "${CMAKE_CURRENT_SOURCE_DIR}/hello.c"
-          hello.copy.c
-  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
-  DEPFILE "test.d"
-  )

+ 0 - 1
Tests/RunCMake/Make/RunCMakeTest.cmake

@@ -39,7 +39,6 @@ function(run_VerboseBuild)
 endfunction()
 run_VerboseBuild()
 
-run_cmake(CustomCommandDepfile-ERROR)
 run_cmake(IncludeRegexSubdir)
 
 function(run_MakefileConflict)