• 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	"path/filepath"
22	"strings"
23
24	"android/soong/android"
25	"android/soong/cc"
26
27	"github.com/google/blueprint"
28)
29
30type PythonBinaryInfo struct{}
31
32var PythonBinaryInfoProvider = blueprint.NewProvider[PythonBinaryInfo]()
33
34func init() {
35	registerPythonBinaryComponents(android.InitRegistrationContext)
36}
37
38func registerPythonBinaryComponents(ctx android.RegistrationContext) {
39	ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
40}
41
42type BinaryProperties struct {
43	// the name of the source file that is the main entry point of the program.
44	// this file must also be listed in srcs.
45	// If left unspecified, module name is used instead.
46	// If name doesn’t match any filename in srcs, main must be specified.
47	Main *string
48
49	// set the name of the output binary.
50	Stem *string `android:"arch_variant"`
51
52	// append to the name of the output binary.
53	Suffix *string `android:"arch_variant"`
54
55	// list of compatibility suites (for example "cts", "vts") that the module should be
56	// installed into.
57	Test_suites []string `android:"arch_variant"`
58
59	// whether to use `main` when starting the executable. The default is true, when set to
60	// false it will act much like the normal `python` executable, but with the sources and
61	// libraries automatically included in the PYTHONPATH.
62	Autorun *bool `android:"arch_variant"`
63
64	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
65	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
66	// explicitly.
67	Auto_gen_config *bool
68}
69
70type PythonBinaryModule struct {
71	PythonLibraryModule
72	binaryProperties BinaryProperties
73
74	// (.intermediate) module output path as installation source.
75	installSource android.Path
76
77	// Final installation path.
78	installedDest android.Path
79
80	androidMkSharedLibs []string
81}
82
83var _ android.AndroidMkEntriesProvider = (*PythonBinaryModule)(nil)
84var _ android.Module = (*PythonBinaryModule)(nil)
85
86type IntermPathProvider interface {
87	IntermPathForModuleOut() android.OptionalPath
88}
89
90func NewBinary(hod android.HostOrDeviceSupported) *PythonBinaryModule {
91	return &PythonBinaryModule{
92		PythonLibraryModule: *newModule(hod, android.MultilibFirst),
93	}
94}
95
96func PythonBinaryHostFactory() android.Module {
97	return NewBinary(android.HostSupported).init()
98}
99
100func (p *PythonBinaryModule) init() android.Module {
101	p.AddProperties(&p.properties, &p.protoProperties)
102	p.AddProperties(&p.binaryProperties)
103	android.InitAndroidArchModule(p, p.hod, p.multilib)
104	android.InitDefaultableModule(p)
105	return p
106}
107
108func (p *PythonBinaryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
109	p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
110	p.buildBinary(ctx)
111	p.installedDest = ctx.InstallFile(installDir(ctx, "bin", "", ""),
112		p.installSource.Base(), p.installSource)
113
114	android.SetProvider(ctx, PythonBinaryInfoProvider, PythonBinaryInfo{})
115
116	ctx.SetOutputFiles(android.Paths{p.installSource}, "")
117
118	moduleInfoJSON := ctx.ModuleInfoJSON()
119	moduleInfoJSON.Class = []string{"EXECUTABLES"}
120	moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, p.androidMkSharedLibs...)
121	moduleInfoJSON.SharedLibs = append(moduleInfoJSON.SharedLibs, p.androidMkSharedLibs...)
122	moduleInfoJSON.SystemSharedLibs = []string{"none"}
123}
124
125func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) {
126	embeddedLauncher := p.isEmbeddedLauncherEnabled()
127	depsSrcsZips := p.collectPathsFromTransitiveDeps(ctx, embeddedLauncher)
128	bundleSharedLibs := p.collectSharedLibDeps(ctx)
129	main := ""
130	if p.autorun() {
131		main = p.getPyMainFile(ctx, p.srcsPathMappings)
132	}
133
134	var launcherPath android.OptionalPath
135	if embeddedLauncher {
136		ctx.VisitDirectDepsProxyWithTag(launcherTag, func(m android.ModuleProxy) {
137			if provider, ok := android.OtherModuleProvider(ctx, m, cc.LinkableInfoProvider); ok {
138				if launcherPath.Valid() {
139					panic(fmt.Errorf("launcher path was found before: %q",
140						launcherPath))
141				}
142				launcherPath = provider.OutputFile
143			}
144		})
145	}
146	srcsZips := make(android.Paths, 0, len(depsSrcsZips)+1)
147	if embeddedLauncher {
148		srcsZips = append(srcsZips, p.precompiledSrcsZip)
149	} else {
150		srcsZips = append(srcsZips, p.srcsZip)
151	}
152	srcsZips = append(srcsZips, depsSrcsZips...)
153	if ctx.Host() && len(bundleSharedLibs) > 0 {
154		// only bundle shared libs for host binaries
155		sharedLibZip := p.zipSharedLibs(ctx, bundleSharedLibs)
156		srcsZips = append(srcsZips, sharedLibZip)
157	}
158	p.installSource = registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
159		"python3", main, p.getStem(ctx), srcsZips)
160
161	var sharedLibs []string
162	// if embedded launcher is enabled, we need to collect the shared library dependencies of the
163	// launcher
164	for _, dep := range ctx.GetDirectDepsProxyWithTag(launcherSharedLibTag) {
165		sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
166	}
167	p.androidMkSharedLibs = sharedLibs
168
169	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
170		TestSuites: p.binaryProperties.Test_suites,
171	})
172}
173
174func (p *PythonBinaryModule) AndroidMkEntries() []android.AndroidMkEntries {
175	entries := android.AndroidMkEntries{OutputFile: android.OptionalPathForPath(p.installSource)}
176
177	entries.Class = "EXECUTABLES"
178
179	entries.ExtraEntries = append(entries.ExtraEntries,
180		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
181			entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
182		})
183
184	entries.Required = append(entries.Required, "libc++")
185	entries.ExtraEntries = append(entries.ExtraEntries,
186		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
187			path, file := filepath.Split(p.installedDest.String())
188			stem := strings.TrimSuffix(file, filepath.Ext(file))
189
190			entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
191			entries.SetString("LOCAL_MODULE_PATH", path)
192			entries.SetString("LOCAL_MODULE_STEM", stem)
193			entries.AddStrings("LOCAL_SHARED_LIBRARIES", p.androidMkSharedLibs...)
194			entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
195		})
196
197	return []android.AndroidMkEntries{entries}
198}
199
200func (p *PythonBinaryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
201	p.PythonLibraryModule.DepsMutator(ctx)
202
203	if p.isEmbeddedLauncherEnabled() {
204		p.AddDepsOnPythonLauncherAndStdlib(ctx, pythonLibTag, launcherTag, launcherSharedLibTag, p.autorun(), ctx.Target())
205	}
206}
207
208// HostToolPath returns a path if appropriate such that this module can be used as a host tool,
209// fulfilling the android.HostToolProvider interface.
210func (p *PythonBinaryModule) HostToolPath() android.OptionalPath {
211	// TODO: This should only be set when building host binaries -- tests built for device would be
212	// setting this incorrectly.
213	return android.OptionalPathForPath(p.installedDest)
214}
215
216func (p *PythonBinaryModule) isEmbeddedLauncherEnabled() bool {
217	return BoolDefault(p.properties.Embedded_launcher, true)
218}
219
220func (b *PythonBinaryModule) autorun() bool {
221	return BoolDefault(b.binaryProperties.Autorun, true)
222}
223
224// find main program path within runfiles tree.
225func (p *PythonBinaryModule) getPyMainFile(ctx android.ModuleContext,
226	srcsPathMappings []pathMapping) string {
227	var main string
228	if String(p.binaryProperties.Main) == "" {
229		main = ctx.ModuleName() + pyExt
230	} else {
231		main = String(p.binaryProperties.Main)
232	}
233
234	for _, path := range srcsPathMappings {
235		if main == path.src.Rel() {
236			return path.dest
237		}
238	}
239	ctx.PropertyErrorf("main", "%q is not listed in srcs.", main)
240
241	return ""
242}
243
244func (p *PythonBinaryModule) getStem(ctx android.ModuleContext) string {
245	stem := ctx.ModuleName()
246	if String(p.binaryProperties.Stem) != "" {
247		stem = String(p.binaryProperties.Stem)
248	}
249
250	return stem + String(p.binaryProperties.Suffix)
251}
252
253func installDir(ctx android.ModuleContext, dir, dir64, relative string) android.InstallPath {
254	if ctx.Arch().ArchType.Multilib == "lib64" && dir64 != "" {
255		dir = dir64
256	}
257	if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
258		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
259	}
260	return android.PathForModuleInstall(ctx, dir, relative)
261}
262