aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/build_rules/rust/rust.bzl
diff options
context:
space:
mode:
Diffstat (limited to 'tools/build_rules/rust/rust.bzl')
-rw-r--r--tools/build_rules/rust/rust.bzl315
1 files changed, 241 insertions, 74 deletions
diff --git a/tools/build_rules/rust/rust.bzl b/tools/build_rules/rust/rust.bzl
index 943c6c73a3..ace888949c 100644
--- a/tools/build_rules/rust/rust.bzl
+++ b/tools/build_rules/rust/rust.bzl
@@ -12,13 +12,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+"""Rust rules for Bazel"""
+
RUST_FILETYPE = FileType([".rs"])
A_FILETYPE = FileType([".a"])
+# Used by rust_docs
+HTML_MD_FILETYPE = FileType([".html", ".md"])
+CSS_FILETYPE = FileType([".css"])
+
+ZIP_PATH = "/usr/bin/zip"
+
def _relative(src_path, dest_path):
- """
- Returns the relative path from src_path to dest_path
- """
+ """Returns the relative path from src_path to dest_path."""
src_parts = src_path.split("/")
dest_parts = dest_path.split("/")
n = 0
@@ -45,11 +51,25 @@ def _create_setup_cmd(lib, deps_dir):
deps_dir + "/" + lib.basename + "\n"
)
-# TODO(dzc): rust_binary should not be able to depend on cc_library
-def _setup_deps(deps, name, working_dir):
+def _setup_deps(deps, name, working_dir, is_library=False):
"""
Walks through dependencies and constructs the necessary commands for linking
to all the necessary dependencies.
+
+ Args:
+ deps: List of Labels containing deps from ctx.attr.deps.
+ name: Name of the current target.
+ working_dir: The output directory for the current target's outputs.
+ is_library: True the current target is a rust_library target, False
+ otherwise.
+
+ Returns:
+ Returns a struct containing the following fields:
+ libs:
+ transitive_libs:
+ setup_cmd:
+ search_flags:
+ link_flags:
"""
deps_dir = working_dir + "/" + name + ".deps"
setup_cmd = ["rm -rf " + deps_dir + "; mkdir " + deps_dir + "\n"]
@@ -63,21 +83,21 @@ def _setup_deps(deps, name, working_dir):
link_flags = []
for dep in deps:
if hasattr(dep, "rust_lib"):
+ # This dependency is a rust_library
libs += [dep.rust_lib]
- transitive_libs += [dep.rust_lib]
- symlinked_libs += [dep.rust_lib]
+ transitive_libs += [dep.rust_lib] + dep.transitive_libs
+ symlinked_libs += [dep.rust_lib] + dep.transitive_libs
link_flags += [(
"--extern " + dep.label.name + "=" +
deps_dir + "/" + dep.rust_lib.basename
)]
has_rlib = True
- if hasattr(dep, "transitive_libs"):
- transitive_libs += dep.transitive_libs
- symlinked_libs += dep.transitive_libs
+ elif hasattr(dep, "cc"):
+ if not is_library:
+ fail("Only rust_library targets can depend on cc_library")
- # If this rule depends on a cc_library
- if hasattr(dep, "cc"):
+ # This dependency is a cc_library
native_libs = A_FILETYPE.filter(dep.cc.libs)
libs += native_libs
transitive_libs += native_libs
@@ -85,22 +105,26 @@ def _setup_deps(deps, name, working_dir):
link_flags += ["-l static=" + dep.label.name]
has_native = True
+ else:
+ fail(("rust_library" if is_library else "rust_binary and rust_test") +
+ " targets can only depend on rust_library " +
+ ("or cc_library " if is_library else "") + "targets")
+
for symlinked_lib in symlinked_libs:
setup_cmd += [_create_setup_cmd(symlinked_lib, deps_dir)]
search_flags = []
if has_rlib:
- search_flags += ["-L dependency=" + deps_dir]
+ search_flags += ["-L dependency=%s" % deps_dir]
if has_native:
- search_flags += ["-L native=" + deps_dir]
+ search_flags += ["-L native=%s" % deps_dir]
return struct(
libs = list(libs),
transitive_libs = list(transitive_libs),
setup_cmd = setup_cmd,
search_flags = search_flags,
- link_flags = link_flags,
- )
+ link_flags = link_flags)
def _get_features_flags(features):
"""
@@ -109,19 +133,37 @@ def _get_features_flags(features):
"""
features_flags = []
for feature in features:
- features_flags += [" --cfg feature=\\\"" + feature + "\\\""]
+ features_flags += ["--cfg feature=\\\"%s\\\"" % feature]
return features_flags
+def _rust_toolchain(ctx):
+ return struct(
+ rustc_path = ctx.file._rustc.path,
+ rustc_lib_path = ctx.files._rustc_lib[0].dirname,
+ rustlib_path = ctx.files._rustlib[0].dirname,
+ rustdoc_path = ctx.file._rustdoc.path)
+
def _build_rustc_command(ctx, crate_type, src, output_dir, depinfo,
extra_flags=[]):
- """
- Builds the rustc command
+ """Builds the rustc command.
+
+ Constructs the rustc command used to build the current target.
+
+ Args:
+ ctx: The ctx object for the current target.
+ crate_type: The type of crate to build ("lib" or "bin")
+ src: The path to the crate root source file ("lib.rs" or "main.rs")
+ output_dir: The output directory for the target.
+ depinfo: Struct containing information about dependencies as returned by
+ _setup_deps
+ extra_flags: Additional command line flags.
+
+ Return:
+ String containing the rustc command.
"""
# Paths to the Rust compiler and standard libraries.
- rustc_path = ctx.file._rustc.path
- rustc_lib_path = ctx.files._rustc_lib[0].dirname
- rustlib_path = ctx.files._rustlib[0].dirname
+ toolchain = _rust_toolchain(ctx)
# Paths to cc (for linker) and ar
cpp_fragment = ctx.fragments.cpp
@@ -141,46 +183,49 @@ def _build_rustc_command(ctx, crate_type, src, output_dir, depinfo,
return " ".join([
"set -e;",
" ".join(depinfo.setup_cmd),
- "LD_LIBRARY_PATH=" + rustc_lib_path,
- "DYLD_LIBRARY_PATH=" + rustc_lib_path,
- rustc_path + " " + src,
- "--crate-name " + ctx.label.name,
- "--crate-type " + crate_type,
- "-g",
+ "LD_LIBRARY_PATH=%s" % toolchain.rustc_lib_path,
+ "DYLD_LIBRARY_PATH=%s" % toolchain.rustc_lib_path,
+ toolchain.rustc_path,
+ src,
+ "--crate-name %s" % ctx.label.name,
+ "--crate-type %s" % crate_type,
+ "-C opt-level=3",
"--codegen ar=%s" % ar,
"--codegen linker=%s" % cc,
- "-L all=" + rustlib_path,
+ "-L all=%s" % toolchain.rustlib_path,
" ".join(extra_flags),
" ".join(features_flags),
- "--out-dir " + output_dir,
+ "--out-dir %s" % output_dir,
"--emit=dep-info,link",
" ".join(depinfo.search_flags),
" ".join(depinfo.link_flags),
" ".join(ctx.attr.rustc_flags),
])
+def _find_crate_root_src(srcs, file_names=["lib.rs"]):
+ """Finds the source file for the crate root."""
+ for src in srcs:
+ if src.basename in file_names:
+ return src.path
+ fail("No %s source file found." % " or ".join(file_names), "srcs")
+
def _rust_library_impl(ctx):
"""
Implementation for rust_library Skylark rule.
"""
# Find lib.rs
- srcs = ctx.files.srcs
- lib_rs = None
- crate_name_rs = ctx.label.name + ".rs"
- for src in srcs:
- if src.basename == "lib.rs" or src.basename == crate_name_rs:
- lib_rs = src.path
-
- if not lib_rs:
- fail("No lib.rs or source file matching crate name found.")
+ lib_rs = _find_crate_root_src(ctx.files.srcs)
# Output library
rust_lib = ctx.outputs.rust_lib
output_dir = rust_lib.dirname
# Dependencies
- depinfo = _setup_deps(ctx.attr.deps, ctx.label.name, output_dir)
+ depinfo = _setup_deps(ctx.attr.deps,
+ ctx.label.name,
+ output_dir,
+ is_library=True)
# Build rustc command
cmd = _build_rustc_command(
@@ -191,64 +236,74 @@ def _rust_library_impl(ctx):
depinfo = depinfo)
# Compile action.
+ compile_inputs = (
+ ctx.files.srcs +
+ ctx.files.data +
+ depinfo.libs +
+ [ctx.file._rustc] +
+ ctx.files._rustc_lib +
+ ctx.files._rustlib)
+
ctx.action(
- inputs = srcs + ctx.files.data + depinfo.libs + [ctx.file._rustc] +
- ctx.files._rustc_lib + ctx.files._rustlib,
+ inputs = compile_inputs,
outputs = [rust_lib],
mnemonic = 'Rustc',
command = cmd,
use_default_shell_env = True,
- progress_message = "Compiling Rust library " + ctx.label.name
- )
+ progress_message = ("Compiling Rust library %s (%d files)"
+ % (ctx.label.name, len(ctx.files.srcs))))
return struct(
files = set([rust_lib]),
+ rust_srcs = ctx.files.srcs,
+ rust_deps = ctx.attr.deps,
transitive_libs = depinfo.transitive_libs,
- rust_lib = rust_lib,
- )
+ rust_lib = rust_lib)
def _rust_binary_impl_common(ctx, extra_flags = []):
- """
- Implementation for rust_binary Skylark rule.
- """
+ """Implementation for rust_binary Skylark rule."""
# Find main.rs.
- srcs = ctx.files.srcs
- main_rs = None
- crate_name_rs = ctx.label.name + ".rs"
- for src in srcs:
- if src.basename == "main.rs" or src.basename == crate_name_rs:
- main_rs = src.path
-
- if not main_rs:
- fail("No main.rs or source file matching crate name found.")
+ main_rs = _find_crate_root_src(ctx.files.srcs, ["main.rs"])
# Output binary
rust_binary = ctx.outputs.executable
output_dir = rust_binary.dirname
# Dependencies
- depinfo = _setup_deps(ctx.attr.deps, ctx.label.name, output_dir)
+ depinfo = _setup_deps(ctx.attr.deps,
+ ctx.label.name,
+ output_dir,
+ is_library=False)
# Build rustc command.
- cmd = _build_rustc_command(
- ctx = ctx,
- crate_type = "bin",
- src = main_rs,
- output_dir = output_dir,
- depinfo = depinfo,
- extra_flags = extra_flags)
+ cmd = _build_rustc_command(ctx = ctx,
+ crate_type = "bin",
+ src = main_rs,
+ output_dir = output_dir,
+ depinfo = depinfo,
+ extra_flags = extra_flags)
# Compile action.
+ compile_inputs = (
+ ctx.files.srcs +
+ ctx.files.data +
+ depinfo.libs +
+ [ctx.file._rustc] +
+ ctx.files._rustc_lib +
+ ctx.files._rustlib)
+
ctx.action(
- inputs = srcs + ctx.files.data + depinfo.libs + [ctx.file._rustc] +
- ctx.files._rustc_lib + ctx.files._rustlib,
+ inputs = compile_inputs,
outputs = [rust_binary],
mnemonic = 'Rustc',
command = cmd,
use_default_shell_env = True,
- progress_message = "Compiling Rust binary " + ctx.label.name
- )
+ progress_message = ("Compiling Rust binary %s (%d files)"
+ % (ctx.label.name, len(ctx.files.srcs))))
+
+ return struct(rust_srcs = ctx.files.srcs,
+ rust_deps = ctx.attr.deps)
def _rust_binary_impl(ctx):
"""
@@ -258,16 +313,101 @@ def _rust_binary_impl(ctx):
def _rust_test_impl(ctx):
"""
- Implementation for rust_test Skylark rule.
+ Implementation for rust_test and rust_bench_test Skylark rules.
"""
return _rust_binary_impl_common(ctx, ["--test"])
+def _build_rustdoc_flags(ctx):
+ """Collects the rustdoc flags."""
+ doc_flags = []
+ doc_flags += [
+ "--markdown-css %s" % css.path for css in ctx.files.markdown_css]
+ if hasattr(ctx.file, "html_in_header"):
+ doc_flags += ["--html-in-header %s" % ctx.file.html_in_header.path]
+ if hasattr(ctx.file, "html_before_content"):
+ doc_flags += ["--html-before-content %s" %
+ ctx.file.html_before_content.path]
+ if hasattr(ctx.file, "html_after_content"):
+ doc_flags += ["--html-after-content %s"]
+ return doc_flags
+
+def _rust_docs_impl(ctx):
+ """Implementation of the rust_docs rule."""
+ rust_doc_zip = ctx.outputs.rust_doc_zip
+
+ # Gather attributes about the rust_library target to generated rustdocs for.
+ target = struct(name = ctx.attr.dep.label.name,
+ srcs = ctx.attr.dep.rust_srcs,
+ deps = ctx.attr.dep.rust_deps)
+
+ # Find lib.rs
+ lib_rs = _find_crate_root_src(target.srcs, ["lib.rs", "main.rs"])
+
+ # Dependencies
+ output_dir = rust_doc_zip.dirname
+ depinfo = _setup_deps(target.deps,
+ target.name,
+ output_dir,
+ is_library=False)
+
+ # Rustdoc flags.
+ doc_flags = _build_rustdoc_flags(ctx)
+
+ # Build rustdoc command.
+ toolchain = _rust_toolchain(ctx)
+ docs_dir = rust_doc_zip.dirname + "/_rust_docs"
+ doc_cmd = " ".join(
+ ["set -e"] +
+ depinfo.setup_cmd + [
+ "rm -rf %s;" % docs_dir,
+ "mkdir %s;" % docs_dir,
+ "LD_LIBRARY_PATH=%s" % toolchain.rustc_lib_path,
+ "DYLD_LIBRARY_PATH=%s" % toolchain.rustc_lib_path,
+ toolchain.rustdoc_path,
+ lib_rs,
+ "--crate-name %s" % target.name,
+ "-L all=%s" % toolchain.rustlib_path,
+ "-o %s" % docs_dir,
+ ] +
+ doc_flags +
+ depinfo.search_flags +
+ depinfo.link_flags + [
+ "&&",
+ "(cd %s" % docs_dir,
+ "&&",
+ ZIP_PATH,
+ "-qR",
+ rust_doc_zip.basename,
+ "$(find . -type f) )",
+ "&&",
+ "mv %s/%s %s" % (docs_dir, rust_doc_zip.basename, rust_doc_zip.path),
+ ])
+
+ # Rustdoc action
+ rustdoc_inputs = (target.srcs +
+ depinfo.libs +
+ [ctx.file._rustdoc] +
+ ctx.files._rustc_lib +
+ ctx.files._rustlib)
+
+ ctx.action(
+ inputs = rustdoc_inputs,
+ outputs = [rust_doc_zip],
+ mnemonic = 'Rustdoc',
+ command = doc_cmd,
+ use_default_shell_env = True,
+ progress_message = ("Generating rustdoc for %s (%d files)"
+ % (target.name, len(target.srcs))))
+
_rust_common_attrs = {
"srcs": attr.label_list(allow_files = RUST_FILETYPE),
"data": attr.label_list(allow_files = True, cfg = DATA_CFG),
"deps": attr.label_list(),
"crate_features": attr.string_list(),
"rustc_flags": attr.string_list(),
+}
+
+_rust_toolchain_attrs = {
"_rustc": attr.label(
default = Label("//tools/build_rules/rust:rustc"),
executable = True,
@@ -275,11 +415,15 @@ _rust_common_attrs = {
"_rustc_lib": attr.label(
default = Label("//tools/build_rules/rust:rustc_lib")),
"_rustlib": attr.label(default = Label("//tools/build_rules/rust:rustlib")),
+ "_rustdoc": attr.label(
+ default = Label("//tools/build_rules/rust:rustdoc"),
+ executable = True,
+ single_file = True),
}
rust_library = rule(
_rust_library_impl,
- attrs = _rust_common_attrs,
+ attrs = _rust_common_attrs + _rust_toolchain_attrs,
outputs = {
"rust_lib": "lib%{name}.rlib",
},
@@ -289,15 +433,38 @@ rust_library = rule(
rust_binary = rule(
_rust_binary_impl,
executable = True,
- attrs = _rust_common_attrs,
+ attrs = _rust_common_attrs + _rust_toolchain_attrs,
fragments = ["cpp"],
)
rust_test = rule(
_rust_test_impl,
executable = True,
- attrs = _rust_common_attrs,
+ attrs = _rust_common_attrs + _rust_toolchain_attrs,
test = True,
fragments = ["cpp"],
)
+rust_bench_test = rule(
+ _rust_test_impl,
+ executable = True,
+ attrs = _rust_common_attrs + _rust_toolchain_attrs,
+ test = True,
+ fragments = ["cpp"],
+)
+
+_rust_doc_attrs = {
+ "dep": attr.label(mandatory = True),
+ "markdown_css": attr.label_list(allow_files = CSS_FILETYPE),
+ "html_in_header": attr.label(allow_files = HTML_MD_FILETYPE),
+ "html_before_content": attr.label(allow_files = HTML_MD_FILETYPE),
+ "html_after_content": attr.label(allow_files = HTML_MD_FILETYPE),
+}
+
+rust_docs = rule(
+ _rust_docs_impl,
+ attrs = _rust_doc_attrs + _rust_toolchain_attrs,
+ outputs = {
+ "rust_doc_zip": "%{name}-docs.zip",
+ },
+)