Browse Source

Merge topic 'skip-linting'

775c369420 Autogen: set SKIP_LINTING ON for generated files
b480315e0c TargetGenerator: Add SKIP_LINTING source property
993dde925f TargetGenerator: Factor out generation of code check rules
023af4ab2f Improve Const Correctness

Acked-by: Kitware Robot <[email protected]>
Acked-by: buildbot <[email protected]>
Merge-request: !8467
Brad King 2 years ago
parent
commit
4d5c32fa2c
32 changed files with 479 additions and 356 deletions
  1. 1 0
      Auxiliary/vim/syntax/cmake.vim
  2. 1 0
      Help/manual/cmake-properties.7.rst
  3. 41 0
      Help/prop_sf/SKIP_LINTING.rst
  4. 7 0
      Help/prop_tgt/LANG_CLANG_TIDY.rst
  5. 7 0
      Help/prop_tgt/LANG_CPPCHECK.rst
  6. 7 0
      Help/prop_tgt/LANG_CPPLINT.rst
  7. 7 0
      Help/prop_tgt/LANG_INCLUDE_WHAT_YOU_USE.rst
  8. 5 0
      Help/release/dev/skip-linting.rst
  9. 165 0
      Source/cmCommonTargetGenerator.cxx
  10. 10 0
      Source/cmCommonTargetGenerator.h
  11. 11 1
      Source/cmGlobalNinjaGenerator.cxx
  12. 65 180
      Source/cmMakefileTargetGenerator.cxx
  13. 4 0
      Source/cmMakefileTargetGenerator.h
  14. 34 173
      Source/cmNinjaTargetGenerator.cxx
  15. 2 2
      Source/cmNinjaTargetGenerator.h
  16. 1 0
      Source/cmQtAutoGenInitializer.cxx
  17. 1 0
      Tests/RunCMake/Autogen/AutogenSkipLinting-build-stderr.txt
  18. 16 0
      Tests/RunCMake/Autogen/AutogenSkipLinting.cmake
  19. 15 0
      Tests/RunCMake/Autogen/RunCMakeTest.cmake
  20. 6 0
      Tests/RunCMake/Autogen/SkipLinting.cxx
  21. 11 0
      Tests/RunCMake/Autogen/SkipLinting.h
  22. 4 0
      Tests/RunCMake/CMakeLists.txt
  23. 3 0
      Tests/RunCMake/MultiLint/C-launch_skip_linting_ON.cmake
  24. 3 0
      Tests/RunCMake/MultiLint/CMakeLists copy.txt
  25. 3 0
      Tests/RunCMake/MultiLint/CXX-launch_skip_linting_ON.cmake
  26. 1 0
      Tests/RunCMake/MultiLint/CXX_skip_linting_OFF-Build-result.txt
  27. 7 0
      Tests/RunCMake/MultiLint/CXX_skip_linting_OFF.cmake
  28. 7 0
      Tests/RunCMake/MultiLint/CXX_skip_linting_ON.cmake
  29. 1 0
      Tests/RunCMake/MultiLint/C_skip_linting_OFF-Build-result.txt
  30. 7 0
      Tests/RunCMake/MultiLint/C_skip_linting_OFF.cmake
  31. 7 0
      Tests/RunCMake/MultiLint/C_skip_linting_ON.cmake
  32. 19 0
      Tests/RunCMake/MultiLint/RunCMakeTest.cmake

+ 1 - 0
Auxiliary/vim/syntax/cmake.vim

@@ -329,6 +329,7 @@ syn keyword cmakeProperty contained
             \ SKIP_REGULAR_EXPRESSION
             \ SKIP_RETURN_CODE
             \ SKIP_UNITY_BUILD_INCLUSION
+            \ SKIP_LINTING
             \ SOURCES
             \ SOURCE_DIR
             \ SOVERSION

+ 1 - 0
Help/manual/cmake-properties.7.rst

@@ -559,6 +559,7 @@ Properties on Source Files
    /prop_sf/SKIP_AUTOUIC
    /prop_sf/SKIP_PRECOMPILE_HEADERS
    /prop_sf/SKIP_UNITY_BUILD_INCLUSION
