From 1f086e2f0d36dfa17d75fca37060d46e95ef193b Mon Sep 17 00:00:00 2001 From: Inseob Kim Date: Thu, 9 May 2019 13:29:15 +0900 Subject: [PATCH] Generate VNDK snapshot with Soong except configs This is the first commit to generate VNDK snapshot with Soong: .so files, some txt files, and notice files are captured with Soong. As ld.config.txt is currently in Android.mk and will be deprecated soon, configs files (and zipping all of artifacts) are still handled with Makefile. Bug: 131564934 Test: 1) DIST_DIR=out/dist development/vndk/snapshot/build.sh Test: 2) try installing vndk snapshot with: development/vndk/snapshot/update.py Change-Id: I8629e1e25bfc461fd495565bb4872c9af176cf92 --- android/config.go | 24 +++ android/util.go | 25 ++++ android/variable.go | 2 + cc/cc.go | 6 +- cc/cc_test.go | 49 +++++- cc/vndk.go | 355 +++++++++++++++++++++++++++++++++++++------- 6 files changed, 402 insertions(+), 59 deletions(-) diff --git a/android/config.go b/android/config.go index 15e2ad4ca..a18feb6dd 100644 --- a/android/config.go +++ b/android/config.go @@ -288,6 +288,10 @@ func TestArchConfig(buildDir string, env map[string]string) Config { config.BuildOsVariant = config.Targets[BuildOs][0].String() config.BuildOsCommonVariant = getCommonTargets(config.Targets[BuildOs])[0].String() + config.TestProductVariables.DeviceArch = proptools.StringPtr("arm64") + config.TestProductVariables.DeviceArchVariant = proptools.StringPtr("armv8-a") + config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm") + config.TestProductVariables.DeviceSecondaryArchVariant = proptools.StringPtr("armv7-a-neon") return testConfig } @@ -1100,3 +1104,23 @@ func (c *config) ProductPrivateSepolicyDirs() []string { func (c *config) ProductCompatibleProperty() bool { return Bool(c.productVariables.ProductCompatibleProperty) } + +func (c *deviceConfig) BoardVndkRuntimeDisable() bool { + return Bool(c.config.productVariables.BoardVndkRuntimeDisable) +} + +func (c *deviceConfig) DeviceArch() string { + return String(c.config.productVariables.DeviceArch) +} + +func (c *deviceConfig) DeviceArchVariant() string { + return String(c.config.productVariables.DeviceArchVariant) +} + +func (c *deviceConfig) DeviceSecondaryArch() string { + return String(c.config.productVariables.DeviceSecondaryArch) +} + +func (c *deviceConfig) DeviceSecondaryArchVariant() string { + return String(c.config.productVariables.DeviceSecondaryArchVariant) +} diff --git a/android/util.go b/android/util.go index f9dce6fe7..f7a3437c3 100644 --- a/android/util.go +++ b/android/util.go @@ -52,6 +52,31 @@ func JoinWithPrefix(strs []string, prefix string) string { return string(ret) } +func JoinWithSuffix(strs []string, suffix string, separator string) string { + if len(strs) == 0 { + return "" + } + + if len(strs) == 1 { + return strs[0] + suffix + } + + n := len(" ") * (len(strs) - 1) + for _, s := range strs { + n += len(suffix) + len(s) + } + + ret := make([]byte, 0, n) + for i, s := range strs { + if i != 0 { + ret = append(ret, separator...) + } + ret = append(ret, s...) + ret = append(ret, suffix...) + } + return string(ret) +} + func sortedKeys(m map[string][]string) []string { s := make([]string, 0, len(m)) for k := range m { diff --git a/android/variable.go b/android/variable.go index c5006716f..ff3ebaf00 100644 --- a/android/variable.go +++ b/android/variable.go @@ -279,6 +279,8 @@ type productVariables struct { BoardPlatPrivateSepolicyDirs []string `json:",omitempty"` BoardSepolicyM4Defs []string `json:",omitempty"` + BoardVndkRuntimeDisable *bool `json:",omitempty"` + VendorVars map[string]map[string]string `json:",omitempty"` Ndk_abis *bool `json:",omitempty"` diff --git a/cc/cc.go b/cc/cc.go index eaf41d887..a3b9a92d9 100644 --- a/cc/cc.go +++ b/cc/cc.go @@ -1011,7 +1011,7 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { } } - if c.installer != nil && !c.Properties.PreventInstall && c.IsForPlatform() && c.outputFile.Valid() { + if c.installable() { c.installer.install(ctx, c.outputFile.Path()) if ctx.Failed() { return @@ -1968,6 +1968,10 @@ func (c *Module) IsInstallableToApex() bool { return false } +func (c *Module) installable() bool { + return c.installer != nil && !c.Properties.PreventInstall && c.IsForPlatform() && c.outputFile.Valid() +} + func (c *Module) imageVariation() string { variation := "core" if c.useVndk() { diff --git a/cc/cc_test.go b/cc/cc_test.go index f3d5e60d5..36d8aa416 100644 --- a/cc/cc_test.go +++ b/cc/cc_test.go @@ -20,6 +20,7 @@ import ( "fmt" "io/ioutil" "os" + "path/filepath" "reflect" "sort" "strings" @@ -75,6 +76,7 @@ func createTestContext(t *testing.T, config android.Config, bp string, os androi ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { ctx.TopDown("double_loadable", checkDoubleLoadableLibraries).Parallel() }) + ctx.RegisterSingletonType("vndk-snapshot", android.SingletonFactoryAdaptor(VndkSnapshotSingleton)) ctx.Register() // add some modules that are required by the compiler and/or linker @@ -286,8 +288,28 @@ func checkVndkModule(t *testing.T, ctx *android.TestContext, name, subDir string } } +func checkVndkSnapshot(t *testing.T, ctx *android.TestContext, name, subDir, variant string) { + vndkSnapshot := ctx.SingletonForTests("vndk-snapshot") + + snapshotPath := filepath.Join(subDir, name+".so") + mod := ctx.ModuleForTests(name, variant).Module().(*Module) + if !mod.outputFile.Valid() { + t.Errorf("%q must have output\n", name) + return + } + + out := vndkSnapshot.Output(snapshotPath) + if out.Input != mod.outputFile.Path() { + t.Errorf("The input of VNDK snapshot must be %q, but %q", out.Input.String(), mod.outputFile.String()) + } +} + func TestVndk(t *testing.T) { - ctx := testCc(t, ` + config := android.TestArchConfig(buildDir, nil) + config.TestProductVariables.DeviceVndkVersion = StringPtr("current") + config.TestProductVariables.Platform_vndk_version = StringPtr("VER") + + ctx := testCcWithConfig(t, ` cc_library { name: "libvndk", vendor_available: true, @@ -325,12 +347,35 @@ func TestVndk(t *testing.T) { }, nocrt: true, } - `) + `, config) checkVndkModule(t, ctx, "libvndk", "vndk-VER", false, "") checkVndkModule(t, ctx, "libvndk_private", "vndk-VER", false, "") checkVndkModule(t, ctx, "libvndk_sp", "vndk-sp-VER", true, "") checkVndkModule(t, ctx, "libvndk_sp_private", "vndk-sp-VER", true, "") + + // Check VNDK snapshot output. + + snapshotDir := "vndk-snapshot" + snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64") + + vndkLibPath := filepath.Join(snapshotVariantPath, fmt.Sprintf("arch-%s-%s", + "arm64", "armv8-a")) + vndkLib2ndPath := filepath.Join(snapshotVariantPath, fmt.Sprintf("arch-%s-%s", + "arm", "armv7-a-neon")) + + vndkCoreLibPath := filepath.Join(vndkLibPath, "shared", "vndk-core") + vndkSpLibPath := filepath.Join(vndkLibPath, "shared", "vndk-sp") + vndkCoreLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-core") + vndkSpLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-sp") + + variant := "android_arm64_armv8-a_vendor_shared" + variant2nd := "android_arm_armv7-a-neon_vendor_shared" + + checkVndkSnapshot(t, ctx, "libvndk", vndkCoreLibPath, variant) + checkVndkSnapshot(t, ctx, "libvndk", vndkCoreLib2ndPath, variant2nd) + checkVndkSnapshot(t, ctx, "libvndk_sp", vndkSpLibPath, variant) + checkVndkSnapshot(t, ctx, "libvndk_sp", vndkSpLib2ndPath, variant2nd) } func TestVndkDepError(t *testing.T) { diff --git a/cc/vndk.go b/cc/vndk.go index 7859fa21f..a1d67af10 100644 --- a/cc/vndk.go +++ b/cc/vndk.go @@ -16,6 +16,8 @@ package cc import ( "errors" + "fmt" + "path/filepath" "sort" "strings" "sync" @@ -197,9 +199,20 @@ var ( llndkLibrariesKey = android.NewOnceKey("llndkLibrarires") vndkPrivateLibrariesKey = android.NewOnceKey("vndkPrivateLibrarires") vndkUsingCoreVariantLibrariesKey = android.NewOnceKey("vndkUsingCoreVariantLibrarires") + modulePathsKey = android.NewOnceKey("modulePaths") + vndkSnapshotOutputsKey = android.NewOnceKey("vndkSnapshotOutputs") vndkLibrariesLock sync.Mutex ) +type vndkSnapshotOutputPaths struct { + configs android.Paths + notices android.Paths + vndkCoreLibs android.Paths + vndkCoreLibs2nd android.Paths + vndkSpLibs android.Paths + vndkSpLibs2nd android.Paths +} + func vndkCoreLibraries(config android.Config) *[]string { return config.Once(vndkCoreLibrariesKey, func() interface{} { return &[]string{} @@ -230,66 +243,296 @@ func vndkUsingCoreVariantLibraries(config android.Config) *[]string { }).(*[]string) } -// gather list of vndk-core, vndk-sp, and ll-ndk libs -func VndkMutator(mctx android.BottomUpMutatorContext) { - if m, ok := mctx.Module().(*Module); ok && m.Enabled() { - if lib, ok := m.linker.(*llndkStubDecorator); ok { - vndkLibrariesLock.Lock() - defer vndkLibrariesLock.Unlock() +func modulePaths(config android.Config) map[string]string { + return config.Once(modulePathsKey, func() interface{} { + return make(map[string]string) + }).(map[string]string) +} - llndkLibraries := llndkLibraries(mctx.Config()) - vndkPrivateLibraries := vndkPrivateLibraries(mctx.Config()) +func vndkSnapshotOutputs(config android.Config) *vndkSnapshotOutputPaths { + return config.Once(vndkSnapshotOutputsKey, func() interface{} { + return &vndkSnapshotOutputPaths{} + }).(*vndkSnapshotOutputPaths) +} - name := strings.TrimSuffix(m.Name(), llndkLibrarySuffix) - if !inList(name, *llndkLibraries) { - *llndkLibraries = append(*llndkLibraries, name) - sort.Strings(*llndkLibraries) - } - if !Bool(lib.Properties.Vendor_available) { - if !inList(name, *vndkPrivateLibraries) { - *vndkPrivateLibraries = append(*vndkPrivateLibraries, name) - sort.Strings(*vndkPrivateLibraries) - } - } - } else { - lib, is_lib := m.linker.(*libraryDecorator) - prebuilt_lib, is_prebuilt_lib := m.linker.(*prebuiltLibraryLinker) - if (is_lib && lib.shared()) || (is_prebuilt_lib && prebuilt_lib.shared()) { - name := strings.TrimPrefix(m.Name(), "prebuilt_") - if m.vndkdep.isVndk() && !m.vndkdep.isVndkExt() { - vndkLibrariesLock.Lock() - defer vndkLibrariesLock.Unlock() +func processLlndkLibrary(mctx android.BottomUpMutatorContext, m *Module) { + lib := m.linker.(*llndkStubDecorator) + name := strings.TrimSuffix(m.Name(), llndkLibrarySuffix) - vndkUsingCoreVariantLibraries := vndkUsingCoreVariantLibraries(mctx.Config()) - vndkSpLibraries := vndkSpLibraries(mctx.Config()) - vndkCoreLibraries := vndkCoreLibraries(mctx.Config()) - vndkPrivateLibraries := vndkPrivateLibraries(mctx.Config()) + vndkLibrariesLock.Lock() + defer vndkLibrariesLock.Unlock() - if mctx.DeviceConfig().VndkUseCoreVariant() && !inList(name, config.VndkMustUseVendorVariantList) { - if !inList(name, *vndkUsingCoreVariantLibraries) { - *vndkUsingCoreVariantLibraries = append(*vndkUsingCoreVariantLibraries, name) - sort.Strings(*vndkUsingCoreVariantLibraries) - } - } - if m.vndkdep.isVndkSp() { - if !inList(name, *vndkSpLibraries) { - *vndkSpLibraries = append(*vndkSpLibraries, name) - sort.Strings(*vndkSpLibraries) - } - } else { - if !inList(name, *vndkCoreLibraries) { - *vndkCoreLibraries = append(*vndkCoreLibraries, name) - sort.Strings(*vndkCoreLibraries) - } - } - if !Bool(m.VendorProperties.Vendor_available) { - if !inList(name, *vndkPrivateLibraries) { - *vndkPrivateLibraries = append(*vndkPrivateLibraries, name) - sort.Strings(*vndkPrivateLibraries) - } - } - } - } + llndkLibraries := llndkLibraries(mctx.Config()) + if !inList(name, *llndkLibraries) { + *llndkLibraries = append(*llndkLibraries, name) + sort.Strings(*llndkLibraries) + } + if !Bool(lib.Properties.Vendor_available) { + vndkPrivateLibraries := vndkPrivateLibraries(mctx.Config()) + if !inList(name, *vndkPrivateLibraries) { + *vndkPrivateLibraries = append(*vndkPrivateLibraries, name) + sort.Strings(*vndkPrivateLibraries) } } } + +func processVndkLibrary(mctx android.BottomUpMutatorContext, m *Module) { + name := strings.TrimPrefix(m.Name(), "prebuilt_") + + vndkLibrariesLock.Lock() + defer vndkLibrariesLock.Unlock() + + modulePaths := modulePaths(mctx.Config()) + if mctx.DeviceConfig().VndkUseCoreVariant() && !inList(name, config.VndkMustUseVendorVariantList) { + vndkUsingCoreVariantLibraries := vndkUsingCoreVariantLibraries(mctx.Config()) + if !inList(name, *vndkUsingCoreVariantLibraries) { + *vndkUsingCoreVariantLibraries = append(*vndkUsingCoreVariantLibraries, name) + sort.Strings(*vndkUsingCoreVariantLibraries) + } + } + if m.vndkdep.isVndkSp() { + vndkSpLibraries := vndkSpLibraries(mctx.Config()) + if !inList(name, *vndkSpLibraries) { + *vndkSpLibraries = append(*vndkSpLibraries, name) + sort.Strings(*vndkSpLibraries) + modulePaths[name] = mctx.ModuleDir() + } + } else { + vndkCoreLibraries := vndkCoreLibraries(mctx.Config()) + if !inList(name, *vndkCoreLibraries) { + *vndkCoreLibraries = append(*vndkCoreLibraries, name) + sort.Strings(*vndkCoreLibraries) + modulePaths[name] = mctx.ModuleDir() + } + } + if !Bool(m.VendorProperties.Vendor_available) { + vndkPrivateLibraries := vndkPrivateLibraries(mctx.Config()) + if !inList(name, *vndkPrivateLibraries) { + *vndkPrivateLibraries = append(*vndkPrivateLibraries, name) + sort.Strings(*vndkPrivateLibraries) + } + } +} + +// gather list of vndk-core, vndk-sp, and ll-ndk libs +func VndkMutator(mctx android.BottomUpMutatorContext) { + m, ok := mctx.Module().(*Module) + if !ok { + return + } + + if !m.Enabled() { + return + } + + if _, ok := m.linker.(*llndkStubDecorator); ok { + processLlndkLibrary(mctx, m) + return + } + + lib, is_lib := m.linker.(*libraryDecorator) + prebuilt_lib, is_prebuilt_lib := m.linker.(*prebuiltLibraryLinker) + + if (is_lib && lib.shared()) || (is_prebuilt_lib && prebuilt_lib.shared()) { + if m.vndkdep.isVndk() && !m.vndkdep.isVndkExt() { + processVndkLibrary(mctx, m) + return + } + } +} + +func init() { + android.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton) + android.RegisterMakeVarsProvider(pctx, func(ctx android.MakeVarsContext) { + outputs := vndkSnapshotOutputs(ctx.Config()) + + ctx.Strict("SOONG_VNDK_SNAPSHOT_CONFIGS", strings.Join(outputs.configs.Strings(), " ")) + ctx.Strict("SOONG_VNDK_SNAPSHOT_NOTICES", strings.Join(outputs.notices.Strings(), " ")) + ctx.Strict("SOONG_VNDK_SNAPSHOT_CORE_LIBS", strings.Join(outputs.vndkCoreLibs.Strings(), " ")) + ctx.Strict("SOONG_VNDK_SNAPSHOT_SP_LIBS", strings.Join(outputs.vndkSpLibs.Strings(), " ")) + ctx.Strict("SOONG_VNDK_SNAPSHOT_CORE_LIBS_2ND", strings.Join(outputs.vndkCoreLibs2nd.Strings(), " ")) + ctx.Strict("SOONG_VNDK_SNAPSHOT_SP_LIBS_2ND", strings.Join(outputs.vndkSpLibs2nd.Strings(), " ")) + }) +} + +func VndkSnapshotSingleton() android.Singleton { + return &vndkSnapshotSingleton{} +} + +type vndkSnapshotSingleton struct{} + +func installVndkSnapshotLib(ctx android.SingletonContext, name string, module *Module, dir string) android.Path { + if !module.outputFile.Valid() { + panic(fmt.Errorf("module %s has no outputFile\n", name)) + } + + out := android.PathForOutput(ctx, dir, name+".so") + + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Input: module.outputFile.Path(), + Output: out, + Description: "vndk snapshot " + dir + "/" + name + ".so", + Args: map[string]string{ + "cpFlags": "-f -L", + }, + }) + + return out +} + +func (c *vndkSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { + // BOARD_VNDK_VERSION must be set to 'current' in order to generate a VNDK snapshot. + if ctx.DeviceConfig().VndkVersion() != "current" { + return + } + + if ctx.DeviceConfig().PlatformVndkVersion() == "" { + return + } + + if ctx.DeviceConfig().BoardVndkRuntimeDisable() { + return + } + + outputs := vndkSnapshotOutputs(ctx.Config()) + + snapshotDir := "vndk-snapshot" + + var vndkLibPath, vndkLib2ndPath string + + snapshotVariantPath := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch()) + if ctx.DeviceConfig().BinderBitness() == "32" { + vndkLibPath = filepath.Join(snapshotVariantPath, "binder32", fmt.Sprintf( + "arch-%s-%s", ctx.DeviceConfig().DeviceArch(), ctx.DeviceConfig().DeviceArchVariant())) + vndkLib2ndPath = filepath.Join(snapshotVariantPath, "binder32", fmt.Sprintf( + "arch-%s-%s", ctx.DeviceConfig().DeviceSecondaryArch(), ctx.DeviceConfig().DeviceSecondaryArchVariant())) + } else { + vndkLibPath = filepath.Join(snapshotVariantPath, fmt.Sprintf( + "arch-%s-%s", ctx.DeviceConfig().DeviceArch(), ctx.DeviceConfig().DeviceArchVariant())) + vndkLib2ndPath = filepath.Join(snapshotVariantPath, fmt.Sprintf( + "arch-%s-%s", ctx.DeviceConfig().DeviceSecondaryArch(), ctx.DeviceConfig().DeviceSecondaryArchVariant())) + } + + vndkCoreLibPath := filepath.Join(vndkLibPath, "shared", "vndk-core") + vndkSpLibPath := filepath.Join(vndkLibPath, "shared", "vndk-sp") + vndkCoreLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-core") + vndkSpLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-sp") + noticePath := filepath.Join(snapshotVariantPath, "NOTICE_FILES") + noticeBuilt := make(map[string]bool) + + tryBuildNotice := func(m *Module) { + name := ctx.ModuleName(m) + + if _, ok := noticeBuilt[name]; ok { + return + } + + noticeBuilt[name] = true + + if m.NoticeFile().Valid() { + out := android.PathForOutput(ctx, noticePath, name+".so.txt") + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Input: m.NoticeFile().Path(), + Output: out, + Description: "vndk snapshot notice " + name + ".so.txt", + Args: map[string]string{ + "cpFlags": "-f -L", + }, + }) + outputs.notices = append(outputs.notices, out) + } + } + + vndkCoreLibraries := vndkCoreLibraries(ctx.Config()) + vndkSpLibraries := vndkSpLibraries(ctx.Config()) + vndkPrivateLibraries := vndkPrivateLibraries(ctx.Config()) + + ctx.VisitAllModules(func(module android.Module) { + m, ok := module.(*Module) + if !ok || !m.Enabled() || !m.useVndk() || !m.installable() { + return + } + + lib, is_lib := m.linker.(*libraryDecorator) + prebuilt_lib, is_prebuilt_lib := m.linker.(*prebuiltLibraryLinker) + + if !(is_lib && lib.shared()) && !(is_prebuilt_lib && prebuilt_lib.shared()) { + return + } + + is_2nd := m.Target().Arch.ArchType != ctx.Config().DevicePrimaryArchType() + + name := ctx.ModuleName(module) + + if inList(name, *vndkCoreLibraries) { + if is_2nd { + out := installVndkSnapshotLib(ctx, name, m, vndkCoreLib2ndPath) + outputs.vndkCoreLibs2nd = append(outputs.vndkCoreLibs2nd, out) + } else { + out := installVndkSnapshotLib(ctx, name, m, vndkCoreLibPath) + outputs.vndkCoreLibs = append(outputs.vndkCoreLibs, out) + } + tryBuildNotice(m) + } else if inList(name, *vndkSpLibraries) { + if is_2nd { + out := installVndkSnapshotLib(ctx, name, m, vndkSpLib2ndPath) + outputs.vndkSpLibs2nd = append(outputs.vndkSpLibs2nd, out) + } else { + out := installVndkSnapshotLib(ctx, name, m, vndkSpLibPath) + outputs.vndkSpLibs = append(outputs.vndkSpLibs, out) + } + tryBuildNotice(m) + } + }) + + configsPath := filepath.Join(snapshotVariantPath, "configs") + vndkCoreTxt := android.PathForOutput(ctx, configsPath, "vndkcore.libraries.txt") + vndkPrivateTxt := android.PathForOutput(ctx, configsPath, "vndkprivate.libraries.txt") + modulePathTxt := android.PathForOutput(ctx, configsPath, "module_paths.txt") + + ctx.Build(pctx, android.BuildParams{ + Rule: android.WriteFile, + Output: vndkCoreTxt, + Description: "vndk snapshot vndkcore.libraries.txt", + Args: map[string]string{ + "content": android.JoinWithSuffix(*vndkCoreLibraries, ".so", "\\n"), + }, + }) + outputs.configs = append(outputs.configs, vndkCoreTxt) + + ctx.Build(pctx, android.BuildParams{ + Rule: android.WriteFile, + Output: vndkPrivateTxt, + Description: "vndk snapshot vndkprivate.libraries.txt", + Args: map[string]string{ + "content": android.JoinWithSuffix(*vndkPrivateLibraries, ".so", "\\n"), + }, + }) + outputs.configs = append(outputs.configs, vndkPrivateTxt) + + var modulePathTxtBuilder strings.Builder + + first := true + for lib, dir := range modulePaths(ctx.Config()) { + if first { + first = false + } else { + modulePathTxtBuilder.WriteString("\\n") + } + modulePathTxtBuilder.WriteString(lib) + modulePathTxtBuilder.WriteString(".so ") + modulePathTxtBuilder.WriteString(dir) + } + + ctx.Build(pctx, android.BuildParams{ + Rule: android.WriteFile, + Output: modulePathTxt, + Description: "vndk snapshot module_paths.txt", + Args: map[string]string{ + "content": modulePathTxtBuilder.String(), + }, + }) + outputs.configs = append(outputs.configs, modulePathTxt) +}