Skip to content

Commit d59f5b2

Browse files
smolkajclaude
andauthored
bazel: build flex and bison hermetically via Bazel toolchains (#5504)
Signed-off-by: Steffen Smolka <steffen.smolka@gmail.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 5097011 commit d59f5b2

File tree

7 files changed

+111
-74
lines changed

7 files changed

+111
-74
lines changed

.github/workflows/ci-bazel.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,6 @@ jobs:
4949
restore-keys: |
5050
${{ runner.os }}-bazel-direct-
5151
52-
- name: Install Flex and Bison
53-
run: sudo apt install bison flex libfl-dev
54-
5552
- name: Save start time
5653
run: echo "START_TIME=$(date +%s)" >> "$GITHUB_ENV"
5754

@@ -89,9 +86,6 @@ jobs:
8986
restore-keys: |
9087
${{ runner.os }}-bazel-indirect-
9188
92-
- name: Install Flex and Bison
93-
run: sudo apt install bison flex libfl-dev
94-
9589
- name: Save start time
9690
run: echo "START_TIME=$(date +%s)" >> "$GITHUB_ENV"
9791

BUILD.bazel

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ load("@protobuf//bazel:proto_library.bzl", "proto_library")
33
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
44
load("@rules_license//rules:license.bzl", "license")
55
load("//bazel:bison.bzl", "genyacc")
6-
load("//bazel:flex.bzl", "genlex")
6+
load("//bazel:flex.bzl", "flex_lexer_h", "genlex")
77

88
package(
99
default_applicable_licenses = ["//:license"],
@@ -89,33 +89,22 @@ genyacc(
8989
visibility = ["//visibility:private"],
9090
)
9191

92-
genrule(
93-
name = "p4lexer_lex",
94-
srcs = ["frontends/parsers/p4/p4lexer.ll"],
95-
outs = ["frontends/parsers/p4/p4lexer.lex"],
96-
cmd = "sed '/%option outfile=\"lex.yy.c\"/d' $(SRCS) > $(OUTS)",
92+
flex_lexer_h(
93+
name = "flex_lexer_h",
9794
visibility = ["//visibility:private"],
9895
)
9996

10097
genlex(
10198
name = "p4lexer",
102-
src = "frontends/parsers/p4/p4lexer.lex",
99+
src = "frontends/parsers/p4/p4lexer.ll",
103100
out = "frontends/parsers/p4/p4lexer.cc",
104101
prefix = "yy",
105102
visibility = ["//visibility:private"],
106103
)
107104

108-
genrule(
109-
name = "v1lexer_lex",
110-
srcs = ["frontends/parsers/v1/v1lexer.ll"],
111-
outs = ["frontends/parsers/v1/v1lexer.lex"],
112-
cmd = "sed '/%option outfile=\"lex.yy.c\"/d' $(SRCS) > $(OUTS)",
113-
visibility = ["//visibility:private"],
114-
)
115-
116105
genlex(
117106
name = "v1lexer",
118-
src = "frontends/parsers/v1/v1lexer.lex",
107+
src = "frontends/parsers/v1/v1lexer.ll",
119108
out = "frontends/parsers/v1/v1lexer.cc",
120109
prefix = "yy",
121110
visibility = ["//visibility:private"],
@@ -240,6 +229,7 @@ cc_library(
240229
],
241230
deps = [
242231
":config_h",
232+
":flex_lexer_h",
243233
":lib",
244234
":p4info_dpdk_cc_proto",
245235
"@abseil-cpp//absl/container:btree",

MODULE.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,13 @@ bazel_dep(name = "googleapis", version = "0.0.0-20260223-edfe7983")
1717
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
1818
bazel_dep(name = "nlohmann_json", version = "3.12.0.bcr.1")
1919
bazel_dep(name = "protobuf", version = "33.5")
20+
bazel_dep(name = "rules_bison", version = "0.4")
2021
bazel_dep(name = "rules_cc", version = "0.2.17")
22+
bazel_dep(name = "rules_flex", version = "0.4")
2123
bazel_dep(name = "rules_license", version = "1.0.0")
24+
25+
# Transitive dep of rules_bison and rules_flex; pinned to 0.3 for Bazel 9+ compatibility.
26+
bazel_dep(name = "rules_m4", version = "0.3")
2227
bazel_dep(name = "rules_proto", version = "7.1.0")
2328
bazel_dep(name = "z3", version = "4.15.2")
2429
bazel_dep(name = "p4runtime", version = "1.5.0")

bazel/bison.bzl

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,64 @@
11
"""Build rule for generating C or C++ sources with Bison."""
22

33
def _genyacc_impl(ctx):
4-
"""Implementation for genyacc rule.
4+
"""Implementation for genyacc rule."""
5+
bison_tc = ctx.toolchains["@rules_bison//bison:toolchain_type"].bison_toolchain
56

6-
Expects to find bison binary on the PATH.
7-
"""
8-
9-
# Argument list
10-
args = []
11-
args.append("--defines=%s" % ctx.outputs.header_out.path)
12-
args.append("--output-file=%s" % ctx.outputs.source_out.path)
7+
args = ctx.actions.args()
8+
args.add("--defines=" + ctx.outputs.header_out.path)
9+
args.add("--output-file=" + ctx.outputs.source_out.path)
1310
if ctx.attr.prefix:
14-
args.append("--name-prefix=%s" % ctx.attr.prefix)
15-
args += [ctx.expand_location(opt) for opt in ctx.attr.extra_options]
16-
args.append(ctx.file.src.path)
11+
args.add("--name-prefix=" + ctx.attr.prefix)
12+
args.add_all([ctx.expand_location(opt) for opt in ctx.attr.extra_options])
13+
args.add(ctx.file.src.path)
1714

18-
# Output files
1915
outputs = ctx.outputs.extra_outs + [
2016
ctx.outputs.header_out,
2117
ctx.outputs.source_out,
2218
]
2319

24-
ctx.actions.run_shell(
25-
use_default_shell_env = True,
26-
command = "bison " + " ".join(args),
27-
inputs = ctx.files.src,
20+
ctx.actions.run(
21+
executable = bison_tc.bison_tool,
22+
arguments = [args],
23+
inputs = depset(direct = ctx.files.src),
2824
outputs = outputs,
25+
env = bison_tc.bison_env,
2926
mnemonic = "Yacc",
30-
progress_message = "Generating %s and %s from %s" %
31-
(
32-
ctx.outputs.source_out.short_path,
33-
ctx.outputs.header_out.short_path,
34-
ctx.file.src.short_path,
35-
),
27+
progress_message = "Generating %s and %s from %s" % (
28+
ctx.outputs.source_out.short_path,
29+
ctx.outputs.header_out.short_path,
30+
ctx.file.src.short_path,
31+
),
3632
)
3733

3834
genyacc = rule(
3935
implementation = _genyacc_impl,
4036
doc = "Generate C/C++-language sources from a Yacc file using Bison.",
4137
attrs = {
42-
"src": attr.label(
43-
mandatory = True,
44-
allow_single_file = [".y", ".yy", ".yc", ".ypp"],
45-
doc = "The .y, .yy, or .yc source file for this rule",
38+
"extra_options": attr.string_list(
39+
doc = "A list of extra options to pass to Bison. These are " +
40+
"subject to $(location ...) expansion.",
4641
),
42+
"extra_outs": attr.output_list(doc = "A list of extra generated output files."),
4743
"header_out": attr.output(
4844
mandatory = True,
4945
doc = "The generated 'defines' header file",
5046
),
51-
"source_out": attr.output(mandatory = True, doc = "The generated source file"),
5247
"prefix": attr.string(
5348
doc = "External symbol prefix for Bison. This string is " +
5449
"passed to bison as the -p option, causing the resulting C " +
5550
"file to define external functions named 'prefix'parse, " +
5651
"'prefix'lex, etc. instead of yyparse, yylex, etc.",
5752
),
58-
"extra_outs": attr.output_list(doc = "A list of extra generated output files."),
59-
"extra_options": attr.string_list(
60-
doc = "A list of extra options to pass to Bison. These are " +
61-
"subject to $(location ...) expansion.",
53+
"source_out": attr.output(mandatory = True, doc = "The generated source file"),
54+
"src": attr.label(
55+
mandatory = True,
56+
allow_single_file = [".y", ".yy", ".yc", ".ypp"],
57+
doc = "The .y, .yy, or .yc source file for this rule",
6258
),
6359
},
60+
toolchains = [
61+
"@rules_bison//bison:toolchain_type",
62+
"@rules_m4//m4:toolchain_type",
63+
],
6464
)

bazel/flex.bzl

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,73 @@
1-
"""Build rule for generating C or C++ sources with Flex."""
1+
"""Build rules for generating C or C++ sources with Flex."""
22

3-
def genlex(name, src, out, prefix, includes = [], visibility = None):
4-
"""Generate a C++ lexer from a lex file using Flex.
3+
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
4+
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
55

6-
Expects to find flex binary on the PATH.
6+
def _genlex_impl(ctx):
7+
"""Implementation for genlex rule."""
8+
flex_tc = ctx.toolchains["@rules_flex//flex:toolchain_type"].flex_toolchain
79

8-
Args:
9-
name: The name of the rule.
10-
src: The .lex source file.
11-
out: The generated source file.
12-
includes: A list of headers included by the .lex file.
13-
prefix: Passed to flex as the -P option.
14-
visibility: visibility of this rule to other packages.
15-
"""
16-
cmd = "flex -o $(location %s) -P %s $(location %s)" % (out, prefix, src)
17-
native.genrule(
18-
name = name,
19-
outs = [out],
20-
srcs = [src] + includes,
21-
cmd = cmd,
22-
visibility = visibility,
10+
args = ctx.actions.args()
11+
args.add("-o", ctx.outputs.out.path)
12+
if ctx.attr.prefix:
13+
args.add("-P")
14+
args.add(ctx.attr.prefix)
15+
args.add(ctx.file.src.path)
16+
17+
ctx.actions.run(
18+
executable = flex_tc.flex_tool,
19+
arguments = [args],
20+
inputs = depset(direct = [ctx.file.src] + ctx.files.includes),
21+
outputs = [ctx.outputs.out],
22+
env = flex_tc.flex_env,
23+
mnemonic = "Flex",
24+
progress_message = "Generating %s from %s" % (
25+
ctx.outputs.out.short_path,
26+
ctx.file.src.short_path,
27+
),
2328
)
29+
30+
genlex = rule(
31+
implementation = _genlex_impl,
32+
doc = "Generate a C or C++ lexer from a lex file using Flex.",
33+
attrs = {
34+
"includes": attr.label_list(
35+
allow_files = True,
36+
doc = "A list of headers included by the .lex file.",
37+
),
38+
"out": attr.output(
39+
mandatory = True,
40+
doc = "The generated source file.",
41+
),
42+
"prefix": attr.string(
43+
doc = "Passed to flex as the -P option.",
44+
),
45+
"src": attr.label(
46+
mandatory = True,
47+
allow_single_file = True,
48+
doc = "The .l, .ll, or .lex source file.",
49+
),
50+
},
51+
toolchains = [
52+
"@rules_flex//flex:toolchain_type",
53+
"@rules_m4//m4:toolchain_type",
54+
],
55+
)
56+
57+
def _flex_lexer_h_impl(ctx):
58+
"""Exposes FlexLexer.h from the Flex toolchain as a CcInfo library."""
59+
hdr = ctx.toolchains["@rules_flex//flex:toolchain_type"].flex_toolchain.flex_lexer_h
60+
compilation_context = cc_common.create_compilation_context(
61+
headers = depset([hdr]),
62+
system_includes = depset([hdr.dirname]),
63+
)
64+
return [
65+
CcInfo(compilation_context = compilation_context),
66+
DefaultInfo(files = depset([hdr])),
67+
]
68+
69+
flex_lexer_h = rule(
70+
implementation = _flex_lexer_h_impl,
71+
doc = "Exposes FlexLexer.h from the Flex toolchain as a cc_library dependency.",
72+
toolchains = ["@rules_flex//flex:toolchain_type"],
73+
)

frontends/parsers/p4/p4lexer.ll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ using Parser = P4::P4Parser;
3535
%}
3636

3737
%option c++
38-
%option outfile="lex.yy.c"
3938
%option yyclass="P4::P4Lexer"
4039
%option prefix="p4"
4140
%option nodefault noyywrap nounput noinput noyyget_leng

frontends/parsers/v1/v1lexer.ll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ using Parser = V1::V1Parser;
3030
%}
3131

3232
%option c++
33-
%option outfile="lex.yy.c"
3433
%option yyclass="V1::V1Lexer"
3534
%option prefix="v1"
3635
%option nodefault noyywrap nounput noinput noyyget_leng

0 commit comments

Comments
 (0)