+   /prop_sf/SKIP_LINTING
    /prop_sf/Swift_DEPENDENCIES_FILE
    /prop_sf/Swift_DIAGNOSTICS_FILE
    /prop_sf/SYMBOLIC

+ 41 - 0
Help/prop_sf/SKIP_LINTING.rst

@@ -0,0 +1,41 @@
+SKIP_LINTING
+------------
+
+.. versionadded:: 3.27
+
+This property allows you to exclude a specific source file
+from the linting process. The linting process involves running
+tools such as :prop_tgt:`<LANG>_CPPLINT`, :prop_tgt:`<LANG>_CLANG_TIDY`,
+:prop_tgt:`<LANG>_CPPCHECK`, and :prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE`
+on the source files. By setting `SKIP_LINTING` on a source file,
+the mentioned linting tools will not be executed for that
+particular file.
+
+EXAMPLE
+^^^^^^^
+
+Consider a `C++` project that includes multiple source files,
+such as `main.cpp`, `things.cpp`, and `generatedBindings.cpp`.
+In this example, you want to exclude the `generatedBindings.cpp`
+file from the linting process. To achieve this, you can utilize
+the `SKIP_LINTING` property with the `set_source_files_properties`
+command as shown below:
+
+.. code-block:: cmake
+
+  add_executable(MyApp main.cpp things.cpp generatedBindings.cpp)
+
+  set_source_files_properties(generatedBindings.cpp PROPERTIES
+      SKIP_LINTING ON
+  )
+
+In the provided code snippet, the `SKIP_LINTING` property is set to `ON`
+for the `generatedBindings.cpp` source file. As a result, when the linting
+tools, such as :prop_tgt:`<LANG>_CPPLINT`, :prop_tgt:`<LANG>_CLANG_TIDY`,
+:prop_tgt:`<LANG>_CPPCHECK`, and :prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE`,
+are executed, they will skip analyzing the `generatedBindings.cpp` file.
+
+By using the `SKIP_LINTING` property, you can selectively exclude specific
+source files from the linting process. This allows you to focus the
+linting tools on the relevant parts of your project, enhancing the efficiency
+and effectiveness of the linting workflow.

+ 7 - 0
Help/prop_tgt/LANG_CLANG_TIDY.rst

@@ -30,3 +30,10 @@ when a target is created.
 
   This property supports
   :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+  :prop_sf:`SKIP_LINTING` can be set on individual source files to exclude
+  them from the linting process, which includes tools like
+  :prop_tgt:`<LANG>_CPPLINT`, :prop_tgt:`<LANG>_CLANG_TIDY`,
+  :prop_tgt:`<LANG>_CPPCHECK`, and :prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE`.
+  When :prop_sf:`SKIP_LINTING` is set on a source file, the mentioned tools
+  will not be run on that specific file.

+ 7 - 0
Help/prop_tgt/LANG_CPPCHECK.rst

@@ -20,3 +20,10 @@ created.
 
   This property supports
   :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+  :prop_sf:`SKIP_LINTING` can be set on individual source files to exclude
+  them from the linting process, which includes tools like
+  :prop_tgt:`<LANG>_CPPLINT`, :prop_tgt:`<LANG>_CLANG_TIDY`,
+  :prop_tgt:`<LANG>_CPPCHECK`, and :prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE`.
+  When :prop_sf:`SKIP_LINTING` is set on a source file, the mentioned tools
+  will not be run on that specific file.

+ 7 - 0
Help/prop_tgt/LANG_CPPLINT.rst

@@ -18,3 +18,10 @@ created.
 
   This property supports
   :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+  :prop_sf:`SKIP_LINTING` can be set on individual source files to exclude
