Add tidy-soong, tidy-dir-path, module-tidy targets

* When WITH_TIDY=1, these targets allow quick check of C/C++
  source code with clang-tidy, without building C/C++ binaries.
* For each module with tidy rules, add a module-tidy target, e.g.,
  libart-tidy, libartd-tidy, bionic-benchmarks-tidy, libnativehelper-tidy, etc.
* Add a tidy-soong phony target that depends on all module-tidy targets.
* For each directory X/Y add a tidy-X-Y phony target that depends
  on all *-tidy targets in X/Y and tidy-X-Y-Z for all X/Y/Z directories,
  e.g., tidy-bionic, tidy-bionic-benchmarks, tidy-libnativehelper, etc.
* Only soong modules are collected for now.
  Tidy rules in .mk files will be collected later.
* Some comment lines reformatted by gofmt.

Test: WITH_TIDY=1 make <some_module>-tidy tidy-<some_directory>
Test: WITH_TIDY=1 make tidy-soong
Bug: 199169329
Change-Id: I45aef3875f70288a8e070761e5f083dbbdfa6e94
diff --git a/android/module.go b/android/module.go
index cc03418..8334cc6 100644
--- a/android/module.go
+++ b/android/module.go
@@ -405,6 +405,7 @@
 	PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec
 
 	CheckbuildFile(srcPath Path)
+	TidyFile(srcPath Path)
 
 	InstallInData() bool
 	InstallInTestcases() bool
@@ -987,12 +988,12 @@
 	DeviceSupported = deviceSupported | deviceDefault
 
 	// By default, _only_ device variant is built. Device variant can be disabled with `device_supported: false`
-    // Host and HostCross are disabled by default and can be enabled with `host_supported: true`
+	// Host and HostCross are disabled by default and can be enabled with `host_supported: true`
 	HostAndDeviceSupported = hostSupported | hostCrossSupported | deviceSupported | deviceDefault
 
 	// Host, HostCross, and Device are built by default.
-    // Building Device can be disabled with `device_supported: false`
-    // Building Host and HostCross can be disabled with `host_supported: false`
+	// Building Device can be disabled with `device_supported: false`
+	// Building Host and HostCross can be disabled with `host_supported: false`
 	HostAndDeviceDefault = hostSupported | hostCrossSupported | hostDefault |
 		deviceSupported | deviceDefault
 
@@ -1190,6 +1191,7 @@
 	installFiles         InstallPaths
 	installFilesDepSet   *installPathsDepSet
 	checkbuildFiles      Paths
+	tidyFiles            Paths
 	packagingSpecs       []PackagingSpec
 	packagingSpecsDepSet *packagingSpecsDepSet
 	noticeFiles          Paths
@@ -1202,6 +1204,7 @@
 	// Only set on the final variant of each module
 	installTarget    WritablePath
 	checkbuildTarget WritablePath
+	tidyTarget       WritablePath
 	blueprintDir     string
 
 	hooks hooks
@@ -1727,10 +1730,12 @@
 func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) {
 	var allInstalledFiles InstallPaths
 	var allCheckbuildFiles Paths
+	var allTidyFiles Paths
 	ctx.VisitAllModuleVariants(func(module Module) {
 		a := module.base()
 		allInstalledFiles = append(allInstalledFiles, a.installFiles...)
 		allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
+		allTidyFiles = append(allTidyFiles, a.tidyFiles...)
 	})
 
 	var deps Paths
@@ -1754,6 +1759,13 @@
 		deps = append(deps, m.checkbuildTarget)
 	}
 
