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