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