cmcmd: Restore support for running multiple lint tools

Refactoring in commit v3.10.0-rc1~115^2 (Clean up iwyu code to not be
one big if statement, 2017-08-28) incorrectly changed the logic to run
only one lint tool at a time.  Restore support for running all tools
specified on the command-line.
This commit is contained in:
Brad King 2017-10-27 08:31:34 -04:00
parent a5197eeac7
commit 992962c76d
15 changed files with 165 additions and 60 deletions

View File

@ -33,15 +33,13 @@
#include "cmsys/Process.h"
#include "cmsys/Terminal.h"
#include <algorithm>
#include <functional>
#include <iostream>
#include <map>
#include <memory> // IWYU pragma: keep
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <utility>
class cmConnection;
@ -328,35 +326,41 @@ static int HandleCppCheck(const std::string& runCmd,
return 0;
}
typedef int (*CoCompileHandler)(const std::string&, const std::string&,
const std::vector<std::string>&);
struct CoCompiler
{
const char* Option;
CoCompileHandler Handler;
bool NoOriginalCommand;
};
static CoCompiler CoCompilers[] = { // Table of options and handlers.
{ "--cppcheck=", HandleCppCheck, false },
{ "--cpplint=", HandleCppLint, false },
{ "--iwyu=", HandleIWYU, false },
{ "--lwyu=", HandleLWYU, true },
{ "--tidy=", HandleTidy, false }
};
struct CoCompileJob
{
std::string Command;
CoCompileHandler Handler;
};
// called when args[0] == "__run_co_compile"
int cmcmd::HandleCoCompileCommands(std::vector<std::string>& args)
{
// initialize a map from command option to handler function
std::map<std::string,
std::function<int(const std::string&, const std::string&,
const std::vector<std::string>&)>>
coCompileTypes;
auto a1 = std::placeholders::_1;
auto a2 = std::placeholders::_2;
auto a3 = std::placeholders::_3;
// create a map from option to handler function for option
// if the option does not call the original command then it will need
// to set runOriginalCmd to false later in this function
coCompileTypes["--iwyu="] = std::bind(&HandleIWYU, a1, a2, a3);
coCompileTypes["--tidy="] = std::bind(&HandleTidy, a1, a2, a3);
coCompileTypes["--lwyu="] = std::bind(&HandleLWYU, a1, a2, a3);
coCompileTypes["--cpplint="] = std::bind(&HandleCppLint, a1, a2, a3);
coCompileTypes["--cppcheck="] = std::bind(&HandleCppCheck, a1, a2, a3);
// copy the command options to a vector of strings
std::vector<std::string> commandOptions;
commandOptions.reserve(coCompileTypes.size());
for (const auto& i : coCompileTypes) {
commandOptions.push_back(i.first);
}
std::vector<CoCompileJob> jobs;
std::string sourceFile; // store --source=
// Default is to run the original command found after -- if the option
// does not need to do that, it should be specified here, currently only
// lwyu does that.
bool runOriginalCmd = true;
std::string runCmd; // command to be run from --thing=command
std::string sourceFile; // store --source=
std::string commandFound; // the command that was in the args list
std::vector<std::string> orig_cmd;
bool doing_options = true;
for (std::string::size_type i = 2; i < args.size(); ++i) {
@ -367,20 +371,25 @@ int cmcmd::HandleCoCompileCommands(std::vector<std::string>& args)
doing_options = false;
} else if (doing_options) {
bool optionFound = false;
// check arg against all the commandOptions
for (auto const& command : commandOptions) {
if (arg.compare(0, command.size(), command) == 0) {
for (CoCompiler const* cc = cmArrayBegin(CoCompilers);
cc != cmArrayEnd(CoCompilers); ++cc) {
size_t optionLen = strlen(cc->Option);
if (arg.compare(0, optionLen, cc->Option) == 0) {
optionFound = true;
runCmd = arg.substr(command.size());
commandFound = command;
CoCompileJob job;
job.Command = arg.substr(optionLen);
job.Handler = cc->Handler;
jobs.push_back(std::move(job));
if (cc->NoOriginalCommand) {
runOriginalCmd = false;
}
}
}
// check arg with --source=
if (cmHasLiteralPrefix(arg, "--source=")) {
sourceFile = arg.substr(9);
optionFound = true;
}
// if it was not a commandOptions or --source then error
// if it was not a co-compiler or --source then error
if (!optionFound) {
std::cerr << "__run_co_compile given unknown argument: " << arg
<< "\n";
@ -390,39 +399,40 @@ int cmcmd::HandleCoCompileCommands(std::vector<std::string>& args)
orig_cmd.push_back(arg);
}
}
if (commandFound.empty()) {
std::cerr << "__run_co_compile missing command to run. Looking for one of "
"the following:\n";
for (const auto& i : commandOptions) {
std::cerr << i << "\n";
if (jobs.empty()) {
std::cerr << "__run_co_compile missing command to run. "
"Looking for one or more of the following:\n";
for (CoCompiler const* cc = cmArrayBegin(CoCompilers);
cc != cmArrayEnd(CoCompilers); ++cc) {
std::cerr << cc->Option << "\n";
}
return 1;
}
// Default is to run the original command found after -- if the option
// does not need to do that, it should be specified here, currently only
// lwyu does that.
bool runOriginalCmd = true;
if (commandFound == "--lwyu=") {
runOriginalCmd = false;
}
if (runOriginalCmd && orig_cmd.empty()) {
std::cerr << "__run_co_compile missing compile command after --\n";
return 1;
}
// call the command handler here
int ret = coCompileTypes[commandFound](runCmd, sourceFile, orig_cmd);
// if the command returns non-zero then return and fail.
// for commands that do not want to break the build, they should return
// 0 no matter what.
if (ret != 0) {
return ret;
for (CoCompileJob const& job : jobs) {
// call the command handler here
int ret = job.Handler(job.Command, sourceFile, orig_cmd);
// if the command returns non-zero then return and fail.
// for commands that do not want to break the build, they should return
// 0 no matter what.
if (ret != 0) {
return ret;
}
}
// if there is no original command to run return now
if (!runOriginalCmd) {
return ret;
return 0;
}
// Now run the real compiler command and return its result value
int ret;
if (!cmSystemTools::RunSingleCommand(orig_cmd, nullptr, nullptr, &ret,
nullptr,
cmSystemTools::OUTPUT_PASSTHROUGH)) {

View File

@ -370,6 +370,12 @@ if("${CMAKE_GENERATOR}" MATCHES "Make|Ninja")
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>)
add_RunCMake_test(MultiLint
-DPSEUDO_TIDY=$<TARGET_FILE:pseudo_tidy>
-DPSEUDO_IWYU=$<TARGET_FILE:pseudo_iwyu>
-DPSEUDO_CPPLINT=$<TARGET_FILE:pseudo_cpplint>
-DPSEUDO_CPPCHECK=$<TARGET_FILE:pseudo_cppcheck>
)
if(DEFINED CMake_TEST_CUDA)
list(APPEND CompilerLauncher_ARGS -DCMake_TEST_CUDA=${CMake_TEST_CUDA})
endif()

View File

@ -1,5 +1,6 @@
^__run_co_compile missing command to run. Looking for one of the following:
.*--cppcheck=
.*--cpplint=
.*--iwyu=
.*--tidy=
^__run_co_compile missing command to run. Looking for one or more of the following:
--cppcheck=
--cpplint=
--iwyu=
--lwyu=
--tidy=

View File

@ -0,0 +1,8 @@
Warning: include-what-you-use reported diagnostics:
should add these lines:
*
#include <\.\.\.>
+
.*Tests[/\]RunCMake[/\]MultiLint[/\]main\.c:0:0: warning: message \[checker\].*
Total errors found: 0
.*Warning: cppcheck reported diagnostics.*error.*warning.*style.*performance.*information.*

View File

@ -0,0 +1,8 @@
Warning: include-what-you-use reported diagnostics:
should add these lines:
*
#include <\.\.\.>
+
.*Tests[/\]RunCMake[/\]MultiLint[/\]main\.c:0:0: warning: message \[checker\].*
Total errors found: 0
.*Warning: cppcheck reported diagnostics.*error.*warning.*style.*performance.*information.*

View File

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

View File

@ -0,0 +1,6 @@
enable_language(C)
set(CMAKE_C_INCLUDE_WHAT_YOU_USE "${PSEUDO_IWYU}" -some -args)
set(CMAKE_C_CLANG_TIDY "${PSEUDO_TIDY}" -some -args)
set(CMAKE_C_CPPLINT "${PSEUDO_CPPLINT}" --verbose=0 --linelength=80)
set(CMAKE_C_CPPCHECK "${PSEUDO_CPPCHECK}")
add_executable(main main.c)

View File

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

View File

@ -0,0 +1,8 @@
Warning: include-what-you-use reported diagnostics:
should add these lines:
*
#include <\.\.\.>
+
.*Tests[/\]RunCMake[/\]MultiLint[/\]main\.cxx:0:0: warning: message \[checker\].*
Total errors found: 0
.*Warning: cppcheck reported diagnostics.*error.*warning.*style.*performance.*information.*

View File

@ -0,0 +1,8 @@
Warning: include-what-you-use reported diagnostics:
should add these lines:
*
#include <\.\.\.>
+
.*Tests[/\]RunCMake[/\]MultiLint[/\]main\.cxx:0:0: warning: message \[checker\].*
Total errors found: 0
.*Warning: cppcheck reported diagnostics.*error.*warning.*style.*performance.*information.*

View File

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

View File

@ -0,0 +1,6 @@
enable_language(CXX)
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "${PSEUDO_IWYU}" -some -args)
set(CMAKE_CXX_CLANG_TIDY "${PSEUDO_TIDY}" -some -args)
set(CMAKE_CXX_CPPLINT "${PSEUDO_CPPLINT}" --verbose=0 --linelength=80)
set(CMAKE_CXX_CPPCHECK "${PSEUDO_CPPCHECK}")
add_executable(main main.cxx)

View File

@ -0,0 +1,27 @@
include(RunCMake)
set(RunCMake_TEST_OPTIONS
"-DPSEUDO_CPPCHECK=${PSEUDO_CPPCHECK}"
"-DPSEUDO_CPPLINT=${PSEUDO_CPPLINT}"
"-DPSEUDO_IWYU=${PSEUDO_IWYU}"
"-DPSEUDO_TIDY=${PSEUDO_TIDY}"
)
function(run_multilint 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_multilint(C)
run_multilint(CXX)
if(NOT RunCMake_GENERATOR STREQUAL "Watcom WMake")
run_multilint(C-launch)
run_multilint(CXX-launch)
endif()

View File

@ -0,0 +1,4 @@
int main(void)
{
return 0;
}

View File

@ -0,0 +1,4 @@
int main()
{
return 0;
}