| // Copyright (C) 2019 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // sysprop package defines a module named sysprop_library that can implement sysprop as API |
| // See https://source.android.com/devices/architecture/sysprops-apis for details |
| package sysprop |
| |
| import ( |
| "fmt" |
| "io" |
| "os" |
| "path" |
| "sync" |
| |
| "android/soong/bazel" |
| |
| "github.com/google/blueprint" |
| "github.com/google/blueprint/proptools" |
| |
| "android/soong/android" |
| "android/soong/cc" |
| "android/soong/java" |
| ) |
| |
| type dependencyTag struct { |
| blueprint.BaseDependencyTag |
| name string |
| } |
| |
| type syspropGenProperties struct { |
| Srcs []string `android:"path"` |
| Scope string |
| Name *string |
| Check_api *string |
| } |
| |
| type syspropJavaGenRule struct { |
| android.ModuleBase |
| |
| properties syspropGenProperties |
| |
| genSrcjars android.Paths |
| } |
| |
| var _ android.OutputFileProducer = (*syspropJavaGenRule)(nil) |
| |
| var ( |
| syspropJava = pctx.AndroidStaticRule("syspropJava", |
| blueprint.RuleParams{ |
| Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` + |
| `$syspropJavaCmd --scope $scope --java-output-dir $out.tmp $in && ` + |
| `$soongZipCmd -jar -o $out -C $out.tmp -D $out.tmp && rm -rf $out.tmp`, |
| CommandDeps: []string{ |
| "$syspropJavaCmd", |
| "$soongZipCmd", |
| }, |
| }, "scope") |
| ) |
| |
| func init() { |
| pctx.HostBinToolVariable("soongZipCmd", "soong_zip") |
| pctx.HostBinToolVariable("syspropJavaCmd", "sysprop_java") |
| } |
| |
| // syspropJavaGenRule module generates srcjar containing generated java APIs. |
| // It also depends on check api rule, so api check has to pass to use sysprop_library. |
| func (g *syspropJavaGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) { |
| var checkApiFileTimeStamp android.WritablePath |
| |
| ctx.VisitDirectDeps(func(dep android.Module) { |
| if m, ok := dep.(*syspropLibrary); ok { |
| checkApiFileTimeStamp = m.checkApiFileTimeStamp |
| } |
| }) |
| |
| for _, syspropFile := range android.PathsForModuleSrc(ctx, g.properties.Srcs) { |
| srcJarFile := android.GenPathWithExt(ctx, "sysprop", syspropFile, "srcjar") |
| |
| ctx.Build(pctx, android.BuildParams{ |
| Rule: syspropJava, |
| Description: "sysprop_java " + syspropFile.Rel(), |
| Output: srcJarFile, |
| Input: syspropFile, |
| Implicit: checkApiFileTimeStamp, |
| Args: map[string]string{ |
| "scope": g.properties.Scope, |
| }, |
| }) |
| |
| g.genSrcjars = append(g.genSrcjars, srcJarFile) |
| } |
| } |
| |
| func (g *syspropJavaGenRule) DepsMutator(ctx android.BottomUpMutatorContext) { |
| // Add a dependency from the stubs to sysprop library so that the generator rule can depend on |
| // the check API rule of the sysprop library. |
| ctx.AddFarVariationDependencies(nil, nil, proptools.String(g.properties.Check_api)) |
| } |
| |
| func (g *syspropJavaGenRule) OutputFiles(tag string) (android.Paths, error) { |
| switch tag { |
| case "": |
| return g.genSrcjars, nil |
| default: |
| return nil, fmt.Errorf("unsupported module reference tag %q", tag) |
| } |
| } |
| |
| func syspropJavaGenFactory() android.Module { |
| g := &syspropJavaGenRule{} |
| g.AddProperties(&g.properties) |
| android.InitAndroidModule(g) |
| return g |
| } |
| |
| type syspropLibrary struct { |
| android.ModuleBase |
| android.ApexModuleBase |
| android.BazelModuleBase |
| |
| properties syspropLibraryProperties |
| |
| checkApiFileTimeStamp android.WritablePath |
| latestApiFile android.OptionalPath |
| currentApiFile android.OptionalPath |
| dumpedApiFile android.WritablePath |
| } |
| |
| type syspropLibraryProperties struct { |
| // Determine who owns this sysprop library. Possible values are |
| // "Platform", "Vendor", or "Odm" |
| Property_owner string |
| |
| // list of package names that will be documented and publicized as API |
| Api_packages []string |
| |
| // If set to true, allow this module to be dexed and installed on devices. |
| Installable *bool |
| |
| // Make this module available when building for ramdisk |
| Ramdisk_available *bool |
| |
| // Make this module available when building for recovery |
| Recovery_available *bool |
| |
| // Make this module available when building for vendor |
| Vendor_available *bool |
| |
| // Make this module available when building for product |
| Product_available *bool |
| |
| // list of .sysprop files which defines the properties. |
| Srcs []string `android:"path"` |
| |
| // If set to true, build a variant of the module for the host. Defaults to false. |
| Host_supported *bool |
| |
| Cpp struct { |
| // Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX). |
| // Forwarded to cc_library.min_sdk_version |
| Min_sdk_version *string |
| } |
| |
| Java struct { |
| // Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX). |
| // Forwarded to java_library.min_sdk_version |
| Min_sdk_version *string |
| } |
| } |
| |
| var ( |
| pctx = android.NewPackageContext("android/soong/sysprop") |
| syspropCcTag = dependencyTag{name: "syspropCc"} |
| |
| syspropLibrariesKey = android.NewOnceKey("syspropLibraries") |
| syspropLibrariesLock sync.Mutex |
| ) |
| |
| // List of sysprop_library used by property_contexts to perform type check. |
| func syspropLibraries(config android.Config) *[]string { |
| return config.Once(syspropLibrariesKey, func() interface{} { |
| return &[]string{} |
| }).(*[]string) |
| } |
| |
| func SyspropLibraries(config android.Config) []string { |
| return append([]string{}, *syspropLibraries(config)...) |
| } |
| |
| func init() { |
| registerSyspropBuildComponents(android.InitRegistrationContext) |
| } |
| |
| func registerSyspropBuildComponents(ctx android.RegistrationContext) { |
| ctx.RegisterModuleType("sysprop_library", syspropLibraryFactory) |
| } |
| |
| func (m *syspropLibrary) Name() string { |
| return m.BaseModuleName() + "_sysprop_library" |
| } |
| |
| func (m *syspropLibrary) Owner() string { |
| return m.properties.Property_owner |
| } |
| |
| func (m *syspropLibrary) CcImplementationModuleName() string { |
| return "lib" + m.BaseModuleName() |
| } |
| |
| func (m *syspropLibrary) javaPublicStubName() string { |
| return m.BaseModuleName() + "_public" |
| } |
| |
| func (m *syspropLibrary) javaGenModuleName() string { |
| return m.BaseModuleName() + "_java_gen" |
| } |
| |
| func (m *syspropLibrary) javaGenPublicStubName() string { |
| return m.BaseModuleName() + "_java_gen_public" |
| } |
| |
| func (m *syspropLibrary) BaseModuleName() string { |
| return m.ModuleBase.Name() |
| } |
| |
| func (m *syspropLibrary) CurrentSyspropApiFile() android.OptionalPath { |
| return m.currentApiFile |
| } |
| |
| // GenerateAndroidBuildActions of sysprop_library handles API dump and API check. |
| // generated java_library will depend on these API files. |
| func (m *syspropLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { |
| baseModuleName := m.BaseModuleName() |
| |
| for _, syspropFile := range android.PathsForModuleSrc(ctx, m.properties.Srcs) { |
| if syspropFile.Ext() != ".sysprop" { |
| ctx.PropertyErrorf("srcs", "srcs contains non-sysprop file %q", syspropFile.String()) |
| } |
| } |
| |
| if ctx.Failed() { |
| return |
| } |
| |
| apiDirectoryPath := path.Join(ctx.ModuleDir(), "api") |
| currentApiFilePath := path.Join(apiDirectoryPath, baseModuleName+"-current.txt") |
| latestApiFilePath := path.Join(apiDirectoryPath, baseModuleName+"-latest.txt") |
| m.currentApiFile = android.ExistentPathForSource(ctx, currentApiFilePath) |
| m.latestApiFile = android.ExistentPathForSource(ctx, latestApiFilePath) |
| |
| // dump API rule |
| rule := android.NewRuleBuilder(pctx, ctx) |
| m.dumpedApiFile = android.PathForModuleOut(ctx, "api-dump.txt") |
| rule.Command(). |
| BuiltTool("sysprop_api_dump"). |
| Output(m.dumpedApiFile). |
| Inputs(android.PathsForModuleSrc(ctx, m.properties.Srcs)) |
| rule.Build(baseModuleName+"_api_dump", baseModuleName+" api dump") |
| |
| // check API rule |
| rule = android.NewRuleBuilder(pctx, ctx) |
| |
| // We allow that the API txt files don't exist, when the sysprop_library only contains internal |
| // properties. But we have to feed current api file and latest api file to the rule builder. |
| // Currently we can't get android.Path representing the null device, so we add any existing API |
| // txt files to implicits, and then directly feed string paths, rather than calling Input(Path) |
| // method. |
| var apiFileList android.Paths |
| currentApiArgument := os.DevNull |
| if m.currentApiFile.Valid() { |
| apiFileList = append(apiFileList, m.currentApiFile.Path()) |
| currentApiArgument = m.currentApiFile.String() |
| } |
| |
| latestApiArgument := os.DevNull |
| if m.latestApiFile.Valid() { |
| apiFileList = append(apiFileList, m.latestApiFile.Path()) |
| latestApiArgument = m.latestApiFile.String() |
| } |
| |
| // 1. compares current.txt to api-dump.txt |
| // current.txt should be identical to api-dump.txt. |
| msg := fmt.Sprintf(`\n******************************\n`+ |
| `API of sysprop_library %s doesn't match with current.txt\n`+ |
| `Please update current.txt by:\n`+ |
| `m %s-dump-api && mkdir -p %q && rm -rf %q && cp -f %q %q\n`+ |
| `******************************\n`, baseModuleName, baseModuleName, |
| apiDirectoryPath, currentApiFilePath, m.dumpedApiFile.String(), currentApiFilePath) |
| |
| rule.Command(). |
| Text("( cmp").Flag("-s"). |
| Input(m.dumpedApiFile). |
| Text(currentApiArgument). |
| Text("|| ( echo").Flag("-e"). |
| Flag(`"` + msg + `"`). |
| Text("; exit 38) )") |
| |
| // 2. compares current.txt to latest.txt (frozen API) |
| // current.txt should be compatible with latest.txt |
| msg = fmt.Sprintf(`\n******************************\n`+ |
| `API of sysprop_library %s doesn't match with latest version\n`+ |
| `Please fix the breakage and rebuild.\n`+ |
| `******************************\n`, baseModuleName) |
| |
| rule.Command(). |
| Text("( "). |
| BuiltTool("sysprop_api_checker"). |
| Text(latestApiArgument). |
| Text(currentApiArgument). |
| Text(" || ( echo").Flag("-e"). |
| Flag(`"` + msg + `"`). |
| Text("; exit 38) )"). |
| Implicits(apiFileList) |
| |
| m.checkApiFileTimeStamp = android.PathForModuleOut(ctx, "check_api.timestamp") |
| |
| rule.Command(). |
| Text("touch"). |
| Output(m.checkApiFileTimeStamp) |
| |
| rule.Build(baseModuleName+"_check_api", baseModuleName+" check api") |
| } |
| |
| func (m *syspropLibrary) AndroidMk() android.AndroidMkData { |
| return android.AndroidMkData{ |
| Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { |
| // sysprop_library module itself is defined as a FAKE module to perform API check. |
| // Actual implementation libraries are created on LoadHookMutator |
| fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)", " # sysprop.syspropLibrary") |
| fmt.Fprintln(w, "LOCAL_MODULE :=", m.Name()) |
| data.Entries.WriteLicenseVariables(w) |
| fmt.Fprintf(w, "LOCAL_MODULE_CLASS := FAKE\n") |
| fmt.Fprintf(w, "LOCAL_MODULE_TAGS := optional\n") |
| fmt.Fprintf(w, "include $(BUILD_SYSTEM)/base_rules.mk\n\n") |
| fmt.Fprintf(w, "$(LOCAL_BUILT_MODULE): %s\n", m.checkApiFileTimeStamp.String()) |
| fmt.Fprintf(w, "\ttouch $@\n\n") |
| fmt.Fprintf(w, ".PHONY: %s-check-api %s-dump-api\n\n", name, name) |
| |
| // dump API rule |
| fmt.Fprintf(w, "%s-dump-api: %s\n\n", name, m.dumpedApiFile.String()) |
| |
| // check API rule |
| fmt.Fprintf(w, "%s-check-api: %s\n\n", name, m.checkApiFileTimeStamp.String()) |
| }} |
| } |
| |
| var _ android.ApexModule = (*syspropLibrary)(nil) |
| |
| // Implements android.ApexModule |
| func (m *syspropLibrary) ShouldSupportSdkVersion(ctx android.BaseModuleContext, |
| sdkVersion android.ApiLevel) error { |
| return fmt.Errorf("sysprop_library is not supposed to be part of apex modules") |
| } |
| |
| // sysprop_library creates schematized APIs from sysprop description files (.sysprop). |
| // Both Java and C++ modules can link against sysprop_library, and API stability check |
| // against latest APIs (see build/soong/scripts/freeze-sysprop-api-files.sh) |
| // is performed. Note that the generated C++ module has its name prefixed with |
| // `lib`, and it is this module that should be depended on from other C++ |
| // modules; i.e., if the sysprop_library module is named `foo`, C++ modules |
| // should depend on `libfoo`. |
| func syspropLibraryFactory() android.Module { |
| m := &syspropLibrary{} |
| |
| m.AddProperties( |
| &m.properties, |
| ) |
| android.InitAndroidModule(m) |
| android.InitApexModule(m) |
| android.InitBazelModule(m) |
| android.AddLoadHook(m, func(ctx android.LoadHookContext) { syspropLibraryHook(ctx, m) }) |
| return m |
| } |
| |
| type ccLibraryProperties struct { |
| Name *string |
| Srcs []string |
| Soc_specific *bool |
| Device_specific *bool |
| Product_specific *bool |
| Sysprop struct { |
| Platform *bool |
| } |
| Target struct { |
| Android struct { |
| Header_libs []string |
| Shared_libs []string |
| } |
| Host struct { |
| Static_libs []string |
| } |
| } |
| Required []string |
| Recovery *bool |
| Recovery_available *bool |
| Vendor_available *bool |
| Product_available *bool |
| Ramdisk_available *bool |
| Host_supported *bool |
| Apex_available []string |
| Min_sdk_version *string |
| Bazel_module struct { |
| Label *string |
| } |
| } |
| |
| type javaLibraryProperties struct { |
| Name *string |
| Srcs []string |
| Soc_specific *bool |
| Device_specific *bool |
| Product_specific *bool |
| Required []string |
| Sdk_version *string |
| Installable *bool |
| Libs []string |
| Stem *string |
| SyspropPublicStub string |
| Apex_available []string |
| Min_sdk_version *string |
| Bazel_module struct { |
| Bp2build_available *bool |
| } |
| } |
| |
| func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) { |
| if len(m.properties.Srcs) == 0 { |
| ctx.PropertyErrorf("srcs", "sysprop_library must specify srcs") |
| } |
| |
| // ctx's Platform or Specific functions represent where this sysprop_library installed. |
| installedInSystem := ctx.Platform() || ctx.SystemExtSpecific() |
| installedInVendorOrOdm := ctx.SocSpecific() || ctx.DeviceSpecific() |
| installedInProduct := ctx.ProductSpecific() |
| isOwnerPlatform := false |
| var javaSyspropStub string |
| |
| // javaSyspropStub contains stub libraries used by generated APIs, instead of framework stub. |
| // This is to make sysprop_library link against core_current. |
| if installedInVendorOrOdm { |
| javaSyspropStub = "sysprop-library-stub-vendor" |
| } else if installedInProduct { |
| javaSyspropStub = "sysprop-library-stub-product" |
| } else { |
| javaSyspropStub = "sysprop-library-stub-platform" |
| } |
| |
| switch m.Owner() { |
| case "Platform": |
| // Every partition can access platform-defined properties |
| isOwnerPlatform = true |
| case "Vendor": |
| // System can't access vendor's properties |
| if installedInSystem { |
| ctx.ModuleErrorf("None of soc_specific, device_specific, product_specific is true. " + |
| "System can't access sysprop_library owned by Vendor") |
| } |
| case "Odm": |
| // Only vendor can access Odm-defined properties |
| if !installedInVendorOrOdm { |
| ctx.ModuleErrorf("Neither soc_speicifc nor device_specific is true. " + |
| "Odm-defined properties should be accessed only in Vendor or Odm") |
| } |
| default: |
| ctx.PropertyErrorf("property_owner", |
| "Unknown value %s: must be one of Platform, Vendor or Odm", m.Owner()) |
| } |
| |
| var label *string |
| if b, ok := ctx.Module().(android.Bazelable); ok && b.ShouldConvertWithBp2build(ctx) { |
| // TODO: b/295566168 - this will need to change once build files are checked in to account for |
| // checked in modules in mixed builds |
| label = proptools.StringPtr( |
| fmt.Sprintf("//%s:%s", ctx.ModuleDir(), m.CcImplementationModuleName())) |
| } |
| |
| // Generate a C++ implementation library. |
| // cc_library can receive *.sysprop files as their srcs, generating sources itself. |
| ccProps := ccLibraryProperties{} |
| ccProps.Name = proptools.StringPtr(m.CcImplementationModuleName()) |
| ccProps.Srcs = m.properties.Srcs |
| ccProps.Soc_specific = proptools.BoolPtr(ctx.SocSpecific()) |
| ccProps.Device_specific = proptools.BoolPtr(ctx.DeviceSpecific()) |
| ccProps.Product_specific = proptools.BoolPtr(ctx.ProductSpecific()) |
| ccProps.Sysprop.Platform = proptools.BoolPtr(isOwnerPlatform) |
| ccProps.Target.Android.Header_libs = []string{"libbase_headers"} |
| ccProps.Target.Android.Shared_libs = []string{"liblog"} |
| ccProps.Target.Host.Static_libs = []string{"libbase", "liblog"} |
| ccProps.Recovery_available = m.properties.Recovery_available |
| ccProps.Vendor_available = m.properties.Vendor_available |
| ccProps.Product_available = m.properties.Product_available |
| ccProps.Ramdisk_available = m.properties.Ramdisk_available |
| ccProps.Host_supported = m.properties.Host_supported |
| ccProps.Apex_available = m.ApexProperties.Apex_available |
| ccProps.Min_sdk_version = m.properties.Cpp.Min_sdk_version |
| ccProps.Bazel_module.Label = label |
| ctx.CreateModule(cc.LibraryFactory, &ccProps) |
| |
| scope := "internal" |
| |
| // We need to only use public version, if the partition where sysprop_library will be installed |
| // is different from owner. |
| if ctx.ProductSpecific() { |
| // Currently product partition can't own any sysprop_library. So product always uses public. |
| scope = "public" |
| } else if isOwnerPlatform && installedInVendorOrOdm { |
| // Vendor or Odm should use public version of Platform's sysprop_library. |
| scope = "public" |
| } |
| |
| // Generate a Java implementation library. |
| // Contrast to C++, syspropJavaGenRule module will generate srcjar and the srcjar will be fed |
| // to Java implementation library. |
| ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{ |
| Srcs: m.properties.Srcs, |
| Scope: scope, |
| Name: proptools.StringPtr(m.javaGenModuleName()), |
| Check_api: proptools.StringPtr(ctx.ModuleName()), |
| }) |
| |
| // if platform sysprop_library is installed in /system or /system-ext, we regard it as an API |
| // and allow any modules (even from different partition) to link against the sysprop_library. |
| // To do that, we create a public stub and expose it to modules with sdk_version: system_*. |
| var publicStub string |
| if isOwnerPlatform && installedInSystem { |
| publicStub = m.javaPublicStubName() |
| } |
| |
| ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{ |
| Name: proptools.StringPtr(m.BaseModuleName()), |
| Srcs: []string{":" + m.javaGenModuleName()}, |
| Soc_specific: proptools.BoolPtr(ctx.SocSpecific()), |
| Device_specific: proptools.BoolPtr(ctx.DeviceSpecific()), |
| Product_specific: proptools.BoolPtr(ctx.ProductSpecific()), |
| Installable: m.properties.Installable, |
| Sdk_version: proptools.StringPtr("core_current"), |
| Libs: []string{javaSyspropStub}, |
| SyspropPublicStub: publicStub, |
| Apex_available: m.ApexProperties.Apex_available, |
| Min_sdk_version: m.properties.Java.Min_sdk_version, |
| Bazel_module: struct { |
| Bp2build_available *bool |
| }{ |
| Bp2build_available: proptools.BoolPtr(false), |
| }, |
| }) |
| |
| if publicStub != "" { |
| ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{ |
| Srcs: m.properties.Srcs, |
| Scope: "public", |
| Name: proptools.StringPtr(m.javaGenPublicStubName()), |
| Check_api: proptools.StringPtr(ctx.ModuleName()), |
| }) |
| |
| ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{ |
| Name: proptools.StringPtr(publicStub), |
| Srcs: []string{":" + m.javaGenPublicStubName()}, |
| Installable: proptools.BoolPtr(false), |
| Sdk_version: proptools.StringPtr("core_current"), |
| Libs: []string{javaSyspropStub}, |
| Stem: proptools.StringPtr(m.BaseModuleName()), |
| Bazel_module: struct { |
| Bp2build_available *bool |
| }{ |
| Bp2build_available: proptools.BoolPtr(false), |
| }, |
| }) |
| } |
| |
| // syspropLibraries will be used by property_contexts to check types. |
| // Record absolute paths of sysprop_library to prevent soong_namespace problem. |
| if m.ExportedToMake() { |
| syspropLibrariesLock.Lock() |
| defer syspropLibrariesLock.Unlock() |
| |
| libraries := syspropLibraries(ctx.Config()) |
| *libraries = append(*libraries, "//"+ctx.ModuleDir()+":"+ctx.ModuleName()) |
| } |
| } |
| |
| // TODO(b/240463568): Additional properties will be added for API validation |
| func (m *syspropLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) { |
| labels := cc.SyspropLibraryLabels{ |
| SyspropLibraryLabel: m.BaseModuleName(), |
| SharedLibraryLabel: m.CcImplementationModuleName(), |
| StaticLibraryLabel: cc.BazelLabelNameForStaticModule(m.CcImplementationModuleName()), |
| } |
| cc.Bp2buildSysprop(ctx, |
| labels, |
| bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Srcs)), |
| m.properties.Cpp.Min_sdk_version) |
| } |