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