Browse Source

Fix combined use of compiler launcher with lint tools

When using ccache with clang-tidy, ccache needs to wrap compiler
invocation, rather than cmake invocation.  But it needs to do it without
affecting the command line that iwyu-like tools are receiving.

With this fix, if __run_co_compile is used, compile launcher is passed
using the new --launcher option, but if __run_co_compile is not needed,
compiler launcher is prepended to the command line as before.

To better illustrate the change: with this fix if running clang-tidy
with CXX_COMPILER_LAUNCHER set to "/usr/bin/time;-p;ccache" (time -p
added strictly for illustration purposes), the command line changes
from:

    /usr/bin/time -p ccache cmake -E __run_co_compile \
        --tidy=clang-tidy ... -- g++ ...

to:

    cmake -E __run_co_compile \
        --launcher="/usr/bin/time;-p;ccache" \
        --tidy=clang-tidy ... -- g++ ...

This allows the compiler to be run via the launcher, but leaves tidy
(& friends) invocations unaffected.

Fixes: #16493
Ilya A. Kriveshko 7 years ago
parent
commit
eaf9f69d41
3 changed files with 70 additions and 36 deletions
  1. 27 14
      Source/cmMakefileTargetGenerator.cxx
  2. 26 13
      Source/cmNinjaTargetGenerator.cxx
  3. 17 9
      Source/cmcmd.cxx

+ 27 - 14
Source/cmMakefileTargetGenerator.cxx

@@ -643,6 +643,18 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile(
         source.GetFullPath(), workingDirectory, compileCommand);
     }
 
