xref: /aosp_15_r20/hardware/interfaces/compatibility_matrices/build/vintf_compatibility_matrix.go (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
1// Copyright (C) 2018 The Android Open Source Project
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 vintf
16
17import (
18	"fmt"
19	"io"
20	"strings"
21
22	"github.com/google/blueprint"
23	"github.com/google/blueprint/proptools"
24
25	"android/soong/android"
26	"android/soong/kernel/configs"
27	"android/soong/selinux"
28)
29
30type dependencyTag struct {
31	blueprint.BaseDependencyTag
32	name string
33}
34
35var (
36	pctx = android.NewPackageContext("android/vintf")
37
38	assembleVintfRule = pctx.AndroidStaticRule("assemble_vintf", blueprint.RuleParams{
39		Command:     `${assembleVintfEnv} ${assembleVintfCmd} -i ${inputs} -o ${out} ${extraArgs}`,
40		CommandDeps: []string{"${assembleVintfCmd}", "${AvbToolCmd}"},
41		Description: "assemble_vintf -i ${inputs}",
42	}, "inputs", "extraArgs", "assembleVintfEnv")
43
44	xmllintXsd = pctx.AndroidStaticRule("xmllint-xsd", blueprint.RuleParams{
45		Command:     `$XmlLintCmd --quiet --schema $xsd $in > /dev/null && touch -a $out`,
46		CommandDeps: []string{"$XmlLintCmd"},
47		Restat:      true,
48	}, "xsd")
49
50	kernelConfigTag  = dependencyTag{name: "kernel-config"}
51	schemaTag        = dependencyTag{name: "matrix-schema"}
52	schemaModuleName = "compatibility_matrix_schema"
53)
54
55const (
56	relpath                  = "vintf"
57	emptyManifest            = "hardware/interfaces/compatibility_matrices/manifest.empty.xml"
58	compatibilityEmptyMatrix = "hardware/interfaces/compatibility_matrices/compatibility_matrix.empty.xml"
59	deviceFcmType            = "device_fcm"
60	productFcmType           = "product_fcm"
61)
62
63type vintfCompatibilityMatrixProperties struct {
64	// set the name of the output
65	Stem *string
66
67	// list of source compatibility matrix XML files
68	Srcs []string
69
70	// list of kernel_config modules to be combined to final output
71	Kernel_configs []string
72
73	// Type of the FCM type, the allowed type are device_fcm and product_fcm and it should only be used under hardware/interfaces/compatibility_matrices
74	Type *string
75}
76
77type vintfCompatibilityMatrixRule struct {
78	android.ModuleBase
79	properties vintfCompatibilityMatrixProperties
80
81	genFile                android.WritablePath
82	additionalDependencies android.WritablePaths
83	phonyOnly              bool
84}
85
86func init() {
87	pctx.HostBinToolVariable("assembleVintfCmd", "assemble_vintf")
88	pctx.HostBinToolVariable("XmlLintCmd", "xmllint")
89	pctx.HostBinToolVariable("AvbToolCmd", "avbtool")
90	android.RegisterModuleType("vintf_compatibility_matrix", vintfCompatibilityMatrixFactory)
91}
92
93func vintfCompatibilityMatrixFactory() android.Module {
94	g := &vintfCompatibilityMatrixRule{}
95	g.AddProperties(&g.properties)
96	android.InitAndroidArchModule(g, android.DeviceSupported, android.MultilibCommon)
97	return g
98}
99
100var _ android.AndroidMkDataProvider = (*vintfCompatibilityMatrixRule)(nil)
101
102func (g *vintfCompatibilityMatrixRule) DepsMutator(ctx android.BottomUpMutatorContext) {
103	android.ExtractSourcesDeps(ctx, g.properties.Srcs)
104	ctx.AddDependency(ctx.Module(), kernelConfigTag, g.properties.Kernel_configs...)
105	ctx.AddDependency(ctx.Module(), schemaTag, schemaModuleName)
106}
107
108func (g *vintfCompatibilityMatrixRule) timestampFilePath(ctx android.ModuleContext, path android.Path) android.WritablePath {
109	return android.GenPathWithExt(ctx, "vintf-xmllint", path, "ts")
110}
111
112func (g *vintfCompatibilityMatrixRule) generateValidateBuildAction(ctx android.ModuleContext, path android.Path, schema android.Path) {
113	timestamp := g.timestampFilePath(ctx, path)
114	ctx.Build(pctx, android.BuildParams{
115		Rule:        xmllintXsd,
116		Description: "xmllint-xsd",
117		Input:       path,
118		Output:      timestamp,
119		Implicit:    schema,
120		Args: map[string]string{
121			"xsd": schema.String(),
122		},
123	})
124	g.additionalDependencies = append(g.additionalDependencies, timestamp)
125}
126
127func (g *vintfCompatibilityMatrixRule) getSchema(ctx android.ModuleContext) android.OptionalPath {
128	schemaModule := ctx.GetDirectDepWithTag(schemaModuleName, schemaTag)
129	sfp, ok := schemaModule.(android.SourceFileProducer)
130	if !ok {
131		ctx.ModuleErrorf("Implicit dependency %q has no srcs", ctx.OtherModuleName(schemaModule))
132		return android.OptionalPath{}
133	}
134
135	schemaSrcs := sfp.Srcs()
136	if len(schemaSrcs) != 1 {
137		ctx.PropertyErrorf(`srcs of implicit dependency %q has length %d != 1`, ctx.OtherModuleName(schemaModule), len(schemaSrcs))
138		return android.OptionalPath{}
139	}
140	return android.OptionalPathForPath(schemaSrcs[0])
141}
142
143func (g *vintfCompatibilityMatrixRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
144	// Types attribute only allow `device_fcm` or `product_fcm` if set and only restricted it being used under
145	// `hardware/interfaces/compatibility_matrices` to prevent accidental external usages.
146	matrixType := proptools.String(g.properties.Type)
147	if matrixType != "" {
148		if matrixType != deviceFcmType && matrixType != productFcmType {
149			panic(fmt.Errorf("The attribute 'type' value must be either 'device_fcm' or 'product_fcm' if set!"))
150		}
151		if !strings.HasPrefix(android.PathForModuleSrc(ctx).String(), "hardware/interfaces/compatibility_matrices") {
152			panic(fmt.Errorf("Attribute type can only be set for module under `hardware/interfaces/compatibility_matrices`!"))
153		}
154		if (len(g.properties.Srcs) + len(g.properties.Kernel_configs)) > 0 {
155			panic(fmt.Errorf("Attribute 'type' and 'srcs' or 'kernel_configs' should not set simultaneously! To update inputs for this rule, edit vintf_compatibility_matrix.go directly."))
156		}
157	}
158
159	outputFilename := proptools.String(g.properties.Stem)
160	if outputFilename == "" {
161		outputFilename = g.Name()
162	}
163
164	schema := g.getSchema(ctx)
165	if !schema.Valid() {
166		return
167	}
168
169	inputPaths := android.PathsForModuleSrc(ctx, g.properties.Srcs)
170	for _, srcPath := range inputPaths {
171		g.generateValidateBuildAction(ctx, srcPath, schema.Path())
172	}
173
174	// No need to validate matrices from kernel configs because they are generated by
175	// assemble_vintf.
176	ctx.VisitDirectDepsWithTag(kernelConfigTag, func(m android.Module) {
177		if k, ok := m.(*configs.KernelConfigRule); ok {
178			inputPaths = append(inputPaths, k.OutputPath())
179		} else {
180			ctx.PropertyErrorf("kernel_configs",
181				"module %q is not a kernel_config", ctx.OtherModuleName(m))
182		}
183	})
184
185	// For product_compatibility_matrix.xml the source is from the product configuration
186	// DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE.
187	extraArgs := []string{}
188	if matrixType == productFcmType {
189		productMatrixs := android.PathsForSource(ctx, ctx.Config().DeviceProductCompatibilityMatrixFile())
190		if len(productMatrixs) > 0 {
191			inputPaths = append(inputPaths, productMatrixs...)
192			extraArgs = append(extraArgs, "-c", android.PathForSource(ctx, emptyManifest).String())
193		} else {
194			// For product_fcm, if DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE not set, treat it as a phony target without any output generated.
195			g.phonyOnly = true
196			return
197		}
198	}
199
200	// For framework_compatibility_matrix.device.xml the source may come from the product configuration
201	// DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE or use compatibilityEmptyMatrix if not set. We can't
202	// use a phony target because we still need to install framework_compatibility_matrix.device.xml to
203	// include sepolicy versions.
204	frameworkRuleImplicits := []android.Path{}
205
206	if matrixType == deviceFcmType {
207		frameworkMatrixs := android.PathsForSource(ctx, ctx.Config().DeviceFrameworkCompatibilityMatrixFile())
208		if len(frameworkMatrixs) > 0 {
209			inputPaths = append(inputPaths, frameworkMatrixs...)
210
211			// Generate BuildAction for generating the check manifest.
212			emptyManifestPath := android.PathForSource(ctx, emptyManifest)
213			genCheckManifest := android.PathForModuleGen(ctx, "manifest.check.xml")
214			checkManifestInputs := []android.Path{emptyManifestPath}
215			genCheckManifestEnvs := []string{
216				"BOARD_SEPOLICY_VERS=" + ctx.DeviceConfig().BoardSepolicyVers(),
217				"VINTF_IGNORE_TARGET_FCM_VERSION=true",
218			}
219
220			ctx.Build(pctx, android.BuildParams{
221				Rule:        assembleVintfRule,
222				Description: "Framework Check Manifest",
223				Implicits:   checkManifestInputs,
224				Output:      genCheckManifest,
225				Args: map[string]string{
226					"inputs":           android.PathForSource(ctx, emptyManifest).String(),
227					"extraArgs":        "",
228					"assembleVintfEnv": strings.Join(genCheckManifestEnvs, " "),
229				},
230			})
231
232			frameworkRuleImplicits = append(frameworkRuleImplicits, genCheckManifest)
233			extraArgs = append(extraArgs, "-c", genCheckManifest.String())
234		} else {
235			inputPaths = append(inputPaths, android.PathForSource(ctx, compatibilityEmptyMatrix))
236		}
237	}
238
239	g.genFile = android.PathForModuleGen(ctx, outputFilename)
240	frameworkRuleImplicits = append(frameworkRuleImplicits, inputPaths...)
241
242	ctx.Build(pctx, android.BuildParams{
243		Rule:        assembleVintfRule,
244		Description: "Framework Compatibility Matrix",
245		Implicits:   frameworkRuleImplicits,
246		Output:      g.genFile,
247		Args: map[string]string{
248			"inputs":           strings.Join(inputPaths.Strings(), ":"),
249			"extraArgs":        strings.Join(extraArgs, " "),
250			"assembleVintfEnv": g.getAssembleVintfEnv(ctx),
251		},
252	})
253	g.generateValidateBuildAction(ctx, g.genFile, schema.Path())
254
255	ctx.InstallFile(android.PathForModuleInstall(ctx, "etc", relpath), outputFilename, g.genFile)
256}
257
258func (g *vintfCompatibilityMatrixRule) getAssembleVintfEnv(ctx android.ModuleContext) string {
259	if proptools.String(g.properties.Type) == deviceFcmType {
260		assembleVintfEnvs := []string{
261			// POLICYVERS defined in system/sepolicy/build/soong/policy.go
262			fmt.Sprintf("POLICYVERS=%d", selinux.PolicyVers),
263			fmt.Sprintf("PLATFORM_SEPOLICY_VERSION=%s", ctx.DeviceConfig().PlatformSepolicyVersion()),
264			fmt.Sprintf("PLATFORM_SEPOLICY_COMPAT_VERSIONS=\"%s\"", strings.Join(ctx.DeviceConfig().PlatformSepolicyCompatVersions(), " ")),
265		}
266
267		if ctx.Config().BoardAvbEnable() {
268			assembleVintfEnvs = append(assembleVintfEnvs, fmt.Sprintf("FRAMEWORK_VBMETA_VERSION=\"$$(${AvbToolCmd} add_hashtree_footer --print_required_libavb_version %s)\"", strings.Join(ctx.Config().BoardAvbSystemAddHashtreeFooterArgs(), " ")))
269		} else {
270			assembleVintfEnvs = append(assembleVintfEnvs, "FRAMEWORK_VBMETA_VERSION=\"0.0\"")
271		}
272
273		return strings.Join(assembleVintfEnvs, " ")
274	}
275
276	return ""
277}
278
279func (g *vintfCompatibilityMatrixRule) AndroidMk() android.AndroidMkData {
280	if g.phonyOnly {
281		return android.AndroidMkData{
282			Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
283				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)", " # vintf.vintf_compatibility_matrix")
284				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
285				fmt.Fprintln(w, "LOCAL_MODULE :=", name)
286				fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
287			},
288		}
289	}
290
291	return android.AndroidMkData{
292		Class:      "ETC",
293		OutputFile: android.OptionalPathForPath(g.genFile),
294		Extra: []android.AndroidMkExtraFunc{
295			func(w io.Writer, outputFile android.Path) {
296				fmt.Fprintln(w, "LOCAL_MODULE_RELATIVE_PATH :=", relpath)
297				if proptools.String(g.properties.Stem) != "" {
298					fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", proptools.String(g.properties.Stem))
299				}
300				for _, path := range g.additionalDependencies {
301					fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES +=", path.String())
302				}
303			},
304		},
305	}
306}
307