1// Copyright (C) 2019 The Android Open Source Project 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 15// sysprop package defines a module named sysprop_library that can implement sysprop as API 16// See https://source.android.com/devices/architecture/sysprops-apis for details 17package sysprop 18 19import ( 20 "fmt" 21 "io" 22 "os" 23 "path" 24 "sync" 25 26 "android/soong/bazel" 27 "github.com/google/blueprint" 28 "github.com/google/blueprint/proptools" 29 30 "android/soong/android" 31 "android/soong/cc" 32 "android/soong/java" 33) 34 35type dependencyTag struct { 36 blueprint.BaseDependencyTag 37 name string 38} 39 40type syspropGenProperties struct { 41 Srcs []string `android:"path"` 42 Scope string 43 Name *string 44 Check_api *string 45} 46 47type syspropJavaGenRule struct { 48 android.ModuleBase 49 50 properties syspropGenProperties 51 52 genSrcjars android.Paths 53} 54 55var _ android.OutputFileProducer = (*syspropJavaGenRule)(nil) 56 57var ( 58 syspropJava = pctx.AndroidStaticRule("syspropJava", 59 blueprint.RuleParams{ 60 Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` + 61 `$syspropJavaCmd --scope $scope --java-output-dir $out.tmp $in && ` + 62 `$soongZipCmd -jar -o $out -C $out.tmp -D $out.tmp && rm -rf $out.tmp`, 63 CommandDeps: []string{ 64 "$syspropJavaCmd", 65 "$soongZipCmd", 66 }, 67 }, "scope") 68) 69 70func init() { 71 pctx.HostBinToolVariable("soongZipCmd", "soong_zip") 72 pctx.HostBinToolVariable("syspropJavaCmd", "sysprop_java") 73} 74 75// syspropJavaGenRule module generates srcjar containing generated java APIs. 76// It also depends on check api rule, so api check has to pass to use sysprop_library. 77func (g *syspropJavaGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 78 var checkApiFileTimeStamp android.WritablePath 79 80 ctx.VisitDirectDeps(func(dep android.Module) { 81 if m, ok := dep.(*syspropLibrary); ok { 82 checkApiFileTimeStamp = m.checkApiFileTimeStamp 83 } 84 }) 85 86 for _, syspropFile := range android.PathsForModuleSrc(ctx, g.properties.Srcs) { 87 srcJarFile := android.GenPathWithExt(ctx, "sysprop", syspropFile, "srcjar") 88 89 ctx.Build(pctx, android.BuildParams{ 90 Rule: syspropJava, 91 Description: "sysprop_java " + syspropFile.Rel(), 92 Output: srcJarFile, 93 Input: syspropFile, 94 Implicit: checkApiFileTimeStamp, 95 Args: map[string]string{ 96 "scope": g.properties.Scope, 97 }, 98 }) 99 100 g.genSrcjars = append(g.genSrcjars, srcJarFile) 101 } 102} 103 104func (g *syspropJavaGenRule) DepsMutator(ctx android.BottomUpMutatorContext) { 105 // Add a dependency from the stubs to sysprop library so that the generator rule can depend on 106 // the check API rule of the sysprop library. 107 ctx.AddFarVariationDependencies(nil, nil, proptools.String(g.properties.Check_api)) 108} 109 110func (g *syspropJavaGenRule) OutputFiles(tag string) (android.Paths, error) { 111 switch tag { 112 case "": 113 return g.genSrcjars, nil 114 default: 115 return nil, fmt.Errorf("unsupported module reference tag %q", tag) 116 } 117} 118 119func syspropJavaGenFactory() android.Module { 120 g := &syspropJavaGenRule{} 121 g.AddProperties(&g.properties) 122 android.InitAndroidModule(g) 123 return g 124} 125 126type syspropLibrary struct { 127 android.ModuleBase 128 android.ApexModuleBase 129 android.BazelModuleBase 130 131 properties syspropLibraryProperties 132 133 checkApiFileTimeStamp android.WritablePath 134 latestApiFile android.OptionalPath 135 currentApiFile android.OptionalPath 136 dumpedApiFile android.WritablePath 137} 138 139type syspropLibraryProperties struct { 140 // Determine who owns this sysprop library. Possible values are 141 // "Platform", "Vendor", or "Odm" 142 Property_owner string 143 144 // list of package names that will be documented and publicized as API 145 Api_packages []string 146 147 // If set to true, allow this module to be dexed and installed on devices. 148 Installable *bool 149 150 // Make this module available when building for ramdisk 151 Ramdisk_available *bool 152 153 // Make this module available when building for recovery 154 Recovery_available *bool 155 156 // Make this module available when building for vendor 157 Vendor_available *bool 158 159 // Make this module available when building for product 160 Product_available *bool 161 162 // list of .sysprop files which defines the properties. 163 Srcs []string `android:"path"` 164 165 // If set to true, build a variant of the module for the host. Defaults to false. 166 Host_supported *bool 167 168 Cpp struct { 169 // Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX). 170 // Forwarded to cc_library.min_sdk_version 171 Min_sdk_version *string 172 } 173 174 Java struct { 175 // Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX). 176 // Forwarded to java_library.min_sdk_version 177 Min_sdk_version *string 178 } 179} 180 181var ( 182 pctx = android.NewPackageContext("android/soong/sysprop") 183 syspropCcTag = dependencyTag{name: "syspropCc"} 184 185 syspropLibrariesKey = android.NewOnceKey("syspropLibraries") 186 syspropLibrariesLock sync.Mutex 187) 188 189// List of sysprop_library used by property_contexts to perform type check. 190func syspropLibraries(config android.Config) *[]string { 191 return config.Once(syspropLibrariesKey, func() interface{} { 192 return &[]string{} 193 }).(*[]string) 194} 195 196func SyspropLibraries(config android.Config) []string { 197 return append([]string{}, *syspropLibraries(config)...) 198} 199 200func init() { 201 registerSyspropBuildComponents(android.InitRegistrationContext) 202} 203 204func registerSyspropBuildComponents(ctx android.RegistrationContext) { 205 ctx.RegisterModuleType("sysprop_library", syspropLibraryFactory) 206} 207 208func (m *syspropLibrary) Name() string { 209 return m.BaseModuleName() + "_sysprop_library" 210} 211 212func (m *syspropLibrary) Owner() string { 213 return m.properties.Property_owner 214} 215 216func (m *syspropLibrary) CcImplementationModuleName() string { 217 return "lib" + m.BaseModuleName() 218} 219 220func (m *syspropLibrary) javaPublicStubName() string { 221 return m.BaseModuleName() + "_public" 222} 223 224func (m *syspropLibrary) javaGenModuleName() string { 225 return m.BaseModuleName() + "_java_gen" 226} 227 228func (m *syspropLibrary) javaGenPublicStubName() string { 229 return m.BaseModuleName() + "_java_gen_public" 230} 231 232func (m *syspropLibrary) BaseModuleName() string { 233 return m.ModuleBase.Name() 234} 235 236func (m *syspropLibrary) CurrentSyspropApiFile() android.OptionalPath { 237 return m.currentApiFile 238} 239 240// GenerateAndroidBuildActions of sysprop_library handles API dump and API check. 241// generated java_library will depend on these API files. 242func (m *syspropLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { 243 baseModuleName := m.BaseModuleName() 244 245 for _, syspropFile := range android.PathsForModuleSrc(ctx, m.properties.Srcs) { 246 if syspropFile.Ext() != ".sysprop" { 247 ctx.PropertyErrorf("srcs", "srcs contains non-sysprop file %q", syspropFile.String()) 248 } 249 } 250 251 if ctx.Failed() { 252 return 253 } 254 255 apiDirectoryPath := path.Join(ctx.ModuleDir(), "api") 256 currentApiFilePath := path.Join(apiDirectoryPath, baseModuleName+"-current.txt") 257 latestApiFilePath := path.Join(apiDirectoryPath, baseModuleName+"-latest.txt") 258 m.currentApiFile = android.ExistentPathForSource(ctx, currentApiFilePath) 259 m.latestApiFile = android.ExistentPathForSource(ctx, latestApiFilePath) 260 261 // dump API rule 262 rule := android.NewRuleBuilder(pctx, ctx) 263 m.dumpedApiFile = android.PathForModuleOut(ctx, "api-dump.txt") 264 rule.Command(). 265 BuiltTool("sysprop_api_dump"). 266 Output(m.dumpedApiFile). 267 Inputs(android.PathsForModuleSrc(ctx, m.properties.Srcs)) 268 rule.Build(baseModuleName+"_api_dump", baseModuleName+" api dump") 269 270 // check API rule 271 rule = android.NewRuleBuilder(pctx, ctx) 272 273 // We allow that the API txt files don't exist, when the sysprop_library only contains internal 274 // properties. But we have to feed current api file and latest api file to the rule builder. 275 // Currently we can't get android.Path representing the null device, so we add any existing API 276 // txt files to implicits, and then directly feed string paths, rather than calling Input(Path) 277 // method. 278 var apiFileList android.Paths 279 currentApiArgument := os.DevNull 280 if m.currentApiFile.Valid() { 281 apiFileList = append(apiFileList, m.currentApiFile.Path()) 282 currentApiArgument = m.currentApiFile.String() 283 } 284 285 latestApiArgument := os.DevNull 286 if m.latestApiFile.Valid() { 287 apiFileList = append(apiFileList, m.latestApiFile.Path()) 288 latestApiArgument = m.latestApiFile.String() 289 } 290 291 // 1. compares current.txt to api-dump.txt 292 // current.txt should be identical to api-dump.txt. 293 msg := fmt.Sprintf(`\n******************************\n`+ 294 `API of sysprop_library %s doesn't match with current.txt\n`+ 295 `Please update current.txt by:\n`+ 296 `m %s-dump-api && mkdir -p %q && rm -rf %q && cp -f %q %q\n`+ 297 `******************************\n`, baseModuleName, baseModuleName, 298 apiDirectoryPath, currentApiFilePath, m.dumpedApiFile.String(), currentApiFilePath) 299 300 rule.Command(). 301 Text("( cmp").Flag("-s"). 302 Input(m.dumpedApiFile). 303 Text(currentApiArgument). 304 Text("|| ( echo").Flag("-e"). 305 Flag(`"` + msg + `"`). 306 Text("; exit 38) )") 307 308 // 2. compares current.txt to latest.txt (frozen API) 309 // current.txt should be compatible with latest.txt 310 msg = fmt.Sprintf(`\n******************************\n`+ 311 `API of sysprop_library %s doesn't match with latest version\n`+ 312 `Please fix the breakage and rebuild.\n`+ 313 `******************************\n`, baseModuleName) 314 315 rule.Command(). 316 Text("( "). 317 BuiltTool("sysprop_api_checker"). 318 Text(latestApiArgument). 319 Text(currentApiArgument). 320 Text(" || ( echo").Flag("-e"). 321 Flag(`"` + msg + `"`). 322 Text("; exit 38) )"). 323 Implicits(apiFileList) 324 325 m.checkApiFileTimeStamp = android.PathForModuleOut(ctx, "check_api.timestamp") 326 327 rule.Command(). 328 Text("touch"). 329 Output(m.checkApiFileTimeStamp) 330 331 rule.Build(baseModuleName+"_check_api", baseModuleName+" check api") 332} 333 334func (m *syspropLibrary) AndroidMk() android.AndroidMkData { 335 return android.AndroidMkData{ 336 Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { 337 // sysprop_library module itself is defined as a FAKE module to perform API check. 338 // Actual implementation libraries are created on LoadHookMutator 339 fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)", " # sysprop.syspropLibrary") 340 fmt.Fprintln(w, "LOCAL_MODULE :=", m.Name()) 341 data.Entries.WriteLicenseVariables(w) 342 fmt.Fprintf(w, "LOCAL_MODULE_CLASS := FAKE\n") 343 fmt.Fprintf(w, "LOCAL_MODULE_TAGS := optional\n") 344 fmt.Fprintf(w, "include $(BUILD_SYSTEM)/base_rules.mk\n\n") 345 fmt.Fprintf(w, "$(LOCAL_BUILT_MODULE): %s\n", m.checkApiFileTimeStamp.String()) 346 fmt.Fprintf(w, "\ttouch $@\n\n") 347 fmt.Fprintf(w, ".PHONY: %s-check-api %s-dump-api\n\n", name, name) 348 349 // dump API rule 350 fmt.Fprintf(w, "%s-dump-api: %s\n\n", name, m.dumpedApiFile.String()) 351 352 // check API rule 353 fmt.Fprintf(w, "%s-check-api: %s\n\n", name, m.checkApiFileTimeStamp.String()) 354 }} 355} 356 357var _ android.ApexModule = (*syspropLibrary)(nil) 358 359// Implements android.ApexModule 360func (m *syspropLibrary) ShouldSupportSdkVersion(ctx android.BaseModuleContext, 361 sdkVersion android.ApiLevel) error { 362 return fmt.Errorf("sysprop_library is not supposed to be part of apex modules") 363} 364 365// sysprop_library creates schematized APIs from sysprop description files (.sysprop). 366// Both Java and C++ modules can link against sysprop_library, and API stability check 367// against latest APIs (see build/soong/scripts/freeze-sysprop-api-files.sh) 368// is performed. Note that the generated C++ module has its name prefixed with 369// `lib`, and it is this module that should be depended on from other C++ 370// modules; i.e., if the sysprop_library module is named `foo`, C++ modules 371// should depend on `libfoo`. 372func syspropLibraryFactory() android.Module { 373 m := &syspropLibrary{} 374 375 m.AddProperties( 376 &m.properties, 377 ) 378 android.InitAndroidModule(m) 379 android.InitApexModule(m) 380 android.InitBazelModule(m) 381 android.AddLoadHook(m, func(ctx android.LoadHookContext) { syspropLibraryHook(ctx, m) }) 382 return m 383} 384 385type ccLibraryProperties struct { 386 Name *string 387 Srcs []string 388 Soc_specific *bool 389 Device_specific *bool 390 Product_specific *bool 391 Sysprop struct { 392 Platform *bool 393 } 394 Target struct { 395 Android struct { 396 Header_libs []string 397 Shared_libs []string 398 } 399 Host struct { 400 Static_libs []string 401 } 402 } 403 Required []string 404 Recovery *bool 405 Recovery_available *bool 406 Vendor_available *bool 407 Product_available *bool 408 Ramdisk_available *bool 409 Host_supported *bool 410 Apex_available []string 411 Min_sdk_version *string 412 Bazel_module struct { 413 Bp2build_available *bool 414 } 415} 416 417type javaLibraryProperties struct { 418 Name *string 419 Srcs []string 420 Soc_specific *bool 421 Device_specific *bool 422 Product_specific *bool 423 Required []string 424 Sdk_version *string 425 Installable *bool 426 Libs []string 427 Stem *string 428 SyspropPublicStub string 429 Apex_available []string 430 Min_sdk_version *string 431} 432 433func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) { 434 if len(m.properties.Srcs) == 0 { 435 ctx.PropertyErrorf("srcs", "sysprop_library must specify srcs") 436 } 437 438 // ctx's Platform or Specific functions represent where this sysprop_library installed. 439 installedInSystem := ctx.Platform() || ctx.SystemExtSpecific() 440 installedInVendorOrOdm := ctx.SocSpecific() || ctx.DeviceSpecific() 441 installedInProduct := ctx.ProductSpecific() 442 isOwnerPlatform := false 443 var javaSyspropStub string 444 445 // javaSyspropStub contains stub libraries used by generated APIs, instead of framework stub. 446 // This is to make sysprop_library link against core_current. 447 if installedInVendorOrOdm { 448 javaSyspropStub = "sysprop-library-stub-vendor" 449 } else if installedInProduct { 450 javaSyspropStub = "sysprop-library-stub-product" 451 } else { 452 javaSyspropStub = "sysprop-library-stub-platform" 453 } 454 455 switch m.Owner() { 456 case "Platform": 457 // Every partition can access platform-defined properties 458 isOwnerPlatform = true 459 case "Vendor": 460 // System can't access vendor's properties 461 if installedInSystem { 462 ctx.ModuleErrorf("None of soc_specific, device_specific, product_specific is true. " + 463 "System can't access sysprop_library owned by Vendor") 464 } 465 case "Odm": 466 // Only vendor can access Odm-defined properties 467 if !installedInVendorOrOdm { 468 ctx.ModuleErrorf("Neither soc_speicifc nor device_specific is true. " + 469 "Odm-defined properties should be accessed only in Vendor or Odm") 470 } 471 default: 472 ctx.PropertyErrorf("property_owner", 473 "Unknown value %s: must be one of Platform, Vendor or Odm", m.Owner()) 474 } 475 476 // Generate a C++ implementation library. 477 // cc_library can receive *.sysprop files as their srcs, generating sources itself. 478 ccProps := ccLibraryProperties{} 479 ccProps.Name = proptools.StringPtr(m.CcImplementationModuleName()) 480 ccProps.Srcs = m.properties.Srcs 481 ccProps.Soc_specific = proptools.BoolPtr(ctx.SocSpecific()) 482 ccProps.Device_specific = proptools.BoolPtr(ctx.DeviceSpecific()) 483 ccProps.Product_specific = proptools.BoolPtr(ctx.ProductSpecific()) 484 ccProps.Sysprop.Platform = proptools.BoolPtr(isOwnerPlatform) 485 ccProps.Target.Android.Header_libs = []string{"libbase_headers"} 486 ccProps.Target.Android.Shared_libs = []string{"liblog"} 487 ccProps.Target.Host.Static_libs = []string{"libbase", "liblog"} 488 ccProps.Recovery_available = m.properties.Recovery_available 489 ccProps.Vendor_available = m.properties.Vendor_available 490 ccProps.Product_available = m.properties.Product_available 491 ccProps.Ramdisk_available = m.properties.Ramdisk_available 492 ccProps.Host_supported = m.properties.Host_supported 493 ccProps.Apex_available = m.ApexProperties.Apex_available 494 ccProps.Min_sdk_version = m.properties.Cpp.Min_sdk_version 495 // A Bazel macro handles this, so this module does not need to be handled 496 // in bp2build 497 // TODO(b/237810289) perhaps do something different here so that we aren't 498 // also disabling these modules in mixed builds 499 ccProps.Bazel_module.Bp2build_available = proptools.BoolPtr(false) 500 ctx.CreateModule(cc.LibraryFactory, &ccProps) 501 502 scope := "internal" 503 504 // We need to only use public version, if the partition where sysprop_library will be installed 505 // is different from owner. 506 if ctx.ProductSpecific() { 507 // Currently product partition can't own any sysprop_library. So product always uses public. 508 scope = "public" 509 } else if isOwnerPlatform && installedInVendorOrOdm { 510 // Vendor or Odm should use public version of Platform's sysprop_library. 511 scope = "public" 512 } 513 514 // Generate a Java implementation library. 515 // Contrast to C++, syspropJavaGenRule module will generate srcjar and the srcjar will be fed 516 // to Java implementation library. 517 ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{ 518 Srcs: m.properties.Srcs, 519 Scope: scope, 520 Name: proptools.StringPtr(m.javaGenModuleName()), 521 Check_api: proptools.StringPtr(ctx.ModuleName()), 522 }) 523 524 // if platform sysprop_library is installed in /system or /system-ext, we regard it as an API 525 // and allow any modules (even from different partition) to link against the sysprop_library. 526 // To do that, we create a public stub and expose it to modules with sdk_version: system_*. 527 var publicStub string 528 if isOwnerPlatform && installedInSystem { 529 publicStub = m.javaPublicStubName() 530 } 531 532 ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{ 533 Name: proptools.StringPtr(m.BaseModuleName()), 534 Srcs: []string{":" + m.javaGenModuleName()}, 535 Soc_specific: proptools.BoolPtr(ctx.SocSpecific()), 536 Device_specific: proptools.BoolPtr(ctx.DeviceSpecific()), 537 Product_specific: proptools.BoolPtr(ctx.ProductSpecific()), 538 Installable: m.properties.Installable, 539 Sdk_version: proptools.StringPtr("core_current"), 540 Libs: []string{javaSyspropStub}, 541 SyspropPublicStub: publicStub, 542 Apex_available: m.ApexProperties.Apex_available, 543 Min_sdk_version: m.properties.Java.Min_sdk_version, 544 }) 545 546 if publicStub != "" { 547 ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{ 548 Srcs: m.properties.Srcs, 549 Scope: "public", 550 Name: proptools.StringPtr(m.javaGenPublicStubName()), 551 Check_api: proptools.StringPtr(ctx.ModuleName()), 552 }) 553 554 ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{ 555 Name: proptools.StringPtr(publicStub), 556 Srcs: []string{":" + m.javaGenPublicStubName()}, 557 Installable: proptools.BoolPtr(false), 558 Sdk_version: proptools.StringPtr("core_current"), 559 Libs: []string{javaSyspropStub}, 560 Stem: proptools.StringPtr(m.BaseModuleName()), 561 }) 562 } 563 564 // syspropLibraries will be used by property_contexts to check types. 565 // Record absolute paths of sysprop_library to prevent soong_namespace problem. 566 if m.ExportedToMake() { 567 syspropLibrariesLock.Lock() 568 defer syspropLibrariesLock.Unlock() 569 570 libraries := syspropLibraries(ctx.Config()) 571 *libraries = append(*libraries, "//"+ctx.ModuleDir()+":"+ctx.ModuleName()) 572 } 573} 574 575// TODO(b/240463568): Additional properties will be added for API validation 576func (m *syspropLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) { 577 labels := cc.SyspropLibraryLabels{ 578 SyspropLibraryLabel: m.BaseModuleName(), 579 SharedLibraryLabel: m.CcImplementationModuleName(), 580 StaticLibraryLabel: cc.BazelLabelNameForStaticModule(m.CcImplementationModuleName()), 581 } 582 cc.Bp2buildSysprop(ctx, 583 labels, 584 bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Srcs)), 585 m.properties.Cpp.Min_sdk_version) 586} 587