+  them from the linting process, which includes tools like
+  :prop_tgt:`<LANG>_CPPLINT`, :prop_tgt:`<LANG>_CLANG_TIDY`,
+  :prop_tgt:`<LANG>_CPPCHECK`, and :prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE`.
+  When :prop_sf:`SKIP_LINTING` is set on a source file, the mentioned tools
+  will not be run on that specific file.

+ 7 - 0
Help/prop_tgt/LANG_INCLUDE_WHAT_YOU_USE.rst

@@ -18,3 +18,10 @@ when a target is created.
 
   This property supports
   :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+  :prop_sf:`SKIP_LINTING` can be set on individual source files to exclude
+  them from the linting process, which includes tools like
+  :prop_tgt:`<LANG>_CPPLINT`, :prop_tgt:`<LANG>_CLANG_TIDY`,
+  :prop_tgt:`<LANG>_CPPCHECK`, and :prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE`.
+  When :prop_sf:`SKIP_LINTING` is set on a source file, the mentioned tools
+  will not be run on that specific file.

+ 5 - 0
Help/release/dev/skip-linting.rst

@@ -0,0 +1,5 @@
+skip-linting
+------------
+
+* The :prop_sf:`SKIP_LINTING` source file property was added to suppress
+  target-wide code checks on specific sources.

+ 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;
   }

+ 65 - 180
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"
@@ -420,7 +419,7 @@ void cmMakefileTargetGenerator::WriteCommonCodeRules()
     this->GeneratorTarget->HasLinkDependencyFile(this->GetConfigName());
 
   if (compilerGenerateDeps || linkerGenerateDeps || ccGenerateDeps) {
-    std::string compilerDependFile =
+    std::string const compilerDependFile =
       cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.make");
     *this->BuildFileStream << "# Include any dependencies generated by the "
                               "compiler for this target.\n"
@@ -613,17 +612,17 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
   }
 
   // Use compiler to generate dependencies, if supported.
-  bool compilerGenerateDeps =
+  bool const compilerGenerateDeps =
     this->GlobalGenerator->SupportsCompilerDependencies() &&
     cmIsOn(this->Makefile->GetDefinition(
       cmStrCat("CMAKE_", lang, "_DEPENDS_USE_COMPILER")));
-  auto scanner = compilerGenerateDeps ? cmDependencyScannerKind::Compiler
-                                      : cmDependencyScannerKind::CMake;
+  auto const scanner = compilerGenerateDeps ? cmDependencyScannerKind::Compiler
+                                            : cmDependencyScannerKind::CMake;
 
   // Get the full path name of the object file.
   std::string const& objectName =
     this->GeneratorTarget->GetObjectName(&source);
-  std::string obj =
+  std::string const obj =
     cmStrCat(this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
              '/', objectName);
 
@@ -642,7 +641,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
   // Create the directory containing the object file.  This may be a
   // subdirectory under the target's directory.
   {
-    std::string dir = cmSystemTools::GetFilenamePath(obj);
+    std::string const dir = cmSystemTools::GetFilenamePath(obj);
     cmSystemTools::MakeDirectory(this->LocalGenerator->ConvertToFullPath(dir));
   }
 
@@ -656,7 +655,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
   std::string objFullPath =
     cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/', obj);
   objFullPath = cmSystemTools::CollapseFullPath(objFullPath);
-  std::string srcFullPath =
+  std::string const srcFullPath =
     cmSystemTools::CollapseFullPath(source.GetFullPath());
   this->LocalGenerator->AddImplicitDepends(this->GeneratorTarget, lang,
                                            objFullPath, srcFullPath, scanner);
@@ -669,8 +668,8 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
   // generate the depend scanning rule
   this->WriteObjectDependRules(source, depends);
 
-  std::string config = this->GetConfigName();
-  std::string configUpper = cmSystemTools::UpperCase(config);
+  std::string const config = this->GetConfigName();
+  std::string const configUpper = cmSystemTools::UpperCase(config);
 
   // Add precompile headers dependencies
   std::vector<std::string> architectures =
@@ -726,7 +725,8 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
   this->GeneratorTarget->AddExplicitLanguageFlags(flags, source);
 
   // Add language-specific flags.
