blob: 7125abc3f3c182f42ba82fe6565d2180ace82db6 [file] [log] [blame]
# Copyright 2017-2020 The Verible Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Build rule for generating C or C++ sources with Flex.
IMPORTANT: we _strongly recommend_ that you include a unique and project-
specific `%option prefix="myproject"` directive in your scanner spec to avoid
very hard-to-debug symbol name conflict problems if two scanners are linked
into the same dynamically-linked executable.
By default, flex includes the definition of a static function `yyunput` in its
output. If you never use the lex `unput` function in your lex rules, however,
`yyunput` will never be called. This causes problems building the output file,
as llvm issues warnings about static functions that are never called. To avoid
this problem, use `%option nounput` in the declarations section if your lex
rules never use `unput`.
Note that if you use the c++ mode of flex, you will need to include the
boilerplate header `FlexLexer.h` file in any `cc_library` which includes the
generated flex scanner directly. This is typically done by
`#include <FlexLexer.h>` which typically is installed in /usr/include
when flex is installed.
Examples
--------
This is a simple example.
```
genlex(
name = "html_lex_lex",
src = "html.lex",
out = "html_lexer.c",
)
```
This example uses a `.tab.hh` file.
```
genlex(
name = "rules_l",
src = "rules.lex",
includes = [
"rules.tab.hh",
],
out = "rules.yy.cc",
)
```
"""
def _genlex_impl(ctx):
"""Implementation for genlex rule."""
# Compute the prefix, if not specified.
if ctx.attr.prefix:
prefix = ctx.attr.prefix
else:
prefix = ctx.file.src.basename.partition(".")[0]
# Construct the arguments.
args = ctx.actions.args()
args.add("-o", ctx.outputs.out)
args.add("-P", prefix)
args.add_all(ctx.attr.lexopts)
args.add(ctx.file.src)
ctx.actions.run(
executable = ctx.executable._flex,
env = {
"M4": ctx.executable._m4.path,
},
arguments = [args],
inputs = ctx.files.src + ctx.files.includes,
tools = [ctx.executable._m4],
outputs = [ctx.outputs.out],
mnemonic = "Flex",
progress_message = "Generating %s from %s" % (
ctx.outputs.out.short_path,
ctx.file.src.short_path,
),
)
genlex = rule(
attrs = {
"src": attr.label(
mandatory = True,
allow_single_file = [
".l",
".ll",
".lex",
".lpp",
],
doc = "The .lex source file for this rule",
),
"includes": attr.label_list(
allow_files = True,
doc = "A list of headers that are included by the .lex file",
),
"out": attr.output(
mandatory = True,
doc = "The generated source file",
),
"prefix": attr.string(
doc = "External symbol prefix for Flex. This string is " +
"passed to flex as the -P option, causing the resulting C " +
"file to define external functions named 'prefix'text, " +
"'prefix'in, etc. The default is the basename of the source" +
"file without the .lex extension.",
),
"lexopts": attr.string_list(
doc = "A list of options to be added to the flex command line.",
),
"_flex": attr.label(
default = Label("//bazel:flex_bin"),
executable = True,
cfg = "host",
),
"_m4": attr.label(
default = Label("//bazel:m4_bin"),
executable = True,
cfg = "host",
),
},
doc = "Generate C/C++-language sources from a lex file using Flex.",
output_to_genfiles = True,
implementation = _genlex_impl,
)