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