Add logic to get file system dependencies

These deps will be categorized to deps, multilib,... later.

This CL also merges product_config_to_bp.go to filesystem_creator.go

Test: CI and manually compared the dependencies with what's
      in the cf system image bp
Bug: 368364861
Change-Id: Icebcc806a8c9d596d765e9887aa3a8ae477c22a0
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index 7db70e8..cd63dc2 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -15,13 +15,18 @@
 package fsgen
 
 import (
-	"android/soong/android"
-	"android/soong/filesystem"
 	"crypto/sha256"
 	"fmt"
+	"slices"
 	"strconv"
+	"strings"
+	"sync"
+
+	"android/soong/android"
+	"android/soong/filesystem"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/parser"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -33,6 +38,56 @@
 
 func registerBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("soong_filesystem_creator", filesystemCreatorFactory)
+	ctx.PreDepsMutators(RegisterCollectFileSystemDepsMutators)
+}
+
+func RegisterCollectFileSystemDepsMutators(ctx android.RegisterMutatorsContext) {
+	ctx.BottomUp("fs_collect_deps", collectDepsMutator).MutatesGlobalState()
+}
+
+var collectFsDepsOnceKey = android.NewOnceKey("CollectFsDeps")
+var depCandidatesOnceKey = android.NewOnceKey("DepCandidates")
+
+func collectDepsMutator(mctx android.BottomUpMutatorContext) {
+	// These additional deps are added according to the cuttlefish system image bp.
+	fsDeps := mctx.Config().Once(collectFsDepsOnceKey, func() interface{} {
+		deps := []string{
+			"android_vintf_manifest",
+			"com.android.apex.cts.shim.v1_prebuilt",
+			"dex_bootjars",
+			"framework_compatibility_matrix.device.xml",
+			"idc_data",
+			"init.environ.rc-soong",
+			"keychars_data",
+			"keylayout_data",
+			"libclang_rt.asan",
+			"libcompiler_rt",
+			"libdmabufheap",
+			"libgsi",
+			"llndk.libraries.txt",
+			"logpersist.start",
+			"preloaded-classes",
+			"public.libraries.android.txt",
+			"update_engine_sideload",
+		}
+		return &deps
+	}).(*[]string)
+
+	depCandidates := mctx.Config().Once(depCandidatesOnceKey, func() interface{} {
+		partitionVars := mctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+		candidates := slices.Concat(partitionVars.ProductPackages, partitionVars.ProductPackagesDebug)
+		return &candidates
+	}).(*[]string)
+
+	m := mctx.Module()
+	mutex := &sync.Mutex{}
+	if slices.Contains(*depCandidates, m.Name()) {
+		if installInSystem(mctx, m) {
+			mutex.Lock()
+			*fsDeps = append(*fsDeps, m.Name())
+			mutex.Unlock()
+		}
+	}
 }
 
 type filesystemCreatorProps struct {
@@ -228,6 +283,11 @@
 	}
 	f.HideFromMake()
 
+	content := generateBpContent(ctx, "system")
+	generatedBp := android.PathForOutput(ctx, "soong_generated_product_config.bp")
+	android.WriteFileRule(ctx, generatedBp, content)
+	ctx.Phony("product_config_to_bp", generatedBp)
+
 	var diffTestFiles []android.Path
 	for _, partitionType := range f.properties.Generated_partition_types {
 		diffTestFiles = append(diffTestFiles, f.createDiffTest(ctx, partitionType))
@@ -237,3 +297,42 @@
 	}
 	ctx.Phony("soong_generated_filesystem_tests", diffTestFiles...)
 }
+
+func installInSystem(ctx android.BottomUpMutatorContext, m android.Module) bool {
+	return m.PartitionTag(ctx.DeviceConfig()) == "system" && !m.InstallInData() &&
+		!m.InstallInTestcases() && !m.InstallInSanitizerDir() && !m.InstallInVendorRamdisk() &&
+		!m.InstallInDebugRamdisk() && !m.InstallInRecovery() && !m.InstallInOdm() &&
+		!m.InstallInVendor()
+}
+
+// TODO: assemble baseProps and fsProps here
+func generateBpContent(ctx android.EarlyModuleContext, partitionType string) string {
+	// Currently only system partition is supported
+	if partitionType != "system" {
+		return ""
+	}
+
+	deps := ctx.Config().Get(collectFsDepsOnceKey).(*[]string)
+	depProps := &android.PackagingProperties{
+		Deps: android.NewSimpleConfigurable(android.SortedUniqueStrings(*deps)),
+	}
+
+	result, err := proptools.RepackProperties([]interface{}{depProps})
+	if err != nil {
+		ctx.ModuleErrorf(err.Error())
+	}
+
+	file := &parser.File{
+		Defs: []parser.Definition{
+			&parser.Module{
+				Type: "module",
+				Map:  *result,
+			},
+		},
+	}
+	bytes, err := parser.Print(file)
+	if err != nil {
+		ctx.ModuleErrorf(err.Error())
+	}
+	return strings.TrimSpace(string(bytes))
+}