-  std::string langFlags = cmStrCat("$(", lang, "_FLAGS", filterArch, ")");
+  std::string const langFlags =
+    cmStrCat("$(", lang, "_FLAGS", filterArch, ")");
   this->LocalGenerator->AppendFlags(flags, langFlags);
 
   cmGeneratorExpressionInterpreter genexInterpreter(
@@ -745,7 +745,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
       cmSystemTools::GetFilenameWithoutLastExtension(objectName);
     ispcSource = cmSystemTools::GetFilenameWithoutLastExtension(ispcSource);
 
-    cmValue ispcSuffixProp =
+    cmValue const ispcSuffixProp =
       this->GeneratorTarget->GetProperty("ISPC_HEADER_SUFFIX");
     assert(ispcSuffixProp);
 
@@ -784,7 +784,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
   // Add precompile headers compile options.
   if (!pchSources.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
     std::string pchOptions;
-    auto pchIt = pchSources.find(source.GetFullPath());
+    auto const pchIt = pchSources.find(source.GetFullPath());
     if (pchIt != pchSources.end()) {
       pchOptions = this->GeneratorTarget->GetPchCreateCompileOptions(
         config, lang, pchIt->second);
@@ -830,7 +830,8 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
                           << "_DEFINES = " << evaluatedDefs << "\n"
                           << "\n";
   }
-  std::string defPropName = cmStrCat("COMPILE_DEFINITIONS_", configUpper);
+  std::string const defPropName =
+    cmStrCat("COMPILE_DEFINITIONS_", configUpper);
   if (cmValue config_compile_defs = source.GetProperty(defPropName)) {
     const std::string& evaluatedDefs =
       genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS);
@@ -841,7 +842,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
   }
 
   // Get the output paths for source and object files.
-  std::string sourceFile = this->LocalGenerator->ConvertToOutputFormat(
+  std::string const sourceFile = this->LocalGenerator->ConvertToOutputFormat(
     source.GetFullPath(), cmOutputConverter::SHELL);
 
   // Construct the build message.
@@ -897,7 +898,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
       targetOutPathCompilePDB.back() = '/';
     }
 
-    std::string compilePdbOutputPath =
+    std::string const compilePdbOutputPath =
       this->GeneratorTarget->GetCompilePDBDirectory(this->GetConfigName());
     cmSystemTools::MakeDirectory(compilePdbOutputPath);
   }
@@ -910,7 +911,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
   vars.TargetPDB = targetOutPathPDB.c_str();
   vars.TargetCompilePDB = targetOutPathCompilePDB.c_str();
   vars.Source = sourceFile.c_str();
-  std::string shellObj =
+  std::string const shellObj =
     this->LocalGenerator->ConvertToOutputFormat(obj, cmOutputConverter::SHELL);
   vars.Object = shellObj.c_str();
   std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
@@ -960,7 +961,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
   // At the moment, it is assumed that C, C++, Fortran, and CUDA have both
   // assembly and preprocessor capabilities. The same is true for the
   // ability to export compile commands
-  bool lang_has_preprocessor =
+  bool const lang_has_preprocessor =
     ((lang == "C") || (lang == "CXX") || (lang == "OBJC") ||
      (lang == "OBJCXX") || (lang == "Fortran") || (lang == "CUDA") ||
      lang == "ISPC" || lang == "HIP" || lang == "ASM");
@@ -1016,31 +1017,31 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
       // no launcher for CMAKE_EXPORT_COMPILE_COMMANDS
       rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
                                                    compileCommand, vars);
-      std::string workingDirectory =
+      std::string const workingDirectory =
         this->LocalGenerator->GetCurrentBinaryDirectory();
       std::string::size_type lfPos = compileCommand.find(langFlags);
       if (lfPos != std::string::npos) {
         compileCommand.replace(lfPos, langFlags.size(),
                                this->GetFlags(lang, this->GetConfigName()));
       }
-      std::string langDefines = std::string("$(") + lang + "_DEFINES)";
-      std::string::size_type ldPos = compileCommand.find(langDefines);
+      std::string const langDefines = std::string("$(") + lang + "_DEFINES)";
+      std::string::size_type const ldPos = compileCommand.find(langDefines);
       if (ldPos != std::string::npos) {
         compileCommand.replace(ldPos, langDefines.size(),
                                this->GetDefines(lang, this->GetConfigName()));
       }