+    // See if we need to use a compiler launcher like ccache or distcc
+    std::string compilerLauncher;
+    if (!compileCommands.empty() && (lang == "C" || lang == "CXX" ||
+                                     lang == "Fortran" || lang == "CUDA")) {
+      std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER";
+      const char* clauncher =
+        this->GeneratorTarget->GetProperty(clauncher_prop);
+      if (clauncher && *clauncher) {
+        compilerLauncher = clauncher;
+      }
+    }
+
     // Maybe insert an include-what-you-use runner.
     if (!compileCommands.empty() && (lang == "C" || lang == "CXX")) {
       std::string const iwyu_prop = lang + "_INCLUDE_WHAT_YOU_USE";
@@ -656,6 +668,13 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile(
       if ((iwyu && *iwyu) || (tidy && *tidy) || (cpplint && *cpplint) ||
           (cppcheck && *cppcheck)) {
         std::string run_iwyu = "$(CMAKE_COMMAND) -E __run_co_compile";
+        if (!compilerLauncher.empty()) {
+          // In __run_co_compile case the launcher command is supplied
+          // via --launcher=<maybe-list> and consumed
+          run_iwyu += " --launcher=";
+          run_iwyu += this->LocalGenerator->EscapeForShell(compilerLauncher);
+          compilerLauncher.clear();
+        }
         if (iwyu && *iwyu) {
           run_iwyu += " --iwyu=";
           run_iwyu += this->LocalGenerator->EscapeForShell(iwyu);
@@ -682,21 +701,15 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile(
       }
     }
 
-    // Maybe insert a compiler launcher like ccache or distcc
-    if (!compileCommands.empty() && (lang == "C" || lang == "CXX" ||
-                                     lang == "Fortran" || lang == "CUDA")) {
-      std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER";
-      const char* clauncher =
-        this->GeneratorTarget->GetProperty(clauncher_prop);
-      if (clauncher && *clauncher) {
-        std::vector<std::string> launcher_cmd;
-        cmSystemTools::ExpandListArgument(clauncher, launcher_cmd, true);
-        for (std::string& i : launcher_cmd) {
-          i = this->LocalGenerator->EscapeForShell(i);
-        }
-        std::string const& run_launcher = cmJoin(launcher_cmd, " ") + " ";
-        compileCommands.front().insert(0, run_launcher);
+    // If compiler launcher was specified and not consumed above, it
+    // goes to the beginning of the command line.
+    if (!compileCommands.empty() && !compilerLauncher.empty()) {
+      std::vector<std::string> args;
+      cmSystemTools::ExpandListArgument(compilerLauncher, args, true);
+      for (std::string& i : args) {
+        i = this->LocalGenerator->EscapeForShell(i);
       }
+      compileCommands.front().insert(0, cmJoin(args, " ") + " ");
     }
 
     std::string launcher;

+ 26 - 13
Source/cmNinjaTargetGenerator.cxx

@@ -653,6 +653,17 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang)
     cmSystemTools::ExpandListArgument(compileCmd, compileCmds);
   }
 
+  // See if we need to use a compiler launcher like ccache or distcc
+  std::string compilerLauncher;
+  if (!compileCmds.empty() &&
+      (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA")) {
+    std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER";
+    const char* clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
+    if (clauncher && *clauncher) {
+      compilerLauncher = clauncher;
+    }
+  }
+
   // Maybe insert an include-what-you-use runner.
   if (!compileCmds.empty() && (lang == "C" || lang == "CXX")) {
     std::string const iwyu_prop = lang + "_INCLUDE_WHAT_YOU_USE";
@@ -668,6 +679,13 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang)
       std::string run_iwyu = this->GetLocalGenerator()->ConvertToOutputFormat(
         cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
       run_iwyu += " -E __run_co_compile";
+      if (!compilerLauncher.empty()) {
+        // In __run_co_compile case the launcher command is supplied
+        // via --launcher=<maybe-list> and consumed
+        run_iwyu += " --launcher=";
+        run_iwyu += this->LocalGenerator->EscapeForShell(compilerLauncher);
+        compilerLauncher.clear();
+      }
       if (iwyu && *iwyu) {
         run_iwyu += " --iwyu=";
         run_iwyu += this->GetLocalGenerator()->EscapeForShell(iwyu);
@@ -693,20 +711,15 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang)
     }
   }
 
-  // Maybe insert a compiler launcher like ccache or distcc
-  if (!compileCmds.empty() &&
-      (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA")) {
-    std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER";
-    const char* clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
-    if (clauncher && *clauncher) {
-      std::vector<std::string> launcher_cmd;
-      cmSystemTools::ExpandListArgument(clauncher, launcher_cmd, true);
-      for (std::string& i : launcher_cmd) {
-        i = this->LocalGenerator->EscapeForShell(i);
-      }
-      std::string const& run_launcher = cmJoin(launcher_cmd, " ") + " ";
-      compileCmds.front().insert(0, run_launcher);
+  // If compiler launcher was specified and not consumed above, it
+  // goes to the beginning of the command line.
+  if (!compileCmds.empty() && !compilerLauncher.empty()) {
+    std::vector<std::string> args;
+    cmSystemTools::ExpandListArgument(compilerLauncher, args, true);
+    for (std::string& i : args) {
+      i = this->LocalGenerator->EscapeForShell(i);
     }
+    compileCmds.front().insert(0, cmJoin(args, " ") + " ");
   }
 
   if (!compileCmds.empty()) {

+ 17 - 9
Source/cmcmd.cxx

@@ -359,7 +359,8 @@ struct CoCompileJob
 int cmcmd::HandleCoCompileCommands(std::vector<std::string>& args)
 {
   std::vector<CoCompileJob> jobs;
-  std::string sourceFile; // store --source=
+  std::string sourceFile;             // store --source=
+  std::vector<std::string> launchers; // store --launcher=
 
   // 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
@@ -390,15 +391,17 @@ int cmcmd::HandleCoCompileCommands(std::vector<std::string>& args)
           }
         }
       }
-      if (cmHasLiteralPrefix(arg, "--source=")) {
-        sourceFile = arg.substr(9);
-        optionFound = true;
-      }
-      // if it was not a co-compiler or --source then error
       if (!optionFound) {
-        std::cerr << "__run_co_compile given unknown argument: " << arg
-                  << "\n";
-        return 1;
+        if (cmHasLiteralPrefix(arg, "--source=")) {
+          sourceFile = arg.substr(9);
+        } else if (cmHasLiteralPrefix(arg, "--launcher=")) {
+          cmSystemTools::ExpandListArgument(arg.substr(11), launchers, true);
+        } else {
+          // if it was not a co-compiler or --source/--launcher then error
+          std::cerr << "__run_co_compile given unknown argument: " << arg
+                    << "\n";
+          return 1;
+        }
       }
     } else { // if not doing_options then push to orig_cmd
       orig_cmd.push_back(arg);
@@ -436,6 +439,11 @@ int cmcmd::HandleCoCompileCommands(std::vector<std::string>& args)
     return 0;
   }
 
+  // Prepend launcher argument(s), if any
+  if (!launchers.empty()) {
+    orig_cmd.insert(orig_cmd.begin(), launchers.begin(), launchers.end());
+  }
+
   // Now run the real compiler command and return its result value
   int ret;
   if (!cmSystemTools::RunSingleCommand(orig_cmd, nullptr, nullptr, &ret,