From 5e62444cff5ead280a111c116a3fe810181379cf Mon Sep 17 00:00:00 2001 From: Daniel Pfeifer Date: Fri, 8 Apr 2016 22:09:27 +0200 Subject: [PATCH] Add options to run clang-tidy with the compiler Create a _CLANG_TIDY target property (initialized by a CMAKE__CLANG_TIDY variable) to specify a clang-tidy command line to be run along with the compiler. --- Help/manual/cmake-properties.7.rst | 1 + Help/manual/cmake-variables.7.rst | 1 + Help/prop_tgt/LANG_CLANG_TIDY.rst | 13 +++ Help/release/dev/clang-tidy.rst | 7 ++ Help/variable/CMAKE_LANG_CLANG_TIDY.rst | 6 ++ Source/cmMakefileTargetGenerator.cxx | 19 ++++- Source/cmNinjaTargetGenerator.cxx | 18 ++++- Source/cmTarget.cxx | 2 + Source/cmcmd.cxx | 80 ++++++++++++++----- Tests/RunCMake/CMakeLists.txt | 2 + Tests/RunCMake/ClangTidy/C-Build-stdout.txt | 1 + .../ClangTidy/C-launch-Build-stdout.txt | 1 + Tests/RunCMake/ClangTidy/C-launch.cmake | 3 + Tests/RunCMake/ClangTidy/C.cmake | 3 + Tests/RunCMake/ClangTidy/CMakeLists.txt | 3 + Tests/RunCMake/ClangTidy/CXX-Build-stdout.txt | 1 + .../ClangTidy/CXX-launch-Build-stdout.txt | 1 + Tests/RunCMake/ClangTidy/CXX-launch.cmake | 3 + Tests/RunCMake/ClangTidy/CXX.cmake | 3 + Tests/RunCMake/ClangTidy/RunCMakeTest.cmake | 22 +++++ Tests/RunCMake/ClangTidy/main.c | 1 + Tests/RunCMake/ClangTidy/main.cxx | 1 + .../E___run_iwyu-no-iwyu-stderr.txt | 2 +- Tests/RunCMake/pseudo_tidy.c | 16 ++++ 24 files changed, 182 insertions(+), 28 deletions(-) create mode 100644 Help/prop_tgt/LANG_CLANG_TIDY.rst create mode 100644 Help/release/dev/clang-tidy.rst create mode 100644 Help/variable/CMAKE_LANG_CLANG_TIDY.rst create mode 100644 Tests/RunCMake/ClangTidy/C-Build-stdout.txt create mode 100644 Tests/RunCMake/ClangTidy/C-launch-Build-stdout.txt create mode 100644 Tests/RunCMake/ClangTidy/C-launch.cmake create mode 100644 Tests/RunCMake/ClangTidy/C.cmake create mode 100644 Tests/RunCMake/ClangTidy/CMakeLists.txt create mode 100644 Tests/RunCMake/ClangTidy/CXX-Build-stdout.txt create mode 100644 Tests/RunCMake/ClangTidy/CXX-launch-Build-stdout.txt create mode 100644 Tests/RunCMake/ClangTidy/CXX-launch.cmake create mode 100644 Tests/RunCMake/ClangTidy/CXX.cmake create mode 100644 Tests/RunCMake/ClangTidy/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/ClangTidy/main.c create mode 100644 Tests/RunCMake/ClangTidy/main.cxx create mode 100644 Tests/RunCMake/pseudo_tidy.c diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 73d1142b0..3403dcd86 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -197,6 +197,7 @@ Properties on Targets /prop_tgt/JOB_POOL_COMPILE /prop_tgt/JOB_POOL_LINK /prop_tgt/LABELS + /prop_tgt/LANG_CLANG_TIDY /prop_tgt/LANG_COMPILER_LAUNCHER /prop_tgt/LANG_INCLUDE_WHAT_YOU_USE /prop_tgt/LANG_VISIBILITY_PRESET diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 3f73b32a1..7cf3a3d60 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -259,6 +259,7 @@ Variables that Control the Build /variable/CMAKE_INSTALL_RPATH /variable/CMAKE_INSTALL_RPATH_USE_LINK_PATH /variable/CMAKE_IOS_INSTALL_COMBINED + /variable/CMAKE_LANG_CLANG_TIDY /variable/CMAKE_LANG_COMPILER_LAUNCHER /variable/CMAKE_LANG_INCLUDE_WHAT_YOU_USE /variable/CMAKE_LANG_VISIBILITY_PRESET diff --git a/Help/prop_tgt/LANG_CLANG_TIDY.rst b/Help/prop_tgt/LANG_CLANG_TIDY.rst new file mode 100644 index 000000000..2887e235d --- /dev/null +++ b/Help/prop_tgt/LANG_CLANG_TIDY.rst @@ -0,0 +1,13 @@ +_CLANG_TIDY +----------------- + +This property is implemented only when ```` is ``C`` or ``CXX``. + +Specify a :ref:`;-list ` containing a command +line for the ``clang-tidy`` tool. The :ref:`Makefile Generators` +and the :generator:`Ninja` generator will run this tool along with the +compiler and report a warning if the tool reports any problems. + +This property is initialized by the value of +the :variable:`CMAKE__CLANG_TIDY` variable if it is set +when a target is created. diff --git a/Help/release/dev/clang-tidy.rst b/Help/release/dev/clang-tidy.rst new file mode 100644 index 000000000..030a7c954 --- /dev/null +++ b/Help/release/dev/clang-tidy.rst @@ -0,0 +1,7 @@ +clang-tidy +---------- + +* A :prop_tgt:`_CLANG_TIDY` target property and supporting + :variable:`CMAKE__CLANG_TIDY` variable were introduced to tell the + :ref:`Makefile Generators` and the :generator:`Ninja` generator to run + ``clang-tidy`` along with the compiler for ``C`` and ``CXX`` languages. diff --git a/Help/variable/CMAKE_LANG_CLANG_TIDY.rst b/Help/variable/CMAKE_LANG_CLANG_TIDY.rst new file mode 100644 index 000000000..492c12c6d --- /dev/null +++ b/Help/variable/CMAKE_LANG_CLANG_TIDY.rst @@ -0,0 +1,6 @@ +CMAKE__CLANG_TIDY +----------------------- + +Default value for :prop_tgt:`_CLANG_TIDY` target property. +This variable is used to initialize the property on each target as it is +created. This is done only when ```` is ``C`` or ``CXX``. diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index eedc6abdb..29708d95b 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -667,10 +667,23 @@ cmMakefileTargetGenerator { std::string const iwyu_prop = lang + "_INCLUDE_WHAT_YOU_USE"; const char *iwyu = this->GeneratorTarget->GetProperty(iwyu_prop); - if (iwyu && *iwyu) + std::string const tidy_prop = lang + "_CLANG_TIDY"; + const char *tidy = this->GeneratorTarget->GetProperty(tidy_prop); + if ((iwyu && *iwyu) || (tidy && *tidy)) { - std::string run_iwyu = "$(CMAKE_COMMAND) -E __run_iwyu --iwyu="; - run_iwyu += this->LocalGenerator->EscapeForShell(iwyu); + std::string run_iwyu = "$(CMAKE_COMMAND) -E __run_iwyu"; + if (iwyu && *iwyu) + { + run_iwyu += " --iwyu="; + run_iwyu += this->LocalGenerator->EscapeForShell(iwyu); + } + if (tidy && *tidy) + { + run_iwyu += " --tidy="; + run_iwyu += this->LocalGenerator->EscapeForShell(tidy); + run_iwyu += " --source="; + run_iwyu += sourceFile; + } run_iwyu += " -- "; compileCommands.front().insert(0, run_iwyu); } diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 753a5ae49..fd1d16dc5 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -419,13 +419,25 @@ cmNinjaTargetGenerator { std::string const iwyu_prop = lang + "_INCLUDE_WHAT_YOU_USE"; const char *iwyu = this->GeneratorTarget->GetProperty(iwyu_prop); - if (iwyu && *iwyu) + std::string const tidy_prop = lang + "_CLANG_TIDY"; + const char *tidy = this->GeneratorTarget->GetProperty(tidy_prop); + if ((iwyu && *iwyu) || (tidy && *tidy)) { std::string run_iwyu = this->GetLocalGenerator()->ConvertToOutputFormat( cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL); - run_iwyu += " -E __run_iwyu --iwyu="; - run_iwyu += this->GetLocalGenerator()->EscapeForShell(iwyu); + run_iwyu += " -E __run_iwyu"; + if (iwyu && *iwyu) + { + run_iwyu += " --iwyu="; + run_iwyu += this->GetLocalGenerator()->EscapeForShell(iwyu); + } + if (tidy && *tidy) + { + run_iwyu += " --tidy="; + run_iwyu += this->GetLocalGenerator()->EscapeForShell(tidy); + run_iwyu += " --source=$in"; + } run_iwyu += " -- "; compileCmds.front().insert(0, run_iwyu); } diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 576189f4c..e11835683 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -142,11 +142,13 @@ void cmTarget::SetMakefile(cmMakefile* mf) this->SetPropertyDefault("MACOSX_BUNDLE", 0); this->SetPropertyDefault("MACOSX_RPATH", 0); this->SetPropertyDefault("NO_SYSTEM_FROM_IMPORTED", 0); + this->SetPropertyDefault("C_CLANG_TIDY", 0); this->SetPropertyDefault("C_COMPILER_LAUNCHER", 0); this->SetPropertyDefault("C_INCLUDE_WHAT_YOU_USE", 0); this->SetPropertyDefault("C_STANDARD", 0); this->SetPropertyDefault("C_STANDARD_REQUIRED", 0); this->SetPropertyDefault("C_EXTENSIONS", 0); + this->SetPropertyDefault("CXX_CLANG_TIDY", 0); this->SetPropertyDefault("CXX_COMPILER_LAUNCHER", 0); this->SetPropertyDefault("CXX_INCLUDE_WHAT_YOU_USE", 0); this->SetPropertyDefault("CXX_STANDARD", 0); diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index e9d77b250..3c28c35cb 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -297,12 +297,14 @@ int cmcmd::ExecuteCMakeCommand(std::vector& args) if (args.size() < 3) { std::cerr << "__run_iwyu Usage: -E __run_iwyu [--iwyu=/path/iwyu]" - " -- compile command\n"; + " [--tidy=/path/tidy] -- compile command\n"; return 1; } bool doing_options = true; std::vector orig_cmd; std::string iwyu; + std::string tidy; + std::string sourceFile; for (std::string::size_type cc = 2; cc < args.size(); cc ++) { std::string const& arg = args[cc]; @@ -314,6 +316,14 @@ int cmcmd::ExecuteCMakeCommand(std::vector& args) { iwyu = arg.substr(7); } + else if (doing_options && cmHasLiteralPrefix(arg, "--tidy=")) + { + tidy = arg.substr(7); + } + else if (doing_options && cmHasLiteralPrefix(arg, "--source=")) + { + sourceFile = arg.substr(9); + } else if (doing_options) { std::cerr << "__run_iwyu given unknown argument: " << arg << "\n"; @@ -324,9 +334,14 @@ int cmcmd::ExecuteCMakeCommand(std::vector& args) orig_cmd.push_back(arg); } } - if (iwyu.empty()) + if (tidy.empty() && iwyu.empty()) { - std::cerr << "__run_iwyu missing --iwyu=\n"; + std::cerr << "__run_iwyu missing --tidy= or --iwyu=\n"; + return 1; + } + if (!tidy.empty() && sourceFile.empty()) + { + std::cerr << "__run_iwyu --tidy= requires --source=\n"; return 1; } if (orig_cmd.empty()) @@ -335,29 +350,52 @@ int cmcmd::ExecuteCMakeCommand(std::vector& args) return 1; } - // Construct the iwyu command line by taking what was given - // and adding all the arguments we give to the compiler. - std::vector iwyu_cmd; - cmSystemTools::ExpandListArgument(iwyu, iwyu_cmd, true); - iwyu_cmd.insert(iwyu_cmd.end(), orig_cmd.begin()+1, orig_cmd.end()); - - // Run the iwyu command line. Capture its stderr and hide its stdout. int ret = 0; - std::string stdErr; - if(!cmSystemTools::RunSingleCommand(iwyu_cmd, 0, &stdErr, &ret, - 0, cmSystemTools::OUTPUT_NONE)) + + if (!iwyu.empty()) { - std::cerr << "Error running '" << iwyu_cmd[0] << "': " - << stdErr << "\n"; - return 1; + // Construct the iwyu command line by taking what was given + // and adding all the arguments we give to the compiler. + std::vector iwyu_cmd; + cmSystemTools::ExpandListArgument(iwyu, iwyu_cmd, true); + iwyu_cmd.insert(iwyu_cmd.end(), orig_cmd.begin()+1, orig_cmd.end()); + + // Run the iwyu command line. Capture its stderr and hide its stdout. + std::string stdErr; + if(!cmSystemTools::RunSingleCommand(iwyu_cmd, 0, &stdErr, &ret, + 0, cmSystemTools::OUTPUT_NONE)) + { + std::cerr << "Error running '" << iwyu_cmd[0] << "': " + << stdErr << "\n"; + return 1; + } + + // Warn if iwyu reported anything. + if(stdErr.find("should remove these lines:") != stdErr.npos + || stdErr.find("should add these lines:") != stdErr.npos) + { + std::cerr << "Warning: include-what-you-use reported diagnostics:\n" + << stdErr << "\n"; + } } - // Warn if iwyu reported anything. - if(stdErr.find("should remove these lines:") != stdErr.npos - || stdErr.find("should add these lines:") != stdErr.npos) + if (!tidy.empty()) { - std::cerr << "Warning: include-what-you-use reported diagnostics:\n" - << stdErr << "\n"; + // Construct the clang-tidy command line by taking what was given + // and adding all the arguments we give to the compiler. + std::vector tidy_cmd; + cmSystemTools::ExpandListArgument(tidy, tidy_cmd, true); + tidy_cmd.push_back(sourceFile); + tidy_cmd.push_back("--"); + tidy_cmd.insert(tidy_cmd.end(), orig_cmd.begin()+1, orig_cmd.end()); + + // Run the tidy command line. + if(!cmSystemTools::RunSingleCommand(tidy_cmd, 0, 0, &ret, 0, + cmSystemTools::OUTPUT_PASSTHROUGH)) + { + std::cerr << "Error running '" << tidy_cmd[0] << "'\n"; + return 1; + } } // Now run the real compiler command and return its result value. diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index d22c39ca0..02e14e6b4 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -301,7 +301,9 @@ if(CMAKE_OSX_ARCHITECTURES AND XCODE AND NOT "${XCODE_VERSION}" MATCHES "^[^12]" endif() if("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") + add_executable(pseudo_tidy pseudo_tidy.c) add_executable(pseudo_iwyu pseudo_iwyu.c) + add_RunCMake_test(ClangTidy -DPSEUDO_TIDY=$) add_RunCMake_test(IncludeWhatYouUse -DPSEUDO_IWYU=$) add_RunCMake_test(CompilerLauncher) endif() diff --git a/Tests/RunCMake/ClangTidy/C-Build-stdout.txt b/Tests/RunCMake/ClangTidy/C-Build-stdout.txt new file mode 100644 index 000000000..a47a2de81 --- /dev/null +++ b/Tests/RunCMake/ClangTidy/C-Build-stdout.txt @@ -0,0 +1 @@ +Tests[/\]RunCMake[/\]ClangTidy[/\]main\.c:0:0: warning: message \[checker\] diff --git a/Tests/RunCMake/ClangTidy/C-launch-Build-stdout.txt b/Tests/RunCMake/ClangTidy/C-launch-Build-stdout.txt new file mode 100644 index 000000000..a47a2de81 --- /dev/null +++ b/Tests/RunCMake/ClangTidy/C-launch-Build-stdout.txt @@ -0,0 +1 @@ +Tests[/\]RunCMake[/\]ClangTidy[/\]main\.c:0:0: warning: message \[checker\] diff --git a/Tests/RunCMake/ClangTidy/C-launch.cmake b/Tests/RunCMake/ClangTidy/C-launch.cmake new file mode 100644 index 000000000..e66ca203e --- /dev/null +++ b/Tests/RunCMake/ClangTidy/C-launch.cmake @@ -0,0 +1,3 @@ +set(CTEST_USE_LAUNCHERS 1) +include(CTestUseLaunchers) +include(C.cmake) diff --git a/Tests/RunCMake/ClangTidy/C.cmake b/Tests/RunCMake/ClangTidy/C.cmake new file mode 100644 index 000000000..b8057b0b1 --- /dev/null +++ b/Tests/RunCMake/ClangTidy/C.cmake @@ -0,0 +1,3 @@ +enable_language(C) +set(CMAKE_C_CLANG_TIDY "${PSEUDO_TIDY}" -some -args) +add_executable(main main.c) diff --git a/Tests/RunCMake/ClangTidy/CMakeLists.txt b/Tests/RunCMake/ClangTidy/CMakeLists.txt new file mode 100644 index 000000000..93ee9dfd5 --- /dev/null +++ b/Tests/RunCMake/ClangTidy/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.5) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ClangTidy/CXX-Build-stdout.txt b/Tests/RunCMake/ClangTidy/CXX-Build-stdout.txt new file mode 100644 index 000000000..47e43af35 --- /dev/null +++ b/Tests/RunCMake/ClangTidy/CXX-Build-stdout.txt @@ -0,0 +1 @@ +Tests[/\]RunCMake[/\]ClangTidy[/\]main\.cxx:0:0: warning: message \[checker\] diff --git a/Tests/RunCMake/ClangTidy/CXX-launch-Build-stdout.txt b/Tests/RunCMake/ClangTidy/CXX-launch-Build-stdout.txt new file mode 100644 index 000000000..47e43af35 --- /dev/null +++ b/Tests/RunCMake/ClangTidy/CXX-launch-Build-stdout.txt @@ -0,0 +1 @@ +Tests[/\]RunCMake[/\]ClangTidy[/\]main\.cxx:0:0: warning: message \[checker\] diff --git a/Tests/RunCMake/ClangTidy/CXX-launch.cmake b/Tests/RunCMake/ClangTidy/CXX-launch.cmake new file mode 100644 index 000000000..3002c9d68 --- /dev/null +++ b/Tests/RunCMake/ClangTidy/CXX-launch.cmake @@ -0,0 +1,3 @@ +set(CTEST_USE_LAUNCHERS 1) +include(CTestUseLaunchers) +include(CXX.cmake) diff --git a/Tests/RunCMake/ClangTidy/CXX.cmake b/Tests/RunCMake/ClangTidy/CXX.cmake new file mode 100644 index 000000000..2d22325cc --- /dev/null +++ b/Tests/RunCMake/ClangTidy/CXX.cmake @@ -0,0 +1,3 @@ +enable_language(CXX) +set(CMAKE_CXX_CLANG_TIDY "${PSEUDO_TIDY}" -some -args) +add_executable(main main.cxx) diff --git a/Tests/RunCMake/ClangTidy/RunCMakeTest.cmake b/Tests/RunCMake/ClangTidy/RunCMakeTest.cmake new file mode 100644 index 000000000..27cd922e0 --- /dev/null +++ b/Tests/RunCMake/ClangTidy/RunCMakeTest.cmake @@ -0,0 +1,22 @@ +include(RunCMake) + +set(RunCMake_TEST_OPTIONS "-DPSEUDO_TIDY=${PSEUDO_TIDY}") + +function(run_tidy lang) + # Use a single build tree for tests without cleaning. + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${lang}-build) + set(RunCMake_TEST_NO_CLEAN 1) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + run_cmake(${lang}) + + set(RunCMake_TEST_OUTPUT_MERGE 1) + run_cmake_command(${lang}-Build ${CMAKE_COMMAND} --build .) +endfunction() + +run_tidy(C) +run_tidy(CXX) +if (NOT RunCMake_GENERATOR STREQUAL "Watcom WMake") + run_tidy(C-launch) + run_tidy(CXX-launch) +endif() diff --git a/Tests/RunCMake/ClangTidy/main.c b/Tests/RunCMake/ClangTidy/main.c new file mode 100644 index 000000000..78f2de106 --- /dev/null +++ b/Tests/RunCMake/ClangTidy/main.c @@ -0,0 +1 @@ +int main(void) { return 0; } diff --git a/Tests/RunCMake/ClangTidy/main.cxx b/Tests/RunCMake/ClangTidy/main.cxx new file mode 100644 index 000000000..76e819701 --- /dev/null +++ b/Tests/RunCMake/ClangTidy/main.cxx @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/Tests/RunCMake/CommandLine/E___run_iwyu-no-iwyu-stderr.txt b/Tests/RunCMake/CommandLine/E___run_iwyu-no-iwyu-stderr.txt index 002409748..0d0d899f6 100644 --- a/Tests/RunCMake/CommandLine/E___run_iwyu-no-iwyu-stderr.txt +++ b/Tests/RunCMake/CommandLine/E___run_iwyu-no-iwyu-stderr.txt @@ -1 +1 @@ -^__run_iwyu missing --iwyu=$ +^__run_iwyu missing --tidy= or --iwyu=$ diff --git a/Tests/RunCMake/pseudo_tidy.c b/Tests/RunCMake/pseudo_tidy.c new file mode 100644 index 000000000..766c99086 --- /dev/null +++ b/Tests/RunCMake/pseudo_tidy.c @@ -0,0 +1,16 @@ +#include + +int main(int argc, char* argv[]) +{ + int i; + for (i = 1; i < argc; ++i) + { + if (argv[i][0] != '-') + { + fprintf(stdout, "%s:0:0: warning: message [checker]\n", argv[i]); + break; + } + } + fprintf(stderr, "1 warning generated.\n"); + return 0; +}