1"""Copyright (C) 2022 The Android Open Source Project 2 3Licensed under the Apache License, Version 2.0 (the "License"); 4you may not use this file except in compliance with the License. 5You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9Unless required by applicable law or agreed to in writing, software 10distributed under the License is distributed on an "AS IS" BASIS, 11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12See the License for the specific language governing permissions and 13limitations under the License. 14""" 15 16load("@bazel_skylib//lib:paths.bzl", "paths") 17 18"""Build rule for converting `.l` or `.ll` to C or C++ sources with Flex. 19 20Uses flex (and m4 under the hood) to convert .l and .ll source files into 21.c and .cc files. Does not support .lex or .lpp extensions 22 23Examples 24-------- 25 26This is a simple example. 27``` 28genlex( 29 name = "html_lex", 30 src = "html.l", 31) 32``` 33 34This example uses some options for flex. 35``` 36genlex( 37 name = "rules_l", 38 src = "rules.l", 39 lexopts = ["-d", "-v"], 40) 41``` 42""" 43 44def _genlex_impl(ctx): 45 """Implementation for genlex rule.""" 46 47 # TODO(b/190006308): When fixed, l and ll sources can coexist. Remove this. 48 exts = [f.extension for f in ctx.files.srcs] 49 contains_l = False 50 contains_ll = False 51 for ext in exts: 52 if ext == "l": 53 contains_l = True 54 if ext == "ll": 55 contains_ll = True 56 if contains_l and contains_ll: 57 fail( 58 "srcs contains both .l and .ll files. Please use separate targets.", 59 ) 60 61 outputs = [] 62 for src_file in ctx.files.srcs: 63 args = ctx.actions.args() 64 output_filename = "" 65 66 src_ext = src_file.extension 67 split_filename = src_file.basename.partition(".") 68 filename_without_ext = split_filename[0] 69 70 if src_ext == "l": 71 output_filename = paths.replace_extension(filename_without_ext, ".c") 72 elif src_ext == "ll": 73 output_filename = paths.replace_extension(filename_without_ext, ".cc") 74 output_file = ctx.actions.declare_file(output_filename) 75 outputs.append(output_file) 76 args.add("-o", output_file.path) 77 78 args.add_all(ctx.attr.lexopts) 79 args.add(src_file) 80 81 ctx.actions.run( 82 executable = ctx.executable._flex, 83 env = { 84 "M4": ctx.executable._m4.path, 85 }, 86 arguments = [args], 87 inputs = [src_file], 88 tools = [ctx.executable._m4], 89 outputs = [output_file], 90 mnemonic = "Flex", 91 progress_message = "Generating %s from %s" % ( 92 output_filename, 93 src_file.short_path, 94 ), 95 ) 96 return [DefaultInfo(files = depset(outputs))] 97 98genlex = rule( 99 implementation = _genlex_impl, 100 doc = "Generate C/C++-language sources from a lex file using Flex.", 101 attrs = { 102 "srcs": attr.label_list( 103 mandatory = True, 104 allow_files = [".l", ".ll"], 105 doc = "The lex source file for this rule", 106 ), 107 "lexopts": attr.string_list( 108 doc = "A list of options to be added to the flex command line.", 109 ), 110 "_flex": attr.label( 111 default = "//prebuilts/build-tools:flex", 112 executable = True, 113 cfg = "exec", 114 ), 115 "_m4": attr.label( 116 default = "//prebuilts/build-tools:m4", 117 executable = True, 118 cfg = "exec", 119 ), 120 }, 121) 122