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