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