Browse Source

TargetGenerator: Factor out generation of code check rules

De-duplicate code check rule generation in Ninja and Makefile generators
by moving their implementation to `cmCommonTargetGenerator`.

Previously Ninja was generating code check rules per language.
It was changed to generate code check rules for each source file.
Orkun Tokdemir 2 years ago
parent
commit
993dde925f

+ 165 - 0
Source/cmCommonTargetGenerator.cxx

@@ -26,6 +26,7 @@
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 #include "cmValue.h"
 
 cmCommonTargetGenerator::cmCommonTargetGenerator(cmGeneratorTarget* gt)
@@ -291,6 +292,170 @@ void cmCommonTargetGenerator::AppendOSXVerFlag(std::string& flags,
   }
 }
 
+std::string cmCommonTargetGenerator::GetCompilerLauncher(
+  std::string const& lang, std::string const& config)
+{
+  std::string compilerLauncher;
+  if (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA" ||
+      lang == "HIP" || lang == "ISPC" || lang == "OBJC" || lang == "OBJCXX") {
+    std::string const clauncher_prop = cmStrCat(lang, "_COMPILER_LAUNCHER");
+    cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
+    std::string const evaluatedClauncher = cmGeneratorExpression::Evaluate(
+      *clauncher, this->GeneratorTarget->GetLocalGenerator(), config,
+      this->GeneratorTarget, nullptr, this->GeneratorTarget, lang);
+    if (!evaluatedClauncher.empty()) {
+      compilerLauncher = evaluatedClauncher;
+    }
+  }
+  return compilerLauncher;
+}
+
+std::string cmCommonTargetGenerator::GenerateCodeCheckRules(
+  cmSourceFile const& source, std::string& compilerLauncher,
+  std::string const& cmakeCmd, std::string const& config,
+  std::function<std::string(std::string const&)> const& pathConverter)
+{
+  auto const lang = source.GetLanguage();
+  std::string tidy;
+  std::string iwyu;
+  std::string cpplint;
+  std::string cppcheck;
+
+  auto evaluateProp = [&](std::string const& prop) -> std::string {
+    auto const value = this->GeneratorTarget->GetProperty(prop);
+    if (!value) {
+      return std::string{};
+    }
+    auto evaluatedProp = cmGeneratorExpression::Evaluate(
+      *value, this->GeneratorTarget->GetLocalGenerator(), config,
+      this->GeneratorTarget, nullptr, this->GeneratorTarget, lang);
+    if (!evaluatedProp.empty()) {
+      return evaluatedProp;
+    }
+    return *value;
+  };
+  std::string const tidy_prop = cmStrCat(lang, "_CLANG_TIDY");
+  tidy = evaluateProp(tidy_prop);
+
+  if (lang == "C" || lang == "CXX") {
+    std::string const iwyu_prop = cmStrCat(lang, "_INCLUDE_WHAT_YOU_USE");
+    iwyu = evaluateProp(iwyu_prop);
+
+    std::string const cpplint_prop = cmStrCat(lang, "_CPPLINT");
+    cpplint = evaluateProp(cpplint_prop);
+
+    std::string const cppcheck_prop = cmStrCat(lang, "_CPPCHECK");
+    cppcheck = evaluateProp(cppcheck_prop);
+  }
+  if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) ||
+      cmNonempty(cppcheck)) {
+    std::string code_check = cmakeCmd + " -E __run_co_compile";
+    if (!compilerLauncher.empty()) {
+      // In __run_co_compile case the launcher command is supplied
+      // via --launcher=<maybe-list> and consumed
+      code_check += " --launcher=";
+      code_check += this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(
+        compilerLauncher);
+      compilerLauncher.clear();
+    }
+    if (cmNonempty(iwyu)) {
+      code_check += " --iwyu=";
+
+      // Only add --driver-mode if it is not already specified, as adding
+      // it unconditionally might override a user-specified driver-mode
+      if (iwyu.find("--driver-mode=") == std::string::npos) {
+        cmValue const p = this->Makefile->GetDefinition(
+          cmStrCat("CMAKE_", lang, "_INCLUDE_WHAT_YOU_USE_DRIVER_MODE"));
+        std::string driverMode;
+
+        if (cmNonempty(p)) {
+          driverMode = *p;
+        } else {
+          driverMode = lang == "C" ? "gcc" : "g++";
+        }
+
+        code_check +=
+          this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(
+            cmStrCat(iwyu, ";--driver-mode=", driverMode));
+      } else {
+        code_check +=
+          this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(iwyu);
+      }
+    }
+    if (cmNonempty(tidy)) {
+      code_check += " --tidy=";
+      cmValue const p = this->Makefile->GetDefinition(
+        "CMAKE_" + lang + "_CLANG_TIDY_DRIVER_MODE");
+      std::string driverMode;
+      if (cmNonempty(p)) {
+        driverMode = *p;
+      } else {
+        driverMode = lang == "C" ? "gcc" : "g++";
+      }
+
+      auto const generatorName = this->GeneratorTarget->GetLocalGenerator()
+                                   ->GetGlobalGenerator()
+                                   ->GetName();
+      auto const clangTidyExportFixedDir =
+        this->GeneratorTarget->GetClangTidyExportFixesDirectory(lang);
+      auto fixesFile = this->GetClangTidyReplacementsFilePath(
+        clangTidyExportFixedDir, source, config);
+      std::string exportFixes;
+      if (!clangTidyExportFixedDir.empty()) {
+        this->GlobalCommonGenerator->AddClangTidyExportFixesDir(
+          clangTidyExportFixedDir);
+      }
+      if (generatorName.find("Make") != std::string::npos) {
+        if (!clangTidyExportFixedDir.empty()) {
+          this->GlobalCommonGenerator->AddClangTidyExportFixesFile(fixesFile);
+          cmSystemTools::MakeDirectory(
+            cmSystemTools::GetFilenamePath(fixesFile));
+          fixesFile = this->GeneratorTarget->GetLocalGenerator()
+                        ->MaybeRelativeToCurBinDir(fixesFile);
+          exportFixes = cmStrCat(";--export-fixes=", fixesFile);
+        }
+        code_check +=
+          this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(
+            cmStrCat(tidy, ";--extra-arg-before=--driver-mode=", driverMode,
+                     exportFixes));
+      } else if (generatorName.find("Ninja") != std::string::npos) {
+        if (!clangTidyExportFixedDir.empty()) {
+          this->GlobalCommonGenerator->AddClangTidyExportFixesFile(fixesFile);
+          cmSystemTools::MakeDirectory(
+            cmSystemTools::GetFilenamePath(fixesFile));
+          if (!pathConverter) {
+            fixesFile = pathConverter(fixesFile);
+          }
+          exportFixes = cmStrCat(";--export-fixes=", fixesFile);
+        }
+        code_check +=
+          this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(
+            cmStrCat(tidy, ";--extra-arg-before=--driver-mode=", driverMode,
+                     exportFixes));
+      }
+    }
+    if (cmNonempty(cpplint)) {
+      code_check += " --cpplint=";
+      code_check +=
+        this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(cpplint);
+    }
+    if (cmNonempty(cppcheck)) {
+      code_check += " --cppcheck=";
+      code_check +=
+        this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(cppcheck);
+    }
+    if (cmNonempty(tidy) || (cmNonempty(cpplint)) || (cmNonempty(cppcheck))) {
+      code_check += " --source=";
+      code_check +=
+        this->GeneratorTarget->GetLocalGenerator()->ConvertToOutputFormat(
+          source.GetFullPath(), cmOutputConverter::SHELL);
+    }
+    code_check += " -- ";
+    return code_check;
+  }
+  return "";
+}
+
 std::string cmCommonTargetGenerator::GetLinkerLauncher(
   const std::string& config)
 {

+ 10 - 0
Source/cmCommonTargetGenerator.h

@@ -4,6 +4,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <functional>
 #include <map>
 #include <set>
 #include <string>
@@ -53,6 +54,9 @@ protected:
 
   virtual void AddIncludeFlags(std::string& flags, std::string const& lang,
                                const std::string& config) = 0;
+  virtual std::string GetClangTidyReplacementsFilePath(
+    std::string const& directory, cmSourceFile const& source,
+    std::string const& config) const = 0;
 
   void AppendOSXVerFlag(std::string& flags, const std::string& lang,
                         const char* name, bool so);
@@ -63,7 +67,13 @@ protected:
   std::string GetIncludes(std::string const& l, const std::string& config);
   std::string GetManifests(const std::string& config);
   std::string GetAIXExports(std::string const& config);
+  std::string GenerateCodeCheckRules(
+    cmSourceFile const& source, std::string& compilerLauncher,
+    std::string const& cmakeCmd, std::string const& config,
+    std::function<std::string(std::string const&)> const& pathConverter);
 
+  std::string GetCompilerLauncher(std::string const& lang,
+                                  std::string const& config);
   std::vector<std::string> GetLinkedTargetDirectories(
     const std::string& lang, const std::string& config) const;
   std::string ComputeTargetCompilePDB(const std::string& config) const;

+ 11 - 1
Source/cmGlobalNinjaGenerator.cxx

@@ -507,8 +507,18 @@ void cmGlobalNinjaGenerator::WriteVariable(std::ostream& os,
     return;
   }
 
+  std::string val;
+  static std::unordered_set<std::string> const variablesShouldNotBeTrimmed = {
+    "CODE_CHECK", "LAUNCHER"
+  };
+  if (variablesShouldNotBeTrimmed.find(name) ==
+      variablesShouldNotBeTrimmed.end()) {
+    val = cmTrimWhitespace(value);
+  } else {
+    val = value;
+  }
+
   // Do not add a variable if the value is empty.
-  std::string val = cmTrimWhitespace(value);
   if (val.empty()) {
     return;
   }

+ 23 - 144
Source/cmMakefileTargetGenerator.cxx

@@ -26,7 +26,6 @@
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
-#include "cmGlobalCommonGenerator.h"
 #include "cmGlobalUnixMakefileGenerator3.h"
 #include "cmLinkLineComputer.h" // IWYU pragma: keep
 #include "cmList.h"
@@ -1054,151 +1053,14 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
 
     // See if we need to use a compiler launcher like ccache or distcc
     std::string compilerLauncher;
-    if (!compileCommands.empty() &&
-        (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA" ||
-         lang == "HIP" || lang == "ISPC" || lang == "OBJC" ||
-         lang == "OBJCXX")) {
-      std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER";
-      cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
-      std::string evaluatedClauncher = cmGeneratorExpression::Evaluate(
-        *clauncher, this->LocalGenerator, config, this->GeneratorTarget,
-        nullptr, this->GeneratorTarget, lang);
-      if (!evaluatedClauncher.empty()) {
-        compilerLauncher = evaluatedClauncher;
-      }
+    if (!compileCommands.empty()) {
+      compilerLauncher = GetCompilerLauncher(lang, config);
     }
 
-    // Maybe insert an include-what-you-use runner.
-    if (!compileCommands.empty() &&
-        (lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX")) {
-      cmValue tidy = nullptr;
-      cmValue iwyu = nullptr;
-      cmValue cpplint = nullptr;
-      cmValue cppcheck = nullptr;
-      std::string evaluatedTIDY;
-      std::string evaluatedIWYU;
-      std::string evaluatedCPPlint;
-      std::string evaluatedCPPcheck;
-
-      std::string const tidy_prop = cmStrCat(lang, "_CLANG_TIDY");
-      tidy = this->GeneratorTarget->GetProperty(tidy_prop);
-      evaluatedTIDY = cmGeneratorExpression::Evaluate(
-        *tidy, this->LocalGenerator, config, this->GeneratorTarget, nullptr,
-        this->GeneratorTarget, lang);
-      if (!evaluatedTIDY.empty()) {
-        tidy = cmValue(&evaluatedTIDY);
-      }
-
-      if (lang == "C" || lang == "CXX") {
-        std::string const iwyu_prop = cmStrCat(lang, "_INCLUDE_WHAT_YOU_USE");
-        iwyu = this->GeneratorTarget->GetProperty(iwyu_prop);
-        evaluatedIWYU = cmGeneratorExpression::Evaluate(
-          *iwyu, this->LocalGenerator, config, this->GeneratorTarget, nullptr,
-          this->GeneratorTarget, lang);
-        if (!evaluatedIWYU.empty()) {
-          iwyu = cmValue(&evaluatedIWYU);
-        }
-
-        std::string const cpplint_prop = cmStrCat(lang, "_CPPLINT");
-        cpplint = this->GeneratorTarget->GetProperty(cpplint_prop);
-        evaluatedCPPlint = cmGeneratorExpression::Evaluate(
-          *cpplint, this->LocalGenerator, config, this->GeneratorTarget,
-          nullptr, this->GeneratorTarget, lang);
-        if (!evaluatedCPPlint.empty()) {
-          cpplint = cmValue(&evaluatedCPPlint);
-        }
-
-        std::string const cppcheck_prop = cmStrCat(lang, "_CPPCHECK");
-        cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop);
-        evaluatedCPPcheck = cmGeneratorExpression::Evaluate(
-          *cppcheck, this->LocalGenerator, config, this->GeneratorTarget,
-          nullptr, this->GeneratorTarget, lang);
-        if (!evaluatedCPPcheck.empty()) {
-          cppcheck = cmValue(&evaluatedCPPcheck);
-        }
-      }
-      if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) ||
-          cmNonempty(cppcheck)) {
-        std::string run_iwyu = "$(CMAKE_COMMAND) -E __run_co_compile";
-        if (!compilerLauncher.empty()) {
-          // In __run_co_compile case the launcher command is supplied
-          // via --launcher=<maybe-list> and consumed
-          run_iwyu += " --launcher=";
-          run_iwyu += this->LocalGenerator->EscapeForShell(compilerLauncher);
-          compilerLauncher.clear();
-        }
-        if (cmNonempty(iwyu)) {
-          run_iwyu += " --iwyu=";
-
-          // Only add --driver-mode if it is not already specified, as adding
-          // it unconditionally might override a user-specified driver-mode
-          if (iwyu.Get()->find("--driver-mode=") == std::string::npos) {
-            cmValue p = this->Makefile->GetDefinition(
-              cmStrCat("CMAKE_", lang, "_INCLUDE_WHAT_YOU_USE_DRIVER_MODE"));
-            std::string driverMode;
-
-            if (cmNonempty(p)) {
-              driverMode = *p;
-            } else {
-              driverMode = lang == "C" ? "gcc" : "g++";
-            }
-
-            run_iwyu += this->LocalGenerator->EscapeForShell(
-              cmStrCat(*iwyu, ";--driver-mode=", driverMode));
-          } else {
-            run_iwyu += this->LocalGenerator->EscapeForShell(*iwyu);
-          }
-        }
-        if (cmNonempty(tidy)) {
-          run_iwyu += " --tidy=";
-          cmValue p = this->Makefile->GetDefinition("CMAKE_" + lang +
-                                                    "_CLANG_TIDY_DRIVER_MODE");
-          std::string driverMode;
-          if (cmNonempty(p)) {
-            driverMode = *p;
-          } else {
-            driverMode = lang == "C" ? "gcc" : "g++";
-          }
-          std::string d =
-            this->GeneratorTarget->GetClangTidyExportFixesDirectory(lang);
-          std::string exportFixes;
-          if (!d.empty()) {
-            this->GlobalCommonGenerator->AddClangTidyExportFixesDir(d);
-            std::string fixesFile = cmSystemTools::CollapseFullPath(cmStrCat(
-              d, '/',
-              this->LocalGenerator->MaybeRelativeToTopBinDir(cmStrCat(
-                this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
-                this->LocalGenerator->GetTargetDirectory(
-                  this->GeneratorTarget),
-                '/', objectName, ".yaml"))));
-            this->GlobalCommonGenerator->AddClangTidyExportFixesFile(
-              fixesFile);
-            cmSystemTools::MakeDirectory(
-              cmSystemTools::GetFilenamePath(fixesFile));
-            fixesFile =
-              this->LocalGenerator->MaybeRelativeToCurBinDir(fixesFile);
-            exportFixes = cmStrCat(";--export-fixes=", fixesFile);
-          }
-          run_iwyu += this->LocalGenerator->EscapeForShell(
-            cmStrCat(*tidy, ";--extra-arg-before=--driver-mode=", driverMode,
-                     exportFixes));
-        }
-        if (cmNonempty(cpplint)) {
-          run_iwyu += " --cpplint=";
-          run_iwyu += this->LocalGenerator->EscapeForShell(*cpplint);
-        }
-        if (cmNonempty(cppcheck)) {
-          run_iwyu += " --cppcheck=";
-          run_iwyu += this->LocalGenerator->EscapeForShell(*cppcheck);
-        }
-        if (cmNonempty(tidy) || (cmNonempty(cpplint)) ||
-            (cmNonempty(cppcheck))) {
-          run_iwyu += " --source=";
-          run_iwyu += sourceFile;
-        }
-        run_iwyu += " -- ";
-        compileCommands.front().insert(0, run_iwyu);
-      }
+    std::string const codeCheck = this->GenerateCodeCheckRules(
+      source, compilerLauncher, "$(CMAKE_COMMAND)", config, nullptr);
+    if (!codeCheck.empty()) {
+      compileCommands.front().insert(0, codeCheck);
     }
 
     // If compiler launcher was specified and not consumed above, it
@@ -1519,6 +1381,23 @@ void cmMakefileTargetGenerator::WriteTargetLinkDependRules()
     this->GeneratorTarget->GetFullPath(this->GetConfigName()), depFile,
     cmDependencyScannerKind::Compiler);
 }
