diff options
Diffstat (limited to 'tools/build_rules/rust/rust.bzl')
-rw-r--r-- | tools/build_rules/rust/rust.bzl | 263 |
1 files changed, 213 insertions, 50 deletions
diff --git a/tools/build_rules/rust/rust.bzl b/tools/build_rules/rust/rust.bzl index 69a54b1a12..5dc3c0b132 100644 --- a/tools/build_rules/rust/rust.bzl +++ b/tools/build_rules/rust/rust.bzl @@ -17,7 +17,9 @@ RUST_FILETYPE = FileType([".rs"]) A_FILETYPE = FileType([".a"]) -# Used by rust_docs +LIBRARY_CRATE_TYPES = ["lib", "rlib", "dylib", "staticlib"] + +# Used by rust_doc HTML_MD_FILETYPE = FileType([".html", ".md"]) CSS_FILETYPE = FileType([".css"]) @@ -143,8 +145,8 @@ def _rust_toolchain(ctx): 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=[]): +def _build_rustc_command(ctx, crate_name, crate_type, src, output_dir, + depinfo, rust_flags=[]): """Builds the rustc command. Constructs the rustc command used to build the current target. @@ -152,11 +154,10 @@ def _build_rustc_command(ctx, crate_type, src, output_dir, depinfo, 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") + src: The File object for 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. @@ -180,42 +181,61 @@ def _build_rustc_command(ctx, crate_type, src, output_dir, depinfo, # Construct features flags features_flags = _get_features_flags(ctx.attr.crate_features) - return " ".join([ - "set -e;", - " ".join(depinfo.setup_cmd), - "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=%s" % toolchain.rustlib_path, - " ".join(extra_flags), - " ".join(features_flags), - "--out-dir %s" % output_dir, - "--emit=dep-info,link", - " ".join(depinfo.search_flags), - " ".join(depinfo.link_flags), - " ".join(ctx.attr.rustc_flags), - ]) + return " ".join( + ["set -e;"] + + depinfo.setup_cmd + + [ + "LD_LIBRARY_PATH=%s" % toolchain.rustc_lib_path, + "DYLD_LIBRARY_PATH=%s" % toolchain.rustc_lib_path, + toolchain.rustc_path, + src.path, + "--crate-name %s" % crate_name, + "--crate-type %s" % crate_type, + "-C opt-level=3", + "--codegen ar=%s" % ar, + "--codegen linker=%s" % cc, + "-L all=%s" % toolchain.rustlib_path, + "--out-dir %s" % output_dir, + "--emit=dep-info,link", + ] + + features_flags + + rust_flags + + depinfo.search_flags + + depinfo.link_flags + + ctx.attr.rustc_flags) def _find_crate_root_src(srcs, file_names=["lib.rs"]): """Finds the source file for the crate root.""" + if len(srcs) == 1: + return srcs[0] for src in srcs: if src.basename in file_names: - return src.path + return src fail("No %s source file found." % " or ".join(file_names), "srcs") +def _crate_root_src(ctx, file_names=["lib.rs"]): + if ctx.file.crate_root == None: + return _find_crate_root_src(ctx.files.srcs, file_names) + else: + return ctx.file.crate_root + def _rust_library_impl(ctx): """ Implementation for rust_library Skylark rule. """ # Find lib.rs - lib_rs = _find_crate_root_src(ctx.files.srcs) + lib_rs = _crate_root_src(ctx) + + # Validate crate_type + crate_type = "" + if ctx.attr.crate_type != "": + if ctx.attr.crate_type not in LIBRARY_CRATE_TYPES: + fail("Invalid crate_type for rust_library. Allowed crate types are: %s" + % " ".join(LIBRARY_CRATE_TYPES), "crate_type") + crate_type += ctx.attr.crate_type + else: + crate_type += "lib" # Output library rust_lib = ctx.outputs.rust_lib @@ -230,7 +250,8 @@ def _rust_library_impl(ctx): # Build rustc command cmd = _build_rustc_command( ctx = ctx, - crate_type = "lib", + crate_name = ctx.label.name, + crate_type = crate_type, src = lib_rs, output_dir = output_dir, depinfo = depinfo) @@ -240,6 +261,7 @@ def _rust_library_impl(ctx): ctx.files.srcs + ctx.files.data + depinfo.libs + + depinfo.transitive_libs + [ctx.file._rustc] + ctx.files._rustc_lib + ctx.files._rustlib) @@ -255,16 +277,18 @@ def _rust_library_impl(ctx): return struct( files = set([rust_lib]), + crate_type = crate_type, + crate_root = lib_rs, rust_srcs = ctx.files.srcs, rust_deps = ctx.attr.deps, transitive_libs = depinfo.transitive_libs, rust_lib = rust_lib) -def _rust_binary_impl_common(ctx, extra_flags = []): +def _rust_binary_impl(ctx): """Implementation for rust_binary Skylark rule.""" # Find main.rs. - main_rs = _find_crate_root_src(ctx.files.srcs, ["main.rs"]) + main_rs = _crate_root_src(ctx, ["main.rs"]) # Output binary rust_binary = ctx.outputs.executable @@ -278,17 +302,18 @@ def _rust_binary_impl_common(ctx, extra_flags = []): # Build rustc command. cmd = _build_rustc_command(ctx = ctx, + crate_name = ctx.label.name, crate_type = "bin", src = main_rs, output_dir = output_dir, - depinfo = depinfo, - extra_flags = extra_flags) + depinfo = depinfo) # Compile action. compile_inputs = ( ctx.files.srcs + ctx.files.data + depinfo.libs + + depinfo.transitive_libs + [ctx.file._rustc] + ctx.files._rustc_lib + ctx.files._rustlib) @@ -303,19 +328,89 @@ def _rust_binary_impl_common(ctx, extra_flags = []): % (ctx.label.name, len(ctx.files.srcs)))) return struct(rust_srcs = ctx.files.srcs, + crate_root = main_rs, rust_deps = ctx.attr.deps) -def _rust_binary_impl(ctx): - """ - Implementation for rust_binary Skylark rule. +def _rust_test_common(ctx, test_binary): + """Builds a Rust test binary. + + Args: + ctx: The ctx object for the current target. + test_binary: The File object for the test binary. """ - return _rust_binary_impl_common(ctx) + output_dir = test_binary.dirname + + if len(ctx.attr.deps) == 1 and len(ctx.files.srcs) == 0: + # Target has a single dependency but no srcs. Build the test binary using + # the dependency's srcs. + dep = ctx.attr.deps[0] + crate_type = dep.crate_type if hasattr(dep, "crate_type") else "bin" + target = struct(name = dep.label.name, + srcs = dep.rust_srcs, + deps = dep.rust_deps, + crate_root = dep.crate_root, + crate_type = crate_type) + else: + # Target is a standalone crate. Build the test binary as its own crate. + target = struct(name = ctx.label.name, + srcs = ctx.files.srcs, + deps = ctx.attr.deps, + crate_root = _crate_root_src(ctx), + crate_type = "lib") + + # Get information about dependencies + depinfo = _setup_deps(target.deps, + target.name, + output_dir, + is_library=False) + + cmd = _build_rustc_command(ctx = ctx, + crate_name = test_binary.basename, + crate_type = target.crate_type, + src = target.crate_root, + output_dir = output_dir, + depinfo = depinfo, + rust_flags = ["--test"]) + + compile_inputs = (target.srcs + + depinfo.libs + + depinfo.transitive_libs + + [ctx.file._rustc] + + ctx.files._rustc_lib + + ctx.files._rustlib) + + ctx.action( + inputs = compile_inputs, + outputs = [test_binary], + mnemonic = "RustcTest", + command = cmd, + use_default_shell_env = True, + progress_message = ("Compiling Rust test %s (%d files)" + % (ctx.label.name, len(target.srcs)))) def _rust_test_impl(ctx): """ - Implementation for rust_test and rust_bench_test Skylark rules. + Implementation for rust_test Skylark rule. """ - return _rust_binary_impl_common(ctx, ["--test"]) + _rust_test_common(ctx, ctx.outputs.executable) + +def _rust_bench_test_impl(ctx): + """Implementation for the rust_bench_test Skylark rule.""" + rust_bench_test = ctx.outputs.executable + test_binary = ctx.new_file(ctx.configuration.bin_dir, + "%s_bin" % rust_bench_test.basename) + _rust_test_common(ctx, test_binary) + + ctx.file_action( + output = rust_bench_test, + content = " ".join([ + "#!/bin/bash\n", + "set -e\n", + "%s --bench\n" % test_binary.short_path]), + executable = True) + + runfiles = ctx.runfiles(files = [test_binary], collect_data = True) + return struct(runfiles = runfiles) def _build_rustdoc_flags(ctx): """Collects the rustdoc flags.""" @@ -331,19 +426,21 @@ def _build_rustdoc_flags(ctx): doc_flags += ["--html-after-content %s"] return doc_flags -def _rust_docs_impl(ctx): - """Implementation of the rust_docs rule.""" +def _rust_doc_impl(ctx): + """Implementation of the rust_doc 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) + deps = ctx.attr.dep.rust_deps, + crate_root = ctx.attr.dep.crate_root) # Find lib.rs - lib_rs = _find_crate_root_src(target.srcs, ["lib.rs", "main.rs"]) + lib_rs = (_find_crate_root_src(target.srcs, ["lib.rs", "main.rs"]) + if target.crate_root == None else target.crate_root) - # Dependencies + # Get information about dependencies output_dir = rust_doc_zip.dirname depinfo = _setup_deps(target.deps, target.name, @@ -364,7 +461,7 @@ def _rust_docs_impl(ctx): "LD_LIBRARY_PATH=%s" % toolchain.rustc_lib_path, "DYLD_LIBRARY_PATH=%s" % toolchain.rustc_lib_path, toolchain.rustdoc_path, - lib_rs, + lib_rs.path, "--crate-name %s" % target.name, "-L all=%s" % toolchain.rustlib_path, "-o %s" % docs_dir, @@ -399,8 +496,60 @@ def _rust_docs_impl(ctx): progress_message = ("Generating rustdoc for %s (%d files)" % (target.name, len(target.srcs)))) +def _rust_doc_test_impl(ctx): + """Implementation for the rust_doc_test rule.""" + rust_doc_test = ctx.outputs.executable + + # 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, + crate_root = ctx.attr.dep.crate_root) + + # Find lib.rs + lib_rs = (_find_crate_root_src(target.srcs, ["lib.rs", "main.rs"]) + if target.crate_root == None else target.crate_root) + + # Get information about dependencies + depinfo = _setup_deps(target.deps, + target.name, + working_dir=".", + is_library=False) + + # Construct rustdoc test command, which will be written to a shell script + # to be executed to run the test. + toolchain = _rust_toolchain(ctx) + doc_test_cmd = " ".join( + ["#!/bin/bash\n"] + + ["set -e\n"] + + depinfo.setup_cmd + + [ + "LD_LIBRARY_PATH=%s" % toolchain.rustc_lib_path, + "DYLD_LIBRARY_PATH=%s" % toolchain.rustc_lib_path, + toolchain.rustdoc_path, + lib_rs.path, + ] + + depinfo.search_flags + + depinfo.link_flags) + + ctx.file_action(output = rust_doc_test, + content = doc_test_cmd, + executable = True) + + doc_test_inputs = (target.srcs + + depinfo.libs + + depinfo.transitive_libs + + [ctx.file._rustdoc] + + ctx.files._rustc_lib + + ctx.files._rustlib) + + runfiles = ctx.runfiles(files = doc_test_inputs, collect_data = True) + return struct(runfiles = runfiles) + _rust_common_attrs = { "srcs": attr.label_list(allow_files = RUST_FILETYPE), + "crate_root": attr.label(allow_files = RUST_FILETYPE, + single_file = True), "data": attr.label_list(allow_files = True, cfg = DATA_CFG), "deps": attr.label_list(), "crate_features": attr.string_list(), @@ -421,9 +570,13 @@ _rust_toolchain_attrs = { single_file = True), } +_rust_library_attrs = _rust_common_attrs + { + "crate_type": attr.string(), +} + rust_library = rule( _rust_library_impl, - attrs = _rust_common_attrs + _rust_toolchain_attrs, + attrs = _rust_library_attrs + _rust_toolchain_attrs, outputs = { "rust_lib": "lib%{name}.rlib", }, @@ -446,25 +599,35 @@ rust_test = rule( ) rust_bench_test = rule( - _rust_test_impl, + _rust_bench_test_impl, executable = True, attrs = _rust_common_attrs + _rust_toolchain_attrs, test = True, fragments = ["cpp"], ) -_rust_doc_attrs = { +_rust_doc_common_attrs = { "dep": attr.label(mandatory = True), +} + +_rust_doc_attrs = _rust_doc_common_attrs + { "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, +rust_doc = rule( + _rust_doc_impl, attrs = _rust_doc_attrs + _rust_toolchain_attrs, outputs = { "rust_doc_zip": "%{name}-docs.zip", }, ) + +rust_doc_test = rule( + _rust_doc_test_impl, + attrs = _rust_doc_common_attrs + _rust_toolchain_attrs, + executable = True, + test = True, +) |