• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package python
16
17// This file contains the module types for building Python binary.
18
19import (
20	"fmt"
21
22	"android/soong/android"
23	"android/soong/bazel"
24
25	"github.com/google/blueprint/proptools"
26)
27
28func init() {
29	registerPythonBinaryComponents(android.InitRegistrationContext)
30}
31
32func registerPythonBinaryComponents(ctx android.RegistrationContext) {
33	ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
34}
35
36type bazelPythonBinaryAttributes struct {
37	Main           *string
38	Srcs           bazel.LabelListAttribute
39	Deps           bazel.LabelListAttribute
40	Python_version *string
41}
42
43func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
44	var main *string
45	for _, propIntf := range m.GetProperties() {
46		if props, ok := propIntf.(*BinaryProperties); ok {
47			// main is optional.
48			if props.Main != nil {
49				main = props.Main
50				break
51			}
52		}
53	}
54
55	// TODO(b/182306917): this doesn't fully handle all nested props versioned
56	// by the python version, which would have been handled by the version split
57	// mutator. This is sufficient for very simple python_binary_host modules
58	// under Bionic.
59	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
60	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
61	var python_version *string
62	if py3Enabled && py2Enabled {
63		panic(fmt.Errorf(
64			"error for '%s' module: bp2build's python_binary_host converter does not support "+
65				"converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
66	} else if py2Enabled {
67		python_version = &pyVersion2
68	} else {
69		// do nothing, since python_version defaults to PY3.
70	}
71
72	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
73	attrs := &bazelPythonBinaryAttributes{
74		Main:           main,
75		Srcs:           baseAttrs.Srcs,
76		Deps:           baseAttrs.Deps,
77		Python_version: python_version,
78	}
79
80	props := bazel.BazelTargetModuleProperties{
81		// Use the native py_binary rule.
82		Rule_class: "py_binary",
83	}
84
85	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
86		Name: m.Name(),
87		Data: baseAttrs.Data,
88	}, attrs)
89}
90
91type BinaryProperties struct {
92	// the name of the source file that is the main entry point of the program.
93	// this file must also be listed in srcs.
94	// If left unspecified, module name is used instead.
95	// If name doesn’t match any filename in srcs, main must be specified.
96	Main *string `android:"arch_variant"`
97
98	// set the name of the output binary.
99	Stem *string `android:"arch_variant"`
100
101	// append to the name of the output binary.
102	Suffix *string `android:"arch_variant"`
103
104	// list of compatibility suites (for example "cts", "vts") that the module should be
105	// installed into.
106	Test_suites []string `android:"arch_variant"`
107
108	// whether to use `main` when starting the executable. The default is true, when set to
109	// false it will act much like the normal `python` executable, but with the sources and
110	// libraries automatically included in the PYTHONPATH.
111	Autorun *bool `android:"arch_variant"`
112
113	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
114	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
115	// explicitly.
116	Auto_gen_config *bool
117}
118
119type binaryDecorator struct {
120	binaryProperties BinaryProperties
121
122	*pythonInstaller
123}
124
125type IntermPathProvider interface {
126	IntermPathForModuleOut() android.OptionalPath
127}
128
129var (
130	StubTemplateHost = "build/soong/python/scripts/stub_template_host.txt"
131)
132
133func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
134	module := newModule(hod, android.MultilibFirst)
135	decorator := &binaryDecorator{pythonInstaller: NewPythonInstaller("bin", "")}
136
137	module.bootstrapper = decorator
138	module.installer = decorator
139
140	return module, decorator
141}
142
143func PythonBinaryHostFactory() android.Module {
144	module, _ := NewBinary(android.HostSupported)
145
146	android.InitBazelModule(module)
147
148	return module.init()
149}
150
151func (binary *binaryDecorator) autorun() bool {
152	return BoolDefault(binary.binaryProperties.Autorun, true)
153}
154
155func (binary *binaryDecorator) bootstrapperProps() []interface{} {
156	return []interface{}{&binary.binaryProperties}
157}
158
159func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actualVersion string,
160	embeddedLauncher bool, srcsPathMappings []pathMapping, srcsZip android.Path,
161	depsSrcsZips android.Paths) android.OptionalPath {
162
163	main := ""
164	if binary.autorun() {
165		main = binary.getPyMainFile(ctx, srcsPathMappings)
166	}
167
168	var launcherPath android.OptionalPath
169	if embeddedLauncher {
170		ctx.VisitDirectDepsWithTag(launcherTag, func(m android.Module) {
171			if provider, ok := m.(IntermPathProvider); ok {
172				if launcherPath.Valid() {
173					panic(fmt.Errorf("launcher path was found before: %q",
174						launcherPath))
175				}
176				launcherPath = provider.IntermPathForModuleOut()
177			}
178		})
179	}
180
181	binFile := registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
182		binary.getHostInterpreterName(ctx, actualVersion),
183		main, binary.getStem(ctx), append(android.Paths{srcsZip}, depsSrcsZips...))
184
185	return android.OptionalPathForPath(binFile)
186}
187
188// get host interpreter name.
189func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
190	actualVersion string) string {
191	var interp string
192	switch actualVersion {
193	case pyVersion2:
194		interp = "python2.7"
195	case pyVersion3:
196		interp = "python3"
197	default:
198		panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.",
199			actualVersion, ctx.ModuleName()))
200	}
201
202	return interp
203}
204
205// find main program path within runfiles tree.
206func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext,
207	srcsPathMappings []pathMapping) string {
208	var main string
209	if String(binary.binaryProperties.Main) == "" {
210		main = ctx.ModuleName() + pyExt
211	} else {
212		main = String(binary.binaryProperties.Main)
213	}
214
215	for _, path := range srcsPathMappings {
216		if main == path.src.Rel() {
217			return path.dest
218		}
219	}
220	ctx.PropertyErrorf("main", "%q is not listed in srcs.", main)
221
222	return ""
223}
224
225func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string {
226	stem := ctx.ModuleName()
227	if String(binary.binaryProperties.Stem) != "" {
228		stem = String(binary.binaryProperties.Stem)
229	}
230
231	return stem + String(binary.binaryProperties.Suffix)
232}
233