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