-      std::string langIncludes = std::string("$(") + lang + "_INCLUDES)";
-      std::string::size_type liPos = compileCommand.find(langIncludes);
+      std::string const langIncludes = std::string("$(") + lang + "_INCLUDES)";
+      std::string::size_type const liPos = compileCommand.find(langIncludes);
       if (liPos != std::string::npos) {
         compileCommand.replace(liPos, langIncludes.size(),
                                this->GetIncludes(lang, this->GetConfigName()));
       }
 
-      cmValue eliminate[] = {
+      cmValue const eliminate[] = {
         this->Makefile->GetDefinition("CMAKE_START_TEMP_FILE"),
         this->Makefile->GetDefinition("CMAKE_END_TEMP_FILE")
       };
-      for (cmValue el : eliminate) {
+      for (cmValue const& el : eliminate) {
         if (el) {
           cmSystemTools::ReplaceString(compileCommand, *el, "");
         }
@@ -1052,150 +1053,16 @@ 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);
+    cmValue const skipCodeCheck = source.GetProperty("SKIP_LINTING");
+    if (!skipCodeCheck.IsOn()) {
+      std::string const codeCheck = this->GenerateCodeCheckRules(
+        source, compilerLauncher, "$(CMAKE_COMMAND)", config, nullptr);
+      if (!codeCheck.empty()) {
+        compileCommands.front().insert(0, codeCheck);
       }
     }
 
@@ -1330,8 +1197,8 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
 
     if (do_preprocess_rules) {
       commands.clear();
-      std::string relativeObjI = relativeObjBase + ".i";
-      std::string objI = objBase + ".i";
+      std::string const relativeObjI = relativeObjBase + ".i";
+      std::string const objI = objBase + ".i";
 
       std::string preprocessEcho =
         cmStrCat("Preprocessing ", lang, " source to ", objI);
@@ -1423,7 +1290,7 @@ void cmMakefileTargetGenerator::WriteTargetCleanRules()
   std::vector<std::string> commands;
 
   // Construct the clean target name.
-  std::string cleanTarget = cmStrCat(
+  std::string const cleanTarget = cmStrCat(
     this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget),
     "/clean");
 
@@ -1451,7 +1318,7 @@ bool cmMakefileTargetGenerator::WriteMakeRule(
   }
 
   // Check whether we need to bother checking for a symbolic output.
-  bool need_symbolic = this->GlobalGenerator->GetNeedSymbolicMark();
+  bool const need_symbolic = this->GlobalGenerator->GetNeedSymbolicMark();
 
   // Check whether the first output is marked as symbolic.
   if (need_symbolic) {
@@ -1481,7 +1348,7 @@ bool cmMakefileTargetGenerator::WriteMakeRule(
 
     bool o_symbolic = false;
     if (need_symbolic) {
-      if (cmSourceFile* sf = this->Makefile->GetSource(output)) {
+      if (cmSourceFile const* sf = this->Makefile->GetSource(output)) {
         o_symbolic = sf->GetPropertyAsBool("SYMBOLIC");
       }
     }
@@ -1517,6 +1384,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()
 {
@@ -1559,7 +1443,7 @@ void cmMakefileTargetGenerator::WriteTargetDependRules()
        "# Targets to which this target links which contain Fortran sources.\n"
        "set(CMAKE_Fortran_TARGET_LINKED_INFO_FILES\n";
     /* clang-format on */
-    std::vector<std::string> dirs =
+    std::vector<std::string> const dirs =
       this->GetLinkedTargetDirectories("Fortran", this->GetConfigName());
     for (std::string const& d : dirs) {
       *this->InfoFileStream << "  \"" << d << "/DependInfo.cmake\"\n";
@@ -1591,7 +1475,7 @@ void cmMakefileTargetGenerator::WriteTargetDependRules()
   std::vector<std::string> commands;
 
   // Construct the name of the dependency generation target.
-  std::string depTarget = cmStrCat(
+  std::string const depTarget = cmStrCat(
     this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget),
     "/depend");
 
@@ -1701,7 +1585,7 @@ void cmMakefileTargetGenerator::WriteDeviceLinkRule(
       deps.emplace_back(cmStrCat(relPath, obj));
     }
 
-    std::unordered_set<std::string> depsSet(deps.begin(), deps.end());
+    std::unordered_set<std::string> const depsSet(deps.begin(), deps.end());
     deps.clear();
     std::copy(depsSet.begin(), depsSet.end(), std::back_inserter(deps));
     return deps;
@@ -1785,7 +1669,7 @@ void cmMakefileTargetGenerator::WriteDeviceLinkRule(
   this->GetDeviceLinkFlags(linkFlags, "CUDA");
   vars.LinkFlags = linkFlags.c_str();
 
-  std::string flags = this->GetFlags("CUDA", this->GetConfigName());
+  std::string const flags = this->GetFlags("CUDA", this->GetConfigName());
   vars.Flags = flags.c_str();
 
   std::string compileCmd = this->GetLinkRule("CMAKE_CUDA_DEVICE_LINK_COMPILE");
@@ -1839,8 +1723,8 @@ void cmMakefileTargetGenerator::GenerateCustomRuleFile(
 
   // Write the rule.
   const std::vector<std::string>& outputs = ccg.GetOutputs();
-  bool symbolic = this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs,
-                                      depends, commands);
+  bool const symbolic = this->WriteMakeRule(*this->BuildFileStream, nullptr,
+                                            outputs, depends, commands);
 
   // Symbolic inputs are not expected to exist, so add dummy rules.
   if (this->CMP0113New && !depends.empty()) {
@@ -2328,7 +2212,7 @@ void cmMakefileTargetGenerator::CreateObjectLists(
                              useWatcomQuote);
   if (useResponseFile) {
     // MSVC response files cannot exceed 128K.
-    std::string::size_type const responseFileLimit = 131000;
+    std::string::size_type constexpr responseFileLimit = 131000;
 
     // Construct the individual object list strings.
     std::vector<std::string> object_strings;
@@ -2461,7 +2345,8 @@ std::string cmMakefileTargetGenerator::GetResponseFlag(
   std::string responseFlag = "@";
   std::string responseFlagVar;
 
-  auto lang = this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName());
+  auto const lang =
+    this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName());
   if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Link) {
     responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_LINK_FLAG");
   } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink) {

+ 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

+ 34 - 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()) {
@@ -1374,6 +1220,33 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
   vars["DEFINES"] = this->ComputeDefines(source, language, config);
   vars["INCLUDES"] = this->ComputeIncludes(source, language, config);
 
+  auto compilerLauncher = this->GetCompilerLauncher(language, config);
+
+  cmValue const skipCodeCheck = source->GetProperty("SKIP_LINTING");
+  if (!skipCodeCheck.IsOn()) {
+    auto const cmakeCmd = this->GetLocalGenerator()->ConvertToOutputFormat(
+      cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL);
+    vars["CODE_CHECK"] =
+      this->GenerateCodeCheckRules(*source, compilerLauncher, cmakeCmd, config,
+                                   [this](const std::string& 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) {
     bool replaceExt(false);
@@ -1397,18 +1270,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,

+ 1 - 0
Source/cmQtAutoGenInitializer.cxx

@@ -1834,6 +1834,7 @@ cmSourceFile* cmQtAutoGenInitializer::RegisterGeneratedSource(
   cmSourceFile* gFile = this->Makefile->GetOrCreateSource(filename, true);
   gFile->MarkAsGenerated();
   gFile->SetProperty("SKIP_AUTOGEN", "1");
+  gFile->SetProperty("SKIP_LINTING", "ON");
   return gFile;
 }
 

+ 1 - 0
Tests/RunCMake/Autogen/AutogenSkipLinting-build-stderr.txt

@@ -0,0 +1 @@
+.*

+ 16 - 0
Tests/RunCMake/Autogen/AutogenSkipLinting.cmake

@@ -0,0 +1,16 @@
+enable_language(CXX)
+
+find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core Widgets Gui)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "$<1:${PSEUDO_IWYU}>" -some -args)
+set(CMAKE_CXX_CLANG_TIDY "$<1:${PSEUDO_TIDY}>" -bad)
+set(CMAKE_CXX_CPPLINT "$<1:${PSEUDO_CPPLINT}>" --error)
+set(CMAKE_CXX_CPPCHECK "$<1:${PSEUDO_CPPCHECK}>" -error)
+
+add_executable(SkipLinting SkipLinting.cxx SkipLinting.h)
+set_source_files_properties(SkipLinting.cxx PROPERTIES SKIP_LINTING TRUE)
+
+target_link_libraries(SkipLinting Qt${with_qt_version}::Core
+                                   Qt${with_qt_version}::Widgets
+                                   Qt${with_qt_version}::Gui)

+ 15 - 0
Tests/RunCMake/Autogen/RunCMakeTest.cmake

@@ -87,5 +87,20 @@ if (DEFINED with_qt_version)
       message(STATUS "RunCMake_TEST_EXPECT_stdout: ${RunCMake_TEST_EXPECT_stdout}")
       run_cmake_command(AutogenUseSystemIncludeOff ${CMAKE_COMMAND} --build . --config Debug --verbose)
     endblock()
+
+    if(RunCMake_GENERATOR MATCHES "Make|Ninja")
+      block()
+        set(RunCMake_TEST_BINARY_DIR  ${RunCMake_BINARY_DIR}/AutogenSkipLinting-build)
+        list(APPEND RunCMake_TEST_OPTIONS
+          "-DPSEUDO_CPPCHECK=${PSEUDO_CPPCHECK}"
+          "-DPSEUDO_CPPLINT=${PSEUDO_CPPLINT}"
+          "-DPSEUDO_IWYU=${PSEUDO_IWYU}"
+          "-DPSEUDO_TIDY=${PSEUDO_TIDY}")
+
+        run_cmake(AutogenSkipLinting)
+        set(RunCMake_TEST_NO_CLEAN 1)
+        run_cmake_command(AutogenSkipLinting-build ${CMAKE_COMMAND} --build . --config Debug --verbose)
+      endblock()
+    endif()
   endif()
 endif ()

+ 6 - 0
Tests/RunCMake/Autogen/SkipLinting.cxx

@@ -0,0 +1,6 @@
+#include "SkipLinting.h"
+
+int main()
+{
+  return 0;
+}

+ 11 - 0
Tests/RunCMake/Autogen/SkipLinting.h

@@ -0,0 +1,11 @@
+#ifndef SKIP_LINTING_H
+#define SKIP_LINTING_H
+
+#include <QObject>
+
+class SkipMe : public QObject
+{
+  Q_OBJECT
+};
+
+#endif

+ 4 - 0
Tests/RunCMake/CMakeLists.txt

@@ -266,6 +266,10 @@ if(CMake_TEST_Qt6 AND Qt6Widgets_FOUND)
     -Dwith_qt_version=6
     "-DQt6_DIR:PATH=${Qt6_DIR}"
     "-DCMAKE_PREFIX_PATH:STRING=${base_dir}"
+    -DPSEUDO_TIDY=$<TARGET_FILE:pseudo_tidy>
+    -DPSEUDO_IWYU=$<TARGET_FILE:pseudo_iwyu>
+    -DPSEUDO_CPPLINT=$<TARGET_FILE:pseudo_cpplint>
+    -DPSEUDO_CPPCHECK=$<TARGET_FILE:pseudo_cppcheck>
   )
   set(want_NoQt_test FALSE)
 endif ()

+ 3 - 0
Tests/RunCMake/MultiLint/C-launch_skip_linting_ON.cmake

@@ -0,0 +1,3 @@
+set(CTEST_USE_LAUNCHERS 1)
+include(CTestUseLaunchers)
+include(C_skip_linting_ON.cmake)

+ 3 - 0
Tests/RunCMake/MultiLint/CMakeLists copy.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.5)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 3 - 0
Tests/RunCMake/MultiLint/CXX-launch_skip_linting_ON.cmake

@@ -0,0 +1,3 @@
+set(CTEST_USE_LAUNCHERS 1)
+include(CTestUseLaunchers)
+include(CXX_skip_linting_ON.cmake)

+ 1 - 0
Tests/RunCMake/MultiLint/CXX_skip_linting_OFF-Build-result.txt

@@ -0,0 +1 @@
+(1|2)

+ 7 - 0
Tests/RunCMake/MultiLint/CXX_skip_linting_OFF.cmake

@@ -0,0 +1,7 @@
+enable_language(CXX)
+set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "$<1:${PSEUDO_IWYU}>" -some -args)
+set(CMAKE_CXX_CLANG_TIDY "$<1:${PSEUDO_TIDY}>" -bad)
+set(CMAKE_CXX_CPPLINT "$<1:${PSEUDO_CPPLINT}>" --error)
+set(CMAKE_CXX_CPPCHECK "$<1:${PSEUDO_CPPCHECK}>" -error)
+add_executable(main main.cxx)
+set_source_files_properties(main.cxx PROPERTIES SKIP_LINTING OFF)

+ 7 - 0
Tests/RunCMake/MultiLint/CXX_skip_linting_ON.cmake

@@ -0,0 +1,7 @@
+enable_language(CXX)
+set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "$<1:${PSEUDO_IWYU}>" -some -args)
+set(CMAKE_CXX_CLANG_TIDY "$<1:${PSEUDO_TIDY}>" -bad)
+set(CMAKE_CXX_CPPLINT "$<1:${PSEUDO_CPPLINT}>" --error)
+set(CMAKE_CXX_CPPCHECK "$<1:${PSEUDO_CPPCHECK}>" -error)
+add_executable(main main.cxx)
+set_source_files_properties(main.cxx PROPERTIES SKIP_LINTING ON)

+ 1 - 0
Tests/RunCMake/MultiLint/C_skip_linting_OFF-Build-result.txt

@@ -0,0 +1 @@
+(1|2)

+ 7 - 0
Tests/RunCMake/MultiLint/C_skip_linting_OFF.cmake

@@ -0,0 +1,7 @@
+enable_language(C)
+set(CMAKE_C_INCLUDE_WHAT_YOU_USE "${PSEUDO_IWYU}" -some -args)
+set(CMAKE_C_CLANG_TIDY "${PSEUDO_TIDY}" -bad)
+set(CMAKE_C_CPPLINT "${PSEUDO_CPPLINT}" --error)
+set(CMAKE_C_CPPCHECK "${PSEUDO_CPPCHECK}" -error)
+add_executable(main main.c)
+set_source_files_properties(main.c PROPERTIES SKIP_LINTING OFF)

+ 7 - 0
Tests/RunCMake/MultiLint/C_skip_linting_ON.cmake

@@ -0,0 +1,7 @@
+enable_language(C)
+set(CMAKE_C_INCLUDE_WHAT_YOU_USE "${PSEUDO_IWYU}" -some -args)
+set(CMAKE_C_CLANG_TIDY "${PSEUDO_TIDY}" -bad)
+set(CMAKE_C_CPPLINT "${PSEUDO_CPPLINT}" --error)
+set(CMAKE_C_CPPCHECK "${PSEUDO_CPPCHECK}" -error)
+add_executable(main main.c)
+set_source_files_properties(main.c PROPERTIES SKIP_LINTING ON)

+ 19 - 0
Tests/RunCMake/MultiLint/RunCMakeTest.cmake

@@ -25,3 +25,22 @@ if(NOT RunCMake_GENERATOR STREQUAL "Watcom WMake")
   run_multilint(C-launch)
   run_multilint(CXX-launch)
 endif()
+
+function(run_skip_linting test_name)
+    set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${test_name}-build")
+    set(RunCMake_TEST_NO_CLEAN 1)
+
+    run_cmake(${test_name})
+    set(RunCMake_TEST_OUTPUT_MERGE 1)
+    run_cmake_command(${test_name}-Build ${CMAKE_COMMAND} --build .)
+endfunction()
+
+run_skip_linting(C_skip_linting_ON)
+run_skip_linting(CXX_skip_linting_ON)
+run_skip_linting(C_skip_linting_OFF)
+run_skip_linting(CXX_skip_linting_OFF)
+
+if(NOT RunCMake_GENERATOR STREQUAL "Watcom WMake")
+  run_skip_linting(C-launch_skip_linting_ON)
+  run_skip_linting(CXX-launch_skip_linting_ON)
+endif()