1// Copyright 2018 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 xml
16
17import (
18	"android/soong/android"
19	"android/soong/etc"
20
21	"github.com/google/blueprint"
22	"github.com/google/blueprint/proptools"
23)
24
25// prebuilt_etc_xml installs an xml file under <partition>/etc/<subdir>.
26// It also optionally validates the xml file against the schema.
27
28var (
29	pctx = android.NewPackageContext("android/soong/xml")
30
31	xmllintDtd = pctx.AndroidStaticRule("xmllint-dtd",
32		blueprint.RuleParams{
33			Command:     `$XmlLintCmd --dtdvalid $dtd $in > /dev/null && touch -a $out`,
34			CommandDeps: []string{"$XmlLintCmd"},
35			Restat:      true,
36		},
37		"dtd")
38
39	xmllintXsd = pctx.AndroidStaticRule("xmllint-xsd",
40		blueprint.RuleParams{
41			Command:     `$XmlLintCmd --schema $xsd $in > /dev/null && touch -a $out`,
42			CommandDeps: []string{"$XmlLintCmd"},
43			Restat:      true,
44		},
45		"xsd")
46
47	xmllintMinimal = pctx.AndroidStaticRule("xmllint-minimal",
48		blueprint.RuleParams{
49			Command:     `$XmlLintCmd $in > /dev/null && touch -a $out`,
50			CommandDeps: []string{"$XmlLintCmd"},
51			Restat:      true,
52		})
53)
54
55func init() {
56	registerXmlBuildComponents(android.InitRegistrationContext)
57	pctx.HostBinToolVariable("XmlLintCmd", "xmllint")
58}
59
60func registerXmlBuildComponents(ctx android.RegistrationContext) {
61	ctx.RegisterModuleType("prebuilt_etc_xml", PrebuiltEtcXmlFactory)
62}
63
64type prebuiltEtcXmlProperties struct {
65	// Optional DTD that will be used to validate the xml file.
66	Schema *string `android:"path"`
67}
68
69type prebuiltEtcXml struct {
70	etc.PrebuiltEtc
71
72	properties prebuiltEtcXmlProperties
73}
74
75func (p *prebuiltEtcXml) timestampFilePath(ctx android.ModuleContext) android.WritablePath {
76	return android.PathForModuleOut(ctx, p.PrebuiltEtc.SourceFilePath(ctx).Base()+"-timestamp")
77}
78
79func (p *prebuiltEtcXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
80	p.PrebuiltEtc.GenerateAndroidBuildActions(ctx)
81
82	if p.properties.Schema != nil {
83		schema := android.PathForModuleSrc(ctx, proptools.String(p.properties.Schema))
84
85		switch schema.Ext() {
86		case ".dtd":
87			ctx.Build(pctx, android.BuildParams{
88				Rule:        xmllintDtd,
89				Description: "xmllint-dtd",
90				Input:       p.PrebuiltEtc.SourceFilePath(ctx),
91				Output:      p.timestampFilePath(ctx),
92				Implicit:    schema,
93				Args: map[string]string{
94					"dtd": schema.String(),
95				},
96			})
97			break
98		case ".xsd":
99			ctx.Build(pctx, android.BuildParams{
100				Rule:        xmllintXsd,
101				Description: "xmllint-xsd",
102				Input:       p.PrebuiltEtc.SourceFilePath(ctx),
103				Output:      p.timestampFilePath(ctx),
104				Implicit:    schema,
105				Args: map[string]string{
106					"xsd": schema.String(),
107				},
108			})
109			break
110		default:
111			ctx.PropertyErrorf("schema", "not supported extension: %q", schema.Ext())
112		}
113	} else {
114		// when schema is not specified, just check if the xml is well-formed
115		ctx.Build(pctx, android.BuildParams{
116			Rule:        xmllintMinimal,
117			Description: "xmllint-minimal",
118			Input:       p.PrebuiltEtc.SourceFilePath(ctx),
119			Output:      p.timestampFilePath(ctx),
120		})
121	}
122
123	p.SetAdditionalDependencies([]android.Path{p.timestampFilePath(ctx)})
124}
125
126func PrebuiltEtcXmlFactory() android.Module {
127	module := &prebuiltEtcXml{}
128	module.AddProperties(&module.properties)
129	etc.InitPrebuiltEtcModule(&module.PrebuiltEtc, "etc")
130	// This module is device-only
131	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
132	return module
133}
134