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