Path: blob/trunk/javascript/selenium-webdriver/private/generate_bidi.bzl
11813 views
"""Bazel rules for generating WebDriver BiDi TypeScript modules from CDDL specification."""
load("@aspect_rules_js//js:defs.bzl", "js_run_binary")
# Language bindings that may consume the shared bidi-ast.json / bidi-model.json artifacts.
_ARTIFACT_VISIBILITY = [
"//java:__subpackages__",
"//py:__subpackages__",
"//rb:__subpackages__",
]
# Output TypeScript file names produced by generate_bidi.mjs, one per domain.
_DOMAIN_TS_FILES = [
"bluetooth.ts",
"browser.ts",
"browsing_context.ts",
"common.ts",
"emulation.ts",
"input.ts",
"log.ts",
"network.ts",
"permissions.ts",
"script.ts",
"session.ts",
"speculation.ts",
"storage.ts",
"user_agent_client_hints.ts",
"webextension.ts",
]
def _merge_cddl_impl(ctx):
"""Merges one or more CDDL files into a single output file."""
out = ctx.outputs.out
args = ctx.actions.args()
args.add(out)
args.add_all(ctx.files.srcs)
ctx.actions.run(
inputs = ctx.files.srcs,
outputs = [out],
executable = ctx.executable.tool,
arguments = [args],
mnemonic = "MergeCddl",
progress_message = "Merging CDDL files into %s" % out.short_path,
)
return [DefaultInfo(files = depset([out]))]
_merge_cddl = rule(
implementation = _merge_cddl_impl,
attrs = {
"srcs": attr.label_list(allow_files = True, mandatory = True),
"out": attr.output(mandatory = True),
"tool": attr.label(
executable = True,
cfg = "exec",
mandatory = True,
),
},
doc = "Merges CDDL specification files into a single file using an external merge tool.",
)
def _compile_bidi_ts_impl(ctx):
ts_files = ctx.files.srcs
output_subdir = ctx.attr.output_subdir
tsc = ctx.executable.tsc
js_outputs = [
ctx.actions.declare_file(output_subdir + "/" + f.basename.replace(".ts", ".js"))
for f in ts_files
]
dts_outputs = [
ctx.actions.declare_file(output_subdir + "/" + f.basename.replace(".ts", ".d.ts"))
for f in ts_files
]
all_outputs = js_outputs + dts_outputs
args = ctx.actions.args()
args.add("--target", "ES2020")
args.add("--module", "NodeNext")
args.add("--moduleResolution", "NodeNext")
args.add("--declaration")
args.add("--outDir", js_outputs[0].dirname)
for f in ts_files:
args.add(f.path)
ctx.actions.run(
inputs = ts_files,
outputs = all_outputs,
executable = tsc,
arguments = [args],
env = {
"BAZEL_BINDIR": ctx.bin_dir.path,
# Prevent the js_binary wrapper from cd-ing to BAZEL_BINDIR.
# Without this, all file paths passed to tsc (which start with
# bazel-out/..., i.e. relative to the execroot) would be resolved
# relative to BAZEL_BINDIR and end up double-prefixed.
"JS_BINARY__NO_CD_BINDIR": "1",
},
mnemonic = "TscCompileBiDi",
progress_message = "Compiling WebDriver BiDi TypeScript to JavaScript",
)
return [DefaultInfo(files = depset(all_outputs))]
_compile_bidi_ts = rule(
implementation = _compile_bidi_ts_impl,
attrs = {
"output_subdir": attr.string(mandatory = True),
"srcs": attr.label_list(allow_files = True, mandatory = True),
"tsc": attr.label(
executable = True,
cfg = "exec",
default = "@npm_typescript//:tsc",
),
},
doc = "Compiles generated BiDi TypeScript files to JavaScript + declaration files",
)
def generate_bidi_library(
name,
cddl_file,
extra_cddl_files = [],
enhancements_manifest = None,
generator = None,
merge_tool = "//py/private:merge_cddl",
spec_version = "1.0",
output_path = "bidi/generated"):
"""Macro that merges CDDL, generates BiDi TypeScript modules, and compiles them to JS.
Args:
name: Base name for the targets.
cddl_file: Primary CDDL spec label (webdriver-bidi-all.cddl).
extra_cddl_files: Additional CDDL files merged before generation.
enhancements_manifest: JSON manifest for per-domain customisations.
generator: The generate_bidi.mjs js_binary label. Defaults to :generate_bidi_script.
merge_tool: Python binary that concatenates CDDL files (output first, then inputs).
spec_version: Spec version string passed to the generator.
output_path: Output path for generated files within the package (default: bidi/generated).
"""
if generator == None:
generator = ":generate_bidi_script"
pkg = native.package_name()
ts_src_path = output_path + "_src"
# Step 1: merge CDDL files into one.
# merge_cddl signature: <output> <input1> [<input2> ...]
# Uses ctx.actions.run so arguments are passed as an argv list rather than
# a shell command string, avoiding quoting/escaping issues with special chars.
merged_name = name + "_merged_cddl"
_merge_cddl(
name = merged_name,
srcs = [cddl_file] + extra_cddl_files,
out = name + "_merged.cddl",
tool = merge_tool,
)
# Step 2: parse the merged CDDL once into the reusable AST artifact.
# Exposed to the other bindings so they can consume it directly.
ast_target = name + "_ast"
ast_out = name + "_ast.json"
js_run_binary(
name = ast_target,
srcs = [":" + merged_name],
outs = [ast_out],
args = [
"--cddl",
"$(location :" + merged_name + ")",
"--dump-ast",
pkg + "/" + ast_out,
],
tool = generator,
visibility = _ARTIFACT_VISIBILITY,
)
# Step 3: extract the binding-neutral command/event model from the AST.
# Exposed to the other bindings so they can consume it directly.
json_target = name + "_json"
model_out = name + "_model.json"
js_run_binary(
name = json_target,
srcs = [":" + ast_target],
outs = [model_out],
args = [
"--ast",
"$(location :" + ast_target + ")",
"--dump-model",
pkg + "/" + model_out,
],
tool = generator,
visibility = _ARTIFACT_VISIBILITY,
)
# Step 4: generate one .ts module per BiDi domain from the AST + model.
ts_outs = [ts_src_path + "/" + f for f in _DOMAIN_TS_FILES]
gen_srcs = [":" + ast_target, ":" + json_target]
gen_args = [
"--ast",
"$(location :" + ast_target + ")",
"--model",
"$(location :" + json_target + ")",
"--output-dir",
pkg + "/" + ts_src_path,
"--spec-version",
spec_version,
]
if enhancements_manifest:
gen_srcs.append(enhancements_manifest)
gen_args += ["--enhancements", "$(location " + enhancements_manifest + ")"]
ts_target = name + "_ts"
js_run_binary(
name = ts_target,
srcs = gen_srcs,
outs = ts_outs,
args = gen_args,
tool = generator,
)
# Step 5: compile .ts → .js + .d.ts via tsc (custom rule for ctx.bin_dir.path).
_compile_bidi_ts(
name = name,
srcs = [":" + ts_target],
output_subdir = output_path,
)