// Copyright 2018 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package xsdc import ( "path/filepath" "strings" "android/soong/android" "android/soong/java" "github.com/google/blueprint" "github.com/google/blueprint/proptools" ) func init() { pctx.Import("android/soong/java/config") android.RegisterModuleType("xsd_config", xsdConfigFactory) } var ( pctx = android.NewPackageContext("android/xsdc") xsdc = pctx.HostBinToolVariable("xsdcCmd", "xsdc") xsdConfigRule = pctx.StaticRule("xsdConfigRule", blueprint.RuleParams{ Command: "cp -f ${in} ${output}", Description: "copy the xsd file: ${in} => ${output}", }, "output") ) type xsdConfigProperties struct { Srcs []string Package_name *string Api_dir *string Gen_writer *bool Nullability *bool // Whether has{element or atrribute} methods are set to public. // It is not applied to C++, because these methods are always // generated to public for C++. Gen_has *bool // Only generate code for enum converters. Applies to C++ only. // This is useful for memory footprint reduction since it avoids // depending on libxml2. Enums_only *bool // Only generate complementary code for XML parser. Applies to C++ only. // The code being generated depends on the enum converters module. Parser_only *bool // Whether getter name of boolean element or attribute is getX or isX. // Default value is false. If the property is true, getter name is isX. Boolean_getter *bool // Generate code that uses libtinyxml2 instead of libxml2. Applies to // C++ only and does not perform the XInclude substitution, or // ENTITY_REFs. // This can improve memory footprint. Default value is false. Tinyxml *bool // Specify root elements explicitly. If not set, XSDC generates parsers and // writers for all elements which can be root element. When set, XSDC // generates parsers and writers for specified root elements. This can be // used to avoid unnecessary code. Root_elements []string // Additional xsd files included by the main xsd file using xs:include // The paths are relative to the module directory. Include_files []string } type xsdConfig struct { android.ModuleBase properties xsdConfigProperties genOutputDir android.Path genOutputs_j android.WritablePath genOutputs_c android.WritablePaths genOutputs_h android.WritablePaths docsPath android.Path xsdConfigPath android.Path xsdIncludeConfigPaths android.Paths genOutputs android.WritablePaths } var _ android.SourceFileProducer = (*xsdConfig)(nil) type ApiToCheck struct { Api_file *string Removed_api_file *string Args *string } type CheckApi struct { Last_released ApiToCheck Current ApiToCheck } type DroidstubsProperties struct { Name *string Installable *bool Srcs []string Sdk_version *string Args *string Api_filename *string Removed_api_filename *string Check_api CheckApi } func (module *xsdConfig) GeneratedSourceFiles() android.Paths { return module.genOutputs_c.Paths() } func (module *xsdConfig) Srcs() android.Paths { var srcs android.WritablePaths srcs = append(srcs, module.genOutputs...) srcs = append(srcs, module.genOutputs_j) return srcs.Paths() } func (module *xsdConfig) GeneratedDeps() android.Paths { return module.genOutputs_h.Paths() } func (module *xsdConfig) GeneratedHeaderDirs() android.Paths { return android.Paths{module.genOutputDir} } func (module *xsdConfig) DepsMutator(ctx android.BottomUpMutatorContext) { android.ExtractSourcesDeps(ctx, module.properties.Srcs) } func (module *xsdConfig) generateXsdConfig(ctx android.ModuleContext) { output := android.PathForModuleGen(ctx, module.Name()+".xsd") module.genOutputs = append(module.genOutputs, output) ctx.ModuleBuild(pctx, android.ModuleBuildParams{ Rule: xsdConfigRule, Input: module.xsdConfigPath, Output: output, Args: map[string]string{ "output": output.String(), }, }) } // This creates a ninja rule to convert xsd file to java sources // The ninja rule runs in a sandbox func (module *xsdConfig) generateJavaSrcInSbox(ctx android.ModuleContext, args string) { rule := android.NewRuleBuilder(pctx, ctx). Sbox(android.PathForModuleGen(ctx, "java"), android.PathForModuleGen(ctx, "java.sbox.textproto")). SandboxInputs() // Run xsdc tool to generate sources genCmd := rule.Command() genCmd. BuiltTool("xsdc"). ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "xsdc.jar")). Input(module.xsdConfigPath). FlagWithArg("-p ", *module.properties.Package_name). // Soong will change execution root to sandbox root. Generate srcs relative to that. Flag("-o ").OutputDir("xsdc"). FlagWithArg("-j ", args) if module.xsdIncludeConfigPaths != nil { genCmd.Implicits(module.xsdIncludeConfigPaths) } if module.docsPath != nil { genCmd.Implicit(module.docsPath) } // Zip the source file to a srcjar rule.Command(). BuiltTool("soong_zip"). Flag("-jar"). FlagWithOutput("-o ", module.genOutputs_j). Flag("-C ").OutputDir("xsdc"). Flag("-D ").OutputDir("xsdc") rule.Build("xsdc_java_"+module.xsdConfigPath.String(), "xsdc java") } // This creates a ninja rule to convert xsd file to cpp sources // The ninja rule runs in a sandbox func (module *xsdConfig) generateCppSrcInSbox(ctx android.ModuleContext, args string) { outDir := android.PathForModuleGen(ctx, "cpp") rule := android.NewRuleBuilder(pctx, ctx). Sbox(outDir, android.PathForModuleGen(ctx, "cpp.sbox.textproto")). SandboxInputs() // Run xsdc tool to generate sources genCmd := rule.Command() genCmd. BuiltTool("xsdc"). ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "xsdc.jar")). Input(module.xsdConfigPath). FlagWithArg("-p ", *module.properties.Package_name). // Soong will change execution root to sandbox root. Generate srcs relative to that. Flag("-o ").OutputDir(). FlagWithArg("-c ", args). ImplicitOutputs(module.genOutputs_c). ImplicitOutputs(module.genOutputs_h) if module.xsdIncludeConfigPaths != nil { genCmd.Implicits(module.xsdIncludeConfigPaths) } rule.Build("xsdc_cpp_"+module.xsdConfigPath.String(), "xsdc cpp") } func (module *xsdConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) { if len(module.properties.Srcs) != 1 { ctx.PropertyErrorf("srcs", "xsd_config must be one src") } ctx.VisitDirectDeps(func(to android.Module) { if doc, ok := to.(java.ApiFilePath); ok { docsPath, err := doc.ApiFilePath(java.Everything) if err != nil { ctx.ModuleErrorf(err.Error()) } else { module.docsPath = docsPath } } }) srcFiles := ctx.ExpandSources(module.properties.Srcs, nil) module.xsdConfigPath = srcFiles[0] module.xsdIncludeConfigPaths = android.PathsForModuleSrc(ctx, module.properties.Include_files) pkgName := *module.properties.Package_name filenameStem := strings.Replace(pkgName, ".", "_", -1) args := "" if proptools.Bool(module.properties.Gen_writer) { args = "-w" } if proptools.Bool(module.properties.Nullability) { args = args + " -n " } if proptools.Bool(module.properties.Gen_has) { args = args + " -g " } if proptools.Bool(module.properties.Enums_only) { args = args + " -e " } if proptools.Bool(module.properties.Parser_only) { args = args + " -x " } if proptools.Bool(module.properties.Boolean_getter) { args = args + " -b " } if proptools.Bool(module.properties.Tinyxml) { args = args + " -t " } for _, elem := range module.properties.Root_elements { args = args + " -r " + elem } module.genOutputs_j = android.PathForModuleGen(ctx, "java", filenameStem+"_xsdcgen.srcjar") module.generateJavaSrcInSbox(ctx, args) if proptools.Bool(module.properties.Enums_only) { module.genOutputs_c = android.WritablePaths{ android.PathForModuleGen(ctx, "cpp", filenameStem+"_enums.cpp")} module.genOutputs_h = android.WritablePaths{ android.PathForModuleGen(ctx, "cpp", "include/"+filenameStem+"_enums.h")} } else if proptools.Bool(module.properties.Parser_only) { module.genOutputs_c = android.WritablePaths{ android.PathForModuleGen(ctx, "cpp", filenameStem+".cpp")} module.genOutputs_h = android.WritablePaths{ android.PathForModuleGen(ctx, "cpp", "include/"+filenameStem+".h")} } else { module.genOutputs_c = android.WritablePaths{ android.PathForModuleGen(ctx, "cpp", filenameStem+".cpp"), android.PathForModuleGen(ctx, "cpp", filenameStem+"_enums.cpp")} module.genOutputs_h = android.WritablePaths{ android.PathForModuleGen(ctx, "cpp", "include/"+filenameStem+".h"), android.PathForModuleGen(ctx, "cpp", "include/"+filenameStem+"_enums.h")} } module.genOutputDir = android.PathForModuleGen(ctx, "cpp", "include") module.generateCppSrcInSbox(ctx, args) module.generateXsdConfig(ctx) module.setOutputFiles(ctx) } func (module *xsdConfig) setOutputFiles(ctx android.ModuleContext) { var defaultOutputFiles android.WritablePaths defaultOutputFiles = append(defaultOutputFiles, module.genOutputs_j) defaultOutputFiles = append(defaultOutputFiles, module.genOutputs_c...) defaultOutputFiles = append(defaultOutputFiles, module.genOutputs_h...) defaultOutputFiles = append(defaultOutputFiles, module.genOutputs...) ctx.SetOutputFiles(defaultOutputFiles.Paths(), "") ctx.SetOutputFiles(android.Paths{module.genOutputs_j}, "java") ctx.SetOutputFiles(module.genOutputs_c.Paths(), "cpp") ctx.SetOutputFiles(module.genOutputs_h.Paths(), "h") } func xsdConfigLoadHook(mctx android.LoadHookContext) { module := mctx.Module().(*xsdConfig) name := module.BaseModuleName() args := " --stub-packages " + *module.properties.Package_name + " --hide MissingPermission --hide BroadcastBehavior" + " --hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol" + " --hide SdkConstant --hide HiddenTypeParameter --hide Todo" api_dir := proptools.StringDefault(module.properties.Api_dir, "api") currentApiFileName := filepath.Join(api_dir, "current.txt") removedApiFileName := filepath.Join(api_dir, "removed.txt") check_api := CheckApi{} check_api.Current.Api_file = proptools.StringPtr(currentApiFileName) check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName) check_api.Last_released.Api_file = proptools.StringPtr( filepath.Join(api_dir, "last_current.txt")) check_api.Last_released.Removed_api_file = proptools.StringPtr( filepath.Join(api_dir, "last_removed.txt")) mctx.CreateModule(java.DroidstubsFactory, &DroidstubsProperties{ Name: proptools.StringPtr(name + ".docs"), Srcs: []string{":" + name}, Args: proptools.StringPtr(args), Api_filename: proptools.StringPtr(currentApiFileName), Removed_api_filename: proptools.StringPtr(removedApiFileName), Check_api: check_api, Installable: proptools.BoolPtr(false), Sdk_version: proptools.StringPtr("core_platform"), }) } func xsdConfigFactory() android.Module { module := &xsdConfig{} module.AddProperties(&module.properties) android.InitAndroidModule(module) android.AddLoadHook(module, xsdConfigLoadHook) return module }