• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2019 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 sh
16
17import (
18	"path/filepath"
19	"strings"
20
21	"android/soong/testing"
22
23	"github.com/google/blueprint"
24	"github.com/google/blueprint/proptools"
25
26	"android/soong/android"
27	"android/soong/cc"
28	"android/soong/tradefed"
29)
30
31// sh_binary is for shell scripts (and batch files) that are installed as
32// executable files into .../bin/
33//
34// Do not use them for prebuilt C/C++/etc files.  Use cc_prebuilt_binary
35// instead.
36
37var pctx = android.NewPackageContext("android/soong/sh")
38
39func init() {
40	pctx.Import("android/soong/android")
41
42	registerShBuildComponents(android.InitRegistrationContext)
43}
44
45func registerShBuildComponents(ctx android.RegistrationContext) {
46	ctx.RegisterModuleType("sh_binary", ShBinaryFactory)
47	ctx.RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
48	ctx.RegisterModuleType("sh_test", ShTestFactory)
49	ctx.RegisterModuleType("sh_test_host", ShTestHostFactory)
50}
51
52// Test fixture preparer that will register most sh build components.
53//
54// Singletons and mutators should only be added here if they are needed for a majority of sh
55// module types, otherwise they should be added under a separate preparer to allow them to be
56// selected only when needed to reduce test execution time.
57//
58// Module types do not have much of an overhead unless they are used so this should include as many
59// module types as possible. The exceptions are those module types that require mutators and/or
60// singletons in order to function in which case they should be kept together in a separate
61// preparer.
62var PrepareForTestWithShBuildComponents = android.GroupFixturePreparers(
63	android.FixtureRegisterWithContext(registerShBuildComponents),
64)
65
66type shBinaryProperties struct {
67	// Source file of this prebuilt.
68	Src *string `android:"path,arch_variant"`
69
70	// optional subdirectory under which this file is installed into
71	Sub_dir *string `android:"arch_variant"`
72
73	// optional name for the installed file. If unspecified, name of the module is used as the file name
74	Filename *string `android:"arch_variant"`
75
76	// when set to true, and filename property is not set, the name for the installed file
77	// is the same as the file name of the source file.
78	Filename_from_src *bool `android:"arch_variant"`
79
80	// Whether this module is directly installable to one of the partitions. Default: true.
81	Installable *bool
82
83	// install symlinks to the binary
84	Symlinks []string `android:"arch_variant"`
85
86	// Make this module available when building for ramdisk.
87	// On device without a dedicated recovery partition, the module is only
88	// available after switching root into
89	// /first_stage_ramdisk. To expose the module before switching root, install
90	// the recovery variant instead.
91	Ramdisk_available *bool
92
93	// Make this module available when building for vendor ramdisk.
94	// On device without a dedicated recovery partition, the module is only
95	// available after switching root into
96	// /first_stage_ramdisk. To expose the module before switching root, install
97	// the recovery variant instead.
98	Vendor_ramdisk_available *bool
99
100	// Make this module available when building for recovery.
101	Recovery_available *bool
102
103	// The name of the image this module is built for
104	ImageVariation string `blueprint:"mutated"`
105
106	// Suffix for the name of Android.mk entries generated by this module
107	SubName string `blueprint:"mutated"`
108}
109
110type TestProperties struct {
111	// list of compatibility suites (for example "cts", "vts") that the module should be
112	// installed into.
113	Test_suites []string `android:"arch_variant"`
114
115	// the name of the test configuration (for example "AndroidTest.xml") that should be
116	// installed with the module.
117	Test_config *string `android:"path,arch_variant"`
118
119	// list of files or filegroup modules that provide data that should be installed alongside
120	// the test.
121	Data []string `android:"path,arch_variant"`
122
123	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
124	// with root permission.
125	Require_root *bool
126
127	// the name of the test configuration template (for example "AndroidTestTemplate.xml") that
128	// should be installed with the module.
129	Test_config_template *string `android:"path,arch_variant"`
130
131	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
132	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
133	// explicitly.
134	Auto_gen_config *bool
135
136	// list of binary modules that should be installed alongside the test
137	Data_bins []string `android:"path,arch_variant"`
138
139	// list of library modules that should be installed alongside the test
140	Data_libs []string `android:"path,arch_variant"`
141
142	// list of device binary modules that should be installed alongside the test.
143	// Only available for host sh_test modules.
144	Data_device_bins []string `android:"path,arch_variant"`
145
146	// list of device library modules that should be installed alongside the test.
147	// Only available for host sh_test modules.
148	Data_device_libs []string `android:"path,arch_variant"`
149
150	// list of java modules that provide data that should be installed alongside the test.
151	Java_data []string
152
153	// Install the test into a folder named for the module in all test suites.
154	Per_testcase_directory *bool
155
156	// Test options.
157	Test_options android.CommonTestOptions
158}
159
160type ShBinary struct {
161	android.ModuleBase
162
163	properties shBinaryProperties
164
165	sourceFilePath android.Path
166	outputFilePath android.OutputPath
167	installedFile  android.InstallPath
168}
169
170var _ android.HostToolProvider = (*ShBinary)(nil)
171
172type ShTest struct {
173	ShBinary
174
175	testProperties TestProperties
176
177	installDir android.InstallPath
178
179	data       []android.DataPath
180	testConfig android.Path
181
182	dataModules map[string]android.Path
183}
184
185func (s *ShBinary) HostToolPath() android.OptionalPath {
186	return android.OptionalPathForPath(s.installedFile)
187}
188
189func (s *ShBinary) DepsMutator(ctx android.BottomUpMutatorContext) {
190}
191
192func (s *ShBinary) OutputFile() android.OutputPath {
193	return s.outputFilePath
194}
195
196func (s *ShBinary) SubDir() string {
197	return proptools.String(s.properties.Sub_dir)
198}
199
200func (s *ShBinary) RelativeInstallPath() string {
201	return s.SubDir()
202}
203func (s *ShBinary) Installable() bool {
204	return s.properties.Installable == nil || proptools.Bool(s.properties.Installable)
205}
206
207func (s *ShBinary) Symlinks() []string {
208	return s.properties.Symlinks
209}
210
211var _ android.ImageInterface = (*ShBinary)(nil)
212
213func (s *ShBinary) ImageMutatorBegin(ctx android.BaseModuleContext) {}
214
215func (s *ShBinary) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
216	return !s.InstallInRecovery() && !s.InstallInRamdisk() && !s.InstallInVendorRamdisk() && !s.ModuleBase.InstallInVendor()
217}
218
219func (s *ShBinary) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
220	return proptools.Bool(s.properties.Ramdisk_available) || s.InstallInRamdisk()
221}
222
223func (s *ShBinary) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
224	return proptools.Bool(s.properties.Vendor_ramdisk_available) || s.InstallInVendorRamdisk()
225}
226
227func (s *ShBinary) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
228	return false
229}
230
231func (s *ShBinary) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
232	return proptools.Bool(s.properties.Recovery_available) || s.InstallInRecovery()
233}
234
235func (s *ShBinary) ExtraImageVariations(ctx android.BaseModuleContext) []string {
236	extraVariations := []string{}
237	if s.InstallInProduct() {
238		extraVariations = append(extraVariations, cc.ProductVariation)
239	}
240	if s.InstallInVendor() {
241		extraVariations = append(extraVariations, cc.VendorVariation)
242	}
243	return extraVariations
244}
245
246func (s *ShBinary) SetImageVariation(ctx android.BaseModuleContext, variation string) {
247	s.properties.ImageVariation = variation
248}
249
250// Overrides ModuleBase.InstallInRamdisk() so that the install rule respects
251// Ramdisk_available property for ramdisk variant
252func (s *ShBinary) InstallInRamdisk() bool {
253	return s.ModuleBase.InstallInRamdisk() ||
254		(proptools.Bool(s.properties.Ramdisk_available) && s.properties.ImageVariation == android.RamdiskVariation)
255}
256
257// Overrides ModuleBase.InstallInVendorRamdisk() so that the install rule respects
258// Vendor_ramdisk_available property for vendor ramdisk variant
259func (s *ShBinary) InstallInVendorRamdisk() bool {
260	return s.ModuleBase.InstallInVendorRamdisk() ||
261		(proptools.Bool(s.properties.Vendor_ramdisk_available) && s.properties.ImageVariation == android.VendorRamdiskVariation)
262}
263
264// Overrides ModuleBase.InstallInRecovery() so that the install rule respects
265// Recovery_available property for recovery variant
266func (s *ShBinary) InstallInRecovery() bool {
267	return s.ModuleBase.InstallInRecovery() ||
268		(proptools.Bool(s.properties.Recovery_available) && s.properties.ImageVariation == android.RecoveryVariation)
269}
270
271func (s *ShBinary) generateAndroidBuildActions(ctx android.ModuleContext) {
272	if s.properties.Src == nil {
273		ctx.PropertyErrorf("src", "missing prebuilt source file")
274	}
275
276	s.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(s.properties.Src))
277	filename := proptools.String(s.properties.Filename)
278	filenameFromSrc := proptools.Bool(s.properties.Filename_from_src)
279	if filename == "" {
280		if filenameFromSrc {
281			filename = s.sourceFilePath.Base()
282		} else {
283			filename = ctx.ModuleName()
284		}
285	} else if filenameFromSrc {
286		ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
287		return
288	}
289	s.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
290
291	// This ensures that outputFilePath has the correct name for others to
292	// use, as the source file may have a different name.
293	ctx.Build(pctx, android.BuildParams{
294		Rule:   android.CpExecutable,
295		Output: s.outputFilePath,
296		Input:  s.sourceFilePath,
297	})
298
299	s.properties.SubName = s.GetSubname(ctx)
300
301	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: []string{s.sourceFilePath.String()}})
302
303	ctx.SetOutputFiles(android.Paths{s.outputFilePath}, "")
304}
305
306func (s *ShBinary) GetSubname(ctx android.ModuleContext) string {
307	ret := ""
308	if s.properties.ImageVariation != "" {
309		if s.properties.ImageVariation != cc.VendorVariation {
310			ret = "." + s.properties.ImageVariation
311		}
312	}
313	return ret
314}
315
316func (s *ShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
317	s.generateAndroidBuildActions(ctx)
318	installDir := android.PathForModuleInstall(ctx, "bin", proptools.String(s.properties.Sub_dir))
319	if !s.Installable() {
320		s.SkipInstall()
321	}
322	s.installedFile = ctx.InstallExecutable(installDir, s.outputFilePath.Base(), s.outputFilePath)
323	for _, symlink := range s.Symlinks() {
324		ctx.InstallSymlink(installDir, symlink, s.installedFile)
325	}
326}
327
328func (s *ShBinary) AndroidMkEntries() []android.AndroidMkEntries {
329	return []android.AndroidMkEntries{{
330		Class:      "EXECUTABLES",
331		OutputFile: android.OptionalPathForPath(s.outputFilePath),
332		Include:    "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
333		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
334			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
335				s.customAndroidMkEntries(entries)
336				entries.SetString("LOCAL_MODULE_RELATIVE_PATH", proptools.String(s.properties.Sub_dir))
337				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !s.Installable())
338			},
339		},
340		SubName: s.properties.SubName,
341	}}
342}
343
344func (s *ShBinary) customAndroidMkEntries(entries *android.AndroidMkEntries) {
345	entries.SetString("LOCAL_MODULE_SUFFIX", "")
346	entries.SetString("LOCAL_MODULE_STEM", s.outputFilePath.Rel())
347	if len(s.properties.Symlinks) > 0 {
348		entries.SetString("LOCAL_MODULE_SYMLINKS", strings.Join(s.properties.Symlinks, " "))
349	}
350}
351
352type dependencyTag struct {
353	blueprint.BaseDependencyTag
354	name string
355}
356
357var (
358	shTestDataBinsTag       = dependencyTag{name: "dataBins"}
359	shTestDataLibsTag       = dependencyTag{name: "dataLibs"}
360	shTestDataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"}
361	shTestDataDeviceLibsTag = dependencyTag{name: "dataDeviceLibs"}
362	shTestJavaDataTag       = dependencyTag{name: "javaData"}
363)
364
365var sharedLibVariations = []blueprint.Variation{{Mutator: "link", Variation: "shared"}}
366
367func (s *ShTest) DepsMutator(ctx android.BottomUpMutatorContext) {
368	s.ShBinary.DepsMutator(ctx)
369
370	ctx.AddFarVariationDependencies(ctx.Target().Variations(), shTestDataBinsTag, s.testProperties.Data_bins...)
371	ctx.AddFarVariationDependencies(append(ctx.Target().Variations(), sharedLibVariations...),
372		shTestDataLibsTag, s.testProperties.Data_libs...)
373	if ctx.Target().Os.Class == android.Host && len(ctx.Config().Targets[android.Android]) > 0 {
374		deviceVariations := ctx.Config().AndroidFirstDeviceTarget.Variations()
375		ctx.AddFarVariationDependencies(deviceVariations, shTestDataDeviceBinsTag, s.testProperties.Data_device_bins...)
376		ctx.AddFarVariationDependencies(append(deviceVariations, sharedLibVariations...),
377			shTestDataDeviceLibsTag, s.testProperties.Data_device_libs...)
378
379		javaDataVariation := []blueprint.Variation{{"arch", android.Common.String()}}
380		ctx.AddVariationDependencies(javaDataVariation, shTestJavaDataTag, s.testProperties.Java_data...)
381
382	} else if ctx.Target().Os.Class != android.Host {
383		if len(s.testProperties.Data_device_bins) > 0 {
384			ctx.PropertyErrorf("data_device_bins", "only available for host modules")
385		}
386		if len(s.testProperties.Data_device_libs) > 0 {
387			ctx.PropertyErrorf("data_device_libs", "only available for host modules")
388		}
389		if len(s.testProperties.Java_data) > 0 {
390			ctx.PropertyErrorf("Java_data", "only available for host modules")
391		}
392	}
393}
394
395func (s *ShTest) addToDataModules(ctx android.ModuleContext, relPath string, path android.Path) {
396	if _, exists := s.dataModules[relPath]; exists {
397		ctx.ModuleErrorf("data modules have a conflicting installation path, %v - %s, %s",
398			relPath, s.dataModules[relPath].String(), path.String())
399		return
400	}
401	s.dataModules[relPath] = path
402	s.data = append(s.data, android.DataPath{SrcPath: path})
403}
404
405func (s *ShTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
406	s.ShBinary.generateAndroidBuildActions(ctx)
407
408	expandedData := android.PathsForModuleSrc(ctx, s.testProperties.Data)
409	// Emulate the data property for java_data dependencies.
410	for _, javaData := range ctx.GetDirectDepsWithTag(shTestJavaDataTag) {
411		expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...)
412	}
413	for _, d := range expandedData {
414		s.data = append(s.data, android.DataPath{SrcPath: d})
415	}
416
417	testDir := "nativetest"
418	if ctx.Target().Arch.ArchType.Multilib == "lib64" {
419		testDir = "nativetest64"
420	}
421	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
422		testDir = filepath.Join(testDir, ctx.Target().NativeBridgeRelativePath)
423	} else if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
424		testDir = filepath.Join(testDir, ctx.Arch().ArchType.String())
425	}
426	if s.SubDir() != "" {
427		// Don't add the module name to the installation path if sub_dir is specified for backward
428		// compatibility.
429		s.installDir = android.PathForModuleInstall(ctx, testDir, s.SubDir())
430	} else {
431		s.installDir = android.PathForModuleInstall(ctx, testDir, s.Name())
432	}
433
434	var configs []tradefed.Config
435	if Bool(s.testProperties.Require_root) {
436		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
437	} else {
438		options := []tradefed.Option{{Name: "force-root", Value: "false"}}
439		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options})
440	}
441	if len(s.testProperties.Data_device_bins) > 0 {
442		moduleName := s.Name()
443		remoteDir := "/data/local/tests/unrestricted/" + moduleName + "/"
444		options := []tradefed.Option{{Name: "cleanup", Value: "true"}}
445		for _, bin := range s.testProperties.Data_device_bins {
446			options = append(options, tradefed.Option{Name: "push-file", Key: bin, Value: remoteDir + bin})
447		}
448		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.PushFilePreparer", options})
449	}
450	s.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
451		TestConfigProp:         s.testProperties.Test_config,
452		TestConfigTemplateProp: s.testProperties.Test_config_template,
453		TestSuites:             s.testProperties.Test_suites,
454		Config:                 configs,
455		AutoGenConfig:          s.testProperties.Auto_gen_config,
456		OutputFileName:         s.outputFilePath.Base(),
457		DeviceTemplate:         "${ShellTestConfigTemplate}",
458		HostTemplate:           "${ShellTestConfigTemplate}",
459	})
460
461	s.dataModules = make(map[string]android.Path)
462	ctx.VisitDirectDeps(func(dep android.Module) {
463		depTag := ctx.OtherModuleDependencyTag(dep)
464		switch depTag {
465		case shTestDataBinsTag, shTestDataDeviceBinsTag:
466			path := android.OutputFileForModule(ctx, dep, "")
467			s.addToDataModules(ctx, path.Base(), path)
468		case shTestDataLibsTag, shTestDataDeviceLibsTag:
469			if cc, isCc := dep.(*cc.Module); isCc {
470				// Copy to an intermediate output directory to append "lib[64]" to the path,
471				// so that it's compatible with the default rpath values.
472				var relPath string
473				if cc.Arch().ArchType.Multilib == "lib64" {
474					relPath = filepath.Join("lib64", cc.OutputFile().Path().Base())
475				} else {
476					relPath = filepath.Join("lib", cc.OutputFile().Path().Base())
477				}
478				if _, exist := s.dataModules[relPath]; exist {
479					return
480				}
481				relocatedLib := android.PathForModuleOut(ctx, "relocated").Join(ctx, relPath)
482				ctx.Build(pctx, android.BuildParams{
483					Rule:   android.Cp,
484					Input:  cc.OutputFile().Path(),
485					Output: relocatedLib,
486				})
487				s.addToDataModules(ctx, relPath, relocatedLib)
488				return
489			}
490			property := "data_libs"
491			if depTag == shTestDataDeviceBinsTag {
492				property = "data_device_libs"
493			}
494			ctx.PropertyErrorf(property, "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
495		}
496	})
497
498	installedData := ctx.InstallTestData(s.installDir, s.data)
499	s.installedFile = ctx.InstallExecutable(s.installDir, s.outputFilePath.Base(), s.outputFilePath, installedData...)
500
501	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
502}
503
504func (s *ShTest) InstallInData() bool {
505	return true
506}
507
508func (s *ShTest) AndroidMkEntries() []android.AndroidMkEntries {
509	return []android.AndroidMkEntries{android.AndroidMkEntries{
510		Class:      "NATIVE_TESTS",
511		OutputFile: android.OptionalPathForPath(s.outputFilePath),
512		Include:    "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
513		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
514			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
515				s.customAndroidMkEntries(entries)
516				entries.SetPath("LOCAL_MODULE_PATH", s.installDir)
517				entries.AddCompatibilityTestSuites(s.testProperties.Test_suites...)
518				if s.testConfig != nil {
519					entries.SetPath("LOCAL_FULL_TEST_CONFIG", s.testConfig)
520				}
521				if s.testProperties.Data_bins != nil {
522					entries.AddStrings("LOCAL_TEST_DATA_BINS", s.testProperties.Data_bins...)
523				}
524				entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(s.testProperties.Per_testcase_directory))
525
526				s.testProperties.Test_options.SetAndroidMkEntries(entries)
527			},
528		},
529	}}
530}
531
532func initShBinaryModule(s *ShBinary) {
533	s.AddProperties(&s.properties)
534}
535
536// sh_binary is for a shell script or batch file to be installed as an
537// executable binary to <partition>/bin.
538func ShBinaryFactory() android.Module {
539	module := &ShBinary{}
540	initShBinaryModule(module)
541	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst)
542	return module
543}
544
545// sh_binary_host is for a shell script to be installed as an executable binary
546// to $(HOST_OUT)/bin.
547func ShBinaryHostFactory() android.Module {
548	module := &ShBinary{}
549	initShBinaryModule(module)
550	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
551	return module
552}
553
554// sh_test defines a shell script based test module.
555func ShTestFactory() android.Module {
556	module := &ShTest{}
557	initShBinaryModule(&module.ShBinary)
558	module.AddProperties(&module.testProperties)
559
560	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst)
561	return module
562}
563
564// sh_test_host defines a shell script based test module that runs on a host.
565func ShTestHostFactory() android.Module {
566	module := &ShTest{}
567	initShBinaryModule(&module.ShBinary)
568	module.AddProperties(&module.testProperties)
569	// Default sh_test_host to unit_tests = true
570	if module.testProperties.Test_options.Unit_test == nil {
571		module.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
572	}
573
574	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
575	return module
576}
577
578var Bool = proptools.Bool
579