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)
28
29type dependencyTag struct {
30	blueprint.BaseDependencyTag
31	name string
32}
33
34var (
35	pctx = android.NewPackageContext("android/vintf")
36
37	assembleVintfRule = pctx.AndroidStaticRule("assemble_vintf", blueprint.RuleParams{
38		Command:     `${assembleVintfCmd} -i ${inputs} -o ${out}`,
39		CommandDeps: []string{"${assembleVintfCmd}"},
40		Description: "assemble_vintf -i ${inputs}",
41	}, "inputs")
42
43	xmllintXsd = pctx.AndroidStaticRule("xmllint-xsd", blueprint.RuleParams{
44		Command:     `$XmlLintCmd --schema $xsd $in > /dev/null && touch -a $out`,
45		CommandDeps: []string{"$XmlLintCmd"},
46		Restat:      true,
47	}, "xsd")
48
49	kernelConfigTag  = dependencyTag{name: "kernel-config"}
50	schemaTag        = dependencyTag{name: "matrix-schema"}
51	schemaModuleName = "compatibility_matrix_schema"
52)
53
54const (
55	relpath = "vintf"
56)
57
58type vintfCompatibilityMatrixProperties struct {
59	// set the name of the output
60	Stem *string
61
62	// list of source compatibility matrix XML files
63	Srcs []string
64
65	// list of kernel_config modules to be combined to final output
66	Kernel_configs []string
67}
68
69type vintfCompatibilityMatrixRule struct {
70	android.ModuleBase
71	properties vintfCompatibilityMatrixProperties
72
73	genFile                android.WritablePath
74	additionalDependencies android.WritablePaths
75}
76
77func init() {
78	pctx.HostBinToolVariable("assembleVintfCmd", "assemble_vintf")
79	pctx.HostBinToolVariable("XmlLintCmd", "xmllint")
80	android.RegisterModuleType("vintf_compatibility_matrix", vintfCompatibilityMatrixFactory)
81}
82
83func vintfCompatibilityMatrixFactory() android.Module {
84	g := &vintfCompatibilityMatrixRule{}
85	g.AddProperties(&g.properties)
86	android.InitAndroidArchModule(g, android.DeviceSupported, android.MultilibCommon)
87	return g
88}
89
90var _ android.AndroidMkDataProvider = (*vintfCompatibilityMatrixRule)(nil)
91
92func (g *vintfCompatibilityMatrixRule) DepsMutator(ctx android.BottomUpMutatorContext) {
93	android.ExtractSourcesDeps(ctx, g.properties.Srcs)
94	ctx.AddDependency(ctx.Module(), kernelConfigTag, g.properties.Kernel_configs...)
95	ctx.AddDependency(ctx.Module(), schemaTag, schemaModuleName)
96}
97
98func (g *vintfCompatibilityMatrixRule) timestampFilePath(ctx android.ModuleContext, path android.Path) android.WritablePath {
99	return android.GenPathWithExt(ctx, "vintf-xmllint", path, "ts")
100}
101
102func (g *vintfCompatibilityMatrixRule) generateValidateBuildAction(ctx android.ModuleContext, path android.Path, schema android.Path) {
103	timestamp := g.timestampFilePath(ctx, path)
104	ctx.Build(pctx, android.BuildParams{
105		Rule:        xmllintXsd,
106		Description: "xmllint-xsd",
107		Input:       path,
108		Output:      timestamp,
109		Implicit:    schema,
110		Args: map[string]string{
111			"xsd": schema.String(),
112		},
113	})
114	g.additionalDependencies = append(g.additionalDependencies, timestamp)
115}
116
117func (g *vintfCompatibilityMatrixRule) getSchema(ctx android.ModuleContext) android.OptionalPath {
118	schemaModule := ctx.GetDirectDepWithTag(schemaModuleName, schemaTag)
119	sfp, ok := schemaModule.(android.SourceFileProducer)
120	if !ok {
121		ctx.ModuleErrorf("Implicit dependency %q has no srcs", ctx.OtherModuleName(schemaModule))
122		return android.OptionalPath{}
123	}
124
125	schemaSrcs := sfp.Srcs()
126	if len(schemaSrcs) != 1 {
127		ctx.PropertyErrorf(`srcs of implicit dependency %q has length %d != 1`, ctx.OtherModuleName(schemaModule), len(schemaSrcs))
128		return android.OptionalPath{}
129	}
130	return android.OptionalPathForPath(schemaSrcs[0])
131}
132
133func (g *vintfCompatibilityMatrixRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
134
135	outputFilename := proptools.String(g.properties.Stem)
136	if outputFilename == "" {
137		outputFilename = g.Name()
138	}
139
140	schema := g.getSchema(ctx)
141	if !schema.Valid() {
142		return
143	}
144
145	inputPaths := android.PathsForModuleSrc(ctx, g.properties.Srcs)
146	for _, srcPath := range inputPaths {
147		g.generateValidateBuildAction(ctx, srcPath, schema.Path())
148	}
149
150	// No need to validate matrices from kernel configs because they are generated by
151	// assemble_vintf.
152	ctx.VisitDirectDepsWithTag(kernelConfigTag, func(m android.Module) {
153		if k, ok := m.(*configs.KernelConfigRule); ok {
154			inputPaths = append(inputPaths, k.OutputPath())
155		} else {
156			ctx.PropertyErrorf("kernel_config",
157				"module %q is not a kernel_config", ctx.OtherModuleName(m))
158		}
159	})
160
161	g.genFile = android.PathForModuleGen(ctx, outputFilename)
162
163	ctx.Build(pctx, android.BuildParams{
164		Rule:        assembleVintfRule,
165		Description: "Framework Compatibility Matrix",
166		Implicits:   inputPaths,
167		Output:      g.genFile,
168		Args: map[string]string{
169			"inputs": strings.Join(inputPaths.Strings(), ":"),
170		},
171	})
172	g.generateValidateBuildAction(ctx, g.genFile, schema.Path())
173
174	ctx.InstallFile(android.PathForModuleInstall(ctx, "etc", relpath), outputFilename, g.genFile)
175}
176
177func (g *vintfCompatibilityMatrixRule) AndroidMk() android.AndroidMkData {
178	return android.AndroidMkData{
179		Class:      "ETC",
180		OutputFile: android.OptionalPathForPath(g.genFile),
181		Extra: []android.AndroidMkExtraFunc{
182			func(w io.Writer, outputFile android.Path) {
183				fmt.Fprintln(w, "LOCAL_MODULE_RELATIVE_PATH :=", relpath)
184				if proptools.String(g.properties.Stem) != "" {
185					fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", proptools.String(g.properties.Stem))
186				}
187				for _, path := range g.additionalDependencies {
188					fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES +=", path.String())
189				}
190			},
191		},
192	}
193}
194