feat(third_party/bazel): Check in rules_haskell from Tweag
This commit is contained in:
parent
2eb1dc26e4
commit
f723b8b878
479 changed files with 51484 additions and 0 deletions
395
third_party/bazel/rules_haskell/haskell/protobuf.bzl
vendored
Normal file
395
third_party/bazel/rules_haskell/haskell/protobuf.bzl
vendored
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
"""Support for protocol buffers"""
|
||||
|
||||
load(
|
||||
":private/haskell_impl.bzl",
|
||||
_haskell_library_impl = "haskell_library_impl",
|
||||
)
|
||||
load("@bazel_skylib//lib:paths.bzl", "paths")
|
||||
load(
|
||||
"@io_tweag_rules_haskell//haskell:providers.bzl",
|
||||
"HaskellInfo",
|
||||
"HaskellLibraryInfo",
|
||||
"HaskellProtobufInfo",
|
||||
)
|
||||
|
||||
def _capitalize_first_letter(c):
|
||||
"""Capitalize the first letter of the input. Unlike the built-in
|
||||
`capitalize()` method, doesn't lower-case the other characters. This helps
|
||||
mimic the behavior of `proto-lens-protoc`, which turns `Foo/Bar/BAZ.proto`
|
||||
into `Foo/Bar/BAZ.hs` (rather than `Foo/Bar/Baz.hs`).
|
||||
|
||||
Args:
|
||||
c: A non-empty string word.
|
||||
|
||||
Returns:
|
||||
The input with the first letter upper-cased.
|
||||
"""
|
||||
return c[0].capitalize() + c[1:]
|
||||
|
||||
def _camel_case(comp):
|
||||
"""Camel-case the input string, preserving any existing capital letters.
|
||||
"""
|
||||
|
||||
# Split on both "-" and "_", matching the behavior of proto-lens-protoc.
|
||||
# Be sure to ignore any empty segments from input with leading or trailing
|
||||
# delimiters.
|
||||
return "".join([
|
||||
_capitalize_first_letter(c2)
|
||||
for c1 in comp.split("_")
|
||||
for c2 in c1.split("-")
|
||||
if len(c2) > 0
|
||||
])
|
||||
|
||||
def _proto_lens_output_file(path):
|
||||
"""The output file from `proto-lens-protoc` when run on the given `path`.
|
||||
"""
|
||||
|
||||
path = path[:-len(".proto")]
|
||||
result = "/".join([_camel_case(p) for p in path.split("/")]) + ".hs"
|
||||
|
||||
return "Proto/" + result
|
||||
|
||||
def _proto_lens_fields_file(path):
|
||||
"""The fields file from `proto-lens-protoc` when run on the given `path`.
|
||||
"""
|
||||
|
||||
path = path[:-len(".proto")]
|
||||
result = "/".join([_camel_case(p) for p in path.split("/")]) + "_Fields.hs"
|
||||
|
||||
return "Proto/" + result
|
||||
|
||||
def _proto_path(proto, proto_source_roots):
|
||||
"""A path to the proto file which matches any import statements."""
|
||||
proto_path = proto.path
|
||||
for p in proto_source_roots:
|
||||
if proto_path.startswith(p):
|
||||
return paths.relativize(proto_path, p)
|
||||
|
||||
return paths.relativize(
|
||||
proto_path,
|
||||
paths.join(proto.root.path, proto.owner.workspace_root),
|
||||
)
|
||||
|
||||
def _haskell_proto_aspect_impl(target, ctx):
|
||||
pb = ctx.toolchains["@io_tweag_rules_haskell//protobuf:toolchain"].tools
|
||||
|
||||
args = ctx.actions.args()
|
||||
|
||||
src_prefix = paths.join(
|
||||
ctx.label.workspace_root,
|
||||
ctx.label.package,
|
||||
)
|
||||
|
||||
args.add("--plugin=protoc-gen-haskell=" + pb.plugin.path)
|
||||
|
||||
hs_files = []
|
||||
inputs = []
|
||||
|
||||
direct_proto_paths = [target.proto.proto_source_root]
|
||||
transitive_proto_paths = target.proto.transitive_proto_path
|
||||
|
||||
args.add_all([
|
||||
"-I{0}={1}".format(_proto_path(s, transitive_proto_paths), s.path)
|
||||
for s in target.proto.transitive_sources.to_list()
|
||||
])
|
||||
|
||||
inputs.extend(target.proto.transitive_sources.to_list())
|
||||
|
||||
for src in target.proto.direct_sources:
|
||||
inputs.append(src)
|
||||
|
||||
# As with the native rules, require the .proto file to be in the same
|
||||
# Bazel package as the proto_library rule. This allows us to put the
|
||||
# output .hs file next to the input .proto file. Unfortunately Skylark
|
||||
# doesn't let us check the package of the file directly, so instead we
|
||||
# just look at its short_path and rely on the proto_library rule itself
|
||||
# to check for consistency. We use the file's path rather than its
|
||||
# dirname/basename in case it's in a subdirectory; for example, if the
|
||||
# proto_library rule is in "foo/BUILD" but the .proto file is
|
||||
# "foo/bar/baz.proto".
|
||||
|
||||
if not src.path.startswith(paths.join(src.root.path, src_prefix)):
|
||||
fail("Mismatch between rule context " + str(ctx.label.package) +
|
||||
" and source file " + src.short_path)
|
||||
if src.basename[-6:] != ".proto":
|
||||
fail("bad extension for proto file " + src)
|
||||
|
||||
args.add(src.path)
|
||||
hs_files.append(ctx.actions.declare_file(
|
||||
_proto_lens_output_file(
|
||||
_proto_path(src, direct_proto_paths),
|
||||
),
|
||||
))
|
||||
hs_files.append(ctx.actions.declare_file(
|
||||
_proto_lens_fields_file(
|
||||
_proto_path(src, direct_proto_paths),
|
||||
),
|
||||
))
|
||||
|
||||
args.add_all([
|
||||
"--proto_path=" + target.proto.proto_source_root,
|
||||
"--haskell_out=no-runtime:" + paths.join(
|
||||
hs_files[0].root.path,
|
||||
src_prefix,
|
||||
),
|
||||
])
|
||||
|
||||
ctx.actions.run(
|
||||
inputs = depset([pb.protoc, pb.plugin] + inputs),
|
||||
outputs = hs_files,
|
||||
mnemonic = "HaskellProtoc",
|
||||
executable = pb.protoc,
|
||||
arguments = [args],
|
||||
)
|
||||
|
||||
patched_attrs = {
|
||||
"compiler_flags": [],
|
||||
"src_strip_prefix": "",
|
||||
"repl_interpreted": True,
|
||||
"repl_ghci_args": [],
|
||||
"version": "",
|
||||
"linkstatic": False,
|
||||
"_ghci_script": ctx.attr._ghci_script,
|
||||
"_ghci_repl_wrapper": ctx.attr._ghci_repl_wrapper,
|
||||
"hidden_modules": [],
|
||||
"exports": {},
|
||||
"name": "proto-autogen-" + ctx.rule.attr.name,
|
||||
"srcs": hs_files,
|
||||
"deps": ctx.rule.attr.deps +
|
||||
ctx.toolchains["@io_tweag_rules_haskell//protobuf:toolchain"].deps,
|
||||
"prebuilt_dependencies": ctx.toolchains["@io_tweag_rules_haskell//protobuf:toolchain"].prebuilt_deps,
|
||||
"plugins": [],
|
||||
"_cc_toolchain": ctx.attr._cc_toolchain,
|
||||
}
|
||||
|
||||
patched_ctx = struct(
|
||||
actions = ctx.actions,
|
||||
attr = struct(**patched_attrs),
|
||||
bin_dir = ctx.bin_dir,
|
||||
disabled_features = ctx.rule.attr.features,
|
||||
executable = struct(
|
||||
_ls_modules = ctx.executable._ls_modules,
|
||||
),
|
||||
# Necessary for CC interop (see cc.bzl).
|
||||
features = ctx.rule.attr.features,
|
||||
file = ctx.file,
|
||||
files = struct(
|
||||
srcs = hs_files,
|
||||
_cc_toolchain = ctx.files._cc_toolchain,
|
||||
extra_srcs = depset(),
|
||||
),
|
||||
genfiles_dir = ctx.genfiles_dir,
|
||||
label = ctx.label,
|
||||
toolchains = ctx.toolchains,
|
||||
var = ctx.var,
|
||||
)
|
||||
|
||||
# TODO this pattern match is very brittle. Let's not do this. The
|
||||
# order should match the order in the return value expression in
|
||||
# haskell_library_impl().
|
||||
[hs_info, cc_info, coverage_info, default_info, library_info] = _haskell_library_impl(patched_ctx)
|
||||
|
||||
return [
|
||||
cc_info, # CcInfo
|
||||
hs_info, # HaskellInfo
|
||||
library_info, # HaskellLibraryInfo
|
||||
# We can't return DefaultInfo here because target already provides that.
|
||||
HaskellProtobufInfo(files = default_info.files),
|
||||
]
|
||||
|
||||
_haskell_proto_aspect = aspect(
|
||||
_haskell_proto_aspect_impl,
|
||||
attr_aspects = ["deps"],
|
||||
attrs = {
|
||||
"_ghci_script": attr.label(
|
||||
allow_single_file = True,
|
||||
default = Label("@io_tweag_rules_haskell//haskell:assets/ghci_script"),
|
||||
),
|
||||
"_ghci_repl_wrapper": attr.label(
|
||||
allow_single_file = True,
|
||||
default = Label("@io_tweag_rules_haskell//haskell:private/ghci_repl_wrapper.sh"),
|
||||
),
|
||||
"_ls_modules": attr.label(
|
||||
executable = True,
|
||||
cfg = "host",
|
||||
default = Label("@io_tweag_rules_haskell//haskell:ls_modules"),
|
||||
),
|
||||
"_cc_toolchain": attr.label(
|
||||
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
|
||||
),
|
||||
},
|
||||
toolchains = [
|
||||
"@io_tweag_rules_haskell//haskell:toolchain",
|
||||
"@io_tweag_rules_haskell//protobuf:toolchain",
|
||||
],
|
||||
)
|
||||
|
||||
def _haskell_proto_library_impl(ctx):
|
||||
dep = ctx.attr.deps[0] # FIXME
|
||||
return [
|
||||
dep[CcInfo],
|
||||
dep[HaskellInfo],
|
||||
dep[HaskellLibraryInfo],
|
||||
DefaultInfo(files = dep[HaskellProtobufInfo].files),
|
||||
]
|
||||
|
||||
haskell_proto_library = rule(
|
||||
_haskell_proto_library_impl,
|
||||
attrs = {
|
||||
"deps": attr.label_list(
|
||||
mandatory = True,
|
||||
allow_files = False,
|
||||
aspects = [_haskell_proto_aspect],
|
||||
doc = "List of `proto_library` targets to use for generation.",
|
||||
),
|
||||
},
|
||||
toolchains = [
|
||||
"@io_tweag_rules_haskell//haskell:toolchain",
|
||||
"@io_tweag_rules_haskell//protobuf:toolchain",
|
||||
],
|
||||
)
|
||||
|
||||
"""Generate Haskell library allowing to use protobuf definitions with help
|
||||
of [`proto-lens`](https://github.com/google/proto-lens#readme).
|
||||
|
||||
Example:
|
||||
```bzl
|
||||
proto_library(
|
||||
name = "foo_proto",
|
||||
srcs = ["foo.proto"],
|
||||
)
|
||||
|
||||
haskell_proto_library(
|
||||
name = "foo_haskell_proto",
|
||||
deps = [":foo_proto"],
|
||||
)
|
||||
```
|
||||
|
||||
`haskell_proto_library` targets require `haskell_proto_toolchain` to be
|
||||
registered.
|
||||
"""
|
||||
|
||||
def _protobuf_toolchain_impl(ctx):
|
||||
if ctx.attr.prebuilt_deps:
|
||||
print("""The attribute 'prebuilt_deps' has been deprecated,
|
||||
use the 'deps' attribute instead.
|
||||
""")
|
||||
|
||||
return [
|
||||
platform_common.ToolchainInfo(
|
||||
name = ctx.label.name,
|
||||
tools = struct(
|
||||
plugin = ctx.executable.plugin,
|
||||
protoc = ctx.executable.protoc,
|
||||
),
|
||||
deps = ctx.attr.deps,
|
||||
prebuilt_deps = ctx.attr.prebuilt_deps,
|
||||
),
|
||||
]
|
||||
|
||||
_protobuf_toolchain = rule(
|
||||
_protobuf_toolchain_impl,
|
||||
attrs = {
|
||||
"protoc": attr.label(
|
||||
executable = True,
|
||||
cfg = "host",
|
||||
allow_single_file = True,
|
||||
mandatory = True,
|
||||
doc = "protoc compiler",
|
||||
),
|
||||
"plugin": attr.label(
|
||||
executable = True,
|
||||
cfg = "host",
|
||||
allow_single_file = True,
|
||||
mandatory = True,
|
||||
doc = "proto-lens-protoc plugin for protoc",
|
||||
),
|
||||
"deps": attr.label_list(
|
||||
doc = "List of other Haskell libraries to be linked to protobuf libraries.",
|
||||
),
|
||||
"prebuilt_deps": attr.string_list(
|
||||
doc = "Non-Bazel supplied Cabal dependencies for protobuf libraries.",
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def haskell_proto_toolchain(
|
||||
name,
|
||||
plugin,
|
||||
deps = [],
|
||||
prebuilt_deps = [],
|
||||
protoc = Label("@com_google_protobuf//:protoc"),
|
||||
**kwargs):
|
||||
"""Declare a Haskell protobuf toolchain.
|
||||
|
||||
You need at least one of these declared somewhere in your `BUILD` files
|
||||
for the `haskell_proto_library` rules to work. Once declared, you then
|
||||
need to *register* the toolchain using `register_toolchains` in your
|
||||
`WORKSPACE` file (see example below).
|
||||
|
||||
Example:
|
||||
|
||||
In a `BUILD` file:
|
||||
|
||||
```bzl
|
||||
haskell_proto_toolchain(
|
||||
name = "protobuf-toolchain",
|
||||
protoc = "@com_google_protobuf//:protoc",
|
||||
plugin = "@hackage-proto-lens-protoc//:bin/proto-lens-protoc",
|
||||
prebuilt_deps = [
|
||||
"base",
|
||||
"bytestring",
|
||||
"containers",
|
||||
"data-default-class",
|
||||
"lens-family",
|
||||
"lens-labels",
|
||||
"proto-lens",
|
||||
"text",
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
The `prebuilt_deps` and `deps` arguments allow to specify Haskell
|
||||
libraries to use to compile the auto-generated source files.
|
||||
|
||||
In `WORKSPACE` you could have something like this:
|
||||
|
||||
```bzl
|
||||
http_archive(
|
||||
name = "com_google_protobuf",
|
||||
sha256 = "cef7f1b5a7c5fba672bec2a319246e8feba471f04dcebfe362d55930ee7c1c30",
|
||||
strip_prefix = "protobuf-3.5.0",
|
||||
urls = ["https://github.com/google/protobuf/archive/v3.5.0.zip"],
|
||||
)
|
||||
|
||||
nixpkgs_package(
|
||||
name = "protoc_gen_haskell",
|
||||
repository = "@nixpkgs",
|
||||
attribute_path = "haskell.packages.ghc822.proto-lens-protoc
|
||||
)
|
||||
|
||||
register_toolchains(
|
||||
"//tests:ghc", # assuming you called your Haskell toolchain "ghc"
|
||||
"//tests:protobuf-toolchain",
|
||||
)
|
||||
```
|
||||
"""
|
||||
impl_name = name + "-impl"
|
||||
_protobuf_toolchain(
|
||||
name = impl_name,
|
||||
plugin = plugin,
|
||||
deps = deps,
|
||||
prebuilt_deps = prebuilt_deps,
|
||||
protoc = protoc,
|
||||
visibility = ["//visibility:public"],
|
||||
**kwargs
|
||||
)
|
||||
|
||||
native.toolchain(
|
||||
name = name,
|
||||
toolchain_type = "@io_tweag_rules_haskell//protobuf:toolchain",
|
||||
toolchain = ":" + impl_name,
|
||||
exec_compatible_with = [
|
||||
"@bazel_tools//platforms:x86_64",
|
||||
],
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue