• 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
106// Test option struct.
107type TestOptions struct {
108	// If the test is a hostside(no device required) unittest that shall be run during presubmit check.
109	Unit_test *bool
110}
111
112type TestProperties struct {
113	// list of compatibility suites (for example "cts", "vts") that the module should be
114	// installed into.
115	Test_suites []string `android:"arch_variant"`
116
117	// the name of the test configuration (for example "AndroidTest.xml") that should be
118	// installed with the module.
119	Test_config *string `android:"path,arch_variant"`
120
121	// list of files or filegroup modules that provide data that should be installed alongside
122	// the test.
123	Data []string `android:"path,arch_variant"`
124
125	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
126	// with root permission.
127	Require_root *bool
128
129	// the name of the test configuration template (for example "AndroidTestTemplate.xml") that
130	// should be installed with the module.
131	Test_config_template *string `android:"path,arch_variant"`
132
133	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
134	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
135	// explicitly.
136	Auto_gen_config *bool
137
138	// list of binary modules that should be installed alongside the test
139	Data_bins []string `android:"path,arch_variant"`
140
141	// list of library modules that should be installed alongside the test
142	Data_libs []string `android:"path,arch_variant"`
143
144	// list of device binary modules that should be installed alongside the test.
145	// Only available for host sh_test modules.
146	Data_device_bins []string `android:"path,arch_variant"`
147
148	// list of device library modules that should be installed alongside the test.
149	// Only available for host sh_test modules.
150	Data_device_libs []string `android:"path,arch_variant"`
151
152	// Install the test into a folder named for the module in all test suites.
153	Per_testcase_directory *bool
154
155	// Test options.
156	Test_options TestOptions
157}
158
159type ShBinary struct {
160	android.ModuleBase
161	android.BazelModuleBase
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.Paths
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.ModuleBase.InstallInRecovery() && !s.ModuleBase.InstallInRamdisk()
217}
218
219func (s *ShBinary) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
220	return proptools.Bool(s.properties.Ramdisk_available) || s.ModuleBase.InstallInRamdisk()
221}
222
223func (s *ShBinary) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
224	return proptools.Bool(s.properties.Vendor_ramdisk_available) || s.ModuleBase.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.ModuleBase.InstallInRecovery()
233}
234
235func (s *ShBinary) ExtraImageVariations(ctx android.BaseModuleContext) []string {
236	return nil
237}
238
239func (s *ShBinary) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
240}
241
242func (s *ShBinary) generateAndroidBuildActions(ctx android.ModuleContext) {
243	if s.properties.Src == nil {
244		ctx.PropertyErrorf("src", "missing prebuilt source file")
245	}
246
247	s.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(s.properties.Src))
248	filename := proptools.String(s.properties.Filename)
249	filenameFromSrc := proptools.Bool(s.properties.Filename_from_src)
250	if filename == "" {
251		if filenameFromSrc {
252			filename = s.sourceFilePath.Base()
253		} else {
254			filename = ctx.ModuleName()
255		}
256	} else if filenameFromSrc {
257		ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
258		return
259	}
260	s.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
261
262	// This ensures that outputFilePath has the correct name for others to
263	// use, as the source file may have a different name.
264	ctx.Build(pctx, android.BuildParams{
265		Rule:   android.CpExecutable,
266		Output: s.outputFilePath,
267		Input:  s.sourceFilePath,
268	})
269}
270
271func (s *ShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
272	s.generateAndroidBuildActions(ctx)
273	installDir := android.PathForModuleInstall(ctx, "bin", proptools.String(s.properties.Sub_dir))
274	if !s.Installable() {
275		s.SkipInstall()
276	}
277	s.installedFile = ctx.InstallExecutable(installDir, s.outputFilePath.Base(), s.outputFilePath)
278	for _, symlink := range s.Symlinks() {
279		ctx.InstallSymlink(installDir, symlink, s.installedFile)
280	}
281}
282
283func (s *ShBinary) AndroidMkEntries() []android.AndroidMkEntries {
284	return []android.AndroidMkEntries{android.AndroidMkEntries{
285		Class:      "EXECUTABLES",
286		OutputFile: android.OptionalPathForPath(s.outputFilePath),
287		Include:    "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
288		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
289			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
290				s.customAndroidMkEntries(entries)
291				entries.SetString("LOCAL_MODULE_RELATIVE_PATH", proptools.String(s.properties.Sub_dir))
292				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !s.Installable())
293			},
294		},
295	}}
296}
297
298func (s *ShBinary) customAndroidMkEntries(entries *android.AndroidMkEntries) {
299	entries.SetString("LOCAL_MODULE_SUFFIX", "")
300	entries.SetString("LOCAL_MODULE_STEM", s.outputFilePath.Rel())
301	if len(s.properties.Symlinks) > 0 {
302		entries.SetString("LOCAL_MODULE_SYMLINKS", strings.Join(s.properties.Symlinks, " "))
303	}
304}
305
306type dependencyTag struct {
307	blueprint.BaseDependencyTag
308	name string
309}
310
311var (
312	shTestDataBinsTag       = dependencyTag{name: "dataBins"}
313	shTestDataLibsTag       = dependencyTag{name: "dataLibs"}
314	shTestDataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"}
315	shTestDataDeviceLibsTag = dependencyTag{name: "dataDeviceLibs"}
316)
317
318var sharedLibVariations = []blueprint.Variation{{Mutator: "link", Variation: "shared"}}
319
320func (s *ShTest) DepsMutator(ctx android.BottomUpMutatorContext) {
321	s.ShBinary.DepsMutator(ctx)
322
323	ctx.AddFarVariationDependencies(ctx.Target().Variations(), shTestDataBinsTag, s.testProperties.Data_bins...)
324	ctx.AddFarVariationDependencies(append(ctx.Target().Variations(), sharedLibVariations...),
325		shTestDataLibsTag, s.testProperties.Data_libs...)
326	if (ctx.Target().Os.Class == android.Host || ctx.BazelConversionMode()) && len(ctx.Config().Targets[android.Android]) > 0 {
327		deviceVariations := ctx.Config().AndroidFirstDeviceTarget.Variations()
328		ctx.AddFarVariationDependencies(deviceVariations, shTestDataDeviceBinsTag, s.testProperties.Data_device_bins...)
329		ctx.AddFarVariationDependencies(append(deviceVariations, sharedLibVariations...),
330			shTestDataDeviceLibsTag, s.testProperties.Data_device_libs...)
331	} else if ctx.Target().Os.Class != android.Host {
332		if len(s.testProperties.Data_device_bins) > 0 {
333			ctx.PropertyErrorf("data_device_bins", "only available for host modules")
334		}
335		if len(s.testProperties.Data_device_libs) > 0 {
336			ctx.PropertyErrorf("data_device_libs", "only available for host modules")
337		}
338	}
339}
340
341func (s *ShTest) addToDataModules(ctx android.ModuleContext, relPath string, path android.Path) {
342	if _, exists := s.dataModules[relPath]; exists {
343		ctx.ModuleErrorf("data modules have a conflicting installation path, %v - %s, %s",
344			relPath, s.dataModules[relPath].String(), path.String())
345		return
346	}
347	s.dataModules[relPath] = path
348}
349
350func (s *ShTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
351	s.ShBinary.generateAndroidBuildActions(ctx)
352	testDir := "nativetest"
353	if ctx.Target().Arch.ArchType.Multilib == "lib64" {
354		testDir = "nativetest64"
355	}
356	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
357		testDir = filepath.Join(testDir, ctx.Target().NativeBridgeRelativePath)
358	} else if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
359		testDir = filepath.Join(testDir, ctx.Arch().ArchType.String())
360	}
361	if s.SubDir() != "" {
362		// Don't add the module name to the installation path if sub_dir is specified for backward
363		// compatibility.
364		s.installDir = android.PathForModuleInstall(ctx, testDir, s.SubDir())
365	} else {
366		s.installDir = android.PathForModuleInstall(ctx, testDir, s.Name())
367	}
368	s.installedFile = ctx.InstallExecutable(s.installDir, s.outputFilePath.Base(), s.outputFilePath)
369
370	s.data = android.PathsForModuleSrc(ctx, s.testProperties.Data)
371
372	var configs []tradefed.Config
373	if Bool(s.testProperties.Require_root) {
374		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
375	} else {
376		options := []tradefed.Option{{Name: "force-root", Value: "false"}}
377		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options})
378	}
379	if len(s.testProperties.Data_device_bins) > 0 {
380		moduleName := s.Name()
381		remoteDir := "/data/local/tests/unrestricted/" + moduleName + "/"
382		options := []tradefed.Option{{Name: "cleanup", Value: "true"}}
383		for _, bin := range s.testProperties.Data_device_bins {
384			options = append(options, tradefed.Option{Name: "push-file", Key: bin, Value: remoteDir + bin})
385		}
386		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.PushFilePreparer", options})
387	}
388	s.testConfig = tradefed.AutoGenShellTestConfig(ctx, s.testProperties.Test_config,
389		s.testProperties.Test_config_template, s.testProperties.Test_suites, configs, s.testProperties.Auto_gen_config, s.outputFilePath.Base())
390
391	s.dataModules = make(map[string]android.Path)
392	ctx.VisitDirectDeps(func(dep android.Module) {
393		depTag := ctx.OtherModuleDependencyTag(dep)
394		switch depTag {
395		case shTestDataBinsTag, shTestDataDeviceBinsTag:
396			path := android.OutputFileForModule(ctx, dep, "")
397			s.addToDataModules(ctx, path.Base(), path)
398		case shTestDataLibsTag, shTestDataDeviceLibsTag:
399			if cc, isCc := dep.(*cc.Module); isCc {
400				// Copy to an intermediate output directory to append "lib[64]" to the path,
401				// so that it's compatible with the default rpath values.
402				var relPath string
403				if cc.Arch().ArchType.Multilib == "lib64" {
404					relPath = filepath.Join("lib64", cc.OutputFile().Path().Base())
405				} else {
406					relPath = filepath.Join("lib", cc.OutputFile().Path().Base())
407				}
408				if _, exist := s.dataModules[relPath]; exist {
409					return
410				}
411				relocatedLib := android.PathForModuleOut(ctx, "relocated", relPath)
412				ctx.Build(pctx, android.BuildParams{
413					Rule:   android.Cp,
414					Input:  cc.OutputFile().Path(),
415					Output: relocatedLib,
416				})
417				s.addToDataModules(ctx, relPath, relocatedLib)
418				return
419			}
420			property := "data_libs"
421			if depTag == shTestDataDeviceBinsTag {
422				property = "data_device_libs"
423			}
424			ctx.PropertyErrorf(property, "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
425		}
426	})
427}
428
429func (s *ShTest) InstallInData() bool {
430	return true
431}
432
433func (s *ShTest) AndroidMkEntries() []android.AndroidMkEntries {
434	return []android.AndroidMkEntries{android.AndroidMkEntries{
435		Class:      "NATIVE_TESTS",
436		OutputFile: android.OptionalPathForPath(s.outputFilePath),
437		Include:    "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
438		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
439			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
440				s.customAndroidMkEntries(entries)
441				entries.SetPath("LOCAL_MODULE_PATH", s.installDir)
442				entries.AddCompatibilityTestSuites(s.testProperties.Test_suites...)
443				if s.testConfig != nil {
444					entries.SetPath("LOCAL_FULL_TEST_CONFIG", s.testConfig)
445				}
446				for _, d := range s.data {
447					rel := d.Rel()
448					path := d.String()
449					if !strings.HasSuffix(path, rel) {
450						panic(fmt.Errorf("path %q does not end with %q", path, rel))
451					}
452					path = strings.TrimSuffix(path, rel)
453					entries.AddStrings("LOCAL_TEST_DATA", path+":"+rel)
454				}
455				relPaths := make([]string, 0)
456				for relPath, _ := range s.dataModules {
457					relPaths = append(relPaths, relPath)
458				}
459				sort.Strings(relPaths)
460				for _, relPath := range relPaths {
461					dir := strings.TrimSuffix(s.dataModules[relPath].String(), relPath)
462					entries.AddStrings("LOCAL_TEST_DATA", dir+":"+relPath)
463				}
464				if s.testProperties.Data_bins != nil {
465					entries.AddStrings("LOCAL_TEST_DATA_BINS", s.testProperties.Data_bins...)
466				}
467				if Bool(s.testProperties.Test_options.Unit_test) {
468					entries.SetBool("LOCAL_IS_UNIT_TEST", true)
469				}
470				entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(s.testProperties.Per_testcase_directory))
471			},
472		},
473	}}
474}
475
476func InitShBinaryModule(s *ShBinary) {
477	s.AddProperties(&s.properties)
478	android.InitBazelModule(s)
479}
480
481// sh_binary is for a shell script or batch file to be installed as an
482// executable binary to <partition>/bin.
483func ShBinaryFactory() android.Module {
484	module := &ShBinary{}
485	InitShBinaryModule(module)
486	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst)
487	return module
488}
489
490// sh_binary_host is for a shell script to be installed as an executable binary
491// to $(HOST_OUT)/bin.
492func ShBinaryHostFactory() android.Module {
493	module := &ShBinary{}
494	InitShBinaryModule(module)
495	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
496	return module
497}
498
499// sh_test defines a shell script based test module.
500func ShTestFactory() android.Module {
501	module := &ShTest{}
502	InitShBinaryModule(&module.ShBinary)
503	module.AddProperties(&module.testProperties)
504
505	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst)
506	return module
507}
508
509// sh_test_host defines a shell script based test module that runs on a host.
510func ShTestHostFactory() android.Module {
511	module := &ShTest{}
512	InitShBinaryModule(&module.ShBinary)
513	module.AddProperties(&module.testProperties)
514	// Default sh_test_host to unit_tests = true
515	if module.testProperties.Test_options.Unit_test == nil {
516		module.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
517	}
518
519	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
520	return module
521}
522
523type bazelShBinaryAttributes struct {
524	Srcs     bazel.LabelListAttribute
525	Filename *string
526	Sub_dir  *string
527	// Bazel also supports the attributes below, but (so far) these are not required for Bionic
528	// deps
529	// data
530	// args
531	// compatible_with
532	// deprecation
533	// distribs
534	// env
535	// exec_compatible_with
536	// exec_properties
537	// features
538	// licenses
539	// output_licenses
540	// restricted_to
541	// tags
542	// target_compatible_with
543	// testonly
544	// toolchains
545	// visibility
546}
547
548func (m *ShBinary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
549	srcs := bazel.MakeLabelListAttribute(
550		android.BazelLabelForModuleSrc(ctx, []string{*m.properties.Src}))
551
552	var filename *string
553	if m.properties.Filename != nil {
554		filename = m.properties.Filename
555	}
556
557	var subDir *string
558	if m.properties.Sub_dir != nil {
559		subDir = m.properties.Sub_dir
560	}
561
562	attrs := &bazelShBinaryAttributes{
563		Srcs:     srcs,
564		Filename: filename,
565		Sub_dir:  subDir,
566	}
567
568	props := bazel.BazelTargetModuleProperties{
569		Rule_class:        "sh_binary",
570		Bzl_load_location: "//build/bazel/rules:sh_binary.bzl",
571	}
572
573	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
574}
575
576var Bool = proptools.Bool
577
578var _ snapshot.RelativeInstallPath = (*ShBinary)(nil)
579