diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 3f29671c7e..d4ae79e261 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -11,6 +11,8 @@ tasks: ubuntu1604: build_targets: *default_linux_targets test_targets: *default_linux_targets + build_flags: + - "--@io_bazel_rules_rust//worker:use_worker=True" ubuntu1804: build_targets: *default_linux_targets test_targets: @@ -51,6 +53,8 @@ tasks: windows: build_flags: - "--enable_runfiles" # this is not enabled by default on windows and is necessary for the cargo build scripts + - "--worker_quit_after_build" # configuration changes that prompt a worker rebuild will fail if it's still running + - "--@io_bazel_rules_rust//worker:use_worker=True" windows_targets: &windows_targets - "--" # Allows negative patterns; hack for https://github.com/bazelbuild/continuous-integration/pull/245 - "@examples//..." diff --git a/docs/BUILD b/docs/BUILD index 973778ed46..fa1590ea27 100644 --- a/docs/BUILD +++ b/docs/BUILD @@ -18,6 +18,7 @@ bzl_library( "@io_bazel_rules_rust//proto:rules", "@io_bazel_rules_rust//rust:rules", "@io_bazel_rules_rust//wasm_bindgen:rules", + "@io_bazel_rules_rust//worker:rules", "@rules_proto//proto:rules", ], ) diff --git a/docs/index.md b/docs/index.md index 3c50f10699..9648237db6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -66,6 +66,32 @@ Similarly, `rustfmt_version` may also be configured: rust_repositories(rustfmt_version = "1.48.0") ``` +# Using Bazel Persistent Workers + +When building with Cargo, the Rust compiler stores incremental build products, and reuses them where possible to speed up subsequent builds. The Rust rules do not do this by default, which can mean small changes take longer to compile in Bazel than they do with Cargo. + +These rules come with experimental support for [incremental compilation](https://doc.rust-lang.org/edition-guide/rust-2018/the-compiler/incremental-compilation-for-faster-compiles.html). This is possible in Bazel by using a [Bazel Persistent Worker](https://docs.bazel.build/versions/master/persistent-workers.html) to invoke the Rust compiler, and direct its intermediate build files to a rustc-worker folder in your TEMP folder. + +You can enable the worker by passing +`--@io_bazel_rules_rust//worker:use_worker=True` on the command line, or by +placing the following into your .bazelrc file: + +``` +build --@io_bazel_rules_rust//worker:use_worker=True +build:windows --worker_quit_after_build +``` + +The second line is only required on Windows, and works around a file-locking issue. + +The incremental worker has received little testing so far, and there has been a +report of compilation failures after changing dependencies, which required a +manual purge of the /tmp/rustc-worker\* folders - so you may wish to avoid using +this in production for now. But it can mean a 2-3x speedup on short +edit/compile/repeat cycles, so you may feel the risks are worth it during +development. + +Because this feature is currently experimental, it may change or go away at any time. + ## External Dependencies Currently the most common approach to managing external dependencies is using diff --git a/proto/proto.bzl b/proto/proto.bzl index 00769f86fa..8dbd7b76d7 100644 --- a/proto/proto.bzl +++ b/proto/proto.bzl @@ -305,6 +305,7 @@ rust_proto_library = rule( "@io_bazel_rules_rust//proto:toolchain", "@io_bazel_rules_rust//rust:toolchain", "@bazel_tools//tools/cpp:toolchain_type", + "@io_bazel_rules_rust//worker:toolchain_type", ], doc = """\ Builds a Rust library crate from a set of `proto_library`s. @@ -384,6 +385,7 @@ rust_grpc_library = rule( "@io_bazel_rules_rust//proto:toolchain", "@io_bazel_rules_rust//rust:toolchain", "@bazel_tools//tools/cpp:toolchain_type", + "@io_bazel_rules_rust//worker:toolchain_type", ], doc = """\ Builds a Rust library crate from a set of `proto_library`s suitable for gRPC. diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl index a17ab9c4e4..41ae7e599a 100644 --- a/rust/private/rust.bzl +++ b/rust/private/rust.bzl @@ -507,6 +507,7 @@ rust_library = rule( toolchains = [ "@io_bazel_rules_rust//rust:toolchain", "@bazel_tools//tools/cpp:toolchain_type", + "@io_bazel_rules_rust//worker:toolchain_type", ], doc = """\ Builds a Rust library crate. @@ -597,6 +598,7 @@ rust_binary = rule( toolchains = [ "@io_bazel_rules_rust//rust:toolchain", "@bazel_tools//tools/cpp:toolchain_type", + "@io_bazel_rules_rust//worker:toolchain_type", ], doc = """\ Builds a Rust binary crate. @@ -695,6 +697,7 @@ rust_test = rule( toolchains = [ "@io_bazel_rules_rust//rust:toolchain", "@bazel_tools//tools/cpp:toolchain_type", + "@io_bazel_rules_rust//worker:toolchain_type", ], doc = """\ Builds a Rust test crate. @@ -843,6 +846,7 @@ rust_test_binary = rule( toolchains = [ "@io_bazel_rules_rust//rust:toolchain", "@bazel_tools//tools/cpp:toolchain_type", + "@io_bazel_rules_rust//worker:toolchain_type", ], doc = """\ Builds a Rust test binary, without marking this rule as a Bazel test. @@ -866,6 +870,7 @@ rust_benchmark = rule( toolchains = [ "@io_bazel_rules_rust//rust:toolchain", "@bazel_tools//tools/cpp:toolchain_type", + "@io_bazel_rules_rust//worker:toolchain_type", ], doc = """\ Builds a Rust benchmark test. diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index ff7ca979f2..733b7c15a1 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -382,7 +382,8 @@ def construct_arguments( build_flags_files, maker_path = None, aspect = False, - emit = ["dep-info", "link"]): + emit = ["dep-info", "link"], + use_worker = False): """Builds an Args object containing common rustc flags Args: @@ -402,6 +403,7 @@ def construct_arguments( maker_path (File): An optional clippy marker file aspect (bool): True if called in an aspect context. emit (list): Values for the --emit flag to rustc. + use_worker (bool): If True, sets up the arguments in a worker-compatible fashion Returns: tuple: A tuple of the following items @@ -416,6 +418,10 @@ def construct_arguments( # Wrapper args first args = ctx.actions.args() + if use_worker: + # Write the args to a param file that will be used by Bazel to send messages to the worker. + args.set_param_file_format("multiline") + args.use_param_file("@%s", use_always = True) if build_env_file != None: args.add("--env-file", build_env_file) @@ -563,6 +569,8 @@ def rustc_compile_action( - (DepInfo): The transitive dependencies of this crate. - (DefaultInfo): The output file for this crate, and its runfiles. """ + worker_binary = ctx.toolchains["@io_bazel_rules_rust//worker:toolchain_type"].worker_binary + dep_info, build_info = collect_deps( ctx.label, crate_info.deps, @@ -597,6 +605,7 @@ def rustc_compile_action( out_dir, build_env_file, build_flags_files, + use_worker = worker_binary != None, ) if hasattr(ctx.attr, "version") and ctx.attr.version != "0.0.0": @@ -604,13 +613,27 @@ def rustc_compile_action( else: formatted_version = "" + if worker_binary != None: + executable = worker_binary + tools = [ctx.executable._process_wrapper] + arguments = [ctx.executable._process_wrapper.path, toolchain.rustc.path, ctx.var["COMPILATION_MODE"], args] + execution_requirements = {"supports-workers": "1"} + else: + # Not all execution platforms support a worker. + executable = ctx.executable._process_wrapper + tools = [] + arguments = [args] + execution_requirements = {} + ctx.actions.run( - executable = ctx.executable._process_wrapper, + executable = executable, inputs = compile_inputs, outputs = [crate_info.output], + tools = tools, env = env, - arguments = [args], + arguments = arguments, mnemonic = "Rustc", + execution_requirements = execution_requirements, progress_message = "Compiling Rust {} {}{} ({} files)".format( crate_info.type, ctx.label.name, diff --git a/rust/repositories.bzl b/rust/repositories.bzl index 5eb4f25279..7ccf459416 100644 --- a/rust/repositories.bzl +++ b/rust/repositories.bzl @@ -142,6 +142,10 @@ def rust_repositories( edition = edition, ) + native.register_toolchains( + "@io_bazel_rules_rust//worker", + ) + def _check_version_valid(version, iso_date, param_prefix = ""): """Verifies that the provided rust version and iso_date make sense.""" diff --git a/worker/BUILD b/worker/BUILD new file mode 100644 index 0000000000..8e32b87c76 --- /dev/null +++ b/worker/BUILD @@ -0,0 +1,44 @@ +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +load("//worker:toolchain.bzl", "worker_toolchain") +load(":bootstrap.bzl", "rust_cargo_binary") +load("@bazel_skylib//rules:common_settings.bzl", "bool_flag") + +package(default_visibility = ["//visibility:public"]) + +exports_files([ + "repositories.bzl", +]) + +bzl_library( + name = "rules", + srcs = glob(["**/*.bzl"]), +) + +bool_flag( + name = "use_worker", + build_setting_default = False, +) + +toolchain_type(name = "toolchain_type") + +rust_cargo_binary( + name = "rustc-worker", + srcs = [ + "main.rs", + "Cargo.toml", + "Cargo.lock", + ] + glob(["lib/*.rs"]), + tags = ["manual"], +) + +worker_toolchain( + name = "worker_toolchain", + enabled = ":use_worker", + worker_binary = ":rustc-worker", +) + +toolchain( + name = "worker", + toolchain = ":worker_toolchain", + toolchain_type = ":toolchain_type", +) diff --git a/worker/Cargo.lock b/worker/Cargo.lock new file mode 100644 index 0000000000..d62f15482a --- /dev/null +++ b/worker/Cargo.lock @@ -0,0 +1,48 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" + +[[package]] +name = "protobuf" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70731852eec72c56d11226c8a5f96ad5058a3dab73647ca5f7ee351e464f2571" +dependencies = [ + "bytes", +] + +[[package]] +name = "rustc-worker" +version = "0.1.0" +dependencies = [ + "protobuf", +] diff --git a/worker/Cargo.toml b/worker/Cargo.toml new file mode 100644 index 0000000000..9a8e9a5de4 --- /dev/null +++ b/worker/Cargo.toml @@ -0,0 +1,18 @@ +[workspace] +# declare ourselves as a workspace so we don't break on Windows when the user +# workspace has a Cargo workspace + +[package] +name = "rustc-worker" +version = "0.1.0" +authors = ["Nikhil Marathe "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[[bin]] +name = "rustc-worker" +path = "main.rs" + +[dependencies] +protobuf = { version = "=2.8.2", features = ["with-bytes"] } diff --git a/worker/README.md b/worker/README.md new file mode 100644 index 0000000000..e669e6819f --- /dev/null +++ b/worker/README.md @@ -0,0 +1,25 @@ +# Rust Persistent Worker + +The Rust Persistent Worker is itself implemented in Rust. It is built by invoking Cargo in bootstrap.bzl. + +## Using the worker + +Place the following in your project's .bazelrc: + +``` +build --@io_bazel_rules_rust//worker:use_worker=True +build:windows --worker_quit_after_build +``` + +This code is still experimental, and has not had a lot of testing. It may +change or go away at any time. + +## Why is this built by invoking Cargo directly? + +Because the rust_binary() and similar rules depend on a worker toolchain (even a dummy one), we can't +use them to build the worker binary - it results in a cyclic dependency. So bootstrap.bzl calls +cargo to fetch the dependencies and build the worker. + +## How about rewriting the worker in C++? + +That is certainly an option! diff --git a/worker/bootstrap.bzl b/worker/bootstrap.bzl new file mode 100644 index 0000000000..303f7381f7 --- /dev/null +++ b/worker/bootstrap.bzl @@ -0,0 +1,106 @@ +""" +A simplified rust_binary implementation based on cargo so we can compile +the worker binary. + +If using this on Windows, it's a good idea to place the following in your .bazelrc: + +build:windows --worker_quit_after_build + +This will ensure workers quit at the end of a build. If they're allowed to continue +running until Bazel shuts down, configuration changes that prompt the worker to be +recompiled will fail, as we can't write to a running .exe file on Windows. +""" + +load("@io_bazel_rules_rust//rust:private/rustc.bzl", "get_cc_toolchain", "get_linker_and_args") + +def _rewrite_to_unix_path(path): + if not path: + return "" + drive = path[0] + return "/" + drive + path[2:].replace("\\", "/") + +def _rewrite_to_unix_paths(paths): + """Turn a semicolon-delimited set of Windows paths into their Unix equivalents.""" + return ":".join([_rewrite_to_unix_path(path) for path in paths.split(";")]) + +def _get_windows_env_vars(ctx): + """Get required PATH and LIB env vars on Windows. + + Returns (path, lib). + + We must prepend PATH to the standard run_shell() path so that MSVC's link.exe is found + instead of MSYS's. + + LIB must be defined for linking to succeed on Windows.""" + + cc_toolchain, feature_configuration = get_cc_toolchain(ctx) + _, _, env = get_linker_and_args(ctx, cc_toolchain, feature_configuration, depset()) + + lib = env.get("LIB", "") + if not lib: + # not MSVC + return "", "" + + lib = _rewrite_to_unix_path(lib) + path = _rewrite_to_unix_paths(env["PATH"]) + + return path, lib + +def _rust_binary_impl(ctx): + toolchain = ctx.toolchains["@io_bazel_rules_rust//rust:toolchain"] + output = ctx.actions.declare_file(ctx.label.name + toolchain.binary_ext) + cache_dir = ctx.actions.declare_directory(ctx.label.name + "_cache") + target_dir = ctx.actions.declare_directory(ctx.label.name + "_target") + folder = ctx.files.srcs[0].dirname + rust_bin = toolchain.cargo.dirname + msvc_path, msvc_lib = _get_windows_env_vars(ctx) + + ctx.actions.run_shell( + inputs = ctx.files.srcs, + outputs = [output, cache_dir, target_dir], + command = """\ +export RUSTC="$(pwd)/{rustc}"; \ +export CARGO="$(pwd)/{cargo}"; \ +export OUTPUT="$(pwd)/{output}"; \ +export CARGO_HOME="$(pwd)/{cache_dir}"; \ +export CARGO_TARGET_DIR="$(pwd)/{target_dir}"; \ +export PATH="{msvc_path}:$PATH"; \ +export LIB="{msvc_lib}"; \ +cd {folder} && \ +"$CARGO" build -q --release && \ +mv $CARGO_TARGET_DIR/release/rustc-worker "$OUTPUT" """.format( + rust_bin = rust_bin, + folder = folder, + cargo = toolchain.cargo.path, + rustc = toolchain.rustc.path, + output = output.path, + cache_dir = cache_dir.path, + target_dir = target_dir.path, + msvc_path = msvc_path, + msvc_lib = msvc_lib, + ), + tools = [toolchain.cargo, toolchain.rustc] + toolchain.rust_lib.files.to_list() + toolchain.rustc_lib.files.to_list(), + use_default_shell_env = True, + progress_message = "Building rust worker", + ) + return [ + DefaultInfo(executable = output), + ] + +rust_cargo_binary = rule( + attrs = { + "srcs": attr.label_list(allow_files = True), + "_cc_toolchain": attr.label( + default = "@bazel_tools//tools/cpp:current_cc_toolchain", + ), + }, + fragments = ["cpp"], + host_fragments = ["cpp"], + executable = True, + toolchains = [ + "@bazel_tools//tools/cpp:toolchain_type", + "@io_bazel_rules_rust//rust:toolchain", + ], + implementation = _rust_binary_impl, + doc = "Internal, do not use this.", +) diff --git a/worker/lib/mod.rs b/worker/lib/mod.rs new file mode 100644 index 0000000000..1aa0b5dc7e --- /dev/null +++ b/worker/lib/mod.rs @@ -0,0 +1,101 @@ +use protobuf::CodedInputStream; +use protobuf::CodedOutputStream; +use protobuf::Message; +use protobuf::ProtobufResult; +use std::collections::hash_map::DefaultHasher; +use std::hash::Hash; +use std::hash::Hasher; +use std::io; +use std::io::BufRead; +use std::path::PathBuf; + +mod worker_protocol; +use worker_protocol::WorkRequest; +use worker_protocol::WorkResponse; + +pub struct Worker { + program_path: PathBuf, + incremental_dir: std::path::PathBuf, +} + +impl Worker { + pub fn new>( + program_path: PathBuf, + rustc: PathBuf, + compilation_mode: C, + ) -> io::Result { + // The incremental cache directory includes the rustc wrapper's hash to discriminate + // between multiple workspaces having the same name (usually __main__). + let mut cache_path = std::env::temp_dir(); + let mut hasher = DefaultHasher::new(); + rustc.hash(&mut hasher); + + cache_path.push(format!( + "rustc-worker-{}-{}", + hasher.finish(), + compilation_mode.into() + )); + std::fs::create_dir_all(&cache_path)?; + Ok(Worker { + program_path, + incremental_dir: cache_path, + }) + } + + fn handle_request(&self, request: WorkRequest) -> ProtobufResult { + let mut incremental_arg = std::ffi::OsString::from("incremental="); + incremental_arg.push(&self.incremental_dir); + let mut cmd = std::process::Command::new(&self.program_path); + cmd.args(request.get_arguments()); + cmd.arg("--codegen"); + cmd.arg(incremental_arg); + let output = cmd.output()?; + Ok(WorkResponse { + request_id: request.request_id, + exit_code: output.status.code().unwrap(), + output: String::from_utf8(output.stderr).expect("TODO: use the Result"), + ..Default::default() + }) + } + + pub fn main_loop( + &self, + reader: &mut R, + writer: &mut W, + ) -> ProtobufResult<()> { + let mut stream = CodedInputStream::new(reader); + loop { + let msg_len = stream.read_raw_varint32()?; + let limit = stream.push_limit(msg_len as u64)?; + let mut message = WorkRequest::default(); + message.merge_from(&mut stream)?; + stream.pop_limit(limit); + + let response = self.handle_request(message)?; + let mut output_stream = CodedOutputStream::new(writer); + output_stream.write_raw_varint32(response.compute_size())?; + response.write_to_with_cached_sizes(&mut output_stream)?; + output_stream.flush()?; + writer.flush()?; + } + } + + pub fn once_with_response_file>( + &self, + response_file_path: P, + ) -> io::Result { + let file = std::io::BufReader::new(std::fs::File::open(response_file_path)?); + + let mut cmd = std::process::Command::new(&self.program_path); + for line in file.lines() { + cmd.arg(line?); + } + cmd.status() + } +} + +#[cfg(test)] +mod test { + #[test] + fn test_eof() {} +} diff --git a/worker/lib/worker_protocol.proto b/worker/lib/worker_protocol.proto new file mode 100644 index 0000000000..c628b7eb7a --- /dev/null +++ b/worker/lib/worker_protocol.proto @@ -0,0 +1,62 @@ +// Copyright 2015 The Bazel Authors. All rights reserved. +// +// 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. + +syntax = "proto3"; + +package blaze.worker; + +option java_package = "com.google.devtools.build.lib.worker"; + +// An input file. +message Input { + // The path in the file system where to read this input artifact from. This is + // either a path relative to the execution root (the worker process is + // launched with the working directory set to the execution root), or an + // absolute path. + string path = 1; + + // A hash-value of the contents. The format of the contents is unspecified and + // the digest should be treated as an opaque token. + bytes digest = 2; +} + +// This represents a single work unit that Blaze sends to the worker. +message WorkRequest { + repeated string arguments = 1; + + // The inputs that the worker is allowed to read during execution of this + // request. + repeated Input inputs = 2; + + // To support multiplex worker, each WorkRequest must have an unique ID. This + // ID should be attached unchanged to the WorkResponse. + int32 request_id = 3; +} + +// The worker sends this message to Blaze when it finished its work on the +// WorkRequest message. +message WorkResponse { + int32 exit_code = 1; + + // This is printed to the user after the WorkResponse has been received and is + // supposed to contain compiler warnings / errors etc. - thus we'll use a + // string type here, which gives us UTF-8 encoding. + string output = 2; + + // To support multiplex worker, each WorkResponse must have an unique ID. + // Since worker processes which support multiplex worker will handle multiple + // WorkRequests in parallel, this ID will be used to determined which + // WorkerProxy does this WorkResponse belong to. + int32 request_id = 3; +} diff --git a/worker/lib/worker_protocol.rs b/worker/lib/worker_protocol.rs new file mode 100644 index 0000000000..0026cc13bf --- /dev/null +++ b/worker/lib/worker_protocol.rs @@ -0,0 +1,826 @@ +// This file is generated by rust-protobuf 2.8.2. Do not edit +// @generated + +// https://github.com/Manishearth/rust-clippy/issues/702 +#![allow(unknown_lints)] +#![allow(clippy::all)] + +#![cfg_attr(rustfmt, rustfmt_skip)] + +#![allow(box_pointers)] +#![allow(dead_code)] +#![allow(missing_docs)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(trivial_casts)] +#![allow(unsafe_code)] +#![allow(unused_imports)] +#![allow(unused_results)] +//! Generated file from `src/worker_protocol.proto` + +use protobuf::Message as Message_imported_for_functions; +use protobuf::ProtobufEnum as ProtobufEnum_imported_for_functions; + +/// Generated files are compatible only with the same version +/// of protobuf runtime. +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_8_2; + +#[derive(PartialEq,Clone,Default)] +pub struct Input { + // message fields + pub path: ::std::string::String, + pub digest: ::std::vec::Vec, + // special fields + pub unknown_fields: ::protobuf::UnknownFields, + pub cached_size: ::protobuf::CachedSize, +} + +impl<'a> ::std::default::Default for &'a Input { + fn default() -> &'a Input { + ::default_instance() + } +} + +impl Input { + pub fn new() -> Input { + ::std::default::Default::default() + } + + // string path = 1; + + + pub fn get_path(&self) -> &str { + &self.path + } + pub fn clear_path(&mut self) { + self.path.clear(); + } + + // Param is passed by value, moved + pub fn set_path(&mut self, v: ::std::string::String) { + self.path = v; + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_path(&mut self) -> &mut ::std::string::String { + &mut self.path + } + + // Take field + pub fn take_path(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.path, ::std::string::String::new()) + } + + // bytes digest = 2; + + + pub fn get_digest(&self) -> &[u8] { + &self.digest + } + pub fn clear_digest(&mut self) { + self.digest.clear(); + } + + // Param is passed by value, moved + pub fn set_digest(&mut self, v: ::std::vec::Vec) { + self.digest = v; + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_digest(&mut self) -> &mut ::std::vec::Vec { + &mut self.digest + } + + // Take field + pub fn take_digest(&mut self) -> ::std::vec::Vec { + ::std::mem::replace(&mut self.digest, ::std::vec::Vec::new()) + } +} + +impl ::protobuf::Message for Input { + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { + while !is.eof()? { + let (field_number, wire_type) = is.read_tag_unpack()?; + match field_number { + 1 => { + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.path)?; + }, + 2 => { + ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.digest)?; + }, + _ => { + ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u32 { + let mut my_size = 0; + if !self.path.is_empty() { + my_size += ::protobuf::rt::string_size(1, &self.path); + } + if !self.digest.is_empty() { + my_size += ::protobuf::rt::bytes_size(2, &self.digest); + } + my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); + self.cached_size.set(my_size); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { + if !self.path.is_empty() { + os.write_string(1, &self.path)?; + } + if !self.digest.is_empty() { + os.write_bytes(2, &self.digest)?; + } + os.write_unknown_fields(self.get_unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn get_cached_size(&self) -> u32 { + self.cached_size.get() + } + + fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { + &self.unknown_fields + } + + fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { + &mut self.unknown_fields + } + + fn as_any(&self) -> &dyn (::std::any::Any) { + self as &dyn (::std::any::Any) + } + fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) { + self as &mut dyn (::std::any::Any) + } + fn into_any(self: Box) -> ::std::boxed::Box { + self + } + + fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { + Self::descriptor_static() + } + + fn new() -> Input { + Input::new() + } + + fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { + static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, + }; + unsafe { + descriptor.get(|| { + let mut fields = ::std::vec::Vec::new(); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + "path", + |m: &Input| { &m.path }, + |m: &mut Input| { &mut m.path }, + )); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>( + "digest", + |m: &Input| { &m.digest }, + |m: &mut Input| { &mut m.digest }, + )); + ::protobuf::reflect::MessageDescriptor::new::( + "Input", + fields, + file_descriptor_proto() + ) + }) + } + } + + fn default_instance() -> &'static Input { + static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const Input, + }; + unsafe { + instance.get(Input::new) + } + } +} + +impl ::protobuf::Clear for Input { + fn clear(&mut self) { + self.path.clear(); + self.digest.clear(); + self.unknown_fields.clear(); + } +} + +impl ::std::fmt::Debug for Input { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for Input { + fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef { + ::protobuf::reflect::ProtobufValueRef::Message(self) + } +} + +#[derive(PartialEq,Clone,Default)] +pub struct WorkRequest { + // message fields + pub arguments: ::protobuf::RepeatedField<::std::string::String>, + pub inputs: ::protobuf::RepeatedField, + pub request_id: i32, + // special fields + pub unknown_fields: ::protobuf::UnknownFields, + pub cached_size: ::protobuf::CachedSize, +} + +impl<'a> ::std::default::Default for &'a WorkRequest { + fn default() -> &'a WorkRequest { + ::default_instance() + } +} + +impl WorkRequest { + pub fn new() -> WorkRequest { + ::std::default::Default::default() + } + + // repeated string arguments = 1; + + + pub fn get_arguments(&self) -> &[::std::string::String] { + &self.arguments + } + pub fn clear_arguments(&mut self) { + self.arguments.clear(); + } + + // Param is passed by value, moved + pub fn set_arguments(&mut self, v: ::protobuf::RepeatedField<::std::string::String>) { + self.arguments = v; + } + + // Mutable pointer to the field. + pub fn mut_arguments(&mut self) -> &mut ::protobuf::RepeatedField<::std::string::String> { + &mut self.arguments + } + + // Take field + pub fn take_arguments(&mut self) -> ::protobuf::RepeatedField<::std::string::String> { + ::std::mem::replace(&mut self.arguments, ::protobuf::RepeatedField::new()) + } + + // repeated .blaze.worker.Input inputs = 2; + + + pub fn get_inputs(&self) -> &[Input] { + &self.inputs + } + pub fn clear_inputs(&mut self) { + self.inputs.clear(); + } + + // Param is passed by value, moved + pub fn set_inputs(&mut self, v: ::protobuf::RepeatedField) { + self.inputs = v; + } + + // Mutable pointer to the field. + pub fn mut_inputs(&mut self) -> &mut ::protobuf::RepeatedField { + &mut self.inputs + } + + // Take field + pub fn take_inputs(&mut self) -> ::protobuf::RepeatedField { + ::std::mem::replace(&mut self.inputs, ::protobuf::RepeatedField::new()) + } + + // int32 request_id = 3; + + + pub fn get_request_id(&self) -> i32 { + self.request_id + } + pub fn clear_request_id(&mut self) { + self.request_id = 0; + } + + // Param is passed by value, moved + pub fn set_request_id(&mut self, v: i32) { + self.request_id = v; + } +} + +impl ::protobuf::Message for WorkRequest { + fn is_initialized(&self) -> bool { + for v in &self.inputs { + if !v.is_initialized() { + return false; + } + }; + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { + while !is.eof()? { + let (field_number, wire_type) = is.read_tag_unpack()?; + match field_number { + 1 => { + ::protobuf::rt::read_repeated_string_into(wire_type, is, &mut self.arguments)?; + }, + 2 => { + ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.inputs)?; + }, + 3 => { + if wire_type != ::protobuf::wire_format::WireTypeVarint { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + let tmp = is.read_int32()?; + self.request_id = tmp; + }, + _ => { + ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u32 { + let mut my_size = 0; + for value in &self.arguments { + my_size += ::protobuf::rt::string_size(1, &value); + }; + for value in &self.inputs { + let len = value.compute_size(); + my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; + }; + if self.request_id != 0 { + my_size += ::protobuf::rt::value_size(3, self.request_id, ::protobuf::wire_format::WireTypeVarint); + } + my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); + self.cached_size.set(my_size); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { + for v in &self.arguments { + os.write_string(1, &v)?; + }; + for v in &self.inputs { + os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?; + os.write_raw_varint32(v.get_cached_size())?; + v.write_to_with_cached_sizes(os)?; + }; + if self.request_id != 0 { + os.write_int32(3, self.request_id)?; + } + os.write_unknown_fields(self.get_unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn get_cached_size(&self) -> u32 { + self.cached_size.get() + } + + fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { + &self.unknown_fields + } + + fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { + &mut self.unknown_fields + } + + fn as_any(&self) -> &dyn (::std::any::Any) { + self as &dyn (::std::any::Any) + } + fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) { + self as &mut dyn (::std::any::Any) + } + fn into_any(self: Box) -> ::std::boxed::Box { + self + } + + fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { + Self::descriptor_static() + } + + fn new() -> WorkRequest { + WorkRequest::new() + } + + fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { + static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, + }; + unsafe { + descriptor.get(|| { + let mut fields = ::std::vec::Vec::new(); + fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + "arguments", + |m: &WorkRequest| { &m.arguments }, + |m: &mut WorkRequest| { &mut m.arguments }, + )); + fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( + "inputs", + |m: &WorkRequest| { &m.inputs }, + |m: &mut WorkRequest| { &mut m.inputs }, + )); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt32>( + "request_id", + |m: &WorkRequest| { &m.request_id }, + |m: &mut WorkRequest| { &mut m.request_id }, + )); + ::protobuf::reflect::MessageDescriptor::new::( + "WorkRequest", + fields, + file_descriptor_proto() + ) + }) + } + } + + fn default_instance() -> &'static WorkRequest { + static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const WorkRequest, + }; + unsafe { + instance.get(WorkRequest::new) + } + } +} + +impl ::protobuf::Clear for WorkRequest { + fn clear(&mut self) { + self.arguments.clear(); + self.inputs.clear(); + self.request_id = 0; + self.unknown_fields.clear(); + } +} + +impl ::std::fmt::Debug for WorkRequest { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for WorkRequest { + fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef { + ::protobuf::reflect::ProtobufValueRef::Message(self) + } +} + +#[derive(PartialEq,Clone,Default)] +pub struct WorkResponse { + // message fields + pub exit_code: i32, + pub output: ::std::string::String, + pub request_id: i32, + // special fields + pub unknown_fields: ::protobuf::UnknownFields, + pub cached_size: ::protobuf::CachedSize, +} + +impl<'a> ::std::default::Default for &'a WorkResponse { + fn default() -> &'a WorkResponse { + ::default_instance() + } +} + +impl WorkResponse { + pub fn new() -> WorkResponse { + ::std::default::Default::default() + } + + // int32 exit_code = 1; + + + pub fn get_exit_code(&self) -> i32 { + self.exit_code + } + pub fn clear_exit_code(&mut self) { + self.exit_code = 0; + } + + // Param is passed by value, moved + pub fn set_exit_code(&mut self, v: i32) { + self.exit_code = v; + } + + // string output = 2; + + + pub fn get_output(&self) -> &str { + &self.output + } + pub fn clear_output(&mut self) { + self.output.clear(); + } + + // Param is passed by value, moved + pub fn set_output(&mut self, v: ::std::string::String) { + self.output = v; + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_output(&mut self) -> &mut ::std::string::String { + &mut self.output + } + + // Take field + pub fn take_output(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.output, ::std::string::String::new()) + } + + // int32 request_id = 3; + + + pub fn get_request_id(&self) -> i32 { + self.request_id + } + pub fn clear_request_id(&mut self) { + self.request_id = 0; + } + + // Param is passed by value, moved + pub fn set_request_id(&mut self, v: i32) { + self.request_id = v; + } +} + +impl ::protobuf::Message for WorkResponse { + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { + while !is.eof()? { + let (field_number, wire_type) = is.read_tag_unpack()?; + match field_number { + 1 => { + if wire_type != ::protobuf::wire_format::WireTypeVarint { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + let tmp = is.read_int32()?; + self.exit_code = tmp; + }, + 2 => { + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.output)?; + }, + 3 => { + if wire_type != ::protobuf::wire_format::WireTypeVarint { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + let tmp = is.read_int32()?; + self.request_id = tmp; + }, + _ => { + ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u32 { + let mut my_size = 0; + if self.exit_code != 0 { + my_size += ::protobuf::rt::value_size(1, self.exit_code, ::protobuf::wire_format::WireTypeVarint); + } + if !self.output.is_empty() { + my_size += ::protobuf::rt::string_size(2, &self.output); + } + if self.request_id != 0 { + my_size += ::protobuf::rt::value_size(3, self.request_id, ::protobuf::wire_format::WireTypeVarint); + } + my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); + self.cached_size.set(my_size); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { + if self.exit_code != 0 { + os.write_int32(1, self.exit_code)?; + } + if !self.output.is_empty() { + os.write_string(2, &self.output)?; + } + if self.request_id != 0 { + os.write_int32(3, self.request_id)?; + } + os.write_unknown_fields(self.get_unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn get_cached_size(&self) -> u32 { + self.cached_size.get() + } + + fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { + &self.unknown_fields + } + + fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { + &mut self.unknown_fields + } + + fn as_any(&self) -> &dyn (::std::any::Any) { + self as &dyn (::std::any::Any) + } + fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) { + self as &mut dyn (::std::any::Any) + } + fn into_any(self: Box) -> ::std::boxed::Box { + self + } + + fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { + Self::descriptor_static() + } + + fn new() -> WorkResponse { + WorkResponse::new() + } + + fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { + static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, + }; + unsafe { + descriptor.get(|| { + let mut fields = ::std::vec::Vec::new(); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt32>( + "exit_code", + |m: &WorkResponse| { &m.exit_code }, + |m: &mut WorkResponse| { &mut m.exit_code }, + )); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + "output", + |m: &WorkResponse| { &m.output }, + |m: &mut WorkResponse| { &mut m.output }, + )); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt32>( + "request_id", + |m: &WorkResponse| { &m.request_id }, + |m: &mut WorkResponse| { &mut m.request_id }, + )); + ::protobuf::reflect::MessageDescriptor::new::( + "WorkResponse", + fields, + file_descriptor_proto() + ) + }) + } + } + + fn default_instance() -> &'static WorkResponse { + static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const WorkResponse, + }; + unsafe { + instance.get(WorkResponse::new) + } + } +} + +impl ::protobuf::Clear for WorkResponse { + fn clear(&mut self) { + self.exit_code = 0; + self.output.clear(); + self.request_id = 0; + self.unknown_fields.clear(); + } +} + +impl ::std::fmt::Debug for WorkResponse { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for WorkResponse { + fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef { + ::protobuf::reflect::ProtobufValueRef::Message(self) + } +} + +static file_descriptor_proto_data: &'static [u8] = b"\ + \n\x19src/worker_protocol.proto\x12\x0cblaze.worker\"3\n\x05Input\x12\ + \x12\n\x04path\x18\x01\x20\x01(\tR\x04path\x12\x16\n\x06digest\x18\x02\ + \x20\x01(\x0cR\x06digest\"w\n\x0bWorkRequest\x12\x1c\n\targuments\x18\ + \x01\x20\x03(\tR\targuments\x12+\n\x06inputs\x18\x02\x20\x03(\x0b2\x13.b\ + laze.worker.InputR\x06inputs\x12\x1d\n\nrequest_id\x18\x03\x20\x01(\x05R\ + \trequestId\"b\n\x0cWorkResponse\x12\x1b\n\texit_code\x18\x01\x20\x01(\ + \x05R\x08exitCode\x12\x16\n\x06output\x18\x02\x20\x01(\tR\x06output\x12\ + \x1d\n\nrequest_id\x18\x03\x20\x01(\x05R\trequestIdB&\n$com.google.devto\ + ols.build.lib.workerJ\xf6\x12\n\x06\x12\x04\x0e\0=\x01\n\xd8\x04\n\x01\ + \x0c\x12\x03\x0e\0\x122\xcd\x04\x20Copyright\x202015\x20The\x20Bazel\x20\ + Authors.\x20All\x20rights\x20reserved.\n\n\x20Licensed\x20under\x20the\ + \x20Apache\x20License,\x20Version\x202.0\x20(the\x20\"License\");\n\x20y\ + ou\x20may\x20not\x20use\x20this\x20file\x20except\x20in\x20compliance\ + \x20with\x20the\x20License.\n\x20You\x20may\x20obtain\x20a\x20copy\x20of\ + \x20the\x20License\x20at\n\n\x20\x20\x20\x20http://www.apache.org/licens\ + es/LICENSE-2.0\n\n\x20Unless\x20required\x20by\x20applicable\x20law\x20o\ + r\x20agreed\x20to\x20in\x20writing,\x20software\n\x20distributed\x20unde\ + r\x20the\x20License\x20is\x20distributed\x20on\x20an\x20\"AS\x20IS\"\x20\ + BASIS,\n\x20WITHOUT\x20WARRANTIES\x20OR\x20CONDITIONS\x20OF\x20ANY\x20KI\ + ND,\x20either\x20express\x20or\x20implied.\n\x20See\x20the\x20License\ + \x20for\x20the\x20specific\x20language\x20governing\x20permissions\x20an\ + d\n\x20limitations\x20under\x20the\x20License.\n\n\x08\n\x01\x02\x12\x03\ + \x10\0\x15\n\x08\n\x01\x08\x12\x03\x12\0=\n\t\n\x02\x08\x01\x12\x03\x12\ + \0=\n\x1c\n\x02\x04\0\x12\x04\x15\0\x1f\x01\x1a\x10\x20An\x20input\x20fi\ + le.\n\n\n\n\x03\x04\0\x01\x12\x03\x15\x08\r\n\xf7\x01\n\x04\x04\0\x02\0\ + \x12\x03\x1a\x02\x12\x1a\xe9\x01\x20The\x20path\x20in\x20the\x20file\x20\ + system\x20where\x20to\x20read\x20this\x20input\x20artifact\x20from.\x20T\ + his\x20is\n\x20either\x20a\x20path\x20relative\x20to\x20the\x20execution\ + \x20root\x20(the\x20worker\x20process\x20is\n\x20launched\x20with\x20the\ + \x20working\x20directory\x20set\x20to\x20the\x20execution\x20root),\x20o\ + r\x20an\n\x20absolute\x20path.\n\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x1a\ + \x02\x08\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x1a\t\r\n\x0c\n\x05\x04\0\ + \x02\0\x03\x12\x03\x1a\x10\x11\n\x8c\x01\n\x04\x04\0\x02\x01\x12\x03\x1e\ + \x02\x13\x1a\x7f\x20A\x20hash-value\x20of\x20the\x20contents.\x20The\x20\ + format\x20of\x20the\x20contents\x20is\x20unspecified\x20and\n\x20the\x20\ + digest\x20should\x20be\x20treated\x20as\x20an\x20opaque\x20token.\n\n\ + \x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x1e\x02\x07\n\x0c\n\x05\x04\0\x02\ + \x01\x01\x12\x03\x1e\x08\x0e\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x1e\ + \x11\x12\nP\n\x02\x04\x01\x12\x04\"\0,\x01\x1aD\x20This\x20represents\ + \x20a\x20single\x20work\x20unit\x20that\x20Blaze\x20sends\x20to\x20the\ + \x20worker.\n\n\n\n\x03\x04\x01\x01\x12\x03\"\x08\x13\n\x0b\n\x04\x04\ + \x01\x02\0\x12\x03#\x02\x20\n\x0c\n\x05\x04\x01\x02\0\x04\x12\x03#\x02\n\ + \n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03#\x0b\x11\n\x0c\n\x05\x04\x01\x02\ + \0\x01\x12\x03#\x12\x1b\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03#\x1e\x1f\n\ + _\n\x04\x04\x01\x02\x01\x12\x03'\x02\x1c\x1aR\x20The\x20inputs\x20that\ + \x20the\x20worker\x20is\x20allowed\x20to\x20read\x20during\x20execution\ + \x20of\x20this\n\x20request.\n\n\x0c\n\x05\x04\x01\x02\x01\x04\x12\x03'\ + \x02\n\n\x0c\n\x05\x04\x01\x02\x01\x06\x12\x03'\x0b\x10\n\x0c\n\x05\x04\ + \x01\x02\x01\x01\x12\x03'\x11\x17\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\ + \x03'\x1a\x1b\n\x90\x01\n\x04\x04\x01\x02\x02\x12\x03+\x02\x17\x1a\x82\ + \x01\x20To\x20support\x20multiplex\x20worker,\x20each\x20WorkRequest\x20\ + must\x20have\x20an\x20unique\x20ID.\x20This\n\x20ID\x20should\x20be\x20a\ + ttached\x20unchanged\x20to\x20the\x20WorkResponse.\n\n\x0c\n\x05\x04\x01\ + \x02\x02\x05\x12\x03+\x02\x07\n\x0c\n\x05\x04\x01\x02\x02\x01\x12\x03+\ + \x08\x12\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03+\x15\x16\nk\n\x02\x04\ + \x02\x12\x040\0=\x01\x1a_\x20The\x20worker\x20sends\x20this\x20message\ + \x20to\x20Blaze\x20when\x20it\x20finished\x20its\x20work\x20on\x20the\n\ + \x20WorkRequest\x20message.\n\n\n\n\x03\x04\x02\x01\x12\x030\x08\x14\n\ + \x0b\n\x04\x04\x02\x02\0\x12\x031\x02\x16\n\x0c\n\x05\x04\x02\x02\0\x05\ + \x12\x031\x02\x07\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x031\x08\x11\n\x0c\n\ + \x05\x04\x02\x02\0\x03\x12\x031\x14\x15\n\xd5\x01\n\x04\x04\x02\x02\x01\ + \x12\x036\x02\x14\x1a\xc7\x01\x20This\x20is\x20printed\x20to\x20the\x20u\ + ser\x20after\x20the\x20WorkResponse\x20has\x20been\x20received\x20and\ + \x20is\n\x20supposed\x20to\x20contain\x20compiler\x20warnings\x20/\x20er\ + rors\x20etc.\x20-\x20thus\x20we'll\x20use\x20a\n\x20string\x20type\x20he\ + re,\x20which\x20gives\x20us\x20UTF-8\x20encoding.\n\n\x0c\n\x05\x04\x02\ + \x02\x01\x05\x12\x036\x02\x08\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x036\t\ + \x0f\n\x0c\n\x05\x04\x02\x02\x01\x03\x12\x036\x12\x13\n\x95\x02\n\x04\ + \x04\x02\x02\x02\x12\x03<\x02\x17\x1a\x87\x02\x20To\x20support\x20multip\ + lex\x20worker,\x20each\x20WorkResponse\x20must\x20have\x20an\x20unique\ + \x20ID.\n\x20Since\x20worker\x20processes\x20which\x20support\x20multipl\ + ex\x20worker\x20will\x20handle\x20multiple\n\x20WorkRequests\x20in\x20pa\ + rallel,\x20this\x20ID\x20will\x20be\x20used\x20to\x20determined\x20which\ + \n\x20WorkerProxy\x20does\x20this\x20WorkResponse\x20belong\x20to.\n\n\ + \x0c\n\x05\x04\x02\x02\x02\x05\x12\x03<\x02\x07\n\x0c\n\x05\x04\x02\x02\ + \x02\x01\x12\x03<\x08\x12\n\x0c\n\x05\x04\x02\x02\x02\x03\x12\x03<\x15\ + \x16b\x06proto3\ +"; + +static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const ::protobuf::descriptor::FileDescriptorProto, +}; + +fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto { + ::protobuf::parse_from_bytes(file_descriptor_proto_data).unwrap() +} + +pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto { + unsafe { + file_descriptor_proto_lazy.get(|| { + parse_descriptor_proto() + }) + } +} diff --git a/worker/main.rs b/worker/main.rs new file mode 100644 index 0000000000..ff711a8ad6 --- /dev/null +++ b/worker/main.rs @@ -0,0 +1,43 @@ +use protobuf::ProtobufResult; +mod lib; + +fn main() -> ProtobufResult<()> { + let mut args = std::env::args_os().peekable(); + // Always discard the executable name. + args.next().unwrap(); + + let program = std::fs::canonicalize(args.next().expect("program name"))?; + let rustc_path = std::fs::canonicalize(args.next().expect("rustc path"))?; + let compilation_mode = args + .next() + .expect("compilation mode") + .into_string() + .expect("compilation mode must be valid utf-8"); + // TODO: program and rustc_path will combine when this is merged into rules_rust. + let worker = lib::Worker::new(program, rustc_path, compilation_mode)?; + + // If started as a persistent worker. + if let Some(arg) = args.peek() { + if arg == "--persistent_worker" { + let stdin = std::io::stdin(); + let stdout = std::io::stdout(); + let mut stdin_locked = stdin.lock(); + let mut stdout_locked = stdout.lock(); + return worker.main_loop(&mut stdin_locked, &mut stdout_locked); + } + } + + // Spawn process as normal. + // The process wrapper does not support response files. + let response_file_arg = args + .next() + .unwrap() + .into_string() + .expect("response file path is valid utf-8"); + // The response file has to be the last (and only) argument left. + assert!(args.peek().is_none(), "iterator should be consumed!"); + assert!(response_file_arg.starts_with("@")); + let response_file_path = &response_file_arg[1..]; + let status = worker.once_with_response_file(response_file_path)?; + std::process::exit(status.code().unwrap()); +} diff --git a/worker/toolchain.bzl b/worker/toolchain.bzl new file mode 100644 index 0000000000..a34982659f --- /dev/null +++ b/worker/toolchain.bzl @@ -0,0 +1,24 @@ +""" +Define a worker toolchain. +""" + +load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") + +def _worker_toolchain_impl(ctx): + if ctx.attr.enabled[BuildSettingInfo].value: + binary = ctx.executable.worker_binary + else: + binary = None + + toolchain_info = platform_common.ToolchainInfo( + worker_binary = binary, + ) + return [toolchain_info] + +worker_toolchain = rule( + implementation = _worker_toolchain_impl, + attrs = { + "worker_binary": attr.label(allow_single_file = True, executable = True, cfg = "exec"), + "enabled": attr.label(), + }, +)