+	if len(allTidyFiles) > 0 {
+		name := namespacePrefix + ctx.ModuleName() + "-tidy"
+		ctx.Phony(name, allTidyFiles...)
+		m.tidyTarget = PathForPhony(ctx, name)
+		deps = append(deps, m.tidyTarget)
+	}
+
 	if len(deps) > 0 {
 		suffix := ""
 		if ctx.Config().KatiEnabled() {
@@ -1962,6 +1974,7 @@
 
 		m.installFiles = append(m.installFiles, ctx.installFiles...)
 		m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
+		m.tidyFiles = append(m.tidyFiles, ctx.tidyFiles...)
 		m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...)
 		for k, v := range ctx.phonies {
 			m.phonies[k] = append(m.phonies[k], v...)
@@ -2160,6 +2173,7 @@
 	packagingSpecs  []PackagingSpec
 	installFiles    InstallPaths
 	checkbuildFiles Paths
+	tidyFiles       Paths
 	module          Module
 	phonies         map[string]Paths
 
@@ -2892,6 +2906,10 @@
 	m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
 }
 
+func (m *moduleContext) TidyFile(srcPath Path) {
+	m.tidyFiles = append(m.tidyFiles, srcPath)
+}
+
 func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext {
 	return m.bp
 }
@@ -3150,19 +3168,49 @@
 
 type buildTargetSingleton struct{}
 
+func addAncestors(ctx SingletonContext, dirMap map[string]Paths, mmName func(string) string) []string {
+	// Ensure ancestor directories are in dirMap
+	// Make directories build their direct subdirectories
+	dirs := SortedStringKeys(dirMap)
+	for _, dir := range dirs {
+		dir := parentDir(dir)
+		for dir != "." && dir != "/" {
+			if _, exists := dirMap[dir]; exists {
+				break
+			}
+			dirMap[dir] = nil
+			dir = parentDir(dir)
+		}
+	}
+	dirs = SortedStringKeys(dirMap)
+	for _, dir := range dirs {
+		p := parentDir(dir)
+		if p != "." && p != "/" {
+			dirMap[p] = append(dirMap[p], PathForPhony(ctx, mmName(dir)))
+		}
+	}
+	return SortedStringKeys(dirMap)
+}
+
 func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) {
 	var checkbuildDeps Paths
+	var tidyDeps Paths
 
 	mmTarget := func(dir string) string {
 		return "MODULES-IN-" + strings.Replace(filepath.Clean(dir), "/", "-", -1)
 	}
+	mmTidyTarget := func(dir string) string {
+		return "tidy-" + strings.Replace(filepath.Clean(dir), "/", "-", -1)
+	}
 
 	modulesInDir := make(map[string]Paths)
+	tidyModulesInDir := make(map[string]Paths)
 
 	ctx.VisitAllModules(func(module Module) {
 		blueprintDir := module.base().blueprintDir
 		installTarget := module.base().installTarget
 		checkbuildTarget := module.base().checkbuildTarget
+		tidyTarget := module.base().tidyTarget
 
 		if checkbuildTarget != nil {
 			checkbuildDeps = append(checkbuildDeps, checkbuildTarget)
@@ -3172,6 +3220,16 @@
 		if installTarget != nil {
 			modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], installTarget)
 		}
+
+		if tidyTarget != nil {
+			tidyDeps = append(tidyDeps, tidyTarget)
+			// tidyTarget is in modulesInDir so it will be built with "mm".
+			modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], tidyTarget)
+			// tidyModulesInDir contains tidyTarget but not checkbuildTarget
+			// or installTarget, so tidy targets in a directory can be built
+			// without other checkbuild or install targets.
+			tidyModulesInDir[blueprintDir] = append(tidyModulesInDir[blueprintDir], tidyTarget)
+		}
 	})
 
 	suffix := ""
@@ -3182,31 +3240,24 @@
 	// Create a top-level checkbuild target that depends on all modules
 	ctx.Phony("checkbuild"+suffix, checkbuildDeps...)
 
+	// Create a top-level tidy target that depends on all modules
+	ctx.Phony("tidy"+suffix, tidyDeps...)
+
+	dirs := addAncestors(ctx, tidyModulesInDir, mmTidyTarget)
+
+	// Kati does not generate tidy-* phony targets yet.
+	// Create a tidy-<directory> target that depends on all subdirectories
+	// and modules in the directory.
+	for _, dir := range dirs {
+		ctx.Phony(mmTidyTarget(dir), tidyModulesInDir[dir]...)
+	}
+
 	// Make will generate the MODULES-IN-* targets
 	if ctx.Config().KatiEnabled() {
 		return
 	}
 
-	// Ensure ancestor directories are in modulesInDir
-	dirs := SortedStringKeys(modulesInDir)
-	for _, dir := range dirs {
-		dir := parentDir(dir)
-		for dir != "." && dir != "/" {
-			if _, exists := modulesInDir[dir]; exists {
-				break
-			}
-			modulesInDir[dir] = nil
-			dir = parentDir(dir)
-		}
-	}
-
-	// Make directories build their direct subdirectories
-	for _, dir := range dirs {
-		p := parentDir(dir)
-		if p != "." && p != "/" {
-			modulesInDir[p] = append(modulesInDir[p], PathForPhony(ctx, mmTarget(dir)))
-		}
-	}
+	dirs = addAncestors(ctx, modulesInDir, mmTarget)
 
 	// Create a MODULES-IN-<directory> target that depends on all modules in a directory, and
 	// depends on the MODULES-IN-* targets of all of its subdirectories that contain Android.bp
diff --git a/cc/builder.go b/cc/builder.go
index 748377b..a8219d7 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -633,6 +633,7 @@
 				rule = clangTidyRE
 			}
 
+			ctx.TidyFile(tidyFile)
 			ctx.Build(pctx, android.BuildParams{
 				Rule:        rule,
 				Description: "clang-tidy " + srcFile.Rel(),