Squashed 'third_party/rules_rust/' content from commit bf59038ca

git-subtree-dir: third_party/rules_rust
git-subtree-split: bf59038cac11798cbaef9f3bf965bad8182b97fa
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I5a20e403203d670df467ea97dde9a4ac40339a8d
diff --git a/cargo/cargo_build_script_runner/bin.rs b/cargo/cargo_build_script_runner/bin.rs
new file mode 100644
index 0000000..58f0363
--- /dev/null
+++ b/cargo/cargo_build_script_runner/bin.rs
@@ -0,0 +1,345 @@
+// Copyright 2018 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.
+
+// A simple wrapper around a build_script execution to generate file to reuse
+// by rust_library/rust_binary.
+extern crate cargo_build_script_output_parser;
+
+use cargo_build_script_output_parser::{BuildScriptOutput, CompileAndLinkFlags};
+use std::collections::BTreeMap;
+use std::env;
+use std::fs::{create_dir_all, read_to_string, write};
+use std::path::Path;
+use std::process::Command;
+
+fn run_buildrs() -> Result<(), String> {
+    // We use exec_root.join rather than std::fs::canonicalize, to avoid resolving symlinks, as
+    // some execution strategies and remote execution environments may use symlinks in ways which
+    // canonicalizing them may break them, e.g. by having input files be symlinks into a /cas
+    // directory - resolving these may cause tools which inspect $0, or try to resolve files
+    // relative to themselves, to fail.
+    let exec_root = env::current_dir().expect("Failed to get current directory");
+    let manifest_dir_env = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR was not set");
+    let rustc_env = env::var("RUSTC").expect("RUSTC was not set");
+    let manifest_dir = exec_root.join(&manifest_dir_env);
+    let rustc = exec_root.join(&rustc_env);
+    let Options {
+        progname,
+        crate_links,
+        out_dir,
+        env_file,
+        compile_flags_file,
+        link_flags_file,
+        link_search_paths_file,
+        output_dep_env_path,
+        stdout_path,
+        stderr_path,
+        input_dep_env_paths,
+    } = parse_args()?;
+
+    let out_dir_abs = exec_root.join(&out_dir);
+    // For some reason Google's RBE does not create the output directory, force create it.
+    create_dir_all(&out_dir_abs)
+        .unwrap_or_else(|_| panic!("Failed to make output directory: {:?}", out_dir_abs));
+
+    let target_env_vars =
+        get_target_env_vars(&rustc_env).expect("Error getting target env vars from rustc");
+
+    let mut command = Command::new(exec_root.join(&progname));
+    command
+        .current_dir(&manifest_dir)
+        .envs(target_env_vars)
+        .env("OUT_DIR", out_dir_abs)
+        .env("CARGO_MANIFEST_DIR", manifest_dir)
+        .env("RUSTC", rustc)
+        .env("RUST_BACKTRACE", "full");
+
+    for dep_env_path in input_dep_env_paths.iter() {
+        if let Ok(contents) = read_to_string(dep_env_path) {
+            for line in contents.split('\n') {
+                // split on empty contents will still produce a single empty string in iterable.
+                if line.is_empty() {
+                    continue;
+                }
+                let mut key_val = line.splitn(2, '=');
+                match (key_val.next(), key_val.next()) {
+                    (Some(key), Some(value)) => {
+                        command.env(key, value.replace("${pwd}", &exec_root.to_string_lossy()));
+                    }
+                    _ => {
+                        return Err(
+                            "error: Wrong environment file format, should not happen".to_owned()
+                        )
+                    }
+                }
+            }
+        } else {
+            return Err("error: Dependency environment file unreadable".to_owned());
+        }
+    }
+
+    for compiler_env_var in &["CC", "CXX"] {
+        if let Some(compiler_path) = env::var_os(compiler_env_var) {
+            let mut compiler_path = exec_root.join(compiler_path).into_os_string();
+            if let Some(sysroot_path) = env::var_os("SYSROOT") {
+                compiler_path.push(" --sysroot=");
+                compiler_path.push(&exec_root.join(sysroot_path));
+            }
+            command.env(compiler_env_var, compiler_path);
+        }
+    }
+
+    if let Some(ar_path) = env::var_os("AR") {
+        // The default OSX toolchain uses libtool as ar_executable not ar.
+        // This doesn't work when used as $AR, so simply don't set it - tools will probably fall back to
+        // /usr/bin/ar which is probably good enough.
+        if Path::new(&ar_path).file_name() == Some("libtool".as_ref()) {
+            command.env_remove("AR");
+        } else {
+            command.env("AR", exec_root.join(ar_path));
+        }
+    }
+
+    if let Some(ld_path) = env::var_os("LD") {
+        command.env("LD", exec_root.join(ld_path));
+    }
+
+    // replace env vars with a ${pwd} prefix with the exec_root
+    for (key, value) in env::vars() {
+        let exec_root_str = exec_root.to_str().expect("exec_root not in utf8");
+        if value.contains("${pwd}") {
+            env::set_var(key, value.replace("${pwd}", exec_root_str));
+        }
+    }
+
+    // Bazel does not support byte strings so in order to correctly represent `CARGO_ENCODED_RUSTFLAGS`
+    // the escaped `\x1f` sequences need to be unescaped
+    if let Ok(encoded_rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") {
+        command.env(
+            "CARGO_ENCODED_RUSTFLAGS",
+            encoded_rustflags.replace("\\x1f", "\x1f"),
+        );
+    }
+
+    let (buildrs_outputs, process_output) = BuildScriptOutput::outputs_from_command(&mut command)
+        .map_err(|process_output| {
+        format!(
+            "Build script process failed{}\n--stdout:\n{}\n--stderr:\n{}",
+            if let Some(exit_code) = process_output.status.code() {
+                format!(" with exit code {}", exit_code)
+            } else {
+                String::new()
+            },
+            String::from_utf8(process_output.stdout)
+                .expect("Failed to parse stdout of child process"),
+            String::from_utf8(process_output.stderr)
+                .expect("Failed to parse stdout of child process"),
+        )
+    })?;
+
+    write(
+        &env_file,
+        BuildScriptOutput::outputs_to_env(&buildrs_outputs, &exec_root.to_string_lossy())
+            .as_bytes(),
+    )
+    .unwrap_or_else(|_| panic!("Unable to write file {:?}", env_file));
+    write(
+        &output_dep_env_path,
+        BuildScriptOutput::outputs_to_dep_env(
+            &buildrs_outputs,
+            &crate_links,
+            &exec_root.to_string_lossy(),
+        )
+        .as_bytes(),
+    )
+    .unwrap_or_else(|_| panic!("Unable to write file {:?}", output_dep_env_path));
+    write(&stdout_path, process_output.stdout)
+        .unwrap_or_else(|_| panic!("Unable to write file {:?}", stdout_path));
+    write(&stderr_path, process_output.stderr)
+        .unwrap_or_else(|_| panic!("Unable to write file {:?}", stderr_path));
+
+    let CompileAndLinkFlags {
+        compile_flags,
+        link_flags,
+        link_search_paths,
+    } = BuildScriptOutput::outputs_to_flags(&buildrs_outputs, &exec_root.to_string_lossy());
+
+    write(&compile_flags_file, compile_flags.as_bytes())
+        .unwrap_or_else(|_| panic!("Unable to write file {:?}", compile_flags_file));
+    write(&link_flags_file, link_flags.as_bytes())
+        .unwrap_or_else(|_| panic!("Unable to write file {:?}", link_flags_file));
+    write(&link_search_paths_file, link_search_paths.as_bytes())
+        .unwrap_or_else(|_| panic!("Unable to write file {:?}", link_search_paths_file));
+    Ok(())
+}
+
+/// A representation of expected command line arguments.
+struct Options {
+    progname: String,
+    crate_links: String,
+    out_dir: String,
+    env_file: String,
+    compile_flags_file: String,
+    link_flags_file: String,
+    link_search_paths_file: String,
+    output_dep_env_path: String,
+    stdout_path: String,
+    stderr_path: String,
+    input_dep_env_paths: Vec<String>,
+}
+
+/// Parses positional comamnd line arguments into a well defined struct
+fn parse_args() -> Result<Options, String> {
+    let mut args = env::args().skip(1);
+
+    // TODO: we should consider an alternative to positional arguments.
+    match (args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next()) {
+        (
+            Some(progname),
+            Some(crate_links),
+            Some(out_dir),
+            Some(env_file),
+            Some(compile_flags_file),
+            Some(link_flags_file),
+            Some(link_search_paths_file),
+            Some(output_dep_env_path),
+            Some(stdout_path),
+            Some(stderr_path),
+        ) => {
+            Ok(Options{
+                progname,
+                crate_links,
+                out_dir,
+                env_file,
+                compile_flags_file,
+                link_flags_file,
+                link_search_paths_file,
+                output_dep_env_path,
+                stdout_path,
+                stderr_path,
+                input_dep_env_paths: args.collect(),
+            })
+        }
+        _ => {
+            Err(format!("Usage: $0 progname crate_links out_dir env_file compile_flags_file link_flags_file link_search_paths_file output_dep_env_path stdout_path stderr_path input_dep_env_paths[arg1...argn]\nArguments passed: {:?}", args.collect::<Vec<String>>()))
+        }
+    }
+}
+
+fn get_target_env_vars<P: AsRef<Path>>(rustc: &P) -> Result<BTreeMap<String, String>, String> {
+    // As done by Cargo when constructing a cargo::core::compiler::build_context::target_info::TargetInfo.
+    let output = Command::new(rustc.as_ref())
+        .arg("--print=cfg")
+        .arg(format!(
+            "--target={}",
+            env::var("TARGET").expect("missing TARGET")
+        ))
+        .output()
+        .map_err(|err| format!("Error running rustc to get target information: {}", err))?;
+    if !output.status.success() {
+        return Err(format!(
+            "Error running rustc to get target information: {:?}",
+            output
+        ));
+    }
+    let stdout = std::str::from_utf8(&output.stdout)
+        .map_err(|err| format!("Non-UTF8 stdout from rustc: {:?}", err))?;
+
+    Ok(parse_rustc_cfg_output(stdout))
+}
+
+fn parse_rustc_cfg_output(stdout: &str) -> BTreeMap<String, String> {
+    let mut values = BTreeMap::new();
+
+    for line in stdout.lines() {
+        if line.starts_with("target_") && line.contains('=') {
+            let mut parts = line.splitn(2, '=');
+            // UNWRAP: Verified that line contains = and split into exactly 2 parts.
+            let key = parts.next().unwrap();
+            let value = parts.next().unwrap();
+            if value.starts_with('"') && value.ends_with('"') && value.len() >= 2 {
+                values
+                    .entry(key)
+                    .or_insert_with(Vec::new)
+                    .push(value[1..(value.len() - 1)].to_owned());
+            }
+        } else if ["windows", "unix"].contains(&line) {
+            // the 'windows' or 'unix' line received from rustc will be turned
+            // into eg. CARGO_CFG_WINDOWS='' below
+            values.insert(line, vec![]);
+        }
+    }
+
+    values
+        .into_iter()
+        .map(|(key, value)| (format!("CARGO_CFG_{}", key.to_uppercase()), value.join(",")))
+        .collect()
+}
+
+fn main() {
+    std::process::exit(match run_buildrs() {
+        Ok(_) => 0,
+        Err(err) => {
+            // Neatly print errors
+            eprintln!("{}", err);
+            1
+        }
+    });
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn rustc_cfg_parsing() {
+        let macos_output = r#"\
+debug_assertions
+target_arch="x86_64"
+target_endian="little"
+target_env=""
+target_family="unix"
+target_feature="fxsr"
+target_feature="sse"
+target_feature="sse2"
+target_feature="sse3"
+target_feature="ssse3"
+target_os="macos"
+target_pointer_width="64"
+target_vendor="apple"
+unix
+"#;
+        let tree = parse_rustc_cfg_output(macos_output);
+        assert_eq!(tree["CARGO_CFG_UNIX"], "");
+        assert_eq!(tree["CARGO_CFG_TARGET_FAMILY"], "unix");
+
+        let windows_output = r#"\
+debug_assertions
+target_arch="x86_64"
+target_endian="little"
+target_env="msvc"
+target_family="windows"
+target_feature="fxsr"
+target_feature="sse"
+target_feature="sse2"
+target_os="windows"
+target_pointer_width="64"
+target_vendor="pc"
+windows
+"#;
+        let tree = parse_rustc_cfg_output(windows_output);
+        assert_eq!(tree["CARGO_CFG_WINDOWS"], "");
+        assert_eq!(tree["CARGO_CFG_TARGET_FAMILY"], "windows");
+    }
+}