| // 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. |
| |
| package sysprop |
| |
| import ( |
| "fmt" |
| "io" |
| "path" |
| |
| "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 |
| } |
| |
| 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") |
| |
| android.PreArchMutators(func(ctx android.RegisterMutatorsContext) { |
| ctx.BottomUp("sysprop_deps", syspropDepsMutator).Parallel() |
| }) |
| } |
| |
| 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) 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 |
| |
| properties syspropLibraryProperties |
| |
| checkApiFileTimeStamp android.WritablePath |
| latestApiFile android.Path |
| currentApiFile android.Path |
| 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 recovery |
| Recovery_available *bool |
| |
| // Make this module available when building for vendor |
| Vendor_available *bool |
| |
| // list of .sysprop files which defines the properties. |
| Srcs []string `android:"path"` |
| } |
| |
| var ( |
| pctx = android.NewPackageContext("android/soong/sysprop") |
| syspropCcTag = dependencyTag{name: "syspropCc"} |
| ) |
| |
| func init() { |
| android.RegisterModuleType("sysprop_library", syspropLibraryFactory) |
| } |
| |
| func (m *syspropLibrary) Name() string { |
| return m.BaseModuleName() + "_sysprop_library" |
| } |
| |
| func (m *syspropLibrary) CcModuleName() string { |
| return "lib" + m.BaseModuleName() |
| } |
| |
| func (m *syspropLibrary) javaGenModuleName() string { |
| return m.BaseModuleName() + "_java_gen" |
| } |
| |
| func (m *syspropLibrary) BaseModuleName() string { |
| return m.ModuleBase.Name() |
| } |
| |
| 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 |
| } |
| |
| m.currentApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-current.txt") |
| m.latestApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-latest.txt") |
| |
| // dump API rule |
| rule := android.NewRuleBuilder() |
| m.dumpedApiFile = android.PathForModuleOut(ctx, "api-dump.txt") |
| rule.Command(). |
| BuiltTool(ctx, "sysprop_api_dump"). |
| Output(m.dumpedApiFile). |
| Inputs(android.PathsForModuleSrc(ctx, m.properties.Srcs)) |
| rule.Build(pctx, ctx, baseModuleName+"_api_dump", baseModuleName+" api dump") |
| |
| // check API rule |
| rule = android.NewRuleBuilder() |
| |
| // 1. current.txt <-> 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 && rm -rf %q && cp -f %q %q\n`+ |
| `******************************\n`, baseModuleName, baseModuleName, |
| m.currentApiFile.String(), m.dumpedApiFile.String(), m.currentApiFile.String()) |
| |
| rule.Command(). |
| Text("( cmp").Flag("-s"). |
| Input(m.dumpedApiFile). |
| Input(m.currentApiFile). |
| Text("|| ( echo").Flag("-e"). |
| Flag(`"` + msg + `"`). |
| Text("; exit 38) )") |
| |
| // 2. current.txt <-> 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(ctx, "sysprop_api_checker"). |
| Input(m.latestApiFile). |
| Input(m.currentApiFile). |
| Text(" || ( echo").Flag("-e"). |
| Flag(`"` + msg + `"`). |
| Text("; exit 38) )") |
| |
| m.checkApiFileTimeStamp = android.PathForModuleOut(ctx, "check_api.timestamp") |
| |
| rule.Command(). |
| Text("touch"). |
| Output(m.checkApiFileTimeStamp) |
| |
| rule.Build(pctx, ctx, 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)") |
| fmt.Fprintf(w, "LOCAL_MODULE := %s\n", m.Name()) |
| 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()) |
| }} |
| } |
| |
| // 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. |
| func syspropLibraryFactory() android.Module { |
| m := &syspropLibrary{} |
| |
| m.AddProperties( |
| &m.properties, |
| ) |
| android.InitAndroidModule(m) |
| android.AddLoadHook(m, func(ctx android.LoadHookContext) { syspropLibraryHook(ctx, m) }) |
| return m |
| } |
| |
| func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) { |
| if len(m.properties.Srcs) == 0 { |
| ctx.PropertyErrorf("srcs", "sysprop_library must specify srcs") |
| } |
| |
| missing_api := false |
| |
| for _, txt := range []string{"-current.txt", "-latest.txt"} { |
| path := path.Join(ctx.ModuleDir(), "api", m.BaseModuleName()+txt) |
| file := android.ExistentPathForSource(ctx, path) |
| if !file.Valid() { |
| ctx.ModuleErrorf("API file %#v doesn't exist", path) |
| missing_api = true |
| } |
| } |
| |
| if missing_api { |
| script := "build/soong/scripts/gen-sysprop-api-files.sh" |
| p := android.ExistentPathForSource(ctx, script) |
| |
| if !p.Valid() { |
| panic(fmt.Sprintf("script file %s doesn't exist", script)) |
| } |
| |
| ctx.ModuleErrorf("One or more api files are missing. "+ |
| "You can create them by:\n"+ |
| "%s %q %q", script, ctx.ModuleDir(), m.BaseModuleName()) |
| return |
| } |
| |
| socSpecific := ctx.SocSpecific() |
| deviceSpecific := ctx.DeviceSpecific() |
| productSpecific := ctx.ProductSpecific() |
| |
| owner := m.properties.Property_owner |
| stub := "sysprop-library-stub-" |
| |
| switch owner { |
| case "Platform": |
| // Every partition can access platform-defined properties |
| stub += "platform" |
| case "Vendor": |
| // System can't access vendor's properties |
| if !socSpecific && !deviceSpecific && !productSpecific { |
| ctx.ModuleErrorf("None of soc_specific, device_specific, product_specific is true. " + |
| "System can't access sysprop_library owned by Vendor") |
| } |
| stub += "vendor" |
| case "Odm": |
| // Only vendor can access Odm-defined properties |
| if !socSpecific && !deviceSpecific { |
| ctx.ModuleErrorf("Neither soc_speicifc nor device_specific is true. " + |
| "Odm-defined properties should be accessed only in Vendor or Odm") |
| } |
| stub += "vendor" |
| default: |
| ctx.PropertyErrorf("property_owner", |
| "Unknown value %s: must be one of Platform, Vendor or Odm", owner) |
| } |
| |
| ccProps := struct { |
| Name *string |
| Srcs []string |
| Soc_specific *bool |
| Device_specific *bool |
| Product_specific *bool |
| Sysprop struct { |
| Platform *bool |
| } |
| Header_libs []string |
| Shared_libs []string |
| Required []string |
| Recovery *bool |
| Recovery_available *bool |
| Vendor_available *bool |
| }{} |
| |
| ccProps.Name = proptools.StringPtr(m.CcModuleName()) |
| ccProps.Srcs = m.properties.Srcs |
| ccProps.Soc_specific = proptools.BoolPtr(socSpecific) |
| ccProps.Device_specific = proptools.BoolPtr(deviceSpecific) |
| ccProps.Product_specific = proptools.BoolPtr(productSpecific) |
| ccProps.Sysprop.Platform = proptools.BoolPtr(owner == "Platform") |
| ccProps.Header_libs = []string{"libbase_headers"} |
| ccProps.Shared_libs = []string{"liblog"} |
| ccProps.Recovery_available = m.properties.Recovery_available |
| ccProps.Vendor_available = m.properties.Vendor_available |
| |
| ctx.CreateModule(cc.LibraryFactory, &ccProps) |
| |
| // internal scope contains all properties |
| // public scope only contains public properties |
| // use public if the owner is different from client |
| scope := "internal" |
| isProduct := ctx.ProductSpecific() |
| isVendor := ctx.SocSpecific() |
| isOwnerPlatform := owner == "Platform" |
| |
| if isProduct { |
| // product can't own any sysprop_library now, so product must use public scope |
| scope = "public" |
| } else if isVendor && !isOwnerPlatform { |
| // vendor and odm can't use system's internal property. |
| scope = "public" |
| } |
| |
| javaGenProps := struct { |
| Srcs []string |
| Scope string |
| Name *string |
| }{ |
| Srcs: m.properties.Srcs, |
| Scope: scope, |
| Name: proptools.StringPtr(m.javaGenModuleName()), |
| } |
| |
| ctx.CreateModule(syspropJavaGenFactory, &javaGenProps) |
| |
| javaProps := struct { |
| Name *string |
| Srcs []string |
| Soc_specific *bool |
| Device_specific *bool |
| Product_specific *bool |
| Required []string |
| Sdk_version *string |
| Installable *bool |
| Libs []string |
| }{} |
| |
| javaProps.Name = proptools.StringPtr(m.BaseModuleName()) |
| javaProps.Srcs = []string{":" + *javaGenProps.Name} |
| javaProps.Soc_specific = proptools.BoolPtr(socSpecific) |
| javaProps.Device_specific = proptools.BoolPtr(deviceSpecific) |
| javaProps.Product_specific = proptools.BoolPtr(productSpecific) |
| javaProps.Installable = m.properties.Installable |
| javaProps.Sdk_version = proptools.StringPtr("core_current") |
| javaProps.Libs = []string{stub} |
| |
| ctx.CreateModule(java.LibraryFactory, &javaProps) |
| } |
| |
| func syspropDepsMutator(ctx android.BottomUpMutatorContext) { |
| if m, ok := ctx.Module().(*syspropLibrary); ok { |
| ctx.AddReverseDependency(m, nil, m.javaGenModuleName()) |
| } |
| } |