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 "Base" module type for building Python program.
18
19import (
20	"fmt"
21	"path/filepath"
22	"regexp"
23	"sort"
24	"strings"
25
26	"github.com/google/blueprint"
27	"github.com/google/blueprint/proptools"
28
29	"android/soong/android"
30)
31
32func init() {
33	android.PreDepsMutators(RegisterPythonPreDepsMutators)
34}
35
36func RegisterPythonPreDepsMutators(ctx android.RegisterMutatorsContext) {
37	ctx.BottomUp("version_split", versionSplitMutator()).Parallel()
38}
39
40// the version properties that apply to python libraries and binaries.
41type VersionProperties struct {
42	// true, if the module is required to be built with this version.
43	Enabled *bool `android:"arch_variant"`
44
45	// non-empty list of .py files under this strict Python version.
46	// srcs may reference the outputs of other modules that produce source files like genrule
47	// or filegroup using the syntax ":module".
48	Srcs []string `android:"path,arch_variant"`
49
50	// list of source files that should not be used to build the Python module.
51	// This is most useful in the arch/multilib variants to remove non-common files
52	Exclude_srcs []string `android:"path,arch_variant"`
53
54	// list of the Python libraries under this Python version.
55	Libs []string `android:"arch_variant"`
56
57	// true, if the binary is required to be built with embedded launcher.
58	// TODO(nanzhang): Remove this flag when embedded Python3 is supported later.
59	Embedded_launcher *bool `android:"arch_variant"`
60}
61
62// properties that apply to python libraries and binaries.
63type BaseProperties struct {
64	// the package path prefix within the output artifact at which to place the source/data
65	// files of the current module.
66	// eg. Pkg_path = "a/b/c"; Other packages can reference this module by using
67	// (from a.b.c import ...) statement.
68	// if left unspecified, all the source/data files path is unchanged within zip file.
69	Pkg_path *string `android:"arch_variant"`
70
71	// true, if the Python module is used internally, eg, Python std libs.
72	Is_internal *bool `android:"arch_variant"`
73
74	// list of source (.py) files compatible both with Python2 and Python3 used to compile the
75	// Python module.
76	// srcs may reference the outputs of other modules that produce source files like genrule
77	// or filegroup using the syntax ":module".
78	// Srcs has to be non-empty.
79	Srcs []string `android:"path,arch_variant"`
80
81	// list of source files that should not be used to build the C/C++ module.
82	// This is most useful in the arch/multilib variants to remove non-common files
83	Exclude_srcs []string `android:"path,arch_variant"`
84
85	// list of files or filegroup modules that provide data that should be installed alongside
86	// the test. the file extension can be arbitrary except for (.py).
87	Data []string `android:"path,arch_variant"`
88
89	// list of the Python libraries compatible both with Python2 and Python3.
90	Libs []string `android:"arch_variant"`
91
92	Version struct {
93		// all the "srcs" or Python dependencies that are to be used only for Python2.
94		Py2 VersionProperties `android:"arch_variant"`
95
96		// all the "srcs" or Python dependencies that are to be used only for Python3.
97		Py3 VersionProperties `android:"arch_variant"`
98	} `android:"arch_variant"`
99
100	// the actual version each module uses after variations created.
101	// this property name is hidden from users' perspectives, and soong will populate it during
102	// runtime.
103	Actual_version string `blueprint:"mutated"`
104}
105
106type pathMapping struct {
107	dest string
108	src  android.Path
109}
110
111type Module struct {
112	android.ModuleBase
113	android.DefaultableModuleBase
114
115	properties      BaseProperties
116	protoProperties android.ProtoProperties
117
118	// initialize before calling Init
119	hod      android.HostOrDeviceSupported
120	multilib android.Multilib
121
122	// the bootstrapper is used to bootstrap .par executable.
123	// bootstrapper might be nil (Python library module).
124	bootstrapper bootstrapper
125
126	// the installer might be nil.
127	installer installer
128
129	// the Python files of current module after expanding source dependencies.
130	// pathMapping: <dest: runfile_path, src: source_path>
131	srcsPathMappings []pathMapping
132
133	// the data files of current module after expanding source dependencies.
134	// pathMapping: <dest: runfile_path, src: source_path>
135	dataPathMappings []pathMapping
136
137	// the zip filepath for zipping current module source/data files.
138	srcsZip android.Path
139
140	// dependency modules' zip filepath for zipping current module source/data files.
141	depsSrcsZips android.Paths
142
143	// (.intermediate) module output path as installation source.
144	installSource android.OptionalPath
145
146	subAndroidMkOnce map[subAndroidMkProvider]bool
147}
148
149func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
150	return &Module{
151		hod:      hod,
152		multilib: multilib,
153	}
154}
155
156type bootstrapper interface {
157	bootstrapperProps() []interface{}
158	bootstrap(ctx android.ModuleContext, ActualVersion string, embeddedLauncher bool,
159		srcsPathMappings []pathMapping, srcsZip android.Path,
160		depsSrcsZips android.Paths) android.OptionalPath
161
162	autorun() bool
163}
164
165type installer interface {
166	install(ctx android.ModuleContext, path android.Path)
167	setAndroidMkSharedLibs(sharedLibs []string)
168}
169
170type PythonDependency interface {
171	GetSrcsPathMappings() []pathMapping
172	GetDataPathMappings() []pathMapping
173	GetSrcsZip() android.Path
174}
175
176func (p *Module) GetSrcsPathMappings() []pathMapping {
177	return p.srcsPathMappings
178}
179
180func (p *Module) GetDataPathMappings() []pathMapping {
181	return p.dataPathMappings
182}
183
184func (p *Module) GetSrcsZip() android.Path {
185	return p.srcsZip
186}
187
188var _ PythonDependency = (*Module)(nil)
189
190var _ android.AndroidMkDataProvider = (*Module)(nil)
191
192func (p *Module) Init() android.Module {
193
194	p.AddProperties(&p.properties, &p.protoProperties)
195	if p.bootstrapper != nil {
196		p.AddProperties(p.bootstrapper.bootstrapperProps()...)
197	}
198
199	android.InitAndroidArchModule(p, p.hod, p.multilib)
200	android.InitDefaultableModule(p)
201
202	return p
203}
204
205type dependencyTag struct {
206	blueprint.BaseDependencyTag
207	name string
208}
209
210var (
211	pythonLibTag         = dependencyTag{name: "pythonLib"}
212	launcherTag          = dependencyTag{name: "launcher"}
213	launcherSharedLibTag = dependencyTag{name: "launcherSharedLib"}
214	pyIdentifierRegexp   = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
215	pyExt                = ".py"
216	protoExt             = ".proto"
217	pyVersion2           = "PY2"
218	pyVersion3           = "PY3"
219	initFileName         = "__init__.py"
220	mainFileName         = "__main__.py"
221	entryPointFile       = "entry_point.txt"
222	parFileExt           = ".zip"
223	internal             = "internal"
224)
225
226// create version variants for modules.
227func versionSplitMutator() func(android.BottomUpMutatorContext) {
228	return func(mctx android.BottomUpMutatorContext) {
229		if base, ok := mctx.Module().(*Module); ok {
230			versionNames := []string{}
231			// PY3 is first so that we alias the PY3 variant rather than PY2 if both
232			// are available
233			if !(base.properties.Version.Py3.Enabled != nil &&
234				*(base.properties.Version.Py3.Enabled) == false) {
235				versionNames = append(versionNames, pyVersion3)
236			}
237			if base.properties.Version.Py2.Enabled != nil &&
238				*(base.properties.Version.Py2.Enabled) == true {
239				versionNames = append(versionNames, pyVersion2)
240			}
241			modules := mctx.CreateVariations(versionNames...)
242			if len(versionNames) > 0 {
243				mctx.AliasVariation(versionNames[0])
244			}
245			for i, v := range versionNames {
246				// set the actual version for Python module.
247				modules[i].(*Module).properties.Actual_version = v
248			}
249		}
250	}
251}
252
253func (p *Module) HostToolPath() android.OptionalPath {
254	if p.installer == nil {
255		// python_library is just meta module, and doesn't have any installer.
256		return android.OptionalPath{}
257	}
258	return android.OptionalPathForPath(p.installer.(*binaryDecorator).path)
259}
260
261func (p *Module) OutputFiles(tag string) (android.Paths, error) {
262	switch tag {
263	case "":
264		if outputFile := p.installSource; outputFile.Valid() {
265			return android.Paths{outputFile.Path()}, nil
266		}
267		return android.Paths{}, nil
268	default:
269		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
270	}
271}
272
273func (p *Module) isEmbeddedLauncherEnabled(actual_version string) bool {
274	switch actual_version {
275	case pyVersion2:
276		return Bool(p.properties.Version.Py2.Embedded_launcher)
277	case pyVersion3:
278		return Bool(p.properties.Version.Py3.Embedded_launcher)
279	}
280
281	return false
282}
283
284func hasSrcExt(srcs []string, ext string) bool {
285	for _, src := range srcs {
286		if filepath.Ext(src) == ext {
287			return true
288		}
289	}
290
291	return false
292}
293
294func (p *Module) hasSrcExt(ctx android.BottomUpMutatorContext, ext string) bool {
295	if hasSrcExt(p.properties.Srcs, protoExt) {
296		return true
297	}
298	switch p.properties.Actual_version {
299	case pyVersion2:
300		return hasSrcExt(p.properties.Version.Py2.Srcs, protoExt)
301	case pyVersion3:
302		return hasSrcExt(p.properties.Version.Py3.Srcs, protoExt)
303	default:
304		panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
305			p.properties.Actual_version, ctx.ModuleName()))
306	}
307}
308
309func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
310	android.ProtoDeps(ctx, &p.protoProperties)
311
312	if p.hasSrcExt(ctx, protoExt) && p.Name() != "libprotobuf-python" {
313		ctx.AddVariationDependencies(nil, pythonLibTag, "libprotobuf-python")
314	}
315	switch p.properties.Actual_version {
316	case pyVersion2:
317		ctx.AddVariationDependencies(nil, pythonLibTag,
318			uniqueLibs(ctx, p.properties.Libs, "version.py2.libs",
319				p.properties.Version.Py2.Libs)...)
320
321		if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled(pyVersion2) {
322			ctx.AddVariationDependencies(nil, pythonLibTag, "py2-stdlib")
323
324			launcherModule := "py2-launcher"
325			if p.bootstrapper.autorun() {
326				launcherModule = "py2-launcher-autorun"
327			}
328			ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule)
329
330			// Add py2-launcher shared lib dependencies. Ideally, these should be
331			// derived from the `shared_libs` property of "py2-launcher". However, we
332			// cannot read the property at this stage and it will be too late to add
333			// dependencies later.
334			ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, "libsqlite")
335
336			if ctx.Target().Os.Bionic() {
337				ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
338					"libc", "libdl", "libm")
339			}
340		}
341
342	case pyVersion3:
343		ctx.AddVariationDependencies(nil, pythonLibTag,
344			uniqueLibs(ctx, p.properties.Libs, "version.py3.libs",
345				p.properties.Version.Py3.Libs)...)
346
347		if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled(pyVersion3) {
348			ctx.AddVariationDependencies(nil, pythonLibTag, "py3-stdlib")
349
350			launcherModule := "py3-launcher"
351			if p.bootstrapper.autorun() {
352				launcherModule = "py3-launcher-autorun"
353			}
354			ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule)
355
356			// Add py3-launcher shared lib dependencies. Ideally, these should be
357			// derived from the `shared_libs` property of "py3-launcher". However, we
358			// cannot read the property at this stage and it will be too late to add
359			// dependencies later.
360			ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, "libsqlite")
361
362			if ctx.Device() {
363				ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
364					"liblog")
365			}
366
367			if ctx.Target().Os.Bionic() {
368				ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
369					"libc", "libdl", "libm")
370			}
371		}
372	default:
373		panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
374			p.properties.Actual_version, ctx.ModuleName()))
375	}
376}
377
378// check "libs" duplicates from current module dependencies.
379func uniqueLibs(ctx android.BottomUpMutatorContext,
380	commonLibs []string, versionProp string, versionLibs []string) []string {
381	set := make(map[string]string)
382	ret := []string{}
383
384	// deps from "libs" property.
385	for _, l := range commonLibs {
386		if _, found := set[l]; found {
387			ctx.PropertyErrorf("libs", "%q has duplicates within libs.", l)
388		} else {
389			set[l] = "libs"
390			ret = append(ret, l)
391		}
392	}
393	// deps from "version.pyX.libs" property.
394	for _, l := range versionLibs {
395		if _, found := set[l]; found {
396			ctx.PropertyErrorf(versionProp, "%q has duplicates within %q.", set[l])
397		} else {
398			set[l] = versionProp
399			ret = append(ret, l)
400		}
401	}
402
403	return ret
404}
405
406func (p *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
407	p.GeneratePythonBuildActions(ctx)
408
409	// Only Python binaries and test has non-empty bootstrapper.
410	if p.bootstrapper != nil {
411		p.walkTransitiveDeps(ctx)
412		embeddedLauncher := false
413		if p.properties.Actual_version == pyVersion2 {
414			embeddedLauncher = p.isEmbeddedLauncherEnabled(pyVersion2)
415		} else {
416			embeddedLauncher = p.isEmbeddedLauncherEnabled(pyVersion3)
417		}
418		p.installSource = p.bootstrapper.bootstrap(ctx, p.properties.Actual_version,
419			embeddedLauncher, p.srcsPathMappings, p.srcsZip, p.depsSrcsZips)
420	}
421
422	if p.installer != nil {
423		var sharedLibs []string
424		ctx.VisitDirectDeps(func(dep android.Module) {
425			if ctx.OtherModuleDependencyTag(dep) == launcherSharedLibTag {
426				sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
427			}
428		})
429		p.installer.setAndroidMkSharedLibs(sharedLibs)
430
431		if p.installSource.Valid() {
432			p.installer.install(ctx, p.installSource.Path())
433		}
434	}
435
436}
437
438func (p *Module) GeneratePythonBuildActions(ctx android.ModuleContext) {
439	// expand python files from "srcs" property.
440	srcs := p.properties.Srcs
441	exclude_srcs := p.properties.Exclude_srcs
442	switch p.properties.Actual_version {
443	case pyVersion2:
444		srcs = append(srcs, p.properties.Version.Py2.Srcs...)
445		exclude_srcs = append(exclude_srcs, p.properties.Version.Py2.Exclude_srcs...)
446	case pyVersion3:
447		srcs = append(srcs, p.properties.Version.Py3.Srcs...)
448		exclude_srcs = append(exclude_srcs, p.properties.Version.Py3.Exclude_srcs...)
449	default:
450		panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
451			p.properties.Actual_version, ctx.ModuleName()))
452	}
453	expandedSrcs := android.PathsForModuleSrcExcludes(ctx, srcs, exclude_srcs)
454	requiresSrcs := true
455	if p.bootstrapper != nil && !p.bootstrapper.autorun() {
456		requiresSrcs = false
457	}
458	if len(expandedSrcs) == 0 && requiresSrcs {
459		ctx.ModuleErrorf("doesn't have any source files!")
460	}
461
462	// expand data files from "data" property.
463	expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
464
465	// sanitize pkg_path.
466	pkgPath := String(p.properties.Pkg_path)
467	if pkgPath != "" {
468		pkgPath = filepath.Clean(String(p.properties.Pkg_path))
469		if pkgPath == ".." || strings.HasPrefix(pkgPath, "../") ||
470			strings.HasPrefix(pkgPath, "/") {
471			ctx.PropertyErrorf("pkg_path",
472				"%q must be a relative path contained in par file.",
473				String(p.properties.Pkg_path))
474			return
475		}
476		if p.properties.Is_internal != nil && *p.properties.Is_internal {
477			pkgPath = filepath.Join(internal, pkgPath)
478		}
479	} else {
480		if p.properties.Is_internal != nil && *p.properties.Is_internal {
481			pkgPath = internal
482		}
483	}
484
485	p.genModulePathMappings(ctx, pkgPath, expandedSrcs, expandedData)
486
487	p.srcsZip = p.createSrcsZip(ctx, pkgPath)
488}
489
490// generate current module unique pathMappings: <dest: runfiles_path, src: source_path>
491// for python/data files.
492func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkgPath string,
493	expandedSrcs, expandedData android.Paths) {
494	// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
495	// check current module duplicates.
496	destToPySrcs := make(map[string]string)
497	destToPyData := make(map[string]string)
498
499	for _, s := range expandedSrcs {
500		if s.Ext() != pyExt && s.Ext() != protoExt {
501			ctx.PropertyErrorf("srcs", "found non (.py|.proto) file: %q!", s.String())
502			continue
503		}
504		runfilesPath := filepath.Join(pkgPath, s.Rel())
505		identifiers := strings.Split(strings.TrimSuffix(runfilesPath,
506			filepath.Ext(runfilesPath)), "/")
507		for _, token := range identifiers {
508			if !pyIdentifierRegexp.MatchString(token) {
509				ctx.PropertyErrorf("srcs", "the path %q contains invalid token %q.",
510					runfilesPath, token)
511			}
512		}
513		if fillInMap(ctx, destToPySrcs, runfilesPath, s.String(), p.Name(), p.Name()) {
514			p.srcsPathMappings = append(p.srcsPathMappings,
515				pathMapping{dest: runfilesPath, src: s})
516		}
517	}
518
519	for _, d := range expandedData {
520		if d.Ext() == pyExt || d.Ext() == protoExt {
521			ctx.PropertyErrorf("data", "found (.py|.proto) file: %q!", d.String())
522			continue
523		}
524		runfilesPath := filepath.Join(pkgPath, d.Rel())
525		if fillInMap(ctx, destToPyData, runfilesPath, d.String(), p.Name(), p.Name()) {
526			p.dataPathMappings = append(p.dataPathMappings,
527				pathMapping{dest: runfilesPath, src: d})
528		}
529	}
530}
531
532// register build actions to zip current module's sources.
533func (p *Module) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path {
534	relativeRootMap := make(map[string]android.Paths)
535	pathMappings := append(p.srcsPathMappings, p.dataPathMappings...)
536
537	var protoSrcs android.Paths
538	// "srcs" or "data" properties may have filegroup so it might happen that
539	// the relative root for each source path is different.
540	for _, path := range pathMappings {
541		if path.src.Ext() == protoExt {
542			protoSrcs = append(protoSrcs, path.src)
543		} else {
544			var relativeRoot string
545			relativeRoot = strings.TrimSuffix(path.src.String(), path.src.Rel())
546			if v, found := relativeRootMap[relativeRoot]; found {
547				relativeRootMap[relativeRoot] = append(v, path.src)
548			} else {
549				relativeRootMap[relativeRoot] = android.Paths{path.src}
550			}
551		}
552	}
553	var zips android.Paths
554	if len(protoSrcs) > 0 {
555		protoFlags := android.GetProtoFlags(ctx, &p.protoProperties)
556		protoFlags.OutTypeFlag = "--python_out"
557
558		for _, srcFile := range protoSrcs {
559			zip := genProto(ctx, srcFile, protoFlags, pkgPath)
560			zips = append(zips, zip)
561		}
562	}
563
564	if len(relativeRootMap) > 0 {
565		var keys []string
566
567		// in order to keep stable order of soong_zip params, we sort the keys here.
568		for k := range relativeRootMap {
569			keys = append(keys, k)
570		}
571		sort.Strings(keys)
572
573		parArgs := []string{}
574		if pkgPath != "" {
575			parArgs = append(parArgs, `-P `+pkgPath)
576		}
577		implicits := android.Paths{}
578		for _, k := range keys {
579			parArgs = append(parArgs, `-C `+k)
580			for _, path := range relativeRootMap[k] {
581				parArgs = append(parArgs, `-f `+path.String())
582				implicits = append(implicits, path)
583			}
584		}
585
586		origSrcsZip := android.PathForModuleOut(ctx, ctx.ModuleName()+".py.srcszip")
587		ctx.Build(pctx, android.BuildParams{
588			Rule:        zip,
589			Description: "python library archive",
590			Output:      origSrcsZip,
591			Implicits:   implicits,
592			Args: map[string]string{
593				"args": strings.Join(parArgs, " "),
594			},
595		})
596		zips = append(zips, origSrcsZip)
597	}
598	if len(zips) == 1 {
599		return zips[0]
600	} else {
601		combinedSrcsZip := android.PathForModuleOut(ctx, ctx.ModuleName()+".srcszip")
602		ctx.Build(pctx, android.BuildParams{
603			Rule:        combineZip,
604			Description: "combine python library archive",
605			Output:      combinedSrcsZip,
606			Inputs:      zips,
607		})
608		return combinedSrcsZip
609	}
610}
611
612func isPythonLibModule(module blueprint.Module) bool {
613	if m, ok := module.(*Module); ok {
614		// Python library has no bootstrapper or installer.
615		if m.bootstrapper != nil || m.installer != nil {
616			return false
617		}
618		return true
619	}
620	return false
621}
622
623// check Python source/data files duplicates for whole runfiles tree since Python binary/test
624// need collect and zip all srcs of whole transitive dependencies to a final par file.
625func (p *Module) walkTransitiveDeps(ctx android.ModuleContext) {
626	// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
627	// check duplicates.
628	destToPySrcs := make(map[string]string)
629	destToPyData := make(map[string]string)
630
631	for _, path := range p.srcsPathMappings {
632		destToPySrcs[path.dest] = path.src.String()
633	}
634	for _, path := range p.dataPathMappings {
635		destToPyData[path.dest] = path.src.String()
636	}
637
638	seen := make(map[android.Module]bool)
639
640	// visit all its dependencies in depth first.
641	ctx.WalkDeps(func(child, parent android.Module) bool {
642		if ctx.OtherModuleDependencyTag(child) != pythonLibTag {
643			return false
644		}
645		if seen[child] {
646			return false
647		}
648		seen[child] = true
649		// Python modules only can depend on Python libraries.
650		if !isPythonLibModule(child) {
651			panic(fmt.Errorf(
652				"the dependency %q of module %q is not Python library!",
653				ctx.ModuleName(), ctx.OtherModuleName(child)))
654		}
655		if dep, ok := child.(PythonDependency); ok {
656			srcs := dep.GetSrcsPathMappings()
657			for _, path := range srcs {
658				if !fillInMap(ctx, destToPySrcs,
659					path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child)) {
660					continue
661				}
662			}
663			data := dep.GetDataPathMappings()
664			for _, path := range data {
665				fillInMap(ctx, destToPyData,
666					path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child))
667			}
668			p.depsSrcsZips = append(p.depsSrcsZips, dep.GetSrcsZip())
669		}
670		return true
671	})
672}
673
674func fillInMap(ctx android.ModuleContext, m map[string]string,
675	key, value, curModule, otherModule string) bool {
676	if oldValue, found := m[key]; found {
677		ctx.ModuleErrorf("found two files to be placed at the same location within zip %q."+
678			" First file: in module %s at path %q."+
679			" Second file: in module %s at path %q.",
680			key, curModule, oldValue, otherModule, value)
681		return false
682	} else {
683		m[key] = value
684	}
685
686	return true
687}
688
689func (p *Module) InstallInData() bool {
690	return true
691}
692
693var Bool = proptools.Bool
694var BoolDefault = proptools.BoolDefault
695var String = proptools.String
696