1// Copyright 2019 Google Inc. All rights reserved.
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 java
16
17import (
18	"fmt"
19	"path/filepath"
20
21	"android/soong/android"
22
23	"github.com/google/blueprint"
24	"github.com/google/blueprint/proptools"
25)
26
27func init() {
28	registerPlatformCompatConfigBuildComponents(android.InitRegistrationContext)
29
30	android.RegisterSdkMemberType(CompatConfigSdkMemberType)
31}
32
33var CompatConfigSdkMemberType = &compatConfigMemberType{
34	SdkMemberTypeBase: android.SdkMemberTypeBase{
35		PropertyName: "compat_configs",
36		SupportsSdk:  true,
37	},
38}
39
40func registerPlatformCompatConfigBuildComponents(ctx android.RegistrationContext) {
41	ctx.RegisterParallelSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory)
42	ctx.RegisterModuleType("platform_compat_config", PlatformCompatConfigFactory)
43	ctx.RegisterModuleType("prebuilt_platform_compat_config", prebuiltCompatConfigFactory)
44	ctx.RegisterModuleType("global_compat_config", globalCompatConfigFactory)
45}
46
47var PrepareForTestWithPlatformCompatConfig = android.FixtureRegisterWithContext(registerPlatformCompatConfigBuildComponents)
48
49func platformCompatConfigPath(ctx android.PathContext) android.OutputPath {
50	return android.PathForOutput(ctx, "compat_config", "merged_compat_config.xml")
51}
52
53type platformCompatConfigProperties struct {
54	Src *string `android:"path"`
55}
56
57type platformCompatConfig struct {
58	android.ModuleBase
59
60	properties     platformCompatConfigProperties
61	installDirPath android.InstallPath
62	configFile     android.OutputPath
63	metadataFile   android.OutputPath
64}
65
66func (p *platformCompatConfig) compatConfigMetadata() android.Path {
67	return p.metadataFile
68}
69
70func (p *platformCompatConfig) CompatConfig() android.OutputPath {
71	return p.configFile
72}
73
74func (p *platformCompatConfig) SubDir() string {
75	return "compatconfig"
76}
77
78type platformCompatConfigMetadataProvider interface {
79	compatConfigMetadata() android.Path
80}
81
82type PlatformCompatConfigIntf interface {
83	android.Module
84
85	CompatConfig() android.OutputPath
86	// Sub dir under etc dir.
87	SubDir() string
88}
89
90var _ PlatformCompatConfigIntf = (*platformCompatConfig)(nil)
91var _ platformCompatConfigMetadataProvider = (*platformCompatConfig)(nil)
92
93func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
94	rule := android.NewRuleBuilder(pctx, ctx)
95
96	configFileName := p.Name() + ".xml"
97	metadataFileName := p.Name() + "_meta.xml"
98	p.configFile = android.PathForModuleOut(ctx, configFileName).OutputPath
99	p.metadataFile = android.PathForModuleOut(ctx, metadataFileName).OutputPath
100	path := android.PathForModuleSrc(ctx, String(p.properties.Src))
101
102	rule.Command().
103		BuiltTool("process-compat-config").
104		FlagWithInput("--jar ", path).
105		FlagWithOutput("--device-config ", p.configFile).
106		FlagWithOutput("--merged-config ", p.metadataFile)
107
108	p.installDirPath = android.PathForModuleInstall(ctx, "etc", "compatconfig")
109	rule.Build(configFileName, "Extract compat/compat_config.xml and install it")
110
111}
112
113func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries {
114	return []android.AndroidMkEntries{android.AndroidMkEntries{
115		Class:      "ETC",
116		OutputFile: android.OptionalPathForPath(p.configFile),
117		Include:    "$(BUILD_PREBUILT)",
118		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
119			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
120				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String())
121				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
122			},
123		},
124	}}
125}
126
127func PlatformCompatConfigFactory() android.Module {
128	module := &platformCompatConfig{}
129	module.AddProperties(&module.properties)
130	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
131	return module
132}
133
134type compatConfigMemberType struct {
135	android.SdkMemberTypeBase
136}
137
138func (b *compatConfigMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
139	ctx.AddVariationDependencies(nil, dependencyTag, names...)
140}
141
142func (b *compatConfigMemberType) IsInstance(module android.Module) bool {
143	_, ok := module.(*platformCompatConfig)
144	return ok
145}
146
147func (b *compatConfigMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
148	return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_platform_compat_config")
149}
150
151func (b *compatConfigMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
152	return &compatConfigSdkMemberProperties{}
153}
154
155type compatConfigSdkMemberProperties struct {
156	android.SdkMemberPropertiesBase
157
158	Metadata android.Path
159}
160
161func (b *compatConfigSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
162	module := variant.(*platformCompatConfig)
163	b.Metadata = module.metadataFile
164}
165
166func (b *compatConfigSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
167	builder := ctx.SnapshotBuilder()
168	if b.Metadata != nil {
169		snapshotRelativePath := filepath.Join("compat_configs", ctx.Name(), b.Metadata.Base())
170		builder.CopyToSnapshot(b.Metadata, snapshotRelativePath)
171		propertySet.AddProperty("metadata", snapshotRelativePath)
172	}
173}
174
175var _ android.SdkMemberType = (*compatConfigMemberType)(nil)
176
177// A prebuilt version of the platform compat config module.
178type prebuiltCompatConfigModule struct {
179	android.ModuleBase
180	prebuilt android.Prebuilt
181
182	properties prebuiltCompatConfigProperties
183
184	metadataFile android.Path
185}
186
187type prebuiltCompatConfigProperties struct {
188	Metadata *string `android:"path"`
189
190	// Name of the source soong module that gets shadowed by this prebuilt
191	// If unspecified, follows the naming convention that the source module of
192	// the prebuilt is Name() without "prebuilt_" prefix
193	Source_module_name *string
194}
195
196func (module *prebuiltCompatConfigModule) Prebuilt() *android.Prebuilt {
197	return &module.prebuilt
198}
199
200func (module *prebuiltCompatConfigModule) Name() string {
201	return module.prebuilt.Name(module.ModuleBase.Name())
202}
203
204func (module *prebuiltCompatConfigModule) compatConfigMetadata() android.Path {
205	return module.metadataFile
206}
207
208func (module *prebuiltCompatConfigModule) BaseModuleName() string {
209	return proptools.StringDefault(module.properties.Source_module_name, module.ModuleBase.Name())
210}
211
212var _ platformCompatConfigMetadataProvider = (*prebuiltCompatConfigModule)(nil)
213
214func (module *prebuiltCompatConfigModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
215	module.metadataFile = module.prebuilt.SingleSourcePath(ctx)
216}
217
218// A prebuilt version of platform_compat_config that provides the metadata.
219func prebuiltCompatConfigFactory() android.Module {
220	m := &prebuiltCompatConfigModule{}
221	m.AddProperties(&m.properties)
222	android.InitSingleSourcePrebuiltModule(m, &m.properties, "Metadata")
223	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
224	return m
225}
226
227// compat singleton rules
228type platformCompatConfigSingleton struct {
229	metadata android.Path
230}
231
232func (p *platformCompatConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
233
234	var compatConfigMetadata android.Paths
235
236	ctx.VisitAllModules(func(module android.Module) {
237		if !module.Enabled(ctx) {
238			return
239		}
240		if c, ok := module.(platformCompatConfigMetadataProvider); ok {
241			if !android.IsModulePreferred(module) {
242				return
243			}
244			metadata := c.compatConfigMetadata()
245			compatConfigMetadata = append(compatConfigMetadata, metadata)
246		}
247	})
248
249	if compatConfigMetadata == nil {
250		// nothing to do.
251		return
252	}
253
254	rule := android.NewRuleBuilder(pctx, ctx)
255	outputPath := platformCompatConfigPath(ctx)
256
257	rule.Command().
258		BuiltTool("process-compat-config").
259		FlagForEachInput("--xml ", compatConfigMetadata).
260		FlagWithOutput("--merged-config ", outputPath)
261
262	rule.Build("merged-compat-config", "Merge compat config")
263
264	p.metadata = outputPath
265}
266
267func (p *platformCompatConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
268	if p.metadata != nil {
269		ctx.Strict("INTERNAL_PLATFORM_MERGED_COMPAT_CONFIG", p.metadata.String())
270	}
271}
272
273func platformCompatConfigSingletonFactory() android.Singleton {
274	return &platformCompatConfigSingleton{}
275}
276
277// ============== merged_compat_config =================
278type globalCompatConfigProperties struct {
279	// name of the file into which the metadata will be copied.
280	Filename *string
281}
282
283type globalCompatConfig struct {
284	android.ModuleBase
285
286	properties globalCompatConfigProperties
287
288	outputFilePath android.OutputPath
289}
290
291func (c *globalCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
292	filename := String(c.properties.Filename)
293
294	inputPath := platformCompatConfigPath(ctx)
295	c.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
296
297	// This ensures that outputFilePath has the correct name for others to
298	// use, as the source file may have a different name.
299	ctx.Build(pctx, android.BuildParams{
300		Rule:   android.Cp,
301		Output: c.outputFilePath,
302		Input:  inputPath,
303	})
304}
305
306func (h *globalCompatConfig) OutputFiles(tag string) (android.Paths, error) {
307	switch tag {
308	case "":
309		return android.Paths{h.outputFilePath}, nil
310	default:
311		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
312	}
313}
314
315// global_compat_config provides access to the merged compat config xml file generated by the build.
316func globalCompatConfigFactory() android.Module {
317	module := &globalCompatConfig{}
318	module.AddProperties(&module.properties)
319	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
320	return module
321}
322