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	"path/filepath"
19	"strings"
20
21	"android/soong/android"
22	"android/soong/java"
23
24	"github.com/google/blueprint"
25	"github.com/google/blueprint/proptools"
26)
27
28func init() {
29	pctx.Import("android/soong/java/config")
30	android.RegisterModuleType("xsd_config", xsdConfigFactory)
31}
32
33var (
34	pctx = android.NewPackageContext("android/xsdc")
35
36	xsdc = pctx.HostBinToolVariable("xsdcCmd", "xsdc")
37
38	xsdConfigRule = pctx.StaticRule("xsdConfigRule", blueprint.RuleParams{
39		Command:     "cp -f ${in} ${output}",
40		Description: "copy the xsd file: ${in} => ${output}",
41	}, "output")
42)
43
44type xsdConfigProperties struct {
45	Srcs         []string
46	Package_name *string
47	Api_dir      *string
48	Gen_writer   *bool
49	Nullability  *bool
50
51	// Whether has{element or atrribute} methods are set to public.
52	// It is not applied to C++, because these methods are always
53	// generated to public for C++.
54	Gen_has *bool
55	// Only generate code for enum converters. Applies to C++ only.
56	// This is useful for memory footprint reduction since it avoids
57	// depending on libxml2.
58	Enums_only *bool
59	// Only generate complementary code for XML parser. Applies to C++ only.
60	// The code being generated depends on the enum converters module.
61	Parser_only *bool
62	// Whether getter name of boolean element or attribute is getX or isX.
63	// Default value is false. If the property is true, getter name is isX.
64	Boolean_getter *bool
65	// Generate code that uses libtinyxml2 instead of libxml2. Applies to
66	// C++ only and does not perform the XInclude substitution, or
67	// ENTITY_REFs.
68	// This can improve memory footprint. Default value is false.
69	Tinyxml *bool
70	// Specify root elements explicitly. If not set, XSDC generates parsers and
71	// writers for all elements which can be root element. When set, XSDC
72	// generates parsers and writers for specified root elements. This can be
73	// used to avoid unnecessary code.
74	Root_elements []string
75	// Additional xsd files included by the main xsd file using xs:include
76	// The paths are relative to the module directory.
77	Include_files []string
78}
79
80type xsdConfig struct {
81	android.ModuleBase
82
83	properties xsdConfigProperties
84
85	genOutputDir android.Path
86	genOutputs_j android.WritablePath
87	genOutputs_c android.WritablePaths
88	genOutputs_h android.WritablePaths
89
90	docsPath android.Path
91
92	xsdConfigPath         android.Path
93	xsdIncludeConfigPaths android.Paths
94	genOutputs            android.WritablePaths
95}
96
97var _ android.SourceFileProducer = (*xsdConfig)(nil)
98
99type ApiToCheck struct {
100	Api_file         *string
101	Removed_api_file *string
102	Args             *string
103}
104
105type CheckApi struct {
106	Last_released ApiToCheck
107	Current       ApiToCheck
108}
109type DroidstubsProperties struct {
110	Name                 *string
111	Installable          *bool
112	Srcs                 []string
113	Sdk_version          *string
114	Args                 *string
115	Api_filename         *string
116	Removed_api_filename *string
117	Check_api            CheckApi
118}
119
120func (module *xsdConfig) GeneratedSourceFiles() android.Paths {
121	return module.genOutputs_c.Paths()
122}
123
124func (module *xsdConfig) Srcs() android.Paths {
125	var srcs android.WritablePaths
126	srcs = append(srcs, module.genOutputs...)
127	srcs = append(srcs, module.genOutputs_j)
128	return srcs.Paths()
129}
130
131func (module *xsdConfig) GeneratedDeps() android.Paths {
132	return module.genOutputs_h.Paths()
133}
134
135func (module *xsdConfig) GeneratedHeaderDirs() android.Paths {
136	return android.Paths{module.genOutputDir}
137}
138
139func (module *xsdConfig) DepsMutator(ctx android.BottomUpMutatorContext) {
140	android.ExtractSourcesDeps(ctx, module.properties.Srcs)
141}
142
143func (module *xsdConfig) generateXsdConfig(ctx android.ModuleContext) {
144	output := android.PathForModuleGen(ctx, module.Name()+".xsd")
145	module.genOutputs = append(module.genOutputs, output)
146
147	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
148		Rule:   xsdConfigRule,
149		Input:  module.xsdConfigPath,
150		Output: output,
151		Args: map[string]string{
152			"output": output.String(),
153		},
154	})
155}
156
157// This creates a ninja rule to convert xsd file to java sources
158// The ninja rule runs in a sandbox
159func (module *xsdConfig) generateJavaSrcInSbox(ctx android.ModuleContext, args string) {
160	rule := android.NewRuleBuilder(pctx, ctx).
161		Sbox(android.PathForModuleGen(ctx, "java"),
162			android.PathForModuleGen(ctx, "java.sbox.textproto")).
163		SandboxInputs()
164	// Run xsdc tool to generate sources
165	genCmd := rule.Command()
166	genCmd.
167		BuiltTool("xsdc").
168		ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "xsdc.jar")).
169		Input(module.xsdConfigPath).
170		FlagWithArg("-p ", *module.properties.Package_name).
171		// Soong will change execution root to sandbox root. Generate srcs relative to that.
172		Flag("-o ").OutputDir("xsdc").
173		FlagWithArg("-j ", args)
174	if module.xsdIncludeConfigPaths != nil {
175		genCmd.Implicits(module.xsdIncludeConfigPaths)
176	}
177	if module.docsPath != nil {
178		genCmd.Implicit(module.docsPath)
179	}
180	// Zip the source file to a srcjar
181	rule.Command().
182		BuiltTool("soong_zip").
183		Flag("-jar").
184		FlagWithOutput("-o ", module.genOutputs_j).
185		Flag("-C ").OutputDir("xsdc").
186		Flag("-D ").OutputDir("xsdc")
187
188	rule.Build("xsdc_java_"+module.xsdConfigPath.String(), "xsdc java")
189}
190
191// This creates a ninja rule to convert xsd file to cpp sources
192// The ninja rule runs in a sandbox
193func (module *xsdConfig) generateCppSrcInSbox(ctx android.ModuleContext, args string) {
194	outDir := android.PathForModuleGen(ctx, "cpp")
195	rule := android.NewRuleBuilder(pctx, ctx).
196		Sbox(outDir,
197			android.PathForModuleGen(ctx, "cpp.sbox.textproto")).
198		SandboxInputs()
199	// Run xsdc tool to generate sources
200	genCmd := rule.Command()
201	genCmd.
202		BuiltTool("xsdc").
203		ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "xsdc.jar")).
204		Input(module.xsdConfigPath).
205		FlagWithArg("-p ", *module.properties.Package_name).
206		// Soong will change execution root to sandbox root. Generate srcs relative to that.
207		Flag("-o ").OutputDir().
208		FlagWithArg("-c ", args).
209		ImplicitOutputs(module.genOutputs_c).
210		ImplicitOutputs(module.genOutputs_h)
211	if module.xsdIncludeConfigPaths != nil {
212		genCmd.Implicits(module.xsdIncludeConfigPaths)
213	}
214
215	rule.Build("xsdc_cpp_"+module.xsdConfigPath.String(), "xsdc cpp")
216}
217
218func (module *xsdConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
219	if len(module.properties.Srcs) != 1 {
220		ctx.PropertyErrorf("srcs", "xsd_config must be one src")
221	}
222
223	ctx.VisitDirectDeps(func(to android.Module) {
224		if doc, ok := to.(java.ApiFilePath); ok {
225			docsPath, err := doc.ApiFilePath(java.Everything)
226			if err != nil {
227				ctx.ModuleErrorf(err.Error())
228			} else {
229				module.docsPath = docsPath
230			}
231		}
232	})
233
234	srcFiles := ctx.ExpandSources(module.properties.Srcs, nil)
235	module.xsdConfigPath = srcFiles[0]
236	module.xsdIncludeConfigPaths = android.PathsForModuleSrc(ctx, module.properties.Include_files)
237
238	pkgName := *module.properties.Package_name
239	filenameStem := strings.Replace(pkgName, ".", "_", -1)
240
241	args := ""
242	if proptools.Bool(module.properties.Gen_writer) {
243		args = "-w"
244	}
245
246	if proptools.Bool(module.properties.Nullability) {
247		args = args + " -n "
248	}
249
250	if proptools.Bool(module.properties.Gen_has) {
251		args = args + " -g "
252	}
253
254	if proptools.Bool(module.properties.Enums_only) {
255		args = args + " -e "
256	}
257
258	if proptools.Bool(module.properties.Parser_only) {
259		args = args + " -x "
260	}
261
262	if proptools.Bool(module.properties.Boolean_getter) {
263		args = args + " -b "
264	}
265
266	if proptools.Bool(module.properties.Tinyxml) {
267		args = args + " -t "
268	}
269
270	for _, elem := range module.properties.Root_elements {
271		args = args + " -r " + elem
272	}
273
274	module.genOutputs_j = android.PathForModuleGen(ctx, "java", filenameStem+"_xsdcgen.srcjar")
275
276	module.generateJavaSrcInSbox(ctx, args)
277
278	if proptools.Bool(module.properties.Enums_only) {
279		module.genOutputs_c = android.WritablePaths{
280			android.PathForModuleGen(ctx, "cpp", filenameStem+"_enums.cpp")}
281		module.genOutputs_h = android.WritablePaths{
282			android.PathForModuleGen(ctx, "cpp", "include/"+filenameStem+"_enums.h")}
283	} else if proptools.Bool(module.properties.Parser_only) {
284		module.genOutputs_c = android.WritablePaths{
285			android.PathForModuleGen(ctx, "cpp", filenameStem+".cpp")}
286		module.genOutputs_h = android.WritablePaths{
287			android.PathForModuleGen(ctx, "cpp", "include/"+filenameStem+".h")}
288	} else {
289		module.genOutputs_c = android.WritablePaths{
290			android.PathForModuleGen(ctx, "cpp", filenameStem+".cpp"),
291			android.PathForModuleGen(ctx, "cpp", filenameStem+"_enums.cpp")}
292		module.genOutputs_h = android.WritablePaths{
293			android.PathForModuleGen(ctx, "cpp", "include/"+filenameStem+".h"),
294			android.PathForModuleGen(ctx, "cpp", "include/"+filenameStem+"_enums.h")}
295	}
296
297	module.genOutputDir = android.PathForModuleGen(ctx, "cpp", "include")
298
299	module.generateCppSrcInSbox(ctx, args)
300
301	module.generateXsdConfig(ctx)
302
303	module.setOutputFiles(ctx)
304}
305
306func (module *xsdConfig) setOutputFiles(ctx android.ModuleContext) {
307	var defaultOutputFiles android.WritablePaths
308	defaultOutputFiles = append(defaultOutputFiles, module.genOutputs_j)
309	defaultOutputFiles = append(defaultOutputFiles, module.genOutputs_c...)
310	defaultOutputFiles = append(defaultOutputFiles, module.genOutputs_h...)
311	defaultOutputFiles = append(defaultOutputFiles, module.genOutputs...)
312	ctx.SetOutputFiles(defaultOutputFiles.Paths(), "")
313	ctx.SetOutputFiles(android.Paths{module.genOutputs_j}, "java")
314	ctx.SetOutputFiles(module.genOutputs_c.Paths(), "cpp")
315	ctx.SetOutputFiles(module.genOutputs_h.Paths(), "h")
316}
317
318func xsdConfigLoadHook(mctx android.LoadHookContext) {
319	module := mctx.Module().(*xsdConfig)
320	name := module.BaseModuleName()
321
322	args := " --stub-packages " + *module.properties.Package_name +
323		" --hide MissingPermission --hide BroadcastBehavior" +
324		" --hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol" +
325		" --hide SdkConstant --hide HiddenTypeParameter --hide Todo"
326
327	api_dir := proptools.StringDefault(module.properties.Api_dir, "api")
328
329	currentApiFileName := filepath.Join(api_dir, "current.txt")
330	removedApiFileName := filepath.Join(api_dir, "removed.txt")
331
332	check_api := CheckApi{}
333
334	check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
335	check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
336
337	check_api.Last_released.Api_file = proptools.StringPtr(
338		filepath.Join(api_dir, "last_current.txt"))
339	check_api.Last_released.Removed_api_file = proptools.StringPtr(
340		filepath.Join(api_dir, "last_removed.txt"))
341
342	mctx.CreateModule(java.DroidstubsFactory, &DroidstubsProperties{
343		Name:                 proptools.StringPtr(name + ".docs"),
344		Srcs:                 []string{":" + name},
345		Args:                 proptools.StringPtr(args),
346		Api_filename:         proptools.StringPtr(currentApiFileName),
347		Removed_api_filename: proptools.StringPtr(removedApiFileName),
348		Check_api:            check_api,
349		Installable:          proptools.BoolPtr(false),
350		Sdk_version:          proptools.StringPtr("core_platform"),
351	})
352}
353
354func xsdConfigFactory() android.Module {
355	module := &xsdConfig{}
356	module.AddProperties(&module.properties)
357	android.InitAndroidModule(module)
358	android.AddLoadHook(module, xsdConfigLoadHook)
359
360	return module
361}
362