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/BUILD.bazel b/cargo/cargo_build_script_runner/BUILD.bazel
new file mode 100644
index 0000000..11edd45
--- /dev/null
+++ b/cargo/cargo_build_script_runner/BUILD.bazel
@@ -0,0 +1,24 @@
+load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_test")
+
+rust_library(
+    name = "cargo_build_script_output_parser",
+    srcs = ["lib.rs"],
+)
+
+rust_test(
+    name = "test",
+    crate = ":cargo_build_script_output_parser",
+)
+
+rust_binary(
+    name = "cargo_build_script_runner",
+    srcs = ["bin.rs"],
+    visibility = ["//visibility:public"],
+    deps = [":cargo_build_script_output_parser"],
+)
+
+rust_test(
+    name = "bin_test",
+    crate = ":cargo_build_script_runner",
+    deps = [":cargo_build_script_runner"],
+)
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");
+    }
+}
diff --git a/cargo/cargo_build_script_runner/lib.rs b/cargo/cargo_build_script_runner/lib.rs
new file mode 100644
index 0000000..b1aa793
--- /dev/null
+++ b/cargo/cargo_build_script_runner/lib.rs
@@ -0,0 +1,291 @@
+// 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.
+
+//! Parse the output of a cargo build.rs script and generate a list of flags and
+//! environment variable for the build.
+use std::io::{BufRead, BufReader, Read};
+use std::process::{Command, Output};
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct CompileAndLinkFlags {
+    pub compile_flags: String,
+    pub link_flags: String,
+    pub link_search_paths: String,
+}
+
+/// Enum containing all the considered return value from the script
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum BuildScriptOutput {
+    /// cargo:rustc-link-lib
+    LinkLib(String),
+    /// cargo:rustc-link-search
+    LinkSearch(String),
+    /// cargo:rustc-cfg
+    Cfg(String),
+    /// cargo:rustc-flags
+    Flags(String),
+    /// cargo:rustc-link-arg
+    LinkArg(String),
+    /// cargo:rustc-env
+    Env(String),
+    /// cargo:VAR=VALUE
+    DepEnv(String),
+}
+
+impl BuildScriptOutput {
+    /// Converts a line into a [BuildScriptOutput] enum.
+    ///
+    /// Examples
+    /// ```rust
+    /// assert_eq!(BuildScriptOutput::new("cargo:rustc-link-lib=lib"), Some(BuildScriptOutput::LinkLib("lib".to_owned())));
+    /// ```
+    fn new(line: &str) -> Option<BuildScriptOutput> {
+        let split = line.splitn(2, '=').collect::<Vec<_>>();
+        if split.len() <= 1 {
+            // Not a cargo directive.
+            return None;
+        }
+        let param = split[1].trim().to_owned();
+        let key_split = split[0].splitn(2, ':').collect::<Vec<_>>();
+        if key_split.len() <= 1 || key_split[0] != "cargo" {
+            // Not a cargo directive.
+            return None;
+        }
+
+        match key_split[1] {
+            "rustc-link-lib" => Some(BuildScriptOutput::LinkLib(param)),
+            "rustc-link-search" => Some(BuildScriptOutput::LinkSearch(param)),
+            "rustc-cfg" => Some(BuildScriptOutput::Cfg(param)),
+            "rustc-flags" => Some(BuildScriptOutput::Flags(param)),
+            "rustc-link-arg" => Some(BuildScriptOutput::LinkArg(param)),
+            "rustc-env" => Some(BuildScriptOutput::Env(param)),
+            "rerun-if-changed" | "rerun-if-env-changed" =>
+            // Ignored because Bazel will re-run if those change all the time.
+            {
+                None
+            }
+            "warning" => {
+                eprint!("Build Script Warning: {}", split[1]);
+                None
+            }
+            "rustc-cdylib-link-arg" | "rustc-link-arg-bin" | "rustc-link-arg-bins" => {
+                // cargo:rustc-cdylib-link-arg=FLAG — Passes custom flags to a linker for cdylib crates.
+                // cargo:rustc-link-arg-bin=BIN=FLAG – Passes custom flags to a linker for the binary BIN.
+                // cargo:rustc-link-arg-bins=FLAG – Passes custom flags to a linker for binaries.
+                eprint!(
+                    "Warning: build script returned unsupported directive `{}`",
+                    split[0]
+                );
+                None
+            }
+            _ => {
+                // cargo:KEY=VALUE — Metadata, used by links scripts.
+                Some(BuildScriptOutput::DepEnv(format!(
+                    "{}={}",
+                    key_split[1].to_uppercase(),
+                    param
+                )))
+            }
+        }
+    }
+
+    /// Converts a [BufReader] into a vector of [BuildScriptOutput] enums.
+    fn outputs_from_reader<T: Read>(mut reader: BufReader<T>) -> Vec<BuildScriptOutput> {
+        let mut result = Vec::<BuildScriptOutput>::new();
+        let mut line = String::new();
+        while reader.read_line(&mut line).expect("Cannot read line") != 0 {
+            if let Some(bso) = BuildScriptOutput::new(&line) {
+                result.push(bso);
+            }
+            line.clear();
+        }
+        result
+    }
+
+    /// Take a [Command], execute it and converts its input into a vector of [BuildScriptOutput]
+    pub fn outputs_from_command(
+        cmd: &mut Command,
+    ) -> Result<(Vec<BuildScriptOutput>, Output), Output> {
+        let child_output = cmd.output().expect("Unable to start binary");
+        if child_output.status.success() {
+            let reader = BufReader::new(child_output.stdout.as_slice());
+            let output = Self::outputs_from_reader(reader);
+            Ok((output, child_output))
+        } else {
+            Err(child_output)
+        }
+    }
+
+    /// Convert a vector of [BuildScriptOutput] into a list of environment variables.
+    pub fn outputs_to_env(outputs: &[BuildScriptOutput], exec_root: &str) -> String {
+        outputs
+            .iter()
+            .filter_map(|x| {
+                if let BuildScriptOutput::Env(env) = x {
+                    Some(Self::escape_for_serializing(Self::redact_exec_root(
+                        env, exec_root,
+                    )))
+                } else {
+                    None
+                }
+            })
+            .collect::<Vec<_>>()
+            .join("\n")
+    }
+
+    /// Convert a vector of [BuildScriptOutput] into a list of dependencies environment variables.
+    pub fn outputs_to_dep_env(
+        outputs: &[BuildScriptOutput],
+        crate_links: &str,
+        exec_root: &str,
+    ) -> String {
+        let prefix = format!("DEP_{}_", crate_links.replace('-', "_").to_uppercase());
+        outputs
+            .iter()
+            .filter_map(|x| {
+                if let BuildScriptOutput::DepEnv(env) = x {
+                    Some(format!(
+                        "{}{}",
+                        prefix,
+                        Self::escape_for_serializing(Self::redact_exec_root(env, exec_root))
+                    ))
+                } else {
+                    None
+                }
+            })
+            .collect::<Vec<_>>()
+            .join("\n")
+    }
+
+    /// Convert a vector of [BuildScriptOutput] into a flagfile.
+    pub fn outputs_to_flags(outputs: &[BuildScriptOutput], exec_root: &str) -> CompileAndLinkFlags {
+        let mut compile_flags = Vec::new();
+        let mut link_flags = Vec::new();
+        let mut link_search_paths = Vec::new();
+
+        for flag in outputs {
+            match flag {
+                BuildScriptOutput::Cfg(e) => compile_flags.push(format!("--cfg={}", e)),
+                BuildScriptOutput::Flags(e) => compile_flags.push(e.to_owned()),
+                BuildScriptOutput::LinkArg(e) => compile_flags.push(format!("-Clink-arg={}", e)),
+                BuildScriptOutput::LinkLib(e) => link_flags.push(format!("-l{}", e)),
+                BuildScriptOutput::LinkSearch(e) => link_search_paths.push(format!("-L{}", e)),
+                _ => {}
+            }
+        }
+
+        CompileAndLinkFlags {
+            compile_flags: compile_flags.join("\n"),
+            link_flags: Self::redact_exec_root(&link_flags.join("\n"), exec_root),
+            link_search_paths: Self::redact_exec_root(&link_search_paths.join("\n"), exec_root),
+        }
+    }
+
+    fn redact_exec_root(value: &str, exec_root: &str) -> String {
+        value.replace(exec_root, "${pwd}")
+    }
+
+    // The process-wrapper treats trailing backslashes as escapes for following newlines.
+    // If the env var ends with a backslash (and accordingly doesn't have a following newline),
+    // escape it so that it doesn't get turned into a newline by the process-wrapper.
+    //
+    // Note that this code doesn't handle newlines in strings - that's because Cargo treats build
+    // script output as single-line-oriented, so stops processing at the end of a line regardless.
+    fn escape_for_serializing(mut value: String) -> String {
+        if value.ends_with('\\') {
+            value.push('\\');
+        }
+        value
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use std::io::Cursor;
+
+    #[test]
+    fn test_from_read_buffer_to_env_and_flags() {
+        let buff = Cursor::new(
+            "
+cargo:rustc-link-lib=sdfsdf
+cargo:rustc-env=FOO=BAR
+cargo:rustc-link-search=/some/absolute/path/bleh
+cargo:rustc-env=BAR=FOO
+cargo:rustc-flags=-Lblah
+cargo:rerun-if-changed=ignored
+cargo:rustc-cfg=feature=awesome
+cargo:version=123
+cargo:version_number=1010107f
+cargo:include_path=/some/absolute/path/include
+cargo:rustc-env=SOME_PATH=/some/absolute/path/beep
+cargo:rustc-link-arg=-weak_framework
+cargo:rustc-link-arg=Metal
+",
+        );
+        let reader = BufReader::new(buff);
+        let result = BuildScriptOutput::outputs_from_reader(reader);
+        assert_eq!(result.len(), 12);
+        assert_eq!(result[0], BuildScriptOutput::LinkLib("sdfsdf".to_owned()));
+        assert_eq!(result[1], BuildScriptOutput::Env("FOO=BAR".to_owned()));
+        assert_eq!(
+            result[2],
+            BuildScriptOutput::LinkSearch("/some/absolute/path/bleh".to_owned())
+        );
+        assert_eq!(result[3], BuildScriptOutput::Env("BAR=FOO".to_owned()));
+        assert_eq!(result[4], BuildScriptOutput::Flags("-Lblah".to_owned()));
+        assert_eq!(
+            result[5],
+            BuildScriptOutput::Cfg("feature=awesome".to_owned())
+        );
+        assert_eq!(
+            result[6],
+            BuildScriptOutput::DepEnv("VERSION=123".to_owned())
+        );
+        assert_eq!(
+            result[7],
+            BuildScriptOutput::DepEnv("VERSION_NUMBER=1010107f".to_owned())
+        );
+        assert_eq!(
+            result[9],
+            BuildScriptOutput::Env("SOME_PATH=/some/absolute/path/beep".to_owned())
+        );
+        assert_eq!(
+            result[10],
+            BuildScriptOutput::LinkArg("-weak_framework".to_owned())
+        );
+        assert_eq!(result[11], BuildScriptOutput::LinkArg("Metal".to_owned()));
+
+        assert_eq!(
+            BuildScriptOutput::outputs_to_dep_env(&result, "ssh2", "/some/absolute/path"),
+            "DEP_SSH2_VERSION=123\nDEP_SSH2_VERSION_NUMBER=1010107f\nDEP_SSH2_INCLUDE_PATH=${pwd}/include".to_owned()
+        );
+        assert_eq!(
+            BuildScriptOutput::outputs_to_env(&result, "/some/absolute/path"),
+            "FOO=BAR\nBAR=FOO\nSOME_PATH=${pwd}/beep".to_owned()
+        );
+        assert_eq!(
+            BuildScriptOutput::outputs_to_flags(&result, "/some/absolute/path"),
+            CompileAndLinkFlags {
+                // -Lblah was output as a rustc-flags, so even though it probably _should_ be a link
+                // flag, we don't treat it like one.
+                compile_flags:
+                    "-Lblah\n--cfg=feature=awesome\n-Clink-arg=-weak_framework\n-Clink-arg=Metal"
+                        .to_owned(),
+                link_flags: "-lsdfsdf".to_owned(),
+                link_search_paths: "-L${pwd}/bleh".to_owned(),
+            }
+        );
+    }
+}