Write ninja file in parallel.

Bug: 335718784
Test: CI
Change-Id: Ie36d78478a60359b704767689846e76b1c2ba76d
diff --git a/android/util.go b/android/util.go
index de4ca4d..e21e66b 100644
--- a/android/util.go
+++ b/android/util.go
@@ -24,6 +24,8 @@
 	"sort"
 	"strings"
 	"sync"
+
+	"github.com/google/blueprint/proptools"
 )
 
 // CopyOf returns a new slice that has the same contents as s.
@@ -542,25 +544,9 @@
 	return root, suffix, ext
 }
 
-func shard[T ~[]E, E any](toShard T, shardSize int) []T {
-	if len(toShard) == 0 {
-		return nil
-	}
-
-	ret := make([]T, 0, (len(toShard)+shardSize-1)/shardSize)
-	for len(toShard) > shardSize {
-		ret = append(ret, toShard[0:shardSize])
-		toShard = toShard[shardSize:]
-	}
-	if len(toShard) > 0 {
-		ret = append(ret, toShard)
-	}
-	return ret
-}
-
 // ShardPaths takes a Paths, and returns a slice of Paths where each one has at most shardSize paths.
 func ShardPaths(paths Paths, shardSize int) []Paths {
-	return shard(paths, shardSize)
+	return proptools.ShardBySize(paths, shardSize)
 }
 
 // ShardString takes a string and returns a slice of strings where the length of each one is
@@ -583,7 +569,7 @@
 // ShardStrings takes a slice of strings, and returns a slice of slices of strings where each one has at most shardSize
 // elements.
 func ShardStrings(s []string, shardSize int) [][]string {
-	return shard(s, shardSize)
+	return proptools.ShardBySize(s, shardSize)
 }
 
 // CheckDuplicate checks if there are duplicates in given string list.
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 79584c6..9955b1f 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -15,7 +15,6 @@
 package build
 
 import (
-	"android/soong/ui/tracer"
 	"fmt"
 	"io/fs"
 	"os"
@@ -26,6 +25,8 @@
 	"sync/atomic"
 	"time"
 
+	"android/soong/ui/tracer"
+
 	"android/soong/bazel"
 	"android/soong/ui/metrics"
 	"android/soong/ui/metrics/metrics_proto"
@@ -270,7 +271,13 @@
 	} else if !exists {
 		// The tree is out of date for the current epoch, delete files used by bootstrap
 		// and force the primary builder to rerun.
-		os.Remove(config.SoongNinjaFile())
+		soongNinjaFile := config.SoongNinjaFile()
+		os.Remove(soongNinjaFile)
+		for _, file := range blueprint.GetNinjaShardFiles(soongNinjaFile) {
+			if ok, _ := fileExists(file); ok {
+				os.Remove(file)
+			}
+		}
 		for _, globFile := range bootstrapGlobFileList(config) {
 			os.Remove(globFile)
 		}
@@ -680,7 +687,13 @@
 
 	loadSoongBuildMetrics(ctx, config, beforeSoongTimestamp)
 
-	distGzipFile(ctx, config, config.SoongNinjaFile(), "soong")
+	soongNinjaFile := config.SoongNinjaFile()
+	distGzipFile(ctx, config, soongNinjaFile, "soong")
+	for _, file := range blueprint.GetNinjaShardFiles(soongNinjaFile) {
+		if ok, _ := fileExists(file); ok {
+			distGzipFile(ctx, config, file, "soong")
+		}
+	}
 	distFile(ctx, config, config.SoongVarsFile(), "soong")
 
 	if !config.SkipKati() {