1# Copyright (C) 2023 The Android Open Source Project 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 15load("@bazel_skylib//lib:paths.bzl", "paths") 16load(":cc_library_common.bzl", "create_ccinfo_for_includes") 17load(":cc_library_static.bzl", "cc_library_static") 18 19# Return the relative directory where the cpp files should be generated 20def _cpp_gen_dir(ctx): 21 return ctx.attr.name 22 23# Return the relative directory where the h files should be generated 24def _h_gen_dir(ctx): 25 return paths.join( 26 _cpp_gen_dir(ctx), 27 "include", 28 ) 29 30def _cc_xsdc_args(ctx): 31 args = ctx.actions.args() 32 args.add(ctx.file.src) 33 args.add("-p", ctx.attr.package_name) 34 args.add("-c") # Generate cpp sources 35 36 if ctx.attr.gen_writer: 37 args.add("-w") 38 39 if ctx.attr.enums_only: 40 args.add("-e") 41 42 if ctx.attr.parser_only: 43 # parser includes the enum.h file so generate both 44 # in the wrapper cc_static_library, we will only include the .o file of the parser 45 pass 46 47 if ctx.attr.boolean_getter: 48 args.add("-b") 49 50 if ctx.attr.tinyxml: 51 args.add("-t") 52 53 for root_element in ctx.attr.root_elements: 54 args.add("-r", root_element) 55 56 return args 57 58# Returns a tuple of cpp and h filenames that should be generated 59def _cc_xsdc_outputs(ctx): 60 filename_stem = ctx.attr.package_name.replace(".", "_") 61 parser_cpp = filename_stem + ".cpp" 62 parser_h = filename_stem + ".h" 63 enums_cpp = filename_stem + "_enums.cpp" 64 enums_h = filename_stem + "_enums.h" 65 if ctx.attr.parser_only: 66 # parser_cpp includes enums_h, so we need to return both .h files 67 return [parser_cpp], [parser_h, enums_h] 68 elif ctx.attr.enums_only: 69 return [enums_cpp], [enums_h] 70 71 # Default: Generate both parser and enums 72 return [parser_cpp, enums_cpp], [parser_h, enums_h] 73 74def _cc_xsd_codegen_impl(ctx): 75 outputs_cpp, outputs_h = [], [] 76 cpp_filenames, h_filenames = _cc_xsdc_outputs(ctx) 77 78 # Declare the cpp files 79 for cpp_filename in cpp_filenames: 80 outputs_cpp.append( 81 ctx.actions.declare_file( 82 paths.join( 83 _cpp_gen_dir(ctx), 84 cpp_filename, 85 ), 86 ), 87 ) 88 89 # Declare the h files 90 for h_filename in h_filenames: 91 outputs_h.append( 92 ctx.actions.declare_file( 93 paths.join( 94 _h_gen_dir(ctx), 95 h_filename, 96 ), 97 ), 98 ) 99 100 args = _cc_xsdc_args(ctx) 101 102 # Pass the output directory 103 args.add("-o", outputs_cpp[0].dirname) 104 105 ctx.actions.run( 106 executable = ctx.executable._xsdc, 107 inputs = [ctx.file.src] + ctx.files.include_files, 108 outputs = outputs_cpp + outputs_h, 109 arguments = [args], 110 mnemonic = "XsdcCppCompile", 111 ) 112 113 return [ 114 DefaultInfo( 115 # Return the CPP files. 116 # Skip headers since rdep does not compile headers. 117 files = depset(outputs_cpp), 118 ), 119 create_ccinfo_for_includes( 120 ctx, 121 hdrs = outputs_h, 122 includes = [_h_gen_dir(ctx)], 123 ), 124 ] 125 126_cc_xsd_codegen = rule( 127 implementation = _cc_xsd_codegen_impl, 128 doc = "This rule generates .cpp/.h files from an xsd file using xsdc", 129 attrs = { 130 "src": attr.label( 131 allow_single_file = [".xsd"], 132 doc = "The main xsd file", 133 mandatory = True, 134 ), 135 "include_files": attr.label_list( 136 allow_files = [".xsd"], 137 doc = "The (transitive) xsd files included by `src` using xs:include", 138 ), 139 "package_name": attr.string( 140 doc = "Namespace to use in the generated .cpp file", 141 mandatory = True, 142 ), 143 "gen_writer": attr.bool( 144 doc = "Add xml writer to the generated .cpp file", 145 ), 146 "enums_only": attr.bool(), 147 "parser_only": attr.bool(), 148 "boolean_getter": attr.bool( 149 doc = "Whether getter name of boolean element or attribute is getX or isX. If true, getter name is isX", 150 default = False, 151 ), 152 "tinyxml": attr.bool( 153 doc = "Generate code that uses libtinyxml2 instead of libxml2", 154 default = False, 155 ), 156 "root_elements": attr.string_list( 157 doc = "If set, xsdc will generate parser code only for the specified root elements", 158 ), 159 "_xsdc": attr.label( 160 executable = True, 161 cfg = "exec", 162 default = Label("//system/tools/xsdc"), 163 ), 164 }, 165 provides = [ 166 CcInfo, 167 ], 168) 169 170def cc_xsd_config_library( 171 name, 172 src, 173 include_files = [], 174 package_name = "", 175 gen_writer = False, 176 enums_only = False, 177 parser_only = False, 178 boolean_getter = False, 179 tinyxml = False, 180 root_elements = [], 181 deps = [], 182 implementation_dynamic_deps = [], 183 **kwargs): 184 """ 185 Generate .cpp/.h sources from .xsd file using xsdc and wrap it in a cc_static_library. 186 187 """ 188 _gen = name + "_gen" 189 190 _cc_xsd_codegen( 191 name = _gen, 192 src = src, 193 include_files = include_files, 194 package_name = package_name, 195 gen_writer = gen_writer, 196 enums_only = enums_only, 197 parser_only = parser_only, 198 boolean_getter = boolean_getter, 199 tinyxml = tinyxml, 200 root_elements = root_elements, 201 **kwargs 202 ) 203 204 cc_library_static( 205 name = name, 206 srcs = [_gen], 207 deps = deps + [_gen], # Generated hdrs 208 implementation_dynamic_deps = implementation_dynamic_deps, 209 **kwargs 210 ) 211