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(":library.bzl", "java_library")
16
17# Return the relative directory where the java file should be generated
18def _java_gen_dir(ctx):
19    return ctx.attr.name
20
21# Return the filename of the srcjar containing the generated .java files
22def _java_gen_srcjar(ctx):
23    return ctx.attr.name + ".srcjar"
24
25def _xsdc_args(ctx, intermediate_dir):
26    args = ctx.actions.args()
27    args.add(ctx.file.src)
28    args.add("-p", ctx.attr.package_name)
29    args.add("-j")  # Generate java
30    args.add("-o", intermediate_dir.path)  # Pass the output directory
31
32    if ctx.attr.nullability:
33        args.add("-n")
34
35    if ctx.attr.gen_has:
36        args.add("-g")
37
38    if ctx.attr.gen_writer:
39        args.add("-w")
40
41    if ctx.attr.boolean_getter:
42        args.add("-b")
43
44    for root_element in ctx.attr.root_elements:
45        args.add("-r", root_element)
46
47    return args
48
49def _zip_args(ctx, output_srcjar, intermediate_dir):
50    args = ctx.actions.args()
51    args.add("-jar")
52    args.add("-o", output_srcjar)
53    args.add("-C", intermediate_dir.path)
54    args.add("-D", intermediate_dir.path)
55
56    # The java files inside the sandbox are symlinks
57    # Instruct soong_zip to follow the symlinks
58    args.add("-symlinks=false")
59    return args
60
61def _java_xsd_codegen_impl(ctx):
62    intermediate_dir = ctx.actions.declare_directory(
63        _java_gen_dir(ctx),
64    )
65    output_srcjar = ctx.actions.declare_file(_java_gen_srcjar(ctx))
66
67    # Run xsdc to generate the .java files in an intermedite directory
68    ctx.actions.run(
69        executable = ctx.executable._xsdc,
70        inputs = [ctx.file.src] + ctx.files.include_files,
71        outputs = [intermediate_dir],
72        arguments = [
73            _xsdc_args(ctx, intermediate_dir),
74        ],
75        tools = [
76            ctx.executable._xsdc,
77        ],
78        mnemonic = "XsdcJavaCompile",
79        progress_message = "Generating java files for %s" % ctx.file.src,
80    )
81
82    # Zip the intermediate directory to a srcjar
83    ctx.actions.run(
84        executable = ctx.executable._soong_zip,
85        inputs = [intermediate_dir],
86        outputs = [output_srcjar],
87        arguments = [
88            _zip_args(ctx, output_srcjar, intermediate_dir),
89        ],
90        tools = [
91            ctx.executable._soong_zip,
92        ],
93        mnemonic = "XsdcJavaZip",
94        progress_message = "Generating srcjar for %s" % ctx.file.src,
95    )
96
97    return [
98        DefaultInfo(
99            files = depset([output_srcjar]),
100        ),
101    ]
102
103_java_xsd_codegen = rule(
104    implementation = _java_xsd_codegen_impl,
105    doc = "This rule generates .java parser files from an xsd file using xsdc",
106    attrs = {
107        "src": attr.label(
108            allow_single_file = [".xsd"],
109            doc = "The main xsd file",
110            mandatory = True,
111        ),
112        "include_files": attr.label_list(
113            allow_files = [".xsd"],
114            doc = "The (transitive) xsd files included by `src` using xs:include",
115        ),
116        "package_name": attr.string(
117            doc = "Package name to use in the generated .java files",
118            mandatory = True,
119        ),
120        "nullability": attr.bool(
121            doc = "Add @NonNull or @Nullable annotation to the generated .java files",
122            default = False,
123        ),
124        "gen_has": attr.bool(
125            doc = "Generate public hasX() method",
126            default = False,
127        ),
128        "gen_writer": attr.bool(
129            doc = "Add xml writer to the generated .java files",
130        ),
131        "boolean_getter": attr.bool(
132            doc = "Whether getter name of boolean element or attribute is getX or isX. If true, getter name is isX",
133            default = False,
134        ),
135        "root_elements": attr.string_list(
136            doc = "If set, xsdc will generate parser code only for the specified root elements",
137        ),
138        "_xsdc": attr.label(
139            executable = True,
140            cfg = "exec",
141            default = Label("//system/tools/xsdc"),
142        ),
143        "_soong_zip": attr.label(
144            executable = True,
145            cfg = "exec",
146            allow_single_file = True,
147            default = Label("//build/soong/zip/cmd:soong_zip"),
148        ),
149    },
150)
151
152def java_xsd_config_library(
153        name,
154        src,
155        sdk_version = "none",
156        include_files = [],
157        package_name = "",
158        nullability = False,
159        gen_has = False,
160        gen_writer = False,
161        boolean_getter = False,
162        root_elements = [],
163        deps = [],
164        **kwargs):
165    """
166    Generate .java parser file from .xsd file using xsdc and wrap it in a java_library.
167
168    """
169    _gen = name + "_gen"
170
171    _java_xsd_codegen(
172        name = _gen,
173        src = src,
174        include_files = include_files,
175        package_name = package_name,
176        nullability = nullability,
177        gen_has = gen_has,
178        gen_writer = gen_writer,
179        boolean_getter = boolean_getter,
180        root_elements = root_elements,
181        **kwargs
182    )
183
184    java_library(
185        name = name,
186        srcs = [_gen],
187        deps = deps,
188        sdk_version = sdk_version,
189        **kwargs
190    )
191