Explorar o código

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.
Brad King %!s(int64=8) %!d(string=hai) anos
pai
achega
992962c76d

+ 66 - 56
Source/cmcmd.cxx

@@ -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::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<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::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)) {

+ 6 - 0
Tests/RunCMake/CMakeLists.txt

@@ -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()

+ 6 - 5
Tests/RunCMake/CommandLine/E___run_co_compile-no-iwyu-stderr.txt

@@ -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=

+ 8 - 0
Tests/RunCMake/MultiLint/C-Build-stdout.txt

@@ -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.*

+ 8 - 0
Tests/RunCMake/MultiLint/C-launch-Build-stdout.txt

@@ -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.*

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

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

+ 6 - 0
Tests/RunCMake/MultiLint/C.cmake

@@ -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)

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

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

+ 8 - 0
Tests/RunCMake/MultiLint/CXX-Build-stdout.txt

@@ -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.*

+ 8 - 0
Tests/RunCMake/MultiLint/CXX-launch-Build-stdout.txt

@@ -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.*

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

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

+ 6 - 0
Tests/RunCMake/MultiLint/CXX.cmake

@@ -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)

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

@@ -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()

+ 4 - 0
Tests/RunCMake/MultiLint/main.c

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

+ 4 - 0
Tests/RunCMake/MultiLint/main.cxx

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