|  | // Copyright 2019 Google Inc. All rights reserved. | 
|  | // | 
|  | // 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 java | 
|  |  | 
|  | import ( | 
|  | "path/filepath" | 
|  |  | 
|  | "android/soong/android" | 
|  |  | 
|  | "github.com/google/blueprint" | 
|  | "github.com/google/blueprint/proptools" | 
|  | ) | 
|  |  | 
|  | func init() { | 
|  | registerPlatformCompatConfigBuildComponents(android.InitRegistrationContext) | 
|  |  | 
|  | android.RegisterSdkMemberType(CompatConfigSdkMemberType) | 
|  | } | 
|  |  | 
|  | var CompatConfigSdkMemberType = &compatConfigMemberType{ | 
|  | SdkMemberTypeBase: android.SdkMemberTypeBase{ | 
|  | PropertyName: "compat_configs", | 
|  | SupportsSdk:  true, | 
|  | }, | 
|  | } | 
|  |  | 
|  | func registerPlatformCompatConfigBuildComponents(ctx android.RegistrationContext) { | 
|  | ctx.RegisterParallelSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory) | 
|  | ctx.RegisterModuleType("platform_compat_config", PlatformCompatConfigFactory) | 
|  | ctx.RegisterModuleType("prebuilt_platform_compat_config", prebuiltCompatConfigFactory) | 
|  | ctx.RegisterModuleType("global_compat_config", globalCompatConfigFactory) | 
|  | } | 
|  |  | 
|  | var PrepareForTestWithPlatformCompatConfig = android.FixtureRegisterWithContext(registerPlatformCompatConfigBuildComponents) | 
|  |  | 
|  | func platformCompatConfigPath(ctx android.PathContext) android.OutputPath { | 
|  | return android.PathForOutput(ctx, "compat_config", "merged_compat_config.xml") | 
|  | } | 
|  |  | 
|  | type platformCompatConfigProperties struct { | 
|  | Src *string `android:"path"` | 
|  | } | 
|  |  | 
|  | type platformCompatConfig struct { | 
|  | android.ModuleBase | 
|  |  | 
|  | properties     platformCompatConfigProperties | 
|  | installDirPath android.InstallPath | 
|  | configFile     android.OutputPath | 
|  | metadataFile   android.OutputPath | 
|  |  | 
|  | installConfigFile android.InstallPath | 
|  | } | 
|  |  | 
|  | func (p *platformCompatConfig) compatConfigMetadata() android.Path { | 
|  | return p.metadataFile | 
|  | } | 
|  |  | 
|  | func (p *platformCompatConfig) CompatConfig() android.OutputPath { | 
|  | return p.configFile | 
|  | } | 
|  |  | 
|  | func (p *platformCompatConfig) SubDir() string { | 
|  | return "compatconfig" | 
|  | } | 
|  |  | 
|  | type platformCompatConfigMetadataProvider interface { | 
|  | compatConfigMetadata() android.Path | 
|  | } | 
|  |  | 
|  | type PlatformCompatConfigIntf interface { | 
|  | android.Module | 
|  |  | 
|  | CompatConfig() android.OutputPath | 
|  | // Sub dir under etc dir. | 
|  | SubDir() string | 
|  | } | 
|  |  | 
|  | var _ PlatformCompatConfigIntf = (*platformCompatConfig)(nil) | 
|  | var _ platformCompatConfigMetadataProvider = (*platformCompatConfig)(nil) | 
|  |  | 
|  | func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) { | 
|  | rule := android.NewRuleBuilder(pctx, ctx) | 
|  |  | 
|  | configFileName := p.Name() + ".xml" | 
|  | metadataFileName := p.Name() + "_meta.xml" | 
|  | p.configFile = android.PathForModuleOut(ctx, configFileName).OutputPath | 
|  | p.metadataFile = android.PathForModuleOut(ctx, metadataFileName).OutputPath | 
|  | path := android.PathForModuleSrc(ctx, String(p.properties.Src)) | 
|  |  | 
|  | rule.Command(). | 
|  | BuiltTool("process-compat-config"). | 
|  | FlagWithInput("--jar ", path). | 
|  | FlagWithOutput("--device-config ", p.configFile). | 
|  | FlagWithOutput("--merged-config ", p.metadataFile) | 
|  |  | 
|  | p.installDirPath = android.PathForModuleInstall(ctx, "etc", "compatconfig") | 
|  | p.installConfigFile = android.PathForModuleInstall(ctx, "etc", "compatconfig", p.configFile.Base()) | 
|  | rule.Build(configFileName, "Extract compat/compat_config.xml and install it") | 
|  | ctx.InstallFile(p.installDirPath, p.configFile.Base(), p.configFile) | 
|  | } | 
|  |  | 
|  | func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries { | 
|  | return []android.AndroidMkEntries{android.AndroidMkEntries{ | 
|  | Class:      "ETC", | 
|  | OutputFile: android.OptionalPathForPath(p.configFile), | 
|  | }} | 
|  | } | 
|  |  | 
|  | func PlatformCompatConfigFactory() android.Module { | 
|  | module := &platformCompatConfig{} | 
|  | module.AddProperties(&module.properties) | 
|  | android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) | 
|  | return module | 
|  | } | 
|  |  | 
|  | type compatConfigMemberType struct { | 
|  | android.SdkMemberTypeBase | 
|  | } | 
|  |  | 
|  | func (b *compatConfigMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) { | 
|  | ctx.AddVariationDependencies(nil, dependencyTag, names...) | 
|  | } | 
|  |  | 
|  | func (b *compatConfigMemberType) IsInstance(module android.Module) bool { | 
|  | _, ok := module.(*platformCompatConfig) | 
|  | return ok | 
|  | } | 
|  |  | 
|  | func (b *compatConfigMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule { | 
|  | return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_platform_compat_config") | 
|  | } | 
|  |  | 
|  | func (b *compatConfigMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties { | 
|  | return &compatConfigSdkMemberProperties{} | 
|  | } | 
|  |  | 
|  | type compatConfigSdkMemberProperties struct { | 
|  | android.SdkMemberPropertiesBase | 
|  |  | 
|  | Metadata android.Path | 
|  | } | 
|  |  | 
|  | func (b *compatConfigSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) { | 
|  | module := variant.(*platformCompatConfig) | 
|  | b.Metadata = module.metadataFile | 
|  | } | 
|  |  | 
|  | func (b *compatConfigSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) { | 
|  | builder := ctx.SnapshotBuilder() | 
|  | if b.Metadata != nil { | 
|  | snapshotRelativePath := filepath.Join("compat_configs", ctx.Name(), b.Metadata.Base()) | 
|  | builder.CopyToSnapshot(b.Metadata, snapshotRelativePath) | 
|  | propertySet.AddProperty("metadata", snapshotRelativePath) | 
|  | } | 
|  | } | 
|  |  | 
|  | var _ android.SdkMemberType = (*compatConfigMemberType)(nil) | 
|  |  | 
|  | // A prebuilt version of the platform compat config module. | 
|  | type prebuiltCompatConfigModule struct { | 
|  | android.ModuleBase | 
|  | prebuilt android.Prebuilt | 
|  |  | 
|  | properties prebuiltCompatConfigProperties | 
|  |  | 
|  | metadataFile android.Path | 
|  | } | 
|  |  | 
|  | type prebuiltCompatConfigProperties struct { | 
|  | Metadata *string `android:"path"` | 
|  |  | 
|  | // Name of the source soong module that gets shadowed by this prebuilt | 
|  | // If unspecified, follows the naming convention that the source module of | 
|  | // the prebuilt is Name() without "prebuilt_" prefix | 
|  | Source_module_name *string | 
|  | } | 
|  |  | 
|  | func (module *prebuiltCompatConfigModule) Prebuilt() *android.Prebuilt { | 
|  | return &module.prebuilt | 
|  | } | 
|  |  | 
|  | func (module *prebuiltCompatConfigModule) Name() string { | 
|  | return module.prebuilt.Name(module.ModuleBase.Name()) | 
|  | } | 
|  |  | 
|  | func (module *prebuiltCompatConfigModule) compatConfigMetadata() android.Path { | 
|  | return module.metadataFile | 
|  | } | 
|  |  | 
|  | func (module *prebuiltCompatConfigModule) BaseModuleName() string { | 
|  | return proptools.StringDefault(module.properties.Source_module_name, module.ModuleBase.Name()) | 
|  | } | 
|  |  | 
|  | var _ platformCompatConfigMetadataProvider = (*prebuiltCompatConfigModule)(nil) | 
|  |  | 
|  | func (module *prebuiltCompatConfigModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { | 
|  | module.metadataFile = module.prebuilt.SingleSourcePath(ctx) | 
|  | } | 
|  |  | 
|  | // A prebuilt version of platform_compat_config that provides the metadata. | 
|  | func prebuiltCompatConfigFactory() android.Module { | 
|  | m := &prebuiltCompatConfigModule{} | 
|  | m.AddProperties(&m.properties) | 
|  | android.InitSingleSourcePrebuiltModule(m, &m.properties, "Metadata") | 
|  | android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) | 
|  | return m | 
|  | } | 
|  |  | 
|  | // compat singleton rules | 
|  | type platformCompatConfigSingleton struct { | 
|  | metadata android.Path | 
|  | } | 
|  |  | 
|  | func (p *platformCompatConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) { | 
|  |  | 
|  | var compatConfigMetadata android.Paths | 
|  |  | 
|  | ctx.VisitAllModules(func(module android.Module) { | 
|  | if !module.Enabled(ctx) { | 
|  | return | 
|  | } | 
|  | if c, ok := module.(platformCompatConfigMetadataProvider); ok { | 
|  | if !android.IsModulePreferred(module) { | 
|  | return | 
|  | } | 
|  | metadata := c.compatConfigMetadata() | 
|  | compatConfigMetadata = append(compatConfigMetadata, metadata) | 
|  | } | 
|  | }) | 
|  |  | 
|  | if compatConfigMetadata == nil { | 
|  | // nothing to do. | 
|  | return | 
|  | } | 
|  |  | 
|  | rule := android.NewRuleBuilder(pctx, ctx) | 
|  | outputPath := platformCompatConfigPath(ctx) | 
|  |  | 
|  | rule.Command(). | 
|  | BuiltTool("process-compat-config"). | 
|  | FlagForEachInput("--xml ", compatConfigMetadata). | 
|  | FlagWithOutput("--merged-config ", outputPath) | 
|  |  | 
|  | rule.Build("merged-compat-config", "Merge compat config") | 
|  |  | 
|  | p.metadata = outputPath | 
|  | } | 
|  |  | 
|  | func (p *platformCompatConfigSingleton) MakeVars(ctx android.MakeVarsContext) { | 
|  | if p.metadata != nil { | 
|  | ctx.DistForGoal("droidcore", p.metadata) | 
|  | } | 
|  | } | 
|  |  | 
|  | func platformCompatConfigSingletonFactory() android.Singleton { | 
|  | return &platformCompatConfigSingleton{} | 
|  | } | 
|  |  | 
|  | // ============== merged_compat_config ================= | 
|  | type globalCompatConfigProperties struct { | 
|  | // name of the file into which the metadata will be copied. | 
|  | Filename *string | 
|  | } | 
|  |  | 
|  | type globalCompatConfig struct { | 
|  | android.ModuleBase | 
|  |  | 
|  | properties globalCompatConfigProperties | 
|  | } | 
|  |  | 
|  | func (c *globalCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) { | 
|  | filename := String(c.properties.Filename) | 
|  |  | 
|  | inputPath := platformCompatConfigPath(ctx) | 
|  | outputFilePath := android.PathForModuleOut(ctx, filename).OutputPath | 
|  |  | 
|  | // This ensures that outputFilePath has the correct name for others to | 
|  | // use, as the source file may have a different name. | 
|  | ctx.Build(pctx, android.BuildParams{ | 
|  | Rule:   android.Cp, | 
|  | Output: outputFilePath, | 
|  | Input:  inputPath, | 
|  | }) | 
|  |  | 
|  | ctx.SetOutputFiles(android.Paths{outputFilePath}, "") | 
|  | } | 
|  |  | 
|  | // global_compat_config provides access to the merged compat config xml file generated by the build. | 
|  | func globalCompatConfigFactory() android.Module { | 
|  | module := &globalCompatConfig{} | 
|  | module.AddProperties(&module.properties) | 
|  | android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon) | 
|  | return module | 
|  | } |