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 "path/filepath" 19 "strings" 20 21 "android/soong/testing" 22 23 "github.com/google/blueprint" 24 "github.com/google/blueprint/proptools" 25 26 "android/soong/android" 27 "android/soong/cc" 28 "android/soong/tradefed" 29) 30 31// sh_binary is for shell scripts (and batch files) that are installed as 32// executable files into .../bin/ 33// 34// Do not use them for prebuilt C/C++/etc files. Use cc_prebuilt_binary 35// instead. 36 37var pctx = android.NewPackageContext("android/soong/sh") 38 39func init() { 40 pctx.Import("android/soong/android") 41 42 registerShBuildComponents(android.InitRegistrationContext) 43} 44 45func registerShBuildComponents(ctx android.RegistrationContext) { 46 ctx.RegisterModuleType("sh_binary", ShBinaryFactory) 47 ctx.RegisterModuleType("sh_binary_host", ShBinaryHostFactory) 48 ctx.RegisterModuleType("sh_test", ShTestFactory) 49 ctx.RegisterModuleType("sh_test_host", ShTestHostFactory) 50} 51 52// Test fixture preparer that will register most sh build components. 53// 54// Singletons and mutators should only be added here if they are needed for a majority of sh 55// module types, otherwise they should be added under a separate preparer to allow them to be 56// selected only when needed to reduce test execution time. 57// 58// Module types do not have much of an overhead unless they are used so this should include as many 59// module types as possible. The exceptions are those module types that require mutators and/or 60// singletons in order to function in which case they should be kept together in a separate 61// preparer. 62var PrepareForTestWithShBuildComponents = android.GroupFixturePreparers( 63 android.FixtureRegisterWithContext(registerShBuildComponents), 64) 65 66type shBinaryProperties struct { 67 // Source file of this prebuilt. 68 Src *string `android:"path,arch_variant"` 69 70 // optional subdirectory under which this file is installed into 71 Sub_dir *string `android:"arch_variant"` 72 73 // optional name for the installed file. If unspecified, name of the module is used as the file name 74 Filename *string `android:"arch_variant"` 75 76 // when set to true, and filename property is not set, the name for the installed file 77 // is the same as the file name of the source file. 78 Filename_from_src *bool `android:"arch_variant"` 79 80 // Whether this module is directly installable to one of the partitions. Default: true. 81 Installable *bool 82 83 // install symlinks to the binary 84 Symlinks []string `android:"arch_variant"` 85 86 // Make this module available when building for ramdisk. 87 // On device without a dedicated recovery partition, the module is only 88 // available after switching root into 89 // /first_stage_ramdisk. To expose the module before switching root, install 90 // the recovery variant instead. 91 Ramdisk_available *bool 92 93 // Make this module available when building for vendor ramdisk. 94 // On device without a dedicated recovery partition, the module is only 95 // available after switching root into 96 // /first_stage_ramdisk. To expose the module before switching root, install 97 // the recovery variant instead. 98 Vendor_ramdisk_available *bool 99 100 // Make this module available when building for recovery. 101 Recovery_available *bool 102 103 // The name of the image this module is built for 104 ImageVariation string `blueprint:"mutated"` 105 106 // Suffix for the name of Android.mk entries generated by this module 107 SubName string `blueprint:"mutated"` 108} 109 110type TestProperties struct { 111 // list of compatibility suites (for example "cts", "vts") that the module should be 112 // installed into. 113 Test_suites []string `android:"arch_variant"` 114 115 // the name of the test configuration (for example "AndroidTest.xml") that should be 116 // installed with the module. 117 Test_config *string `android:"path,arch_variant"` 118 119 // list of files or filegroup modules that provide data that should be installed alongside 120 // the test. 121 Data []string `android:"path,arch_variant"` 122 123 // Add RootTargetPreparer to auto generated test config. This guarantees the test to run 124 // with root permission. 125 Require_root *bool 126 127 // the name of the test configuration template (for example "AndroidTestTemplate.xml") that 128 // should be installed with the module. 129 Test_config_template *string `android:"path,arch_variant"` 130 131 // Flag to indicate whether or not to create test config automatically. If AndroidTest.xml 132 // doesn't exist next to the Android.bp, this attribute doesn't need to be set to true 133 // explicitly. 134 Auto_gen_config *bool 135 136 // list of binary modules that should be installed alongside the test 137 Data_bins []string `android:"path,arch_variant"` 138 139 // list of library modules that should be installed alongside the test 140 Data_libs []string `android:"path,arch_variant"` 141 142 // list of device binary modules that should be installed alongside the test. 143 // Only available for host sh_test modules. 144 Data_device_bins []string `android:"path,arch_variant"` 145 146 // list of device library modules that should be installed alongside the test. 147 // Only available for host sh_test modules. 148 Data_device_libs []string `android:"path,arch_variant"` 149 150 // list of java modules that provide data that should be installed alongside the test. 151 Java_data []string 152 153 // Install the test into a folder named for the module in all test suites. 154 Per_testcase_directory *bool 155 156 // Test options. 157 Test_options android.CommonTestOptions 158} 159 160type ShBinary struct { 161 android.ModuleBase 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.DataPath 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.InstallInRecovery() && !s.InstallInRamdisk() && !s.InstallInVendorRamdisk() && !s.ModuleBase.InstallInVendor() 217} 218 219func (s *ShBinary) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool { 220 return proptools.Bool(s.properties.Ramdisk_available) || s.InstallInRamdisk() 221} 222 223func (s *ShBinary) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { 224 return proptools.Bool(s.properties.Vendor_ramdisk_available) || s.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.InstallInRecovery() 233} 234 235func (s *ShBinary) ExtraImageVariations(ctx android.BaseModuleContext) []string { 236 extraVariations := []string{} 237 if s.InstallInProduct() { 238 extraVariations = append(extraVariations, cc.ProductVariation) 239 } 240 if s.InstallInVendor() { 241 extraVariations = append(extraVariations, cc.VendorVariation) 242 } 243 return extraVariations 244} 245 246func (s *ShBinary) SetImageVariation(ctx android.BaseModuleContext, variation string) { 247 s.properties.ImageVariation = variation 248} 249 250// Overrides ModuleBase.InstallInRamdisk() so that the install rule respects 251// Ramdisk_available property for ramdisk variant 252func (s *ShBinary) InstallInRamdisk() bool { 253 return s.ModuleBase.InstallInRamdisk() || 254 (proptools.Bool(s.properties.Ramdisk_available) && s.properties.ImageVariation == android.RamdiskVariation) 255} 256 257// Overrides ModuleBase.InstallInVendorRamdisk() so that the install rule respects 258// Vendor_ramdisk_available property for vendor ramdisk variant 259func (s *ShBinary) InstallInVendorRamdisk() bool { 260 return s.ModuleBase.InstallInVendorRamdisk() || 261 (proptools.Bool(s.properties.Vendor_ramdisk_available) && s.properties.ImageVariation == android.VendorRamdiskVariation) 262} 263 264// Overrides ModuleBase.InstallInRecovery() so that the install rule respects 265// Recovery_available property for recovery variant 266func (s *ShBinary) InstallInRecovery() bool { 267 return s.ModuleBase.InstallInRecovery() || 268 (proptools.Bool(s.properties.Recovery_available) && s.properties.ImageVariation == android.RecoveryVariation) 269} 270 271func (s *ShBinary) generateAndroidBuildActions(ctx android.ModuleContext) { 272 if s.properties.Src == nil { 273 ctx.PropertyErrorf("src", "missing prebuilt source file") 274 } 275 276 s.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(s.properties.Src)) 277 filename := proptools.String(s.properties.Filename) 278 filenameFromSrc := proptools.Bool(s.properties.Filename_from_src) 279 if filename == "" { 280 if filenameFromSrc { 281 filename = s.sourceFilePath.Base() 282 } else { 283 filename = ctx.ModuleName() 284 } 285 } else if filenameFromSrc { 286 ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true") 287 return 288 } 289 s.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath 290 291 // This ensures that outputFilePath has the correct name for others to 292 // use, as the source file may have a different name. 293 ctx.Build(pctx, android.BuildParams{ 294 Rule: android.CpExecutable, 295 Output: s.outputFilePath, 296 Input: s.sourceFilePath, 297 }) 298 299 s.properties.SubName = s.GetSubname(ctx) 300 301 android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: []string{s.sourceFilePath.String()}}) 302 303 ctx.SetOutputFiles(android.Paths{s.outputFilePath}, "") 304} 305 306func (s *ShBinary) GetSubname(ctx android.ModuleContext) string { 307 ret := "" 308 if s.properties.ImageVariation != "" { 309 if s.properties.ImageVariation != cc.VendorVariation { 310 ret = "." + s.properties.ImageVariation 311 } 312 } 313 return ret 314} 315 316func (s *ShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) { 317 s.generateAndroidBuildActions(ctx) 318 installDir := android.PathForModuleInstall(ctx, "bin", proptools.String(s.properties.Sub_dir)) 319 if !s.Installable() { 320 s.SkipInstall() 321 } 322 s.installedFile = ctx.InstallExecutable(installDir, s.outputFilePath.Base(), s.outputFilePath) 323 for _, symlink := range s.Symlinks() { 324 ctx.InstallSymlink(installDir, symlink, s.installedFile) 325 } 326} 327 328func (s *ShBinary) AndroidMkEntries() []android.AndroidMkEntries { 329 return []android.AndroidMkEntries{{ 330 Class: "EXECUTABLES", 331 OutputFile: android.OptionalPathForPath(s.outputFilePath), 332 Include: "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk", 333 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 334 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 335 s.customAndroidMkEntries(entries) 336 entries.SetString("LOCAL_MODULE_RELATIVE_PATH", proptools.String(s.properties.Sub_dir)) 337 entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !s.Installable()) 338 }, 339 }, 340 SubName: s.properties.SubName, 341 }} 342} 343 344func (s *ShBinary) customAndroidMkEntries(entries *android.AndroidMkEntries) { 345 entries.SetString("LOCAL_MODULE_SUFFIX", "") 346 entries.SetString("LOCAL_MODULE_STEM", s.outputFilePath.Rel()) 347 if len(s.properties.Symlinks) > 0 { 348 entries.SetString("LOCAL_MODULE_SYMLINKS", strings.Join(s.properties.Symlinks, " ")) 349 } 350} 351 352type dependencyTag struct { 353 blueprint.BaseDependencyTag 354 name string 355} 356 357var ( 358 shTestDataBinsTag = dependencyTag{name: "dataBins"} 359 shTestDataLibsTag = dependencyTag{name: "dataLibs"} 360 shTestDataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"} 361 shTestDataDeviceLibsTag = dependencyTag{name: "dataDeviceLibs"} 362 shTestJavaDataTag = dependencyTag{name: "javaData"} 363) 364 365var sharedLibVariations = []blueprint.Variation{{Mutator: "link", Variation: "shared"}} 366 367func (s *ShTest) DepsMutator(ctx android.BottomUpMutatorContext) { 368 s.ShBinary.DepsMutator(ctx) 369 370 ctx.AddFarVariationDependencies(ctx.Target().Variations(), shTestDataBinsTag, s.testProperties.Data_bins...) 371 ctx.AddFarVariationDependencies(append(ctx.Target().Variations(), sharedLibVariations...), 372 shTestDataLibsTag, s.testProperties.Data_libs...) 373 if ctx.Target().Os.Class == android.Host && len(ctx.Config().Targets[android.Android]) > 0 { 374 deviceVariations := ctx.Config().AndroidFirstDeviceTarget.Variations() 375 ctx.AddFarVariationDependencies(deviceVariations, shTestDataDeviceBinsTag, s.testProperties.Data_device_bins...) 376 ctx.AddFarVariationDependencies(append(deviceVariations, sharedLibVariations...), 377 shTestDataDeviceLibsTag, s.testProperties.Data_device_libs...) 378 379 javaDataVariation := []blueprint.Variation{{"arch", android.Common.String()}} 380 ctx.AddVariationDependencies(javaDataVariation, shTestJavaDataTag, s.testProperties.Java_data...) 381 382 } else if ctx.Target().Os.Class != android.Host { 383 if len(s.testProperties.Data_device_bins) > 0 { 384 ctx.PropertyErrorf("data_device_bins", "only available for host modules") 385 } 386 if len(s.testProperties.Data_device_libs) > 0 { 387 ctx.PropertyErrorf("data_device_libs", "only available for host modules") 388 } 389 if len(s.testProperties.Java_data) > 0 { 390 ctx.PropertyErrorf("Java_data", "only available for host modules") 391 } 392 } 393} 394 395func (s *ShTest) addToDataModules(ctx android.ModuleContext, relPath string, path android.Path) { 396 if _, exists := s.dataModules[relPath]; exists { 397 ctx.ModuleErrorf("data modules have a conflicting installation path, %v - %s, %s", 398 relPath, s.dataModules[relPath].String(), path.String()) 399 return 400 } 401 s.dataModules[relPath] = path 402 s.data = append(s.data, android.DataPath{SrcPath: path}) 403} 404 405func (s *ShTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { 406 s.ShBinary.generateAndroidBuildActions(ctx) 407 408 expandedData := android.PathsForModuleSrc(ctx, s.testProperties.Data) 409 // Emulate the data property for java_data dependencies. 410 for _, javaData := range ctx.GetDirectDepsWithTag(shTestJavaDataTag) { 411 expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...) 412 } 413 for _, d := range expandedData { 414 s.data = append(s.data, android.DataPath{SrcPath: d}) 415 } 416 417 testDir := "nativetest" 418 if ctx.Target().Arch.ArchType.Multilib == "lib64" { 419 testDir = "nativetest64" 420 } 421 if ctx.Target().NativeBridge == android.NativeBridgeEnabled { 422 testDir = filepath.Join(testDir, ctx.Target().NativeBridgeRelativePath) 423 } else if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) { 424 testDir = filepath.Join(testDir, ctx.Arch().ArchType.String()) 425 } 426 if s.SubDir() != "" { 427 // Don't add the module name to the installation path if sub_dir is specified for backward 428 // compatibility. 429 s.installDir = android.PathForModuleInstall(ctx, testDir, s.SubDir()) 430 } else { 431 s.installDir = android.PathForModuleInstall(ctx, testDir, s.Name()) 432 } 433 434 var configs []tradefed.Config 435 if Bool(s.testProperties.Require_root) { 436 configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil}) 437 } else { 438 options := []tradefed.Option{{Name: "force-root", Value: "false"}} 439 configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options}) 440 } 441 if len(s.testProperties.Data_device_bins) > 0 { 442 moduleName := s.Name() 443 remoteDir := "/data/local/tests/unrestricted/" + moduleName + "/" 444 options := []tradefed.Option{{Name: "cleanup", Value: "true"}} 445 for _, bin := range s.testProperties.Data_device_bins { 446 options = append(options, tradefed.Option{Name: "push-file", Key: bin, Value: remoteDir + bin}) 447 } 448 configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.PushFilePreparer", options}) 449 } 450 s.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{ 451 TestConfigProp: s.testProperties.Test_config, 452 TestConfigTemplateProp: s.testProperties.Test_config_template, 453 TestSuites: s.testProperties.Test_suites, 454 Config: configs, 455 AutoGenConfig: s.testProperties.Auto_gen_config, 456 OutputFileName: s.outputFilePath.Base(), 457 DeviceTemplate: "${ShellTestConfigTemplate}", 458 HostTemplate: "${ShellTestConfigTemplate}", 459 }) 460 461 s.dataModules = make(map[string]android.Path) 462 ctx.VisitDirectDeps(func(dep android.Module) { 463 depTag := ctx.OtherModuleDependencyTag(dep) 464 switch depTag { 465 case shTestDataBinsTag, shTestDataDeviceBinsTag: 466 path := android.OutputFileForModule(ctx, dep, "") 467 s.addToDataModules(ctx, path.Base(), path) 468 case shTestDataLibsTag, shTestDataDeviceLibsTag: 469 if cc, isCc := dep.(*cc.Module); isCc { 470 // Copy to an intermediate output directory to append "lib[64]" to the path, 471 // so that it's compatible with the default rpath values. 472 var relPath string 473 if cc.Arch().ArchType.Multilib == "lib64" { 474 relPath = filepath.Join("lib64", cc.OutputFile().Path().Base()) 475 } else { 476 relPath = filepath.Join("lib", cc.OutputFile().Path().Base()) 477 } 478 if _, exist := s.dataModules[relPath]; exist { 479 return 480 } 481 relocatedLib := android.PathForModuleOut(ctx, "relocated").Join(ctx, relPath) 482 ctx.Build(pctx, android.BuildParams{ 483 Rule: android.Cp, 484 Input: cc.OutputFile().Path(), 485 Output: relocatedLib, 486 }) 487 s.addToDataModules(ctx, relPath, relocatedLib) 488 return 489 } 490 property := "data_libs" 491 if depTag == shTestDataDeviceBinsTag { 492 property = "data_device_libs" 493 } 494 ctx.PropertyErrorf(property, "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep)) 495 } 496 }) 497 498 installedData := ctx.InstallTestData(s.installDir, s.data) 499 s.installedFile = ctx.InstallExecutable(s.installDir, s.outputFilePath.Base(), s.outputFilePath, installedData...) 500 501 android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{}) 502} 503 504func (s *ShTest) InstallInData() bool { 505 return true 506} 507 508func (s *ShTest) AndroidMkEntries() []android.AndroidMkEntries { 509 return []android.AndroidMkEntries{android.AndroidMkEntries{ 510 Class: "NATIVE_TESTS", 511 OutputFile: android.OptionalPathForPath(s.outputFilePath), 512 Include: "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk", 513 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 514 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 515 s.customAndroidMkEntries(entries) 516 entries.SetPath("LOCAL_MODULE_PATH", s.installDir) 517 entries.AddCompatibilityTestSuites(s.testProperties.Test_suites...) 518 if s.testConfig != nil { 519 entries.SetPath("LOCAL_FULL_TEST_CONFIG", s.testConfig) 520 } 521 if s.testProperties.Data_bins != nil { 522 entries.AddStrings("LOCAL_TEST_DATA_BINS", s.testProperties.Data_bins...) 523 } 524 entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(s.testProperties.Per_testcase_directory)) 525 526 s.testProperties.Test_options.SetAndroidMkEntries(entries) 527 }, 528 }, 529 }} 530} 531 532func initShBinaryModule(s *ShBinary) { 533 s.AddProperties(&s.properties) 534} 535 536// sh_binary is for a shell script or batch file to be installed as an 537// executable binary to <partition>/bin. 538func ShBinaryFactory() android.Module { 539 module := &ShBinary{} 540 initShBinaryModule(module) 541 android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst) 542 return module 543} 544 545// sh_binary_host is for a shell script to be installed as an executable binary 546// to $(HOST_OUT)/bin. 547func ShBinaryHostFactory() android.Module { 548 module := &ShBinary{} 549 initShBinaryModule(module) 550 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst) 551 return module 552} 553 554// sh_test defines a shell script based test module. 555func ShTestFactory() android.Module { 556 module := &ShTest{} 557 initShBinaryModule(&module.ShBinary) 558 module.AddProperties(&module.testProperties) 559 560 android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst) 561 return module 562} 563 564// sh_test_host defines a shell script based test module that runs on a host. 565func ShTestHostFactory() android.Module { 566 module := &ShTest{} 567 initShBinaryModule(&module.ShBinary) 568 module.AddProperties(&module.testProperties) 569 // Default sh_test_host to unit_tests = true 570 if module.testProperties.Test_options.Unit_test == nil { 571 module.testProperties.Test_options.Unit_test = proptools.BoolPtr(true) 572 } 573 574 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst) 575 return module 576} 577 578var Bool = proptools.Bool 579