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