1# Copyright 2019 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15# -*- mode: python; -*- 16# vim:set ft=blazebuild: 17"""Build defs for Emboss. 18 19This file exports emboss_library, which creates an Emboss library, and 20cc_emboss_library, which creates a header file and can be used as a dep in a 21`cc_library`, `cc_binary`, or `cc_test` rule. 22 23There is also a convenience macro, `emboss_cc_library()`, which creates an 24`emboss_library` and a `cc_emboss_library` based on it. 25""" 26 27load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") 28 29def emboss_cc_library(name, srcs, deps = [], visibility = None, import_dirs = [], enable_enum_traits = True, **kwargs): 30 """Constructs a C++ library from an .emb file.""" 31 if len(srcs) != 1: 32 fail( 33 "Must specify exactly one Emboss source file for emboss_cc_library.", 34 "srcs", 35 ) 36 37 emboss_library( 38 name = name + "_ir", 39 srcs = srcs, 40 deps = [dep + "_ir" for dep in deps], 41 import_dirs = import_dirs, 42 **kwargs 43 ) 44 45 cc_emboss_library( 46 name = name, 47 deps = [":" + name + "_ir"], 48 visibility = visibility, 49 enable_enum_traits = enable_enum_traits, 50 **kwargs 51 ) 52 53# Full Starlark rules for emboss_library and cc_emboss_library. 54# 55# This implementation is loosely based on the proto_library and 56# cc_proto_library rules that are included with Bazel. 57 58EmbossInfo = provider( 59 doc = "Encapsulates information provided by a `emboss_library.`", 60 fields = { 61 "direct_source": "(File) The `.emb` source files from the `srcs`" + 62 " attribute.", 63 "transitive_sources": "(depset[File]) The `.emb` files from `srcs` " + 64 "and all `deps`.", 65 "transitive_roots": "(list[str]) The root paths for all " + 66 "transitive_sources.", 67 "direct_ir": "(list[File]) The `.emb.ir` files generated from the " + 68 "`srcs`.", 69 "transitive_ir": "(depset[File]) The `.emb.ir` files generated from " + 70 "transitive_srcs.", 71 }, 72) 73 74def _emboss_library_impl(ctx): 75 deps = [dep[EmbossInfo] for dep in ctx.attr.deps] 76 outs = [] 77 if len(ctx.attr.srcs) != 1: 78 fail("`srcs` attribute must contain exactly one label.", attr = "srcs") 79 src = ctx.files.srcs[0] 80 out = ctx.actions.declare_file(src.basename + ".ir", sibling = src) 81 outs.append(out) 82 inputs = depset( 83 direct = [src], 84 transitive = [dep.transitive_sources for dep in deps], 85 ) 86 87 # If the file is in an external repo, we want to use the path to that repo 88 # as the root (e.g. ./external/my-repo) so that import paths are resolved 89 # relative to the external repo root. 90 fixed_src_root = src.root.path 91 if src.path.startswith("external/"): 92 path_segments = src.path.split("/")[:2] 93 fixed_src_root = "/".join(path_segments) 94 95 transitive_roots = depset( 96 direct = [fixed_src_root], 97 transitive = [dep.transitive_roots for dep in deps], 98 ) 99 100 imports = ["--import-dir=" + root for root in transitive_roots.to_list()] 101 imports_arg = ["--import-dir=" + impt.path for impt in ctx.files.import_dirs] 102 ctx.actions.run( 103 inputs = inputs.to_list(), 104 outputs = [out], 105 arguments = [src.path, "--output-file=" + out.path] + imports + imports_arg, 106 executable = ctx.executable._emboss_compiler, 107 ) 108 transitive_sources = depset( 109 direct = [src], 110 transitive = [dep.transitive_sources for dep in deps], 111 ) 112 transitive_ir = depset( 113 direct = outs, 114 transitive = [dep.transitive_ir for dep in deps], 115 ) 116 return [ 117 EmbossInfo( 118 direct_source = src, 119 transitive_sources = transitive_sources, 120 transitive_roots = transitive_roots, 121 direct_ir = outs, 122 transitive_ir = transitive_ir, 123 ), 124 DefaultInfo( 125 files = depset(outs), 126 ), 127 ] 128 129emboss_library = rule( 130 _emboss_library_impl, 131 attrs = { 132 "srcs": attr.label_list( 133 allow_files = [".emb"], 134 ), 135 "deps": attr.label_list( 136 providers = [EmbossInfo], 137 ), 138 "import_dirs": attr.label_list( 139 allow_files = True, 140 ), 141 "licenses": attr.license() if hasattr(attr, "license") else attr.string_list(), 142 "_emboss_compiler": attr.label( 143 executable = True, 144 cfg = "exec", 145 allow_files = True, 146 default = Label( 147 "@com_google_emboss//compiler/front_end:emboss_front_end", 148 ), 149 ), 150 }, 151 provides = [EmbossInfo], 152) 153 154EmbossCcHeaderInfo = provider( 155 fields = { 156 "headers": "(list[File]) The `.emb.h` headers from this rule.", 157 "transitive_headers": "(list[File]) The `.emb.h` headers from this " + 158 "rule and all dependencies.", 159 }, 160 doc = "Provide cc emboss headers.", 161) 162 163def _cc_emboss_aspect_impl(target, ctx): 164 cc_toolchain = find_cpp_toolchain(ctx, mandatory = True) 165 emboss_cc_compiler = ctx.executable._emboss_cc_compiler 166 emboss_info = target[EmbossInfo] 167 feature_configuration = cc_common.configure_features( 168 ctx = ctx, 169 cc_toolchain = cc_toolchain, 170 requested_features = list(ctx.features), 171 unsupported_features = list(ctx.disabled_features), 172 ) 173 src = target[EmbossInfo].direct_source 174 headers = [ ctx.actions.declare_file( src.basename + ".h", sibling = src) ] 175 args = ctx.actions.args() 176 args.add("--input-file") 177 args.add_all(emboss_info.direct_ir) 178 args.add("--output-file") 179 args.add_all(headers) 180 if not ctx.attr.enable_enum_traits: 181 args.add("--no-cc-enum-traits") 182 ctx.actions.run( 183 executable = emboss_cc_compiler, 184 arguments = [args], 185 inputs = emboss_info.direct_ir, 186 outputs = headers, 187 ) 188 runtime_cc_info = ctx.attr._emboss_cc_runtime[CcInfo] 189 transitive_headers = depset( 190 direct = headers, 191 transitive = [ 192 dep[EmbossCcHeaderInfo].transitive_headers 193 for dep in ctx.rule.attr.deps 194 ], 195 ) 196 (cc_compilation_context, cc_compilation_outputs) = cc_common.compile( 197 name = ctx.label.name, 198 actions = ctx.actions, 199 feature_configuration = feature_configuration, 200 cc_toolchain = cc_toolchain, 201 public_hdrs = headers, 202 private_hdrs = transitive_headers.to_list(), 203 compilation_contexts = [runtime_cc_info.compilation_context], 204 ) 205 return [ 206 CcInfo(compilation_context = cc_compilation_context), 207 EmbossCcHeaderInfo( 208 headers = depset(headers), 209 transitive_headers = transitive_headers, 210 ), 211 ] 212 213_cc_emboss_aspect = aspect( 214 implementation = _cc_emboss_aspect_impl, 215 attr_aspects = ["deps"], 216 fragments = ["cpp"], 217 required_providers = [EmbossInfo], 218 attrs = { 219 "_cc_toolchain": attr.label( 220 default = "@bazel_tools//tools/cpp:current_cc_toolchain", 221 ), 222 "_emboss_cc_compiler": attr.label( 223 executable = True, 224 cfg = "exec", 225 default = "@com_google_emboss//compiler/back_end/cpp:emboss_codegen_cpp", 226 ), 227 "_emboss_cc_runtime": attr.label( 228 default = "@com_google_emboss//runtime/cpp:cpp_utils", 229 ), 230 "enable_enum_traits": attr.bool( 231 default = True, 232 ), 233 }, 234 toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], 235) 236 237def _cc_emboss_library_impl(ctx): 238 if len(ctx.attr.deps) != 1: 239 fail("`deps` attribute must contain exactly one label.", attr = "deps") 240 dep = ctx.attr.deps[0] 241 return [ 242 dep[CcInfo], 243 dep[EmbossInfo], 244 DefaultInfo(files = dep[EmbossCcHeaderInfo].headers), 245 ] 246 247cc_emboss_library = rule( 248 implementation = _cc_emboss_library_impl, 249 attrs = { 250 "deps": attr.label_list( 251 aspects = [_cc_emboss_aspect], 252 allow_rules = ["emboss_library"], 253 allow_files = False, 254 ), 255 "enable_enum_traits": attr.bool( 256 default = True, 257 ), 258 }, 259 provides = [CcInfo, EmbossInfo], 260) 261