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	"strings"
21
22	"github.com/google/blueprint/proptools"
23
24	"android/soong/android"
25	"android/soong/tradefed"
26)
27
28// sh_binary is for shell scripts (and batch files) that are installed as
29// executable files into .../bin/
30//
31// Do not use them for prebuilt C/C++/etc files.  Use cc_prebuilt_binary
32// instead.
33
34var pctx = android.NewPackageContext("android/soong/sh")
35
36func init() {
37	pctx.Import("android/soong/android")
38
39	android.RegisterModuleType("sh_binary", ShBinaryFactory)
40	android.RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
41	android.RegisterModuleType("sh_test", ShTestFactory)
42	android.RegisterModuleType("sh_test_host", ShTestHostFactory)
43}
44
45type shBinaryProperties struct {
46	// Source file of this prebuilt.
47	Src *string `android:"path,arch_variant"`
48
49	// optional subdirectory under which this file is installed into
50	Sub_dir *string `android:"arch_variant"`
51
52	// optional name for the installed file. If unspecified, name of the module is used as the file name
53	Filename *string `android:"arch_variant"`
54
55	// when set to true, and filename property is not set, the name for the installed file
56	// is the same as the file name of the source file.
57	Filename_from_src *bool `android:"arch_variant"`
58
59	// Whether this module is directly installable to one of the partitions. Default: true.
60	Installable *bool
61
62	// install symlinks to the binary
63	Symlinks []string `android:"arch_variant"`
64}
65
66type TestProperties struct {
67	// list of compatibility suites (for example "cts", "vts") that the module should be
68	// installed into.
69	Test_suites []string `android:"arch_variant"`
70
71	// the name of the test configuration (for example "AndroidTest.xml") that should be
72	// installed with the module.
73	Test_config *string `android:"path,arch_variant"`
74
75	// list of files or filegroup modules that provide data that should be installed alongside
76	// the test.
77	Data []string `android:"path,arch_variant"`
78
79	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
80	// with root permission.
81	Require_root *bool
82
83	// the name of the test configuration template (for example "AndroidTestTemplate.xml") that
84	// should be installed with the module.
85	Test_config_template *string `android:"path,arch_variant"`
86
87	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
88	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
89	// explicitly.
90	Auto_gen_config *bool
91}
92
93type ShBinary struct {
94	android.ModuleBase
95
96	properties shBinaryProperties
97
98	sourceFilePath android.Path
99	outputFilePath android.OutputPath
100	installedFile  android.InstallPath
101}
102
103var _ android.HostToolProvider = (*ShBinary)(nil)
104
105type ShTest struct {
106	ShBinary
107
108	testProperties TestProperties
109
110	installDir android.InstallPath
111
112	data       android.Paths
113	testConfig android.Path
114}
115
116func (s *ShBinary) HostToolPath() android.OptionalPath {
117	return android.OptionalPathForPath(s.installedFile)
118}
119
120func (s *ShBinary) DepsMutator(ctx android.BottomUpMutatorContext) {
121	if s.properties.Src == nil {
122		ctx.PropertyErrorf("src", "missing prebuilt source file")
123	}
124}
125
126func (s *ShBinary) OutputFile() android.OutputPath {
127	return s.outputFilePath
128}
129
130func (s *ShBinary) SubDir() string {
131	return proptools.String(s.properties.Sub_dir)
132}
133
134func (s *ShBinary) Installable() bool {
135	return s.properties.Installable == nil || proptools.Bool(s.properties.Installable)
136}
137
138func (s *ShBinary) Symlinks() []string {
139	return s.properties.Symlinks
140}
141
142func (s *ShBinary) generateAndroidBuildActions(ctx android.ModuleContext) {
143	s.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(s.properties.Src))
144	filename := proptools.String(s.properties.Filename)
145	filename_from_src := proptools.Bool(s.properties.Filename_from_src)
146	if filename == "" {
147		if filename_from_src {
148			filename = s.sourceFilePath.Base()
149		} else {
150			filename = ctx.ModuleName()
151		}
152	} else if filename_from_src {
153		ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
154		return
155	}
156	s.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
157
158	// This ensures that outputFilePath has the correct name for others to
159	// use, as the source file may have a different name.
160	ctx.Build(pctx, android.BuildParams{
161		Rule:   android.CpExecutable,
162		Output: s.outputFilePath,
163		Input:  s.sourceFilePath,
164	})
165}
166
167func (s *ShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
168	s.generateAndroidBuildActions(ctx)
169	installDir := android.PathForModuleInstall(ctx, "bin", proptools.String(s.properties.Sub_dir))
170	s.installedFile = ctx.InstallExecutable(installDir, s.outputFilePath.Base(), s.outputFilePath)
171}
172
173func (s *ShBinary) AndroidMkEntries() []android.AndroidMkEntries {
174	return []android.AndroidMkEntries{android.AndroidMkEntries{
175		Class:      "EXECUTABLES",
176		OutputFile: android.OptionalPathForPath(s.outputFilePath),
177		Include:    "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
178		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
179			func(entries *android.AndroidMkEntries) {
180				s.customAndroidMkEntries(entries)
181				entries.SetString("LOCAL_MODULE_RELATIVE_PATH", proptools.String(s.properties.Sub_dir))
182			},
183		},
184	}}
185}
186
187func (s *ShBinary) customAndroidMkEntries(entries *android.AndroidMkEntries) {
188	entries.SetString("LOCAL_MODULE_SUFFIX", "")
189	entries.SetString("LOCAL_MODULE_STEM", s.outputFilePath.Rel())
190	if len(s.properties.Symlinks) > 0 {
191		entries.SetString("LOCAL_MODULE_SYMLINKS", strings.Join(s.properties.Symlinks, " "))
192	}
193}
194
195func (s *ShTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
196	s.ShBinary.generateAndroidBuildActions(ctx)
197	testDir := "nativetest"
198	if ctx.Target().Arch.ArchType.Multilib == "lib64" {
199		testDir = "nativetest64"
200	}
201	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
202		testDir = filepath.Join(testDir, ctx.Target().NativeBridgeRelativePath)
203	} else if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
204		testDir = filepath.Join(testDir, ctx.Arch().ArchType.String())
205	}
206	if s.SubDir() != "" {
207		// Don't add the module name to the installation path if sub_dir is specified for backward
208		// compatibility.
209		s.installDir = android.PathForModuleInstall(ctx, testDir, s.SubDir())
210	} else {
211		s.installDir = android.PathForModuleInstall(ctx, testDir, s.Name())
212	}
213	s.installedFile = ctx.InstallExecutable(s.installDir, s.outputFilePath.Base(), s.outputFilePath)
214
215	s.data = android.PathsForModuleSrc(ctx, s.testProperties.Data)
216
217	var configs []tradefed.Config
218	if Bool(s.testProperties.Require_root) {
219		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
220	} else {
221		options := []tradefed.Option{{Name: "force-root", Value: "false"}}
222		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options})
223	}
224	s.testConfig = tradefed.AutoGenShellTestConfig(ctx, s.testProperties.Test_config,
225		s.testProperties.Test_config_template, s.testProperties.Test_suites, configs, s.testProperties.Auto_gen_config, s.outputFilePath.Base())
226}
227
228func (s *ShTest) InstallInData() bool {
229	return true
230}
231
232func (s *ShTest) AndroidMkEntries() []android.AndroidMkEntries {
233	return []android.AndroidMkEntries{android.AndroidMkEntries{
234		Class:      "NATIVE_TESTS",
235		OutputFile: android.OptionalPathForPath(s.outputFilePath),
236		Include:    "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
237		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
238			func(entries *android.AndroidMkEntries) {
239				s.customAndroidMkEntries(entries)
240				entries.SetPath("LOCAL_MODULE_PATH", s.installDir.ToMakePath())
241				entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", s.testProperties.Test_suites...)
242				if s.testConfig != nil {
243					entries.SetPath("LOCAL_FULL_TEST_CONFIG", s.testConfig)
244				}
245				for _, d := range s.data {
246					rel := d.Rel()
247					path := d.String()
248					if !strings.HasSuffix(path, rel) {
249						panic(fmt.Errorf("path %q does not end with %q", path, rel))
250					}
251					path = strings.TrimSuffix(path, rel)
252					entries.AddStrings("LOCAL_TEST_DATA", path+":"+rel)
253				}
254			},
255		},
256	}}
257}
258
259func InitShBinaryModule(s *ShBinary) {
260	s.AddProperties(&s.properties)
261}
262
263// sh_binary is for a shell script or batch file to be installed as an
264// executable binary to <partition>/bin.
265func ShBinaryFactory() android.Module {
266	module := &ShBinary{}
267	InitShBinaryModule(module)
268	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst)
269	return module
270}
271
272// sh_binary_host is for a shell script to be installed as an executable binary
273// to $(HOST_OUT)/bin.
274func ShBinaryHostFactory() android.Module {
275	module := &ShBinary{}
276	InitShBinaryModule(module)
277	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
278	return module
279}
280
281// sh_test defines a shell script based test module.
282func ShTestFactory() android.Module {
283	module := &ShTest{}
284	InitShBinaryModule(&module.ShBinary)
285	module.AddProperties(&module.testProperties)
286
287	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst)
288	return module
289}
290
291// sh_test_host defines a shell script based test module that runs on a host.
292func ShTestHostFactory() android.Module {
293	module := &ShTest{}
294	InitShBinaryModule(&module.ShBinary)
295	module.AddProperties(&module.testProperties)
296
297	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
298	return module
299}
300
301var Bool = proptools.Bool
302