"""Bazel rule for generating WebDriver BiDi Python modules from CDDL specification."""
def _generate_bidi_impl(ctx):
"""Implementation of the generate_bidi rule."""
cddl_file = ctx.file.cddl_file
extra_cddl_files = ctx.files.extra_cddl_files
manifest_file = ctx.file.enhancements_manifest
generator = ctx.executable.generator
output_dir = ctx.attr.module_name
spec_version = ctx.attr.spec_version
# The generator creates BiDi modules from the CDDL spec
# Using snake_case naming convention for Python files
module_names = [
"bluetooth",
"browser",
"browsing_context",
"common",
"console",
"emulation",
"input",
"log",
"network",
"permissions",
"script",
"session",
"speculation",
"storage",
"user_agent_client_hints",
"webextension",
]
# Merge extra CDDL files into the primary spec before generation.
# Bazel requires inputs to be declared upfront, so we concatenate into a
# single file and pass that to the generator instead of the raw primary.
all_cddl = [cddl_file] + extra_cddl_files
if extra_cddl_files:
if not ctx.executable.merge_tool:
fail("merge_tool is required when extra_cddl_files is non-empty")
merged_cddl = ctx.actions.declare_file("merged_bidi.cddl")
ctx.actions.run(
inputs = all_cddl,
outputs = [merged_cddl],
executable = ctx.executable.merge_tool,
arguments = [merged_cddl.path] + [f.path for f in all_cddl],
)
input_cddl = merged_cddl
else:
input_cddl = cddl_file
# Declare all output files
module_files = [
ctx.actions.declare_file(output_dir + "/" + name + ".py")
for name in module_names
]
init_file = ctx.actions.declare_file(output_dir + "/__init__.py")
py_typed = ctx.actions.declare_file(output_dir + "/py.typed")
gen_outputs = module_files + [init_file, py_typed]
# Copy static extra_srcs into the output directory
extra_outputs = []
for src in ctx.files.extra_srcs:
out = ctx.actions.declare_file(output_dir + "/" + src.basename)
ctx.actions.symlink(output = out, target_file = src)
extra_outputs.append(out)
outputs = gen_outputs + extra_outputs
# Output directory for the generator
output_base = init_file.dirname
# Build the command to run the generator
args = [
input_cddl.path,
output_base,
spec_version,
]
# Add enhancement manifest if provided
inputs = [input_cddl]
if manifest_file:
args.extend(["--enhancements-manifest", manifest_file.path])
inputs.append(manifest_file)
ctx.actions.run(
inputs = inputs,
outputs = gen_outputs,
executable = generator,
arguments = args,
use_default_shell_env = True,
)
return [DefaultInfo(files = depset(outputs))]
generate_bidi = rule(
implementation = _generate_bidi_impl,
attrs = {
"cddl_file": attr.label(
allow_single_file = [".cddl"],
mandatory = True,
doc = "Primary CDDL specification file",
),
"enhancements_manifest": attr.label(
allow_single_file = [".py"],
mandatory = False,
doc = "Enhancement manifest Python file (optional)",
),
"extra_cddl_files": attr.label_list(
allow_files = [".cddl"],
mandatory = False,
default = [],
doc = "Additional CDDL files merged into the primary spec before generation",
),
"extra_srcs": attr.label_list(
allow_files = [".py"],
mandatory = False,
default = [],
doc = "Additional static Python files to copy verbatim into the output directory",
),
"generator": attr.label(
executable = True,
cfg = "exec",
mandatory = True,
doc = "Generator script (e.g., generate_bidi.py)",
),
"merge_tool": attr.label(
executable = True,
cfg = "exec",
mandatory = False,
default = None,
doc = "Tool that concatenates multiple CDDL files into one (e.g., merge_cddl.py). Required when extra_cddl_files is non-empty.",
),
"module_name": attr.string(
mandatory = True,
doc = "Name of the module being generated (e.g., 'selenium/webdriver/common/bidi')",
),
"spec_version": attr.string(
default = "1.0",
doc = "WebDriver BiDi specification version",
),
},
doc = "Generates Python WebDriver BiDi modules from CDDL specification",
)