diff options
author | Bill Hoffman <bill.hoffman@kitware.com> | 2017-08-16 16:24:44 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2017-08-30 14:23:46 (GMT) |
commit | 311b7b1a7095ec460fab950a6767571eb3b9652c (patch) | |
tree | 23843c9cd26d9d150e76fadda88a8fbf47d5f12c | |
parent | fff28e30cd01a88b2e5f67db2aaf4c068f1bfc89 (diff) | |
download | CMake-311b7b1a7095ec460fab950a6767571eb3b9652c.zip CMake-311b7b1a7095ec460fab950a6767571eb3b9652c.tar.gz CMake-311b7b1a7095ec460fab950a6767571eb3b9652c.tar.bz2 |
Add properties to run cppcheck along with the compiler
Create a `<LANG>_CPPCHECK` target property (initialized by a
`CMAKE_<LANG>_CPPCHECK` variable) to specify a `cppcheck` command line
to be run along with the compiler.
24 files changed, 184 insertions, 12 deletions
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index f1378c8..eadd00d 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -227,6 +227,7 @@ Properties on Targets /prop_tgt/LABELS /prop_tgt/LANG_CLANG_TIDY /prop_tgt/LANG_COMPILER_LAUNCHER + /prop_tgt/LANG_CPPCHECK /prop_tgt/LANG_CPPLINT /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 bef1171..52395c9 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -302,6 +302,7 @@ Variables that Control the Build /variable/CMAKE_IOS_INSTALL_COMBINED /variable/CMAKE_LANG_CLANG_TIDY /variable/CMAKE_LANG_COMPILER_LAUNCHER + /variable/CMAKE_LANG_CPPCHECK /variable/CMAKE_LANG_CPPLINT /variable/CMAKE_LANG_INCLUDE_WHAT_YOU_USE /variable/CMAKE_LANG_VISIBILITY_PRESET diff --git a/Help/prop_tgt/LANG_CPPCHECK.rst b/Help/prop_tgt/LANG_CPPCHECK.rst new file mode 100644 index 0000000..5f8be00 --- /dev/null +++ b/Help/prop_tgt/LANG_CPPCHECK.rst @@ -0,0 +1,13 @@ +<LANG>_CPPCHECK +--------------- + +This property is supported only when ``<LANG>`` is ``C`` or ``CXX``. + +Specify a :ref:`;-list <CMake Language Lists>` containing a command line +for the ``cppcheck`` static analysis tool. The :ref:`Makefile Generators` +and the :generator:`Ninja` generator will run ``cppcheck`` along with the +compiler and report any problems. + +This property is initialized by the value of the +:variable:`CMAKE_<LANG>_CPPCHECK` variable if it is set when a target is +created. diff --git a/Help/release/dev/add-cppcheck.rst b/Help/release/dev/add-cppcheck.rst new file mode 100644 index 0000000..bf3472a --- /dev/null +++ b/Help/release/dev/add-cppcheck.rst @@ -0,0 +1,7 @@ +add-cppcheck +------------ + +* A :prop_tgt:`<LANG>_CPPCHECK` target property and supporting + :variable:`CMAKE_<LANG>_CPPCHECK` variable were introduced to tell + the :ref:`Makefile Generators` and the :generator:`Ninja` generator to + run ``cppcheck`` with the compiler for ``C`` and ``CXX`` languages. diff --git a/Help/variable/CMAKE_LANG_CPPCHECK.rst b/Help/variable/CMAKE_LANG_CPPCHECK.rst new file mode 100644 index 0000000..50b478f --- /dev/null +++ b/Help/variable/CMAKE_LANG_CPPCHECK.rst @@ -0,0 +1,6 @@ +CMAKE_<LANG>_CPPCHECK +--------------------- + +Default value for :prop_tgt:`<LANG>_CPPCHECK` target property. This variable +is used to initialize the property on each target as it is created. This +is done only when ``<LANG>`` is ``C`` or ``CXX``. diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index b9bcdef..c1cf103 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -633,7 +633,10 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile( const char* tidy = this->GeneratorTarget->GetProperty(tidy_prop); std::string const cpplint_prop = lang + "_CPPLINT"; const char* cpplint = this->GeneratorTarget->GetProperty(cpplint_prop); - if ((iwyu && *iwyu) || (tidy && *tidy) || (cpplint && *cpplint)) { + std::string const cppcheck_prop = lang + "_CPPCHECK"; + const char* cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop); + if ((iwyu && *iwyu) || (tidy && *tidy) || (cpplint && *cpplint) || + (cppcheck && *cppcheck)) { std::string run_iwyu = "$(CMAKE_COMMAND) -E __run_iwyu"; if (iwyu && *iwyu) { run_iwyu += " --iwyu="; @@ -647,7 +650,12 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile( run_iwyu += " --cpplint="; run_iwyu += this->LocalGenerator->EscapeForShell(cpplint); } - if ((tidy && *tidy) || (cpplint && *cpplint)) { + if (cppcheck && *cppcheck) { + run_iwyu += " --cppcheck="; + run_iwyu += this->LocalGenerator->EscapeForShell(cppcheck); + } + if ((tidy && *tidy) || (cpplint && *cpplint) || + (cppcheck && *cppcheck)) { run_iwyu += " --source="; run_iwyu += sourceFile; } diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 4a4e5b2..b08c19b 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -617,7 +617,10 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) const char* tidy = this->GeneratorTarget->GetProperty(tidy_prop); std::string const cpplint_prop = lang + "_CPPLINT"; const char* cpplint = this->GeneratorTarget->GetProperty(cpplint_prop); - if ((iwyu && *iwyu) || (tidy && *tidy) || (cpplint && *cpplint)) { + std::string const cppcheck_prop = lang + "_CPPCHECK"; + const char* cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop); + if ((iwyu && *iwyu) || (tidy && *tidy) || (cpplint && *cpplint) || + (cppcheck && *cppcheck)) { std::string run_iwyu = this->GetLocalGenerator()->ConvertToOutputFormat( cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL); run_iwyu += " -E __run_iwyu"; @@ -633,7 +636,12 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) run_iwyu += " --cpplint="; run_iwyu += this->GetLocalGenerator()->EscapeForShell(cpplint); } - if ((tidy && *tidy) || (cpplint && *cpplint)) { + if (cppcheck && *cppcheck) { + run_iwyu += " --cppcheck="; + run_iwyu += this->GetLocalGenerator()->EscapeForShell(cppcheck); + } + if ((tidy && *tidy) || (cpplint && *cpplint) || + (cppcheck && *cppcheck)) { run_iwyu += " --source=$in"; } run_iwyu += " -- "; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index d4a0bf5..1ca013c 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -262,6 +262,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->SetPropertyDefault("C_CLANG_TIDY", nullptr); this->SetPropertyDefault("C_COMPILER_LAUNCHER", nullptr); this->SetPropertyDefault("C_CPPLINT", nullptr); + this->SetPropertyDefault("C_CPPCHECK", nullptr); this->SetPropertyDefault("C_INCLUDE_WHAT_YOU_USE", nullptr); this->SetPropertyDefault("LINK_WHAT_YOU_USE", nullptr); this->SetPropertyDefault("C_STANDARD", nullptr); @@ -270,6 +271,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->SetPropertyDefault("CXX_CLANG_TIDY", nullptr); this->SetPropertyDefault("CXX_COMPILER_LAUNCHER", nullptr); this->SetPropertyDefault("CXX_CPPLINT", nullptr); + this->SetPropertyDefault("CXX_CPPCHECK", nullptr); this->SetPropertyDefault("CXX_INCLUDE_WHAT_YOU_USE", nullptr); this->SetPropertyDefault("CXX_STANDARD", nullptr); this->SetPropertyDefault("CXX_STANDARD_REQUIRED", nullptr); diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index bf5479c..0791cb3 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -297,6 +297,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) std::string sourceFile; std::string lwyu; std::string cpplint; + std::string cppcheck; for (std::string::size_type cc = 2; cc < args.size(); cc++) { std::string const& arg = args[cc]; if (arg == "--") { @@ -311,6 +312,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) lwyu = arg.substr(7); } else if (doing_options && cmHasLiteralPrefix(arg, "--cpplint=")) { cpplint = arg.substr(10); + } else if (doing_options && cmHasLiteralPrefix(arg, "--cppcheck=")) { + cppcheck = arg.substr(11); } else if (doing_options) { std::cerr << "__run_iwyu given unknown argument: " << arg << "\n"; return 1; @@ -318,14 +321,16 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) orig_cmd.push_back(arg); } } - if (tidy.empty() && iwyu.empty() && lwyu.empty() && cpplint.empty()) { - std::cerr << "__run_iwyu missing --cpplint=, --iwyu=, --lwyu=, and/or" - " --tidy=\n"; + if (tidy.empty() && iwyu.empty() && lwyu.empty() && cpplint.empty() && + cppcheck.empty()) { + std::cerr << "__run_iwyu missing --cpplint=, --iwyu=, --lwyu=, " + "--cppcheck= and/or --tidy=\n"; return 1; } - if ((!cpplint.empty() || !tidy.empty()) && sourceFile.empty()) { - std::cerr << "__run_iwyu --cpplint= and/or __run_iwyu --tidy=" - " require --source=\n"; + if ((!cpplint.empty() || !tidy.empty() || !cppcheck.empty()) && + sourceFile.empty()) { + std::cerr << "__run_iwyu --cpplint=, __run_iwyu --tidy=" + ", __run_iwyu --cppcheck require --source=\n"; return 1; } if (orig_cmd.empty() && lwyu.empty()) { @@ -445,8 +450,56 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) } } + if (!cppcheck.empty()) { + // Construct the cpplint command line. + std::vector<std::string> cppcheck_cmd; + cmSystemTools::ExpandListArgument(cppcheck, cppcheck_cmd, true); + // extract all the -D, -U, and -I options from the compile line + for (size_t i = 0; i < orig_cmd.size(); i++) { + std::string& opt = orig_cmd[i]; + if (opt.size() > 2) { + if ((opt[0] == '-') && + ((opt[1] == 'D') || (opt[1] == 'I') || (opt[1] == 'U'))) { + cppcheck_cmd.push_back(opt); +#if defined(_WIN32) + } else if ((opt[0] == '/') && + ((opt[1] == 'D') || (opt[1] == 'I') || + (opt[1] == 'U'))) { + std::string optcopy = opt; + optcopy[0] = '-'; + cppcheck_cmd.push_back(optcopy); +#endif + } + } + } + // add the source file + cppcheck_cmd.push_back(sourceFile); + + // Run the cpplint command line. Capture its output. + std::string stdOut; + if (!cmSystemTools::RunSingleCommand(cppcheck_cmd, &stdOut, &stdOut, + &ret, nullptr, + cmSystemTools::OUTPUT_NONE)) { + std::cerr << "Error running '" << cppcheck_cmd[0] << "': " << stdOut + << "\n"; + return 1; + } + // Output the output from cpplint to stderr + if (stdOut.find("(error)") != std::string::npos || + stdOut.find("(warning)") != std::string::npos || + stdOut.find("(style)") != std::string::npos || + stdOut.find("(performance)") != std::string::npos || + stdOut.find("(portability)") != std::string::npos || + stdOut.find("(information)") != std::string::npos) { + std::cerr << "Warning: cppcheck reported diagnostics:\n"; + } + std::cerr << stdOut; + } + // ignore the cppcheck error code because it is likely to have them + // from bad -D stuff ret = 0; - // Now run the real compiler command and return its result value. + // Now run the real compiler command and return its result value + // unless we are lwyu if (lwyu.empty() && !cmSystemTools::RunSingleCommand( orig_cmd, nullptr, nullptr, &ret, nullptr, diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 13df4a8..88952e1 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -343,9 +343,11 @@ if("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") add_executable(pseudo_tidy pseudo_tidy.c) add_executable(pseudo_iwyu pseudo_iwyu.c) add_executable(pseudo_cpplint pseudo_cpplint.c) + add_executable(pseudo_cppcheck pseudo_cppcheck.c) add_RunCMake_test(ClangTidy -DPSEUDO_TIDY=$<TARGET_FILE:pseudo_tidy>) add_RunCMake_test(IncludeWhatYouUse -DPSEUDO_IWYU=$<TARGET_FILE:pseudo_iwyu>) add_RunCMake_test(Cpplint -DPSEUDO_CPPLINT=$<TARGET_FILE:pseudo_cpplint>) + add_RunCMake_test(Cppcheck -DPSEUDO_CPPCHECK=$<TARGET_FILE:pseudo_cppcheck>) if(DEFINED CMake_TEST_CUDA) list(APPEND CompilerLauncher_ARGS -DCMake_TEST_CUDA=${CMake_TEST_CUDA}) endif() 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 35f50b7..9db95f8 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 --cpplint=, --iwyu=, --lwyu=, and/or --tidy=$ +^__run_iwyu missing --cpplint=, --iwyu=, --lwyu=, --cppcheck= and/or --tidy=$ diff --git a/Tests/RunCMake/Cppcheck/C-Build-stdout.txt b/Tests/RunCMake/Cppcheck/C-Build-stdout.txt new file mode 100644 index 0000000..26eded3 --- /dev/null +++ b/Tests/RunCMake/Cppcheck/C-Build-stdout.txt @@ -0,0 +1 @@ +.*Warning: cppcheck reported diagnostics.*error.*warning.*style.*performance.*information.* diff --git a/Tests/RunCMake/Cppcheck/C-launch-Build-stdout.txt b/Tests/RunCMake/Cppcheck/C-launch-Build-stdout.txt new file mode 100644 index 0000000..26eded3 --- /dev/null +++ b/Tests/RunCMake/Cppcheck/C-launch-Build-stdout.txt @@ -0,0 +1 @@ +.*Warning: cppcheck reported diagnostics.*error.*warning.*style.*performance.*information.* diff --git a/Tests/RunCMake/Cppcheck/C-launch.cmake b/Tests/RunCMake/Cppcheck/C-launch.cmake new file mode 100644 index 0000000..e66ca20 --- /dev/null +++ b/Tests/RunCMake/Cppcheck/C-launch.cmake @@ -0,0 +1,3 @@ +set(CTEST_USE_LAUNCHERS 1) +include(CTestUseLaunchers) +include(C.cmake) diff --git a/Tests/RunCMake/Cppcheck/C.cmake b/Tests/RunCMake/Cppcheck/C.cmake new file mode 100644 index 0000000..b1f5bf7 --- /dev/null +++ b/Tests/RunCMake/Cppcheck/C.cmake @@ -0,0 +1,4 @@ + +enable_language(C) +set(CMAKE_C_CPPCHECK "${PSEUDO_CPPCHECK}") +add_executable(main main.c) diff --git a/Tests/RunCMake/Cppcheck/CMakeLists.txt b/Tests/RunCMake/Cppcheck/CMakeLists.txt new file mode 100644 index 0000000..a640c56 --- /dev/null +++ b/Tests/RunCMake/Cppcheck/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.7) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Cppcheck/CXX-Build-stdout.txt b/Tests/RunCMake/Cppcheck/CXX-Build-stdout.txt new file mode 100644 index 0000000..26eded3 --- /dev/null +++ b/Tests/RunCMake/Cppcheck/CXX-Build-stdout.txt @@ -0,0 +1 @@ +.*Warning: cppcheck reported diagnostics.*error.*warning.*style.*performance.*information.* diff --git a/Tests/RunCMake/Cppcheck/CXX-launch-Build-stdout.txt b/Tests/RunCMake/Cppcheck/CXX-launch-Build-stdout.txt new file mode 100644 index 0000000..26eded3 --- /dev/null +++ b/Tests/RunCMake/Cppcheck/CXX-launch-Build-stdout.txt @@ -0,0 +1 @@ +.*Warning: cppcheck reported diagnostics.*error.*warning.*style.*performance.*information.* diff --git a/Tests/RunCMake/Cppcheck/CXX-launch.cmake b/Tests/RunCMake/Cppcheck/CXX-launch.cmake new file mode 100644 index 0000000..3002c9d --- /dev/null +++ b/Tests/RunCMake/Cppcheck/CXX-launch.cmake @@ -0,0 +1,3 @@ +set(CTEST_USE_LAUNCHERS 1) +include(CTestUseLaunchers) +include(CXX.cmake) diff --git a/Tests/RunCMake/Cppcheck/CXX.cmake b/Tests/RunCMake/Cppcheck/CXX.cmake new file mode 100644 index 0000000..3b79471 --- /dev/null +++ b/Tests/RunCMake/Cppcheck/CXX.cmake @@ -0,0 +1,3 @@ +enable_language(CXX) +set(CMAKE_CXX_CPPCHECK "${PSEUDO_CPPCHECK}") +add_executable(main main.cxx) diff --git a/Tests/RunCMake/Cppcheck/RunCMakeTest.cmake b/Tests/RunCMake/Cppcheck/RunCMakeTest.cmake new file mode 100644 index 0000000..ae14f8c --- /dev/null +++ b/Tests/RunCMake/Cppcheck/RunCMakeTest.cmake @@ -0,0 +1,22 @@ +include(RunCMake) + +set(RunCMake_TEST_OPTIONS "-DPSEUDO_CPPCHECK=${PSEUDO_CPPCHECK}") + +function(run_cppcheck 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_cppcheck(C) +run_cppcheck(CXX) + +if(NOT RunCMake_GENERATOR STREQUAL "Watcom WMake") + run_cppcheck(C-launch) + run_cppcheck(CXX-launch) +endif() diff --git a/Tests/RunCMake/Cppcheck/main.c b/Tests/RunCMake/Cppcheck/main.c new file mode 100644 index 0000000..8488f4e --- /dev/null +++ b/Tests/RunCMake/Cppcheck/main.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/Tests/RunCMake/Cppcheck/main.cxx b/Tests/RunCMake/Cppcheck/main.cxx new file mode 100644 index 0000000..f8b643a --- /dev/null +++ b/Tests/RunCMake/Cppcheck/main.cxx @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/Tests/RunCMake/pseudo_cppcheck.c b/Tests/RunCMake/pseudo_cppcheck.c new file mode 100644 index 0000000..32e6e28 --- /dev/null +++ b/Tests/RunCMake/pseudo_cppcheck.c @@ -0,0 +1,21 @@ +#include <stdio.h> + +int main(void) +{ + fprintf(stderr, + "[/foo/bar.c:2]: (error) Array 'abc[10]' accessed at index 12," + " which is out of bounds.\n"); + fprintf(stderr, "[/foo/bar.c:2]: (warning) Member variable 'foo::bar' is " + "not initialized in the constructor.\n"); + fprintf(stderr, "[/foo/bar.c:2]: (style) C-style pointer casting.\n"); + fprintf(stderr, "[/foo/bar.c:2]: (performance) Variable 'm_message' is " + "assigned in constructor body. Consider performing " + "initialization in initialization list.\n"); + fprintf(stderr, "[/foo/bar.c:2]: (portability) scanf without field width " + "limits can crash with huge input data on some versions of " + "libc\n"); + fprintf(stderr, "[/foo/bar.c:2]: (information) cannot find all the include " + "files (use --check-config for details)\n"); + // we allow this to return 1 as we ignore it + return 1; +} |