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