+std::string cmMakefileTargetGenerator::GetClangTidyReplacementsFilePath(
+  std::string const& directory, cmSourceFile const& source,
+  std::string const& config) const
+{
+  (void)config;
+  auto const& objectName = this->GeneratorTarget->GetObjectName(&source);
+  auto fixesFile = cmSystemTools::CollapseFullPath(cmStrCat(
+    directory, '/',
+    this->GeneratorTarget->GetLocalGenerator()->MaybeRelativeToTopBinDir(
+      cmStrCat(this->GeneratorTarget->GetLocalGenerator()
+                 ->GetCurrentBinaryDirectory(),
+               '/',
+               this->GeneratorTarget->GetLocalGenerator()->GetTargetDirectory(
+                 this->GeneratorTarget),
+               '/', objectName, ".yaml"))));
+  return fixesFile;
+}
 
 void cmMakefileTargetGenerator::WriteTargetDependRules()
 {

+ 4 - 0
Source/cmMakefileTargetGenerator.h

@@ -82,6 +82,10 @@ protected:
   // write the depend rules for this target
   void WriteTargetDependRules();
 
+  std::string GetClangTidyReplacementsFilePath(
+    std::string const& directory, cmSourceFile const& source,
+    std::string const& config) const override;
+
   // write rules for macOS Application Bundle content.
   struct MacOSXContentGeneratorType
     : cmOSXBundleGenerator::MacOSXContentGeneratorType

+ 30 - 173
Source/cmNinjaTargetGenerator.cxx

@@ -29,7 +29,6 @@
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
-#include "cmGlobalCommonGenerator.h"
 #include "cmGlobalNinjaGenerator.h"
 #include "cmList.h"
 #include "cmLocalGenerator.h"
@@ -399,15 +398,15 @@ std::string cmNinjaTargetGenerator::GetObjectFilePath(
 }
 
 std::string cmNinjaTargetGenerator::GetClangTidyReplacementsFilePath(
-  const std::string& directory, cmSourceFile const* source,
-  const std::string& config) const
+  std::string const& directory, cmSourceFile const& source,
+  std::string const& config) const
 {
-  std::string path = this->LocalGenerator->GetHomeRelativeOutputPath();
+  auto path = this->LocalGenerator->GetHomeRelativeOutputPath();
   if (!path.empty()) {
     path += '/';
   }
   path = cmStrCat(directory, '/', path);
-  std::string const& objectName = this->GeneratorTarget->GetObjectName(source);
+  auto const& objectName = this->GeneratorTarget->GetObjectName(&source);
   path =
     cmStrCat(std::move(path),
              this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
@@ -890,162 +889,9 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
   const std::string& compileCmd = mf->GetRequiredDefinition(cmdVar);
   cmList compileCmds(compileCmd);
 
-  // See if we need to use a compiler launcher like ccache or distcc
-  std::string compilerLauncher;
-  if (!compileCmds.empty() &&
-      (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA" ||
-       lang == "HIP" || lang == "ISPC" || lang == "OBJC" ||
-       lang == "OBJCXX")) {
-    std::string const clauncher_prop = cmStrCat(lang, "_COMPILER_LAUNCHER");
-    cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
-    std::string evaluatedClauncher = cmGeneratorExpression::Evaluate(
-      *clauncher, this->LocalGenerator, config, this->GeneratorTarget, nullptr,
-      this->GeneratorTarget, lang);
-    if (!evaluatedClauncher.empty()) {
-      compilerLauncher = evaluatedClauncher;
-    }
-  }
-
-  // Maybe insert an include-what-you-use runner.
-  if (!compileCmds.empty() &&
-      (lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX")) {
-    cmValue tidy = nullptr;
-    cmValue iwyu = nullptr;
-    cmValue cpplint = nullptr;
-    cmValue cppcheck = nullptr;
-    std::string evaluatedTIDY;
-    std::string evaluatedIWYU;
-    std::string evaluatedCPPlint;
-    std::string evaluatedCPPcheck;
-
-    std::string const tidy_prop = cmStrCat(lang, "_CLANG_TIDY");
-    tidy = this->GeneratorTarget->GetProperty(tidy_prop);
-    evaluatedTIDY = cmGeneratorExpression::Evaluate(
-      *tidy, this->LocalGenerator, config, this->GeneratorTarget, nullptr,
-      this->GeneratorTarget, lang);
-    if (!evaluatedTIDY.empty()) {
-      tidy = cmValue(&evaluatedTIDY);
-    }
-
-    if (lang == "C" || lang == "CXX") {
-      std::string const iwyu_prop = cmStrCat(lang, "_INCLUDE_WHAT_YOU_USE");
-      iwyu = this->GeneratorTarget->GetProperty(iwyu_prop);
-      evaluatedIWYU = cmGeneratorExpression::Evaluate(
-        *iwyu, this->LocalGenerator, config, this->GeneratorTarget, nullptr,
-        this->GeneratorTarget, lang);
-      if (!evaluatedIWYU.empty()) {
-        iwyu = cmValue(&evaluatedIWYU);
-      }
-
-      std::string const cpplint_prop = cmStrCat(lang, "_CPPLINT");
-      cpplint = this->GeneratorTarget->GetProperty(cpplint_prop);
-      evaluatedCPPlint = cmGeneratorExpression::Evaluate(
-        *cpplint, this->LocalGenerator, config, this->GeneratorTarget, nullptr,
-        this->GeneratorTarget, lang);
-      if (!evaluatedCPPlint.empty()) {
-        cpplint = cmValue(&evaluatedCPPlint);
-      }
-
-      std::string const cppcheck_prop = cmStrCat(lang, "_CPPCHECK");
-      cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop);
-      evaluatedCPPcheck = cmGeneratorExpression::Evaluate(
-        *cppcheck, this->LocalGenerator, config, this->GeneratorTarget,
-        nullptr, this->GeneratorTarget, lang);
-      if (!evaluatedCPPcheck.empty()) {
-        cppcheck = cmValue(&evaluatedCPPcheck);
-      }
-    }
-    if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) ||
-        cmNonempty(cppcheck)) {
-      std::string run_iwyu = cmStrCat(cmakeCmd, " -E __run_co_compile");
-      if (!compilerLauncher.empty()) {
-        // In __run_co_compile case the launcher command is supplied
-        // via --launcher=<maybe-list> and consumed
-        run_iwyu +=
-          cmStrCat(" --launcher=",
-                   this->LocalGenerator->EscapeForShell(compilerLauncher));
-        compilerLauncher.clear();
-      }
-      if (cmNonempty(iwyu)) {
-        run_iwyu += " --iwyu=";
-
-        // Only add --driver-mode if it is not already specified, as adding
-        // it unconditionally might override a user-specified driver-mode
-        if (iwyu.Get()->find("--driver-mode=") == std::string::npos) {
-          cmValue p = this->Makefile->GetDefinition(
-            cmStrCat("CMAKE_", lang, "_INCLUDE_WHAT_YOU_USE_DRIVER_MODE"));
-          std::string driverMode;
-
-          if (cmNonempty(p)) {
-            driverMode = *p;
-          } else {
-            driverMode = lang == "C" ? "gcc" : "g++";
-          }
-
-          run_iwyu += this->LocalGenerator->EscapeForShell(
-            cmStrCat(*iwyu, ";--driver-mode=", driverMode));
-        } else {
-          run_iwyu += this->LocalGenerator->EscapeForShell(*iwyu);
-        }
-      }
-      if (cmNonempty(tidy)) {
-        run_iwyu += " --tidy=";
-        cmValue p = this->Makefile->GetDefinition(
-          cmStrCat("CMAKE_", lang, "_CLANG_TIDY_DRIVER_MODE"));
-        std::string driverMode;
-        if (cmNonempty(p)) {
-          driverMode = *p;
-        } else {
-          driverMode = lang == "C" ? "gcc" : "g++";
-        }
-        const bool haveClangTidyExportFixesDir =
-          !this->GeneratorTarget->GetClangTidyExportFixesDirectory(lang)
-             .empty();
-        std::string exportFixes;
-        if (haveClangTidyExportFixesDir) {
-          exportFixes = ";--export-fixes=$CLANG_TIDY_EXPORT_FIXES";
-        }
-        run_iwyu += this->GetLocalGenerator()->EscapeForShell(
-          cmStrCat(*tidy, ";--extra-arg-before=--driver-mode=", driverMode,
-                   exportFixes));
-        if (haveClangTidyExportFixesDir) {
-          std::string search = cmStrCat(
-            this->GetLocalGenerator()->GetState()->UseWindowsShell() ? ""
-                                                                     : "\\",
-            "$$CLANG_TIDY_EXPORT_FIXES");
-          auto loc = run_iwyu.rfind(search);
-          run_iwyu.replace(loc, search.length(), "$CLANG_TIDY_EXPORT_FIXES");
-        }
-      }
-      if (cmNonempty(cpplint)) {
-        run_iwyu += cmStrCat(
-          " --cpplint=", this->GetLocalGenerator()->EscapeForShell(*cpplint));
-      }
-      if (cmNonempty(cppcheck)) {
-        run_iwyu +=
-          cmStrCat(" --cppcheck=",
-                   this->GetLocalGenerator()->EscapeForShell(*cppcheck));
-      }
-      if (cmNonempty(tidy) || cmNonempty(cpplint) || cmNonempty(cppcheck)) {
-        run_iwyu += " --source=$in";
-      }
-      run_iwyu += " -- ";
-      compileCmds.front().insert(0, run_iwyu);
-    }
-  }
-
-  // If compiler launcher was specified and not consumed above, it
-  // goes to the beginning of the command line.
-  if (!compileCmds.empty() && !compilerLauncher.empty()) {
-    cmList args{ compilerLauncher, cmList::EmptyElements::Yes };
-    if (!args.empty()) {
-      args[0] = this->LocalGenerator->ConvertToOutputFormat(
-        args[0], cmOutputConverter::SHELL);
-      for (std::string& i : cmMakeRange(args.begin() + 1, args.end())) {
-        i = this->LocalGenerator->EscapeForShell(i);
-      }
-    }
-    compileCmds.front().insert(0, cmStrCat(args.join(" "), ' '));
+  if (!compileCmds.empty()) {
+    compileCmds.front().insert(0, "${CODE_CHECK}");
+    compileCmds.front().insert(0, "${LAUNCHER}");
   }
 
   if (!compileCmds.empty()) {
@@ -1373,6 +1219,29 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
   vars["FLAGS"] = this->ComputeFlagsForObject(source, language, config);
   vars["DEFINES"] = this->ComputeDefines(source, language, config);
   vars["INCLUDES"] = this->ComputeIncludes(source, language, config);
+  auto const cmakeCmd = this->GetLocalGenerator()->ConvertToOutputFormat(
+    cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL);
+
+  auto compilerLauncher = this->GetCompilerLauncher(language, config);
+  vars["CODE_CHECK"] =
+    this->GenerateCodeCheckRules(*source, compilerLauncher, cmakeCmd, config,
+                                 [this](std::string const& path) {
+                                   return this->ConvertToNinjaPath(path);
+                                 });
+
+  // If compiler launcher was specified and not consumed above, it
+  // goes to the beginning of the command line.
+  if (!compilerLauncher.empty()) {
+    cmList args{ compilerLauncher, cmList::EmptyElements::Yes };
+    if (!args.empty()) {
+      args[0] = this->LocalGenerator->ConvertToOutputFormat(
+        args[0], cmOutputConverter::SHELL);
+      for (std::string& i : cmMakeRange(args.begin() + 1, args.end())) {
+        i = this->LocalGenerator->EscapeForShell(i);
+      }
+      vars["LAUNCHER"] = args.join(" ") + " ";
+    }
+  }
 
   if (this->GetMakefile()->GetSafeDefinition(
         cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT")) != "msvc"_s) {
@@ -1397,18 +1266,6 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
     }
   }
 
-  std::string d =
-    this->GeneratorTarget->GetClangTidyExportFixesDirectory(language);
-  if (!d.empty()) {
-    this->GlobalCommonGenerator->AddClangTidyExportFixesDir(d);
-    std::string fixesFile =
-      this->GetClangTidyReplacementsFilePath(d, source, config);
-    this->GlobalCommonGenerator->AddClangTidyExportFixesFile(fixesFile);
-    cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(fixesFile));
-    fixesFile = this->ConvertToNinjaPath(fixesFile);
-    vars["CLANG_TIDY_EXPORT_FIXES"] = fixesFile;
-  }
-
   if (firstForConfig) {
     this->ExportObjectCompileCommand(
       language, sourceFilePath, objectDir, objectFileName, objectFileDir,

+ 2 - 2
Source/cmNinjaTargetGenerator.h

@@ -136,8 +136,8 @@ protected:
 
   /// @return the clang-tidy replacements file path for the given @a source.
   std::string GetClangTidyReplacementsFilePath(
-    const std::string& directory, cmSourceFile const* source,
-    const std::string& config) const;
+    std::string const& directory, cmSourceFile const& source,
+    std::string const& config) const override;
 
   /// @return the dyndep file path for this target.
   std::string GetDyndepFilePath(std::string const& lang,