Split the clangTidyRE rule

* Add clangTidyDepRE, which is an order-only dependent of
  the new simplified clangTidyRE rule.
* Remove escapeSingleQuotes, used only by the old bash command.
* Remove extra space when TIDY_TIMEOUT is not defined.
* In clang-tidy calls,
  use moduleFlags to match the clang/clang++ commands.

Bug: 218937424
Test: make droid tidy-soong_subset
Change-Id: I162e2318410741ab61fb2bb338725b000a3f148d
diff --git a/cc/builder.go b/cc/builder.go
index a5e5406..ee3ea2d 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -203,25 +203,34 @@
 		"clangBin", "format")
 
 	// Rule for invoking clang-tidy (a clang-based linter).
+	clangTidyDep, clangTidyDepRE = pctx.RemoteStaticRules("clangTidyDep",
+		blueprint.RuleParams{
+			Depfile: "$out",
+			Deps:    blueprint.DepsGCC,
+			Command: "${config.CcWrapper}$ccCmd $cFlags -E -o /dev/null $in " +
+				"-MQ $tidyFile -MD -MF $out",
+			CommandDeps: []string{"$ccCmd"},
+		},
+		&remoteexec.REParams{
+			Labels:       map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
+			ExecStrategy: "${config.REClangTidyExecStrategy}",
+			Inputs:       []string{"$in"},
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
+		}, []string{"ccCmd", "cFlags", "tidyFile"}, []string{})
+
 	clangTidy, clangTidyRE = pctx.RemoteStaticRules("clangTidy",
 		blueprint.RuleParams{
 			Depfile: "${out}.d",
 			Deps:    blueprint.DepsGCC,
-			// Pick bash because some machines with old /bin/sh cannot handle arrays.
-			// All $cFlags and $tidyFlags should have single quotes escaped.
-			// Assume no single quotes in other parameters like $in, $out, $ccCmd.
-			Command: "/bin/bash -c 'SRCF=$in; TIDYF=$out; CLANGFLAGS=($cFlags); " +
-				"rm -f $$TIDYF $${TIDYF}.d && " +
-				"${config.CcWrapper}$ccCmd \"$${CLANGFLAGS[@]}\" -E -o /dev/null $$SRCF " +
-				"-MQ $$TIDYF -MD -MF $${TIDYF}.d && " +
-				"$tidyVars $reTemplate${config.ClangBin}/clang-tidy $tidyFlags $$SRCF " +
-				"-- \"$${CLANGFLAGS[@]}\" && touch $$TIDYF'",
-			CommandDeps: []string{"${config.ClangBin}/clang-tidy", "$ccCmd"},
+			Command: "cp ${out}.dep ${out}.d && " +
+				"$tidyVars$reTemplate${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && " +
+				"touch $out",
+			CommandDeps: []string{"${config.ClangBin}/clang-tidy"},
 		},
 		&remoteexec.REParams{
 			Labels:               map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
 			ExecStrategy:         "${config.REClangTidyExecStrategy}",
-			Inputs:               []string{"$in"},
+			Inputs:               []string{"$in", "${out}.dep"},
 			EnvironmentVariables: []string{"TIDY_TIMEOUT"},
 			// Although clang-tidy has an option to "fix" source files, that feature is hardly useable
 			// under parallel compilation and RBE. So we assume no OutputFiles here.
@@ -230,7 +239,7 @@
 			// (1) New timestamps trigger clang and clang-tidy compilations again.
 			// (2) Changing source files caused concurrent clang or clang-tidy jobs to crash.
 			Platform: map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
-		}, []string{"ccCmd", "cFlags", "tidyFlags", "tidyVars"}, []string{})
+		}, []string{"cFlags", "tidyFlags", "tidyVars"}, []string{})
 
 	_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
 
@@ -449,12 +458,6 @@
 	}
 }
 
-func escapeSingleQuotes(s string) string {
-	// Replace single quotes to work when embedded in a single quoted string for bash.
-	// Relying on string concatenation of bash to get A'B from quoted 'A'\''B'.
-	return strings.Replace(s, `'`, `'\''`, -1)
-}
-
 // Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files
 func transformSourceToObj(ctx ModuleContext, subdir string, srcFiles, noTidySrcs android.Paths,
 	flags builderFlags, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
@@ -470,7 +473,7 @@
 		}
 		tidyTimeout := ctx.Config().Getenv("TIDY_TIMEOUT")
 		if len(tidyTimeout) > 0 {
-			tidyVars += "TIDY_TIMEOUT=" + tidyTimeout
+			tidyVars += "TIDY_TIMEOUT=" + tidyTimeout + " "
 		}
 	}
 	var coverageFiles android.Paths
@@ -674,24 +677,44 @@
 		//  Even with tidy, some src file could be skipped by noTidySrcsMap.
 		if tidy && !noTidySrcsMap[srcFile] {
 			tidyFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy")
+			tidyDepFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy.dep")
 			tidyFiles = append(tidyFiles, tidyFile)
 
+			ruleDep := clangTidyDep
 			rule := clangTidy
 			if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CLANG_TIDY") {
+				ruleDep = clangTidyDepRE
 				rule = clangTidyRE
 			}
 
+			sharedCFlags := shareFlags("cFlags", moduleFlags)
+			srcRelPath := srcFile.Rel()
+
+			// Add the .tidy.d rule
 			ctx.Build(pctx, android.BuildParams{
-				Rule:        rule,
-				Description: "clang-tidy " + srcFile.Rel(),
-				Output:      tidyFile,
+				Rule:        ruleDep,
+				Description: "clang-tidy-dep " + srcRelPath,
+				Output:      tidyDepFile,
 				Input:       srcFile,
 				Implicits:   cFlagsDeps,
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"ccCmd":     ccCmd,
-					"cFlags":    shareFlags("cFlags", escapeSingleQuotes(moduleToolingFlags)),
-					"tidyFlags": shareFlags("tidyFlags", escapeSingleQuotes(config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags))),
+					"ccCmd":    ccCmd,
+					"cFlags":   sharedCFlags,
+					"tidyFile": tidyFile.String(),
+				},
+			})
+			// Add the .tidy rule with order only dependency on the .tidy.d file
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        rule,
+				Description: "clang-tidy " + srcRelPath,
+				Output:      tidyFile,
+				Input:       srcFile,
+				Implicits:   cFlagsDeps,
+				OrderOnly:   append(android.Paths{}, tidyDepFile),
+				Args: map[string]string{
+					"cFlags":    sharedCFlags,
+					"tidyFlags": shareFlags("tidyFlags", config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags)),
 					"tidyVars":  tidyVars, // short and not shared
 				},
 			})