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 xsdc
16
17import (
18	"android/soong/android"
19	"android/soong/java"
20	"path/filepath"
21	"strings"
22
23	"github.com/google/blueprint"
24	"github.com/google/blueprint/proptools"
25)
26
27func init() {
28	pctx.Import("android/soong/java/config")
29	android.RegisterModuleType("xsd_config", xsdConfigFactory)
30
31	android.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
32		ctx.TopDown("xsd_config", xsdConfigMutator).Parallel()
33	})
34}
35
36var (
37	pctx = android.NewPackageContext("android/xsdc")
38
39	xsdc         = pctx.HostBinToolVariable("xsdcCmd", "xsdc")
40	xsdcJavaRule = pctx.StaticRule("xsdcJavaRule", blueprint.RuleParams{
41		Command: `rm -rf "${out}.temp" && mkdir -p "${out}.temp" && ` +
42			`${xsdcCmd} $in -p $pkgName -o ${out}.temp -j $args && ` +
43			`${config.SoongZipCmd} -jar -o ${out} -C ${out}.temp -D ${out}.temp && ` +
44			`rm -rf ${out}.temp`,
45		CommandDeps: []string{"${xsdcCmd}", "${config.SoongZipCmd}"},
46		Description: "xsdc Java ${in} => ${out}",
47	}, "pkgName", "args")
48
49	xsdcCppRule = pctx.StaticRule("xsdcCppRule", blueprint.RuleParams{
50		Command: `rm -rf "${outDir}" && ` +
51			`${xsdcCmd} $in -p $pkgName -o ${outDir} -c $args`,
52		CommandDeps: []string{"${xsdcCmd}", "${config.SoongZipCmd}"},
53		Description: "xsdc C++ ${in} => ${out}",
54	}, "pkgName", "outDir", "args")
55
56	xsdConfigRule = pctx.StaticRule("xsdConfigRule", blueprint.RuleParams{
57		Command:     "cp -f ${in} ${output}",
58		Description: "copy the xsd file: ${in} => ${output}",
59	}, "output")
60)
61
62type xsdConfigProperties struct {
63	Srcs         []string
64	Package_name *string
65	Api_dir      *string
66	Gen_writer   *bool
67	Nullability  *bool
68
69	// Whether has{element or atrribute} methods are set to public.
70	// It is not applied to C++, because these methods are always
71	// generated to public for C++.
72	Gen_has *bool
73	// Only generate code for enum converters. Applies to C++ only.
74	// This is useful for memory footprint reduction since it avoids
75	// depending on libxml2.
76	Enums_only *bool
77	// Only generate complementary code for XML parser. Applies to C++ only.
78	// The code being generated depends on the enum converters module.
79	Parser_only *bool
80	// Whether getter name of boolean element or attribute is getX or isX.
81	// Default value is false. If the property is true, getter name is isX.
82	Boolean_getter *bool
83}
84
85type xsdConfig struct {
86	android.ModuleBase
87
88	properties xsdConfigProperties
89
90	genOutputDir android.Path
91	genOutputs_j android.WritablePath
92	genOutputs_c android.WritablePaths
93	genOutputs_h android.WritablePaths
94
95	docsPath android.Path
96
97	xsdConfigPath android.OptionalPath
98	genOutputs    android.Paths
99}
100
101var _ android.SourceFileProducer = (*xsdConfig)(nil)
102
103type ApiToCheck struct {
104	Api_file         *string
105	Removed_api_file *string
106	Args             *string
107}
108
109type CheckApi struct {
110	Last_released ApiToCheck
111	Current       ApiToCheck
112}
113type DroidstubsProperties struct {
114	Name                 *string
115	Installable          *bool
116	Srcs                 []string
117	Sdk_version          *string
118	Args                 *string
119	Api_filename         *string
120	Removed_api_filename *string
121	Check_api            CheckApi
122}
123
124func (module *xsdConfig) GeneratedSourceFiles() android.Paths {
125	return module.genOutputs_c.Paths()
126}
127
128func (module *xsdConfig) Srcs() android.Paths {
129	return append(module.genOutputs, module.genOutputs_j)
130}
131
132func (module *xsdConfig) GeneratedDeps() android.Paths {
133	return module.genOutputs_h.Paths()
134}
135
136func (module *xsdConfig) GeneratedHeaderDirs() android.Paths {
137	return android.Paths{module.genOutputDir}
138}
139
140func (module *xsdConfig) DepsMutator(ctx android.BottomUpMutatorContext) {
141	android.ExtractSourcesDeps(ctx, module.properties.Srcs)
142}
143
144func (module *xsdConfig) generateXsdConfig(ctx android.ModuleContext) {
145	if !module.xsdConfigPath.Valid() {
146		return
147	}
148
149	output := android.PathForModuleGen(ctx, module.Name()+".xsd")
150	module.genOutputs = append(module.genOutputs, output)
151
152	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
153		Rule:   xsdConfigRule,
154		Input:  module.xsdConfigPath.Path(),
155		Output: output,
156		Args: map[string]string{
157			"output": output.String(),
158		},
159	})
160}
161
162func (module *xsdConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
163	if len(module.properties.Srcs) != 1 {
164		ctx.PropertyErrorf("srcs", "xsd_config must be one src")
165	}
166
167	ctx.VisitDirectDeps(func(to android.Module) {
168		if doc, ok := to.(java.ApiFilePath); ok {
169			module.docsPath = doc.ApiFilePath()
170		}
171	})
172
173	srcFiles := ctx.ExpandSources(module.properties.Srcs, nil)
174	xsdFile := srcFiles[0]
175
176	pkgName := *module.properties.Package_name
177	filenameStem := strings.Replace(pkgName, ".", "_", -1)
178
179	args := ""
180	if proptools.Bool(module.properties.Gen_writer) {
181		args = "-w"
182	}
183
184	if proptools.Bool(module.properties.Nullability) {
185		args = args + " -n "
186	}
187
188	if proptools.Bool(module.properties.Gen_has) {
189		args = args + " -g "
190	}
191
192	if proptools.Bool(module.properties.Enums_only) {
193		args = args + " -e "
194	}
195
196	if proptools.Bool(module.properties.Parser_only) {
197		args = args + " -x "
198	}
199
200	if proptools.Bool(module.properties.Boolean_getter) {
201		args = args + " -b "
202	}
203
204	module.genOutputs_j = android.PathForModuleGen(ctx, "java", filenameStem+"_xsdcgen.srcjar")
205
206	ctx.Build(pctx, android.BuildParams{
207		Rule:        xsdcJavaRule,
208		Description: "xsdc " + xsdFile.String(),
209		Input:       xsdFile,
210		Implicit:    module.docsPath,
211		Output:      module.genOutputs_j,
212		Args: map[string]string{
213			"pkgName": pkgName,
214			"args":    args,
215		},
216	})
217
218	if proptools.Bool(module.properties.Enums_only) {
219		module.genOutputs_c = android.WritablePaths{
220			android.PathForModuleGen(ctx, "cpp", filenameStem+"_enums.cpp")}
221		module.genOutputs_h = android.WritablePaths{
222			android.PathForModuleGen(ctx, "cpp", "include/"+filenameStem+"_enums.h")}
223	} else if proptools.Bool(module.properties.Parser_only) {
224		module.genOutputs_c = android.WritablePaths{
225			android.PathForModuleGen(ctx, "cpp", filenameStem+".cpp")}
226		module.genOutputs_h = android.WritablePaths{
227			android.PathForModuleGen(ctx, "cpp", "include/"+filenameStem+".h")}
228	} else {
229		module.genOutputs_c = android.WritablePaths{
230			android.PathForModuleGen(ctx, "cpp", filenameStem+".cpp"),
231			android.PathForModuleGen(ctx, "cpp", filenameStem+"_enums.cpp")}
232		module.genOutputs_h = android.WritablePaths{
233			android.PathForModuleGen(ctx, "cpp", "include/"+filenameStem+".h"),
234			android.PathForModuleGen(ctx, "cpp", "include/"+filenameStem+"_enums.h")}
235	}
236	module.genOutputDir = android.PathForModuleGen(ctx, "cpp", "include")
237
238	ctx.Build(pctx, android.BuildParams{
239		Rule:            xsdcCppRule,
240		Description:     "xsdc " + xsdFile.String(),
241		Input:           xsdFile,
242		Implicit:        module.docsPath,
243		Outputs:         module.genOutputs_c,
244		ImplicitOutputs: module.genOutputs_h,
245		Args: map[string]string{
246			"pkgName": pkgName,
247			"outDir":  android.PathForModuleGen(ctx, "cpp").String(),
248			"args":    args,
249		},
250	})
251	module.xsdConfigPath = android.ExistentPathForSource(ctx, xsdFile.String())
252	module.generateXsdConfig(ctx)
253}
254
255func xsdConfigMutator(mctx android.TopDownMutatorContext) {
256	if module, ok := mctx.Module().(*xsdConfig); ok {
257		name := module.BaseModuleName()
258
259		args := " --stub-packages " + *module.properties.Package_name +
260			" --hide MissingPermission --hide BroadcastBehavior" +
261			" --hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol" +
262			" --hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo"
263
264		api_dir := proptools.StringDefault(module.properties.Api_dir, "api")
265
266		currentApiFileName := filepath.Join(api_dir, "current.txt")
267		removedApiFileName := filepath.Join(api_dir, "removed.txt")
268
269		check_api := CheckApi{}
270
271		check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
272		check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
273
274		check_api.Last_released.Api_file = proptools.StringPtr(
275			filepath.Join(api_dir, "last_current.txt"))
276		check_api.Last_released.Removed_api_file = proptools.StringPtr(
277			filepath.Join(api_dir, "last_removed.txt"))
278
279		mctx.CreateModule(java.DroidstubsFactory, &DroidstubsProperties{
280			Name:                 proptools.StringPtr(name + ".docs"),
281			Srcs:                 []string{":" + name},
282			Args:                 proptools.StringPtr(args),
283			Api_filename:         proptools.StringPtr(currentApiFileName),
284			Removed_api_filename: proptools.StringPtr(removedApiFileName),
285			Check_api:            check_api,
286			Installable:          proptools.BoolPtr(false),
287			Sdk_version:          proptools.StringPtr("core_platform"),
288		})
289	}
290}
291
292func xsdConfigFactory() android.Module {
293	module := &xsdConfig{}
294	module.AddProperties(&module.properties)
295	android.InitAndroidModule(module)
296
297	return module
298}
299