Support building a par file that does not automatically run

Mainly so that we can build a `python` prebuilt that acts like the
normal python install, though you could also build different ones with
more packages pre-installed.

Bug: 117811537
Test: move built py2-cmd into prebuilts/build-tools/path/linux-x86/python and build
Change-Id: I21215f6fd3754d89f8c65e1dfeb3f2deea23239f
diff --git a/python/binary.go b/python/binary.go
index bf9acb4..140f07a 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -42,6 +42,11 @@
 	// list of compatibility suites (for example "cts", "vts") that the module should be
 	// installed into.
 	Test_suites []string `android:"arch_variant"`
+
+	// whether to use `main` when starting the executable. The default is true, when set to
+	// false it will act much like the normal `python` executable, but with the sources and
+	// libraries automatically included in the PYTHONPATH.
+	Autorun *bool `android:"arch_variant"`
 }
 
 type binaryDecorator struct {
@@ -74,6 +79,10 @@
 	return module.Init()
 }
 
+func (binary *binaryDecorator) autorun() bool {
+	return BoolDefault(binary.binaryProperties.Autorun, true)
+}
+
 func (binary *binaryDecorator) bootstrapperProps() []interface{} {
 	return []interface{}{&binary.binaryProperties}
 }
@@ -82,7 +91,10 @@
 	embeddedLauncher bool, srcsPathMappings []pathMapping, srcsZip android.Path,
 	depsSrcsZips android.Paths) android.OptionalPath {
 
-	main := binary.getPyMainFile(ctx, srcsPathMappings)
+	main := ""
+	if binary.autorun() {
+		main = binary.getPyMainFile(ctx, srcsPathMappings)
+	}
 
 	var launcherPath android.OptionalPath
 	if embeddedLauncher {
diff --git a/python/builder.go b/python/builder.go
index e277bfd..e3b490c 100644
--- a/python/builder.go
+++ b/python/builder.go
@@ -54,7 +54,6 @@
 
 	embeddedPar = pctx.AndroidStaticRule("embeddedPar",
 		blueprint.RuleParams{
-			// `echo -n` to trim the newline, since the python code just wants the name
 			Command: `rm -f $out.main && ` +
 				`sed 's/ENTRY_POINT/$main/' build/soong/python/scripts/main.py >$out.main &&` +
 				`$mergeParCmd -p -pm $out.main --prefix $launcher $out $srcsZips && ` +
@@ -62,6 +61,14 @@
 			CommandDeps: []string{"$mergeParCmd", "$parCmd", "build/soong/python/scripts/main.py"},
 		},
 		"main", "srcsZips", "launcher")
+
+	embeddedParNoMain = pctx.AndroidStaticRule("embeddedParNoMain",
+		blueprint.RuleParams{
+			Command: `$mergeParCmd -p --prefix $launcher $out $srcsZips && ` +
+				`chmod +x $out`,
+			CommandDeps: []string{"$mergeParCmd"},
+		},
+		"srcsZips", "launcher")
 )
 
 func init() {
@@ -107,17 +114,30 @@
 		// added launcherPath to the implicits Ninja dependencies.
 		implicits = append(implicits, launcherPath.Path())
 
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        embeddedPar,
-			Description: "embedded python archive",
-			Output:      binFile,
-			Implicits:   implicits,
-			Args: map[string]string{
-				"main":     strings.Replace(strings.TrimSuffix(main, pyExt), "/", ".", -1),
-				"srcsZips": strings.Join(srcsZips.Strings(), " "),
-				"launcher": launcherPath.String(),
-			},
-		})
+		if main == "" {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        embeddedParNoMain,
+				Description: "embedded python archive",
+				Output:      binFile,
+				Implicits:   implicits,
+				Args: map[string]string{
+					"srcsZips": strings.Join(srcsZips.Strings(), " "),
+					"launcher": launcherPath.String(),
+				},
+			})
+		} else {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        embeddedPar,
+				Description: "embedded python archive",
+				Output:      binFile,
+				Implicits:   implicits,
+				Args: map[string]string{
+					"main":     strings.Replace(strings.TrimSuffix(main, pyExt), "/", ".", -1),
+					"srcsZips": strings.Join(srcsZips.Strings(), " "),
+					"launcher": launcherPath.String(),
+				},
+			})
+		}
 	}
 
 	return binFile
diff --git a/python/python.go b/python/python.go
index ddc3f1f..4445f40 100644
--- a/python/python.go
+++ b/python/python.go
@@ -156,6 +156,8 @@
 	bootstrap(ctx android.ModuleContext, ActualVersion string, embeddedLauncher bool,
 		srcsPathMappings []pathMapping, srcsZip android.Path,
 		depsSrcsZips android.Paths) android.OptionalPath
+
+	autorun() bool
 }
 
 type installer interface {
@@ -307,9 +309,14 @@
 
 		if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled(pyVersion2) {
 			ctx.AddVariationDependencies(nil, pythonLibTag, "py2-stdlib")
+
+			launcherModule := "py2-launcher"
+			if p.bootstrapper.autorun() {
+				launcherModule = "py2-launcher-autorun"
+			}
 			ctx.AddFarVariationDependencies([]blueprint.Variation{
 				{Mutator: "arch", Variation: ctx.Target().String()},
-			}, launcherTag, "py2-launcher")
+			}, launcherTag, launcherModule)
 
 			// Add py2-launcher shared lib dependencies. Ideally, these should be
 			// derived from the `shared_libs` property of "py2-launcher". However, we
@@ -422,7 +429,11 @@
 			p.properties.Actual_version, ctx.ModuleName()))
 	}
 	expandedSrcs := ctx.ExpandSources(srcs, exclude_srcs)
-	if len(expandedSrcs) == 0 {
+	requiresSrcs := true
+	if p.bootstrapper != nil && !p.bootstrapper.autorun() {
+		requiresSrcs = false
+	}
+	if len(expandedSrcs) == 0 && requiresSrcs {
 		ctx.ModuleErrorf("doesn't have any source files!")
 	}
 
@@ -656,4 +667,5 @@
 }
 
 var Bool = proptools.Bool
+var BoolDefault = proptools.BoolDefault
 var String = proptools.String