mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-11 08:20:18 -06:00
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.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
13
Help/prop_tgt/LANG_CPPCHECK.rst
Normal file
13
Help/prop_tgt/LANG_CPPCHECK.rst
Normal file
@@ -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.
|
||||
7
Help/release/dev/add-cppcheck.rst
Normal file
7
Help/release/dev/add-cppcheck.rst
Normal file
@@ -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.
|
||||
6
Help/variable/CMAKE_LANG_CPPCHECK.rst
Normal file
6
Help/variable/CMAKE_LANG_CPPCHECK.rst
Normal file
@@ -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``.
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 += " -- ";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1 +1 @@
|
||||
^__run_iwyu missing --cpplint=, --iwyu=, --lwyu=, and/or --tidy=$
|
||||
^__run_iwyu missing --cpplint=, --iwyu=, --lwyu=, --cppcheck= and/or --tidy=$
|
||||
|
||||
1
Tests/RunCMake/Cppcheck/C-Build-stdout.txt
Normal file
1
Tests/RunCMake/Cppcheck/C-Build-stdout.txt
Normal file
@@ -0,0 +1 @@
|
||||
.*Warning: cppcheck reported diagnostics.*error.*warning.*style.*performance.*information.*
|
||||
1
Tests/RunCMake/Cppcheck/C-launch-Build-stdout.txt
Normal file
1
Tests/RunCMake/Cppcheck/C-launch-Build-stdout.txt
Normal file
@@ -0,0 +1 @@
|
||||
.*Warning: cppcheck reported diagnostics.*error.*warning.*style.*performance.*information.*
|
||||
3
Tests/RunCMake/Cppcheck/C-launch.cmake
Normal file
3
Tests/RunCMake/Cppcheck/C-launch.cmake
Normal file
@@ -0,0 +1,3 @@
|
||||
set(CTEST_USE_LAUNCHERS 1)
|
||||
include(CTestUseLaunchers)
|
||||
include(C.cmake)
|
||||
4
Tests/RunCMake/Cppcheck/C.cmake
Normal file
4
Tests/RunCMake/Cppcheck/C.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
enable_language(C)
|
||||
set(CMAKE_C_CPPCHECK "${PSEUDO_CPPCHECK}")
|
||||
add_executable(main main.c)
|
||||
3
Tests/RunCMake/Cppcheck/CMakeLists.txt
Normal file
3
Tests/RunCMake/Cppcheck/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
project(${RunCMake_TEST} NONE)
|
||||
include(${RunCMake_TEST}.cmake)
|
||||
1
Tests/RunCMake/Cppcheck/CXX-Build-stdout.txt
Normal file
1
Tests/RunCMake/Cppcheck/CXX-Build-stdout.txt
Normal file
@@ -0,0 +1 @@
|
||||
.*Warning: cppcheck reported diagnostics.*error.*warning.*style.*performance.*information.*
|
||||
1
Tests/RunCMake/Cppcheck/CXX-launch-Build-stdout.txt
Normal file
1
Tests/RunCMake/Cppcheck/CXX-launch-Build-stdout.txt
Normal file
@@ -0,0 +1 @@
|
||||
.*Warning: cppcheck reported diagnostics.*error.*warning.*style.*performance.*information.*
|
||||
3
Tests/RunCMake/Cppcheck/CXX-launch.cmake
Normal file
3
Tests/RunCMake/Cppcheck/CXX-launch.cmake
Normal file
@@ -0,0 +1,3 @@
|
||||
set(CTEST_USE_LAUNCHERS 1)
|
||||
include(CTestUseLaunchers)
|
||||
include(CXX.cmake)
|
||||
3
Tests/RunCMake/Cppcheck/CXX.cmake
Normal file
3
Tests/RunCMake/Cppcheck/CXX.cmake
Normal file
@@ -0,0 +1,3 @@
|
||||
enable_language(CXX)
|
||||
set(CMAKE_CXX_CPPCHECK "${PSEUDO_CPPCHECK}")
|
||||
add_executable(main main.cxx)
|
||||
22
Tests/RunCMake/Cppcheck/RunCMakeTest.cmake
Normal file
22
Tests/RunCMake/Cppcheck/RunCMakeTest.cmake
Normal file
@@ -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()
|
||||
4
Tests/RunCMake/Cppcheck/main.c
Normal file
4
Tests/RunCMake/Cppcheck/main.c
Normal file
@@ -0,0 +1,4 @@
|
||||
int main(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
4
Tests/RunCMake/Cppcheck/main.cxx
Normal file
4
Tests/RunCMake/Cppcheck/main.cxx
Normal file
@@ -0,0 +1,4 @@
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
21
Tests/RunCMake/pseudo_cppcheck.c
Normal file
21
Tests/RunCMake/pseudo_cppcheck.c
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user