551 lines
18 KiB
Go
551 lines
18 KiB
Go
// 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"
|
|
|
|
"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
|
|
|
|
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 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() {
|
|
android.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)")
|
|
fmt.Fprintf(w, "LOCAL_MODULE := %s\n", 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.
|
|
func syspropLibraryFactory() android.Module {
|
|
m := &syspropLibrary{}
|
|
|
|
m.AddProperties(
|
|
&m.properties,
|
|
)
|
|
android.InitAndroidModule(m)
|
|
android.InitApexModule(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
|
|
Host_supported *bool
|
|
Apex_available []string
|
|
Min_sdk_version *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
|
|
}
|
|
|
|
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())
|
|
}
|
|
|
|
// 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.Host_supported = m.properties.Host_supported
|
|
ccProps.Apex_available = m.ApexProperties.Apex_available
|
|
ccProps.Min_sdk_version = m.properties.Cpp.Min_sdk_version
|
|
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,
|
|
})
|
|
|
|
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()),
|
|
})
|
|
}
|
|
|
|
// 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())
|
|
}
|
|
}
|