Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add rule for bindgen (#102) #108

Merged
merged 30 commits into from
Feb 6, 2019
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f096c8c
Update bindgen.bzl
mfarrugi Jan 27, 2019
566f2fe
Get bindgen rule working locally.
mfarrugi Jan 27, 2019
ebca9c3
Add a test, too.
mfarrugi Jan 27, 2019
a120a99
Make the test a teensy bit less simple.
mfarrugi Jan 27, 2019
bf76c50
raze
mfarrugi Jan 27, 2019
40e5d52
Use raze (with hacks) for getting the bindgen binary.
mfarrugi Jan 27, 2019
7209c28
Make rustfmt optional and tidy it all up.
mfarrugi Jan 27, 2019
8054608
add note
mfarrugi Jan 27, 2019
ecb07c0
buildifier
mfarrugi Jan 27, 2019
c1baedb
Add docs and move stuff around.
mfarrugi Jan 27, 2019
725501c
tidy more
mfarrugi Jan 27, 2019
5070b0a
var renames
mfarrugi Jan 27, 2019
65c10d4
Add ubuntu 18.04 to .bazelci
mfarrugi Jan 27, 2019
2534cd5
Only run bindgen examples on ubuntu 18.04
mfarrugi Jan 27, 2019
4bbcb7a
revert tar path change
mfarrugi Feb 3, 2019
f558d5e
Move override for libloading out of generated crates.bzl
mfarrugi Feb 3, 2019
651627f
Copyrights to 2019.
mfarrugi Feb 3, 2019
e824e0d
Have macro forward kwargs to rust_library instead of rust_bindgen.
mfarrugi Feb 3, 2019
45592dd
Consolidate 'remove libstdc++ dep' todos.
mfarrugi Feb 3, 2019
485becc
Rename @clang to @bindgen_clang
mfarrugi Feb 3, 2019
25c3ca9
Add more broken stubs for stardoc...
mfarrugi Feb 3, 2019
11616c3
Commit hackily generated rust_bindgen_toolchain doc ...
mfarrugi Feb 3, 2019
243aa62
Fix commented out lines.
mfarrugi Feb 3, 2019
dfe5ded
Refer to ourselves as @io_bazel_rules_rust in WORKSPACE to be a littl…
mfarrugi Feb 3, 2019
20fd058
Add bindgen overview doc, make minor adjustments to proto docs to match.
mfarrugi Feb 3, 2019
55f0075
Replace 'new_local_repository' for libstdc++ with something that shou…
mfarrugi Feb 3, 2019
6c0b35b
take 2
mfarrugi Feb 3, 2019
0e7c86e
Apply suggestions from code review
damienmg Feb 5, 2019
f1f91e3
Address last review comments.
mfarrugi Feb 5, 2019
22ab34a
fix typo
mfarrugi Feb 6, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ load("//proto:repositories.bzl", "rust_proto_repositories")

rust_proto_repositories()

load("//bindgen:repositories.bzl", "rust_bindgen_repositories")

rust_bindgen_repositories()
mfarrugi marked this conversation as resolved.
Show resolved Hide resolved

# Stardoc and its dependencies
git_repository(
name = "io_bazel_skydoc",
Expand Down
19 changes: 19 additions & 0 deletions bindgen/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
load("@io_bazel_rules_rust//bindgen:bindgen.bzl", "rust_bindgen_toolchain")

package(default_visibility = ["//visibility:public"])

toolchain_type(name = "bindgen_toolchain")

rust_bindgen_toolchain(
name = "example-bindgen-toolchain-impl",
bindgen = "//bindgen/raze:cargo_bin_bindgen",
clang = "@clang//:clang",
libclang = "@clang//:libclang.so",
libstdcxx = "@local_linux//:libstdc++",
)

toolchain(
name = "example-bindgen-toolchain",
toolchain = "example-bindgen-toolchain-impl",
toolchain_type = "@io_bazel_rules_rust//bindgen:bindgen_toolchain",
)
167 changes: 167 additions & 0 deletions bindgen/bindgen.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Copyright 2015 The Bazel Authors. All rights reserved.
mfarrugi marked this conversation as resolved.
Show resolved Hide resolved
#
# 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
#
# http://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.

load("@io_bazel_rules_rust//rust:rust.bzl", "rust_library")

def rust_bindgen_library(name, header, cc_lib, **kwargs):
mfarrugi marked this conversation as resolved.
Show resolved Hide resolved
"""Generates a rust source file for `header`, and builds a rust_library."""

rust_bindgen(
name = name + "__bindgen",
header = header,
cc_lib = cc_lib,
**kwargs
)
rust_library(
name = name,
srcs = [name + "__bindgen.rs"],
deps = [cc_lib],
)

def _rust_bindgen_impl(ctx):
# nb. We can't grab the cc_library`s direct headers, so a header must be provided.
cc_lib = ctx.attr.cc_lib
header = ctx.file.header
if header not in cc_lib.cc.transitive_headers:
fail("Header {} is not in {}'s transitive headers.".format(ctx.attr.header, cc_lib), "header")

toolchain = ctx.toolchains["@io_bazel_rules_rust//bindgen:bindgen_toolchain"]
bindgen_bin = toolchain.bindgen
rustfmt_bin = toolchain.rustfmt
clang_bin = toolchain.clang
libclang = toolchain.libclang

# TODO: This should not need to be explicitly provided, see below TODO.
mfarrugi marked this conversation as resolved.
Show resolved Hide resolved
libstdcxx = toolchain.libstdcxx

# rustfmt is not where bindgen expects to find it, so we format manually
bindgen_args = ["--no-rustfmt-bindings"] + ctx.attr.bindgen_flags
clang_args = ctx.attr.clang_flags

output = ctx.outputs.out

# libclang should only have 1 output file
libclang_dir = libclang.cc.libs.to_list()[0].dirname
include_directories = depset(
[f.dirname for f in cc_lib.cc.transitive_headers],
)

if rustfmt_bin:
unformatted_output = ctx.actions.declare_file(output.basename + ".unformatted")
else:
unformatted_output = output

args = ctx.actions.args()
args.add_all(bindgen_args)
args.add(header.path)
args.add("--output", unformatted_output.path)
args.add("--")
args.add_all(include_directories, before_each = "-I")
args.add_all(clang_args)
ctx.actions.run(
executable = bindgen_bin,
inputs = depset(
[header],
transitive = [cc_lib.cc.transitive_headers, libclang.cc.libs, libstdcxx.cc.libs],
),
outputs = [unformatted_output],
mnemonic = "RustBindgen",
progress_message = "Generating bindings for {}..".format(header.path),
env = {
"RUST_BACKTRACE": "1",
"CLANG_PATH": clang_bin.path,
# Bindgen loads libclang at runtime, which also needs libstdc++, so we setup LD_LIBRARY_PATH
"LIBCLANG_PATH": libclang_dir,
# TODO: If libclang were built by bazel from source w/ properly specified dependencies, it
# would have the correct rpaths and not require this nor would this rule
# have a direct dependency on libstdc++.so
# This is unnecessary if the system libstdc++ suffices, which may not always be the case.
"LD_LIBRARY_PATH": ":".join([f.dirname for f in libstdcxx.cc.libs]),
},
arguments = [args],
tools = [clang_bin],
)

if rustfmt_bin:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is running rustfmt needed on generated sources? I meant, they are not made for being read anyway.

Copy link
Collaborator

@acmcarther acmcarther Jan 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've personally found it useful (in my own hacky version of this) when inspecting generated files. In my experience, bindgen can be a bit finnicky and require special params to configure generation. Its much easier to diagnose issues in a formatted file compared to a super long single line unformatted file.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beyond being useful, this is what bindgen does by default. I think it is best to keep parity with other tools whenever possible.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, can you just add a comment about it?

ctx.actions.run_shell(
inputs = depset([rustfmt_bin, unformatted_output]),
outputs = [output],
command = "{} --emit stdout --quiet {} > {}".format(rustfmt_bin.path, unformatted_output.path, output.path),
damienmg marked this conversation as resolved.
Show resolved Hide resolved
tools = [rustfmt_bin],
)

rust_bindgen = rule(
_rust_bindgen_impl,
doc = "Generates a rust source file from a cc_library and a header.",
attrs = {
"header": attr.label(
doc = "The .h file to generate bindings for.",
allow_single_file = True,
),
"cc_lib": attr.label(
doc = "The cc_library that contains the .h file. This is used to find the transitive includes.",
providers = ["cc"],
),
"bindgen_flags": attr.string_list(
doc = "Flags to pass directly to the bindgen executable. See https://rust-lang.github.io/rust-bindgen/ for details.",
),
"clang_flags": attr.string_list(
doc = "Flags to pass directly to the clang executable.",
),
},
outputs = {"out": "%{name}.rs"},
toolchains = [
"@io_bazel_rules_rust//bindgen:bindgen_toolchain",
],
)

def _rust_bindgen_toolchain_impl(ctx):
return platform_common.ToolchainInfo(
bindgen = ctx.executable.bindgen,
clang = ctx.executable.clang,
libclang = ctx.attr.libclang,
libstdcxx = ctx.attr.libstdcxx,
rustfmt = ctx.executable.rustfmt,
)

rust_bindgen_toolchain = rule(
_rust_bindgen_toolchain_impl,
doc = "The tools required for the `rust_bindgen` rule.",
attrs = {
"bindgen": attr.label(
doc = "The label of a `bindgen` executable.",
executable = True,
cfg = "host",
),
"rustfmt": attr.label(
doc = "The label of a `rustfmt` executable. If this is provided, generated sources will be formatted.",
executable = True,
cfg = "host",
mandatory = False,
),
"clang": attr.label(
doc = "The label of a `clang` executable.",
executable = True,
cfg = "host",
),
"libclang": attr.label(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am surprised you did not got hit by bazelbuild/bazel#6889. This will get shipped in the host configuration :(

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if @mfarrugi fully follows the issue, but I'm a bit confused.

Isn't host configuration the configuration we want to run bindgen in?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to run this at build-time on the host, so I don't follow what "shipped" means. (I also don't understand the issue :))

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to run this in host configuration please add cfg = "host". The default value of cfg is "target" but due to bazelbuild/bazel#6889 it somehow becomes the host configuration. This is a bug in Bazel.

doc = "A cc_library that provides bindgen's runtime dependency on libclang.",
providers = ["cc"],
),
"libstdcxx": attr.label(
doc = "A cc_library that satisfies libclang's libstdc++ dependency.",
providers = ["cc"],
),
},
)
14 changes: 14 additions & 0 deletions bindgen/clang.BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package(default_visibility = ["//visibility:public"])

sh_binary(
name = "clang",
srcs = ["bin/clang"],
data = glob(["lib/**"]),
)

cc_library(
name = "libclang.so",
srcs = [
"lib/libclang.so",
],
)
8 changes: 8 additions & 0 deletions bindgen/local_linux.BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package(default_visibility = ["//visibility:public"])

cc_library(
name = "libstdc++",
srcs = [
"libstdc++.so.6",
],
)
20 changes: 20 additions & 0 deletions bindgen/raze/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""
cargo-raze workspace build file.

DO NOT EDIT! Replaced on runs of cargo-raze
"""
package(default_visibility = ["//visibility:public"])

licenses([
"notice" # See individual crates for specific licenses
])
alias(
name = "bindgen",
actual = "@raze__bindgen__0_40_0//:bindgen",
)
alias(
# Extra aliased target, from raze configuration
# N.B.: The exact form of this is subject to change.
name = "cargo_bin_bindgen",
actual = "@raze__bindgen__0_40_0//:cargo_bin_bindgen",
)
23 changes: 23 additions & 0 deletions bindgen/raze/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[raze]
genmode = "Remote"
workspace_path = "//bindgen/raze"

[dependencies]
# TODO: Using "0.47.0" requires yet more overrides for building clang-sys '0.27.0'.
bindgen = "0.40.0"

[raze.crates.bindgen.'0.40.0']
gen_buildrs = true
extra_aliased_targets = ["cargo_bin_bindgen"]

# TODO: Un-patch crates.bzl
# See https://github.com/google/cargo-raze/issues/58
# [raze.crates.libloading.'0.5.0']
# custom_build_file_path = "//bindgen/raze:libloading.BUILD"

[package]
name = "fake_lib"
version = "0.0.1"

[lib]
path = "fake_lib.rs"
Loading