blob: 0c1e6a83a3727d1948b02dea453c2441d80fd19c [file] [log] [blame]
# Copyright 2019 Google LLC
#
# 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
#
# https://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.
# -*- mode: python; -*-
# vim:set ft=blazebuild:
"""Build defs for Emboss.
This file exports emboss_library, which creates an Emboss library, and
cc_emboss_library, which creates a header file and can be used as a dep in a
`cc_library`, `cc_binary`, or `cc_test` rule.
There is also a convenience macro, `emboss_cc_library()`, which creates an
`emboss_library` and a `cc_emboss_library` based on it.
"""
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
def emboss_cc_library(name, srcs, deps = [], visibility = None, import_dirs = [], enable_enum_traits = True):
"""Constructs a C++ library from an .emb file."""
if len(srcs) != 1:
fail(
"Must specify exactly one Emboss source file for emboss_cc_library.",
"srcs",
)
emboss_library(
name = name + "_ir",
srcs = srcs,
deps = [dep + "_ir" for dep in deps],
import_dirs = import_dirs,
)
cc_emboss_library(
name = name,
deps = [":" + name + "_ir"],
visibility = visibility,
enable_enum_traits = enable_enum_traits,
)
# Full Starlark rules for emboss_library and cc_emboss_library.
#
# This implementation is loosely based on the proto_library and
# cc_proto_library rules that are included with Bazel.
EmbossInfo = provider(
doc = "Encapsulates information provided by a `emboss_library.`",
fields = {
"direct_source": "(File) The `.emb` source files from the `srcs`" +
" attribute.",
"transitive_sources": "(depset[File]) The `.emb` files from `srcs` " +
"and all `deps`.",
"transitive_roots": "(list[str]) The root paths for all " +
"transitive_sources.",
"direct_ir": "(list[File]) The `.emb.ir` files generated from the " +
"`srcs`.",
"transitive_ir": "(depset[File]) The `.emb.ir` files generated from " +
"transitive_srcs.",
},
)
def _emboss_library_impl(ctx):
deps = [dep[EmbossInfo] for dep in ctx.attr.deps]
outs = []
if len(ctx.attr.srcs) != 1:
fail("`srcs` attribute must contain exactly one label.", attr = "srcs")
src = ctx.files.srcs[0]
out = ctx.actions.declare_file(src.basename + ".ir", sibling = src)
outs.append(out)
inputs = depset(
direct = [src],
transitive = [dep.transitive_sources for dep in deps],
)
# If the file is in an external repo, we want to use the path to that repo
# as the root (e.g. ./external/my-repo) so that import paths are resolved
# relative to the external repo root.
fixed_src_root = src.root.path
if src.path.startswith("external/"):
path_segments = src.path.split("/")[:2]
fixed_src_root = "/".join(path_segments)
transitive_roots = depset(
direct = [fixed_src_root],
transitive = [dep.transitive_roots for dep in deps],
)
imports = ["--import-dir=" + root for root in transitive_roots.to_list()]
imports_arg = ["--import-dir=" + impt.path for impt in ctx.files.import_dirs]
ctx.actions.run(
inputs = inputs.to_list(),
outputs = [out],
arguments = [src.path, "--output-file=" + out.path] + imports + imports_arg,
executable = ctx.executable._emboss_compiler,
)
transitive_sources = depset(
direct = [src],
transitive = [dep.transitive_sources for dep in deps],
)
transitive_ir = depset(
direct = outs,
transitive = [dep.transitive_ir for dep in deps],
)
return [
EmbossInfo(
direct_source = src,
transitive_sources = transitive_sources,
transitive_roots = transitive_roots,
direct_ir = outs,
transitive_ir = transitive_ir,
),
DefaultInfo(
files = depset(outs),
),
]
emboss_library = rule(
_emboss_library_impl,
attrs = {
"srcs": attr.label_list(
allow_files = [".emb"],
),
"deps": attr.label_list(
providers = [EmbossInfo],
),
"import_dirs": attr.label_list(
allow_files = True,
),
"licenses": attr.license() if hasattr(attr, "license") else attr.string_list(),
"_emboss_compiler": attr.label(
executable = True,
cfg = "exec",
allow_files = True,
default = Label(
"@com_google_emboss//compiler/front_end:emboss_front_end",
),
),
},
provides = [EmbossInfo],
)
EmbossCcHeaderInfo = provider(
fields = {
"headers": "(list[File]) The `.emb.h` headers from this rule.",
"transitive_headers": "(list[File]) The `.emb.h` headers from this " +
"rule and all dependencies.",
},
doc = "Provide cc emboss headers.",
)
def _cc_emboss_aspect_impl(target, ctx):
cc_toolchain = find_cpp_toolchain(ctx, mandatory = True)
emboss_cc_compiler = ctx.executable._emboss_cc_compiler
emboss_info = target[EmbossInfo]
feature_configuration = cc_common.configure_features(
ctx = ctx,
cc_toolchain = cc_toolchain,
requested_features = list(ctx.features),
unsupported_features = list(ctx.disabled_features),
)
src = target[EmbossInfo].direct_source
headers = [ ctx.actions.declare_file( src.basename + ".h", sibling = src) ]
args = ctx.actions.args()
args.add("--input-file")
args.add_all(emboss_info.direct_ir)
args.add("--output-file")
args.add_all(headers)
if not ctx.attr.enable_enum_traits:
args.add("--no-cc-enum-traits")
ctx.actions.run(
executable = emboss_cc_compiler,
arguments = [args],
inputs = emboss_info.direct_ir,
outputs = headers,
)
runtime_cc_info = ctx.attr._emboss_cc_runtime[CcInfo]
transitive_headers = depset(
direct = headers,
transitive = [
dep[EmbossCcHeaderInfo].transitive_headers
for dep in ctx.rule.attr.deps
],
)
(cc_compilation_context, cc_compilation_outputs) = cc_common.compile(
name = ctx.label.name,
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
public_hdrs = headers,
private_hdrs = transitive_headers.to_list(),
compilation_contexts = [runtime_cc_info.compilation_context],
)
return [
CcInfo(compilation_context = cc_compilation_context),
EmbossCcHeaderInfo(
headers = depset(headers),
transitive_headers = transitive_headers,
),
]
_cc_emboss_aspect = aspect(
implementation = _cc_emboss_aspect_impl,
attr_aspects = ["deps"],
fragments = ["cpp"],
required_providers = [EmbossInfo],
attrs = {
"_cc_toolchain": attr.label(
default = "@bazel_tools//tools/cpp:current_cc_toolchain",
),
"_emboss_cc_compiler": attr.label(
executable = True,
cfg = "exec",
default = "@com_google_emboss//compiler/back_end/cpp:emboss_codegen_cpp",
),
"_emboss_cc_runtime": attr.label(
default = "@com_google_emboss//runtime/cpp:cpp_utils",
),
"enable_enum_traits": attr.bool(
default = True,
),
},
toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
)
def _cc_emboss_library_impl(ctx):
if len(ctx.attr.deps) != 1:
fail("`deps` attribute must contain exactly one label.", attr = "deps")
dep = ctx.attr.deps[0]
return [
dep[CcInfo],
dep[EmbossInfo],
DefaultInfo(files = dep[EmbossCcHeaderInfo].headers),
]
cc_emboss_library = rule(
implementation = _cc_emboss_library_impl,
attrs = {
"deps": attr.label_list(
aspects = [_cc_emboss_aspect],
allow_rules = ["emboss_library"],
allow_files = False,
),
"enable_enum_traits": attr.bool(
default = True,
),
},
provides = [CcInfo, EmbossInfo],
)