Merge "Wrap blueprint_go_binary and bootstrap_go_package into android.Modules" into main
diff --git a/android/androidmk.go b/android/androidmk.go
index 5fb0cd1..cac2cfe 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -34,7 +34,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 )
@@ -815,8 +814,6 @@
 		switch x := mod.(type) {
 		case AndroidMkDataProvider:
 			err = translateAndroidModule(ctx, w, moduleInfoJSONs, mod, x)
-		case bootstrap.GoBinaryTool:
-			err = translateGoBinaryModule(ctx, w, mod, x)
 		case AndroidMkEntriesProvider:
 			err = translateAndroidMkEntriesModule(ctx, w, moduleInfoJSONs, mod, x)
 		default:
@@ -831,23 +828,6 @@
 	return err
 }
 
-// A simple, special Android.mk entry output func to make it possible to build blueprint tools using
-// m by making them phony targets.
-func translateGoBinaryModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
-	goBinary bootstrap.GoBinaryTool) error {
-
-	name := ctx.ModuleName(mod)
-	fmt.Fprintln(w, ".PHONY:", name)
-	fmt.Fprintln(w, name+":", goBinary.InstallPath())
-	fmt.Fprintln(w, "")
-	// Assuming no rules in make include go binaries in distributables.
-	// If the assumption is wrong, make will fail to build without the necessary .meta_lic and .meta_module files.
-	// In that case, add the targets and rules here to build a .meta_lic file for `name` and a .meta_module for
-	// `goBinary.InstallPath()` pointing to the `name`.meta_lic file.
-
-	return nil
-}
-
 func (data *AndroidMkData) fillInData(ctx fillInEntriesContext, mod blueprint.Module) {
 	// Get the preamble content through AndroidMkEntries logic.
 	data.Entries = AndroidMkEntries{
diff --git a/android/arch.go b/android/arch.go
index 6d896e5..a7868ba 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -23,7 +23,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -397,33 +396,8 @@
 // device_supported and host_supported properties to determine which OsTypes are enabled for this
 // module, then searches through the Targets to determine which have enabled Targets for this
 // module.
-func osMutator(bpctx blueprint.BottomUpMutatorContext) {
-	var module Module
-	var ok bool
-	if module, ok = bpctx.Module().(Module); !ok {
-		// The module is not a Soong module, it is a Blueprint module.
-		if bootstrap.IsBootstrapModule(bpctx.Module()) {
-			// Bootstrap Go modules are always the build OS or linux bionic.
-			config := bpctx.Config().(Config)
-			osNames := []string{config.BuildOSTarget.OsVariation()}
-			for _, hostCrossTarget := range config.Targets[LinuxBionic] {
-				if hostCrossTarget.Arch.ArchType == config.BuildOSTarget.Arch.ArchType {
-					osNames = append(osNames, hostCrossTarget.OsVariation())
-				}
-			}
-			osNames = FirstUniqueStrings(osNames)
-			bpctx.CreateVariations(osNames...)
-		}
-		return
-	}
-
-	// Bootstrap Go module support above requires this mutator to be a
-	// blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext
-	// filters out non-Soong modules.  Now that we've handled them, create a
-	// normal android.BottomUpMutatorContext.
-	mctx := bottomUpMutatorContextFactory(bpctx, module, false)
-	defer bottomUpMutatorContextPool.Put(mctx)
-
+func osMutator(mctx BottomUpMutatorContext) {
+	module := mctx.Module()
 	base := module.base()
 
 	// Nothing to do for modules that are not architecture specific (e.g. a genrule).
@@ -553,24 +527,8 @@
 //
 // Modules can be initialized with InitAndroidMultiTargetsArchModule, in which case they will be split by OsClass,
 // but will have a common Target that is expected to handle all other selected Targets via ctx.MultiTargets().
-func archMutator(bpctx blueprint.BottomUpMutatorContext) {
-	var module Module
-	var ok bool
-	if module, ok = bpctx.Module().(Module); !ok {
-		if bootstrap.IsBootstrapModule(bpctx.Module()) {
-			// Bootstrap Go modules are always the build architecture.
-			bpctx.CreateVariations(bpctx.Config().(Config).BuildOSTarget.ArchVariation())
-		}
-		return
-	}
-
-	// Bootstrap Go module support above requires this mutator to be a
-	// blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext
-	// filters out non-Soong modules.  Now that we've handled them, create a
-	// normal android.BottomUpMutatorContext.
-	mctx := bottomUpMutatorContextFactory(bpctx, module, false)
-	defer bottomUpMutatorContextPool.Put(mctx)
-
+func archMutator(mctx BottomUpMutatorContext) {
+	module := mctx.Module()
 	base := module.base()
 
 	if !base.ArchSpecific() {
diff --git a/android/module_context.go b/android/module_context.go
index 54fe0be..2bf2a8f 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -85,7 +85,9 @@
 type ModuleContext interface {
 	BaseModuleContext
 
-	blueprintModuleContext() blueprint.ModuleContext
+	// BlueprintModuleContext returns the blueprint.ModuleContext that the ModuleContext wraps.  It may only be
+	// used by the golang module types that need to call into the bootstrap module types.
+	BlueprintModuleContext() blueprint.ModuleContext
 
 	// Deprecated: use ModuleContext.Build instead.
 	ModuleBuild(pctx PackageContext, params ModuleBuildParams)
@@ -779,7 +781,7 @@
 	m.uncheckedModule = true
 }
 
-func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext {
+func (m *moduleContext) BlueprintModuleContext() blueprint.ModuleContext {
 	return m.bp
 }
 
diff --git a/android/mutator.go b/android/mutator.go
index 38fb857..2ef4d7f 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -148,9 +148,9 @@
 }
 
 func registerArchMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUpBlueprint("os", osMutator).Parallel()
+	ctx.BottomUp("os", osMutator).Parallel()
 	ctx.Transition("image", &imageTransitionMutator{})
-	ctx.BottomUpBlueprint("arch", archMutator).Parallel()
+	ctx.BottomUp("arch", archMutator).Parallel()
 }
 
 var preDeps = []RegisterMutatorFunc{
diff --git a/android/paths.go b/android/paths.go
index 6322f75..0a4f891 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -27,7 +27,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/pathtools"
 )
 
@@ -555,13 +554,6 @@
 	return ret
 }
 
-// PathForGoBinary returns the path to the installed location of a bootstrap_go_binary module.
-func PathForGoBinary(ctx PathContext, goBinary bootstrap.GoBinaryTool) Path {
-	goBinaryInstallDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin")
-	rel := Rel(ctx, goBinaryInstallDir.String(), goBinary.InstallPath())
-	return goBinaryInstallDir.Join(ctx, rel)
-}
-
 // Expands Paths to a SourceFileProducer or OutputFileProducer module dependency referenced via ":name" or ":name{.tag}" syntax.
 // If the dependency is not found, a missingErrorDependency is returned.
 // If the module dependency is not a SourceFileProducer or OutputFileProducer, appropriate errors will be returned.
@@ -573,10 +565,6 @@
 	if aModule, ok := module.(Module); ok && !aModule.Enabled(ctx) {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
-	if goBinary, ok := module.(bootstrap.GoBinaryTool); ok && tag == "" {
-		goBinaryPath := PathForGoBinary(ctx, goBinary)
-		return Paths{goBinaryPath}, nil
-	}
 	outputFiles, err := outputFilesForModule(ctx, module, tag)
 	if outputFiles != nil && err == nil {
 		return outputFiles, nil
diff --git a/genrule/genrule.go b/genrule/genrule.go
index fd72d3c..a48038b 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -25,7 +25,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -365,11 +364,6 @@
 						tools = append(tools, path.Path())
 						addLocationLabel(tag.label, toolLocation{android.Paths{path.Path()}})
 					}
-				case bootstrap.GoBinaryTool:
-					// A GoBinaryTool provides the install path to a tool, which will be copied.
-					p := android.PathForGoBinary(ctx, t)
-					tools = append(tools, p)
-					addLocationLabel(tag.label, toolLocation{android.Paths{p}})
 				default:
 					ctx.ModuleErrorf("%q is not a host tool provider", tool)
 					return
diff --git a/golang/Android.bp b/golang/Android.bp
new file mode 100644
index 0000000..3eae94f
--- /dev/null
+++ b/golang/Android.bp
@@ -0,0 +1,22 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-golang",
+    pkgPath: "android/soong/golang",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "blueprint-bootstrap",
+        "soong",
+        "soong-android",
+    ],
+    srcs: [
+        "golang.go",
+    ],
+    testSrcs: [
+        "golang_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/golang/golang.go b/golang/golang.go
new file mode 100644
index 0000000..ede2150
--- /dev/null
+++ b/golang/golang.go
@@ -0,0 +1,122 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package golang wraps the blueprint blueprint_go_binary and bootstrap_go_binary module types in versions
+// that implement android.Module that are used when building in Soong.  This simplifies the code in Soong
+// so it can always assume modules are an android.Module.
+// The original blueprint blueprint_go_binary and bootstrap_go_binary module types are still used during
+// bootstrapping, so the Android.bp entries for these module types must be compatible with both the
+// original blueprint module types and these wrapped module types.
+package golang
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/bootstrap"
+)
+
+func init() {
+	// Wrap the blueprint Go module types with Soong ones that interoperate with the rest of the Soong modules.
+	bootstrap.GoModuleTypesAreWrapped()
+	RegisterGoModuleTypes(android.InitRegistrationContext)
+}
+
+func RegisterGoModuleTypes(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("bootstrap_go_package", goPackageModuleFactory)
+	ctx.RegisterModuleType("blueprint_go_binary", goBinaryModuleFactory)
+}
+
+// A GoPackage is a module for building Go packages.
+type GoPackage struct {
+	android.ModuleBase
+	bootstrap.GoPackage
+}
+
+func goPackageModuleFactory() android.Module {
+	module := &GoPackage{}
+	module.AddProperties(module.Properties()...)
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+	return module
+}
+
+func (g *GoPackage) GenerateBuildActions(ctx blueprint.ModuleContext) {
+	// The embedded ModuleBase and bootstrap.GoPackage each implement GenerateBuildActions,
+	// the delegation has to be implemented manually to disambiguate.  Call ModuleBase's
+	// GenerateBuildActions, which will call GenerateAndroidBuildActions, which will call
+	// bootstrap.GoPackage.GenerateBuildActions.
+	g.ModuleBase.GenerateBuildActions(ctx)
+}
+
+func (g *GoPackage) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	g.GoPackage.GenerateBuildActions(ctx.BlueprintModuleContext())
+}
+
+// A GoBinary is a module for building executable binaries from Go sources.
+type GoBinary struct {
+	android.ModuleBase
+	bootstrap.GoBinary
+
+	outputFile android.Path
+}
+
+func goBinaryModuleFactory() android.Module {
+	module := &GoBinary{}
+	module.AddProperties(module.Properties()...)
+	android.InitAndroidArchModule(module, android.HostSupportedNoCross, android.MultilibFirst)
+	return module
+}
+
+func (g *GoBinary) GenerateBuildActions(ctx blueprint.ModuleContext) {
+	// The embedded ModuleBase and bootstrap.GoBinary each implement GenerateBuildActions,
+	// the delegation has to be implemented manually to disambiguate.  Call ModuleBase's
+	// GenerateBuildActions, which will call GenerateAndroidBuildActions, which will call
+	// bootstrap.GoBinary.GenerateBuildActions.
+	g.ModuleBase.GenerateBuildActions(ctx)
+}
+
+func (g *GoBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Install the file in Soong instead of blueprint so that Soong knows about the install rules.
+	g.GoBinary.SetSkipInstall()
+
+	// Run the build actions from the wrapped blueprint bootstrap module.
+	g.GoBinary.GenerateBuildActions(ctx.BlueprintModuleContext())
+
+	// Translate the bootstrap module's string path into a Path
+	outputFile := android.PathForArbitraryOutput(ctx, android.Rel(ctx, ctx.Config().OutDir(), g.IntermediateFile())).WithoutRel()
+	g.outputFile = outputFile
+
+	installPath := ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), outputFile)
+
+	if !ctx.Config().KatiEnabled() || g.ExportedToMake() {
+		// Modules in an unexported namespace have no install rule, only add modules in the exported namespaces
+		// to the blueprint_tools phony rules.
+		ctx.Phony("blueprint_tools", installPath)
+	}
+
+	ctx.SetOutputFiles(android.Paths{outputFile}, "")
+}
+
+func (g *GoBinary) HostToolPath() android.OptionalPath {
+	return android.OptionalPathForPath(g.outputFile)
+}
+
+func (g *GoBinary) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{
+		{
+			Class:      "EXECUTABLES",
+			OutputFile: android.OptionalPathForPath(g.outputFile),
+			Include:    "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
+		},
+	}
+}
diff --git a/golang/golang_test.go b/golang/golang_test.go
new file mode 100644
index 0000000..b512144
--- /dev/null
+++ b/golang/golang_test.go
@@ -0,0 +1,51 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package golang
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint/bootstrap"
+	"path/filepath"
+	"testing"
+)
+
+func TestGolang(t *testing.T) {
+	bp := `
+		bootstrap_go_package {
+			name: "gopkg",
+			pkgPath: "test/pkg",
+		}
+
+		blueprint_go_binary {
+			name: "gobin",
+			deps: ["gopkg"],
+		}
+	`
+
+	result := android.GroupFixturePreparers(
+		android.PrepareForTestWithArchMutator,
+		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+			RegisterGoModuleTypes(ctx)
+			ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+				ctx.BottomUpBlueprint("bootstrap_deps", bootstrap.BootstrapDeps)
+			})
+		}),
+	).RunTestWithBp(t, bp)
+
+	bin := result.ModuleForTests("gobin", result.Config.BuildOSTarget.String())
+
+	expected := filepath.Join("out/soong/host", result.Config.PrebuiltOS(), "bin/go/gobin/obj/gobin")
+	android.AssertPathsRelativeToTopEquals(t, "output files", []string{expected}, bin.OutputFiles(result.TestContext, t, ""))
+}