Import cargo-gnaw from fuchsia
repo: https://fuchsia.googlesource.com/fuchsia
ref: 492f241811526190ec6d504fe1cf4cfaabaf5301
Change-Id: I6cd76890f9411e516928cfb27df9ee362963b073
Reviewed-on: https://pigweed-review.googlesource.com/c/third_party/fuchsia/cargo-gnaw/+/235274
Lint: Lint 🤖 <android-build-ayeaye@system.gserviceaccount.com>
Reviewed-by: Rob Mohr <mohrr@google.com>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c169014
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+target/
+!/Cargo.toml
diff --git a/BUILD.gn b/BUILD.gn
new file mode 100644
index 0000000..2337b99
--- /dev/null
+++ b/BUILD.gn
@@ -0,0 +1,119 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/host.gni")
+import("//build/rust/rustc_binary.gni")
+import("//build/rust/rustc_library.gni")
+import("//build/rust/rustc_test.gni")
+
+if (is_host) {
+ rustc_binary("cargo-gnaw-bin") {
+ edition = "2021"
+ output_name = "gnaw"
+
+ deps = [
+ ":cargo-gnaw-lib",
+ "//third_party/rust_crates:anyhow",
+ ]
+
+ sources = [ "src/main.rs" ]
+ }
+
+ rustc_library("cargo-gnaw-lib") {
+ edition = "2021"
+ name = "gnaw_lib"
+
+ deps = [
+ "//third_party/rust_crates:anyhow",
+ "//third_party/rust_crates:argh",
+ "//third_party/rust_crates:camino",
+ "//third_party/rust_crates:cargo_metadata",
+ "//third_party/rust_crates:semver",
+ "//third_party/rust_crates:serde",
+ "//third_party/rust_crates:serde_derive",
+ "//third_party/rust_crates:serde_json",
+ "//third_party/rust_crates:toml",
+ "//third_party/rust_crates:walkdir",
+ ]
+
+ sources = [
+ "src/build.rs",
+ "src/cfg.rs",
+ "src/gn.rs",
+ "src/graph.rs",
+ "src/lib.rs",
+ "src/target.rs",
+ "src/types.rs",
+ ]
+
+ inputs = [
+ "templates/gn_header.template",
+ "templates/gn_import.template",
+ "templates/gn_license.template",
+ "templates/gn_rule.template",
+ "templates/gn_sdk_metadata_header.template",
+ "templates/top_level_binary_gn_rule.template",
+ "templates/top_level_gn_rule.template",
+ ]
+ }
+
+ rustc_test("cargo-gnaw-tests") {
+ edition = "2021"
+ source_root = "src/lib.rs"
+ deps = [
+ "//third_party/rust_crates:anyhow",
+ "//third_party/rust_crates:argh",
+ "//third_party/rust_crates:camino",
+ "//third_party/rust_crates:cargo_metadata",
+ "//third_party/rust_crates:semver",
+ "//third_party/rust_crates:serde",
+ "//third_party/rust_crates:serde_derive",
+ "//third_party/rust_crates:serde_json",
+ "//third_party/rust_crates:toml",
+ "//third_party/rust_crates:walkdir",
+ ]
+
+ sources = [
+ "src/build.rs",
+ "src/cfg.rs",
+ "src/gn.rs",
+ "src/graph.rs",
+ "src/lib.rs",
+ "src/target.rs",
+ "src/types.rs",
+ ]
+
+ inputs = [
+ "templates/gn_header.template",
+ "templates/gn_import.template",
+ "templates/gn_license.template",
+ "templates/gn_rule.template",
+ "templates/gn_sdk_metadata_header.template",
+ "templates/top_level_binary_gn_rule.template",
+ "templates/top_level_gn_rule.template",
+ ]
+ }
+}
+
+install_host_tools("install-cargo-gnaw") {
+ deps = [ ":cargo-gnaw-bin" ]
+ outputs = [ "gnaw" ]
+}
+
+group("cargo-gnaw") {
+ testonly = true
+ deps = [
+ ":cargo-gnaw-bin($host_toolchain)",
+ ":install-cargo-gnaw",
+ ":tests",
+ ]
+}
+
+group("tests") {
+ testonly = true
+ deps = [
+ ":cargo-gnaw-tests($host_toolchain)",
+ "tests",
+ ]
+}
diff --git a/Cargo.toml b/Cargo.toml
new file mode 120000
index 0000000..68b7940
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1 @@
+Cargo.toml.crates-io
\ No newline at end of file
diff --git a/Cargo.toml.crates-io b/Cargo.toml.crates-io
new file mode 100644
index 0000000..e40852c
--- /dev/null
+++ b/Cargo.toml.crates-io
@@ -0,0 +1,33 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This file is used when publishing to crates.io
+
+[package]
+edition = "2018"
+name = "gnaw"
+version = "0.1.0"
+authors = ["The Fuchsia Authors"]
+description = "Tooling to convert Cargo.toml files into native GN rules"
+license-file = "../../LICENSE"
+repository = "https://fuchsia.googlesource.com/fuchsia/+/HEAD/tools/cargo-gnaw"
+
+[lib]
+name = "gnaw_lib"
+
+[dependencies]
+anyhow = "1.0.38"
+argh = "0.1.7"
+camino = "1.0.5"
+cargo_metadata = "0.18.1"
+serde = "1.0.116"
+serde_derive = "1.0.116"
+serde_json = "1.0.59"
+toml = "0.5.6"
+walkdir = "2.3.3"
+semver = "1.0.23"
+
+[dev-dependencies]
+pretty_assertions = "0.5.1"
+tempfile = "3.2.0"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7ed244f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright 2019 The Fuchsia Authors.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3ee7e90
--- /dev/null
+++ b/README.md
@@ -0,0 +1,145 @@
+# Cargo GNaw
+**Tooling to convert Cargo.toml files into native GN rules**
+
+[](https://crates.io/crates/cargo-gnaw)
+[](https://github.com/google/cargo-gnaw/LICENSE)
+[](https://docs.rs/crate/cargo-gnaw/)
+
+
+## Install
+```sh
+$ cargo install --path ~/cargo-gnaw
+```
+
+## Run
+```sh
+$ cargo gnaw --manifest-path ~/fuchsia/third_party/rust_crates/Cargo.toml -o ~/fuchsia/third_party/rust_crates/BUILD.gn
+```
+
+### Options
+* --skip-root - Skip the root package in the Cargo.toml and treat it's dependencies as the top-level targets
+* --gn-bin - Path to GN binary for formatting the output
+
+## How it works
+
+Cargo GNaw operates on vendored crates to convert them into [GN](https://gn.googlesource.com/gn/+/HEAD/docs/reference.md) rules. The resulting BUILD.gn file is expected
+to be vendored with the crates and provides targets for the GN build system to reference.
+
+All top-level crates are given an easy to use GN alias group that references the version exposed in the Cargo.toml.
+Direct dependencies of the root crate can be "lifted" to the top-level by skipping the default
+root crate.
+
+### Simple Example
+```toml
+[package]
+name = "simple"
+version = "1.0.25"
+authors = ["Benjamin Brittain <bwb@google.com>"]
+edition = "2018"
+
+[dependencies]
+```
+
+converts to:
+
+```gn
+
+group("simple") {
+ deps = [":simple-1-0-25"]
+}
+
+rust_library("simple-1-0-25") {
+ crate_name = "simple"
+ crate_root = "//tools/cargo-gnaw/src/tests/simple/src/lib.rs"
+ output_name = "simple-9ac42213326ac72d"
+
+ deps = []
+
+ rustenv = []
+ rustflags = ["--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=9ac42213326ac72d",
+ "-Cextra-filename=-9ac42213326ac72d"]
+}
+
+```
+
+### Build Scripts
+GNaw intentionally does not handle build.rs scripts at compilation time. Any evaluation of a build.rs script is done when the crate is vendored.
+The resulting configuration which is usually produced by the build.rs script is put into [a
+section in the source Cargo.toml](#gn-configs).
+Simple build.rs scripts (ones that only depend upon Rust's `std` library) evaluate and
+automatically provide the author with the expected configuration.
+
+### Packages vs. targets vs. crates
+The difference between a "package", "target", and a "crate" is useful knowledge when working with GNaw:
+ - A Cargo "target" corresponds to source files that can be compiled into a Rust "crate".
+ There are multiple target types, including library and binary.
+ - A Cargo "package" contains one or more "targets", and can contain at most one library target.
+
+(Paraphrased from https://doc.rust-lang.org/cargo/reference/cargo-targets.html)
+
+Many Cargo packages only contain a single library target that produces a single library crate.
+Because of this (and because of the catchiness of the term) "crate" is often used imprecisely to
+refer to any of those three items depending on the context. However, when packages contain
+multiple targets, like one or more binaries alongside the library, it becomes important to
+distinguish between these terms.
+
+## GN configs
+Some targets, including but not limited to those that use build.rs scripts, require additional
+configuration that can be specified in the Cargo.toml file. This configuration is consumed by
+GNaw, not Cargo, and used when generating GN targets.
+
+### Basic configuration
+Basic configuration for a package's library target is specified as
+`gn.package.<PackageName>.<ExactVersion>`. This configuration will be applied to the GN library
+target unconditionally (for all platforms) and can include the following arrays:
+
+* `configs` - native GN config
+* `deps` - native GN dependency
+* `env_vars` - environment variables, usually used for pretending to be Cargo
+* `rustflags` - flags to pass through to rustc
+
+#### Example
+```toml
+[gn.package.anyhow."1.0.25"]
+rustflags = [ "--cfg=backtrace" ]
+```
+
+### Platform-specific configuration
+Configuration can also be applied to only specific platforms, for example only when building for
+Fuchsia or only when building for a specific host platform.
+
+Platforms are specified in the Rust cfg format (e.g. `cfg(unix)`). The same four configuration fields (`configs`, `deps`, `env_vars`, `rustflags`) documented above are supported.
+
+#### Example
+```toml
+[gn.package.foo."1.2.3".platform."cfg(target_os = \"fuchsia\")"]
+configs = [ "//some:fuchsia_specific_config" ]
+```
+
+### Generating executables for binary targets
+GNaw also supports generating GN executable targets for Cargo binary targets. GNaw must be told
+to generate such targets; none are generated by default.
+
+Platform-independent or -dependent configuration can be specified as well, similar to above. The
+same four configuration fields (`configs`, `deps`, `env_vars`, `rustflags`) documented above are
+supported.
+
+In the example below, `my-gn-name` will become both the name of the group target to depend upon
+and the executable's output_name, so usages can assume output_name == target_name (and must since
+get_target_outputs only works within the same file).
+
+#### Example
+This example generates a group target named `my-gn-name` and an executable target that produces
+`my-gn-name` from the binary `my-cargo-target` target inside package `my-cargo-package` version
+1.2.3. Also, when building for Unix this example applies an extra GN config.
+
+```toml
+[gn.package.my-cargo-package."1.2.3".binary.my-cargo-target]
+output_name = "my-gn-name"
+rustflags = [ "--cfg=feature" ]
+
+[gn.package.my-cargo-package."1.2.3".binary.my-cargo-target.platform."cfg(unix)"]
+configs = [ "//some:unix_specific_config" ]
+```
diff --git a/src/build.rs b/src/build.rs
new file mode 100644
index 0000000..a43558d
--- /dev/null
+++ b/src/build.rs
@@ -0,0 +1,142 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::target::GnTarget;
+use anyhow::{anyhow, Result};
+use std::path::PathBuf;
+use std::process::Command;
+use std::str::FromStr;
+
+pub struct BuildScriptOutput {
+ pub rustflags: Vec<String>,
+ pub rustenv: Vec<String>,
+}
+
+impl BuildScriptOutput {
+ pub fn parse_from_file(file: PathBuf) -> Result<Self> {
+ let contents = std::fs::read_to_string(file)?;
+ let configs: Vec<&str> = contents.split('\n').collect();
+ Ok(Self::parse(configs))
+ }
+
+ pub fn parse(lines: Vec<&str>) -> Self {
+ let mut bs = BuildScriptOutput { rustflags: vec![], rustenv: vec![] };
+
+ for line in lines {
+ if line.is_empty() {
+ continue;
+ }
+
+ let keyval = line.strip_prefix("cargo:");
+ if keyval.is_none() {
+ eprintln!("warning: build script output includes non-cargo lines: {}", line);
+ continue;
+ }
+
+ // More information on the available cargo instructions is here:
+ // https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script
+ //
+ // The following cases are currently unsupported. They all have to do with specific
+ // artifact type link arguments. The more general cargo:rustc-link-arg is covered.
+ //
+ // 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.
+ //
+ // cargo:rustc-link-arg-tests=FLAG — Passes custom flags to a linker for tests.
+ //
+ // cargo:rustc-link-arg-examples=FLAG — Passes custom flags to a linker for
+ // examples.
+ //
+ // cargo:rustc-link-arg-benches=FLAG — Passes custom flags to a linker for
+ // benchmarks.
+ //
+ // cargo:rustc-cdylib-link-arg=FLAG — Passes custom flags to a linker for cdylib crates.
+
+ let (key, value) = keyval.unwrap().split_once('=').unwrap();
+ match key {
+ "rerun-if-changed" => continue, // ignored because these are always vendored
+ "rerun-if-env-changed" => continue, // ignored because these are always vendored
+ "rustc-cfg" => bs.rustflags.push(format!("\"--cfg={}\"", value)),
+ "rustc-env" => bs.rustenv.push(format!("\"{}\"", value.to_string())),
+ "rustc-flags" => bs.rustflags.push(format!("\"{}\"", value.to_string())),
+ "rustc-link-arg" => bs.rustflags.push(format!("\"-C link-arg={}\"", value)),
+ "rustc-link-lib" => bs.rustflags.push(format!("\"-l {}\"", value)),
+ // "rustc-link-search" => bs.rustflags.push(format!("-L {}", value)),
+ "warning" => eprintln!("warning: {}", value),
+ &_ => eprintln!("warning: unable to parse build script output: {}", value),
+ }
+ }
+
+ bs
+ }
+}
+
+pub struct BuildScript {}
+
+impl BuildScript {
+ pub fn execute(target: &GnTarget<'_>) -> Result<BuildScriptOutput> {
+ // Previously, this code would compile build.rs scripts directly with rustc. However, build
+ // scripts can have dependencies of their own (e.g. build-dependencies in Cargo.toml).
+ // Rather than resolve the entire dependency chain ourselves, we call out to cargo to build
+ // the crate, performing the build.rs compilation along the way. This does more work than
+ // compiling the build.rs directly but we don't want to get into the dependency resolution
+ // business when cargo can do this already.
+ //
+ // Unfortunately, there is no way to tell cargo to build only the build script. We could
+ // link in the cargo library itself and call the correct functions to do only that part of
+ // the build but that seems overkill.
+ let cargo = std::env::var_os("CARGO").unwrap_or_else(|| std::ffi::OsString::from("cargo"));
+
+ let command = Command::new(cargo)
+ .current_dir(target.package_root())
+ .arg("check")
+ .arg("--message-format=json-render-diagnostics")
+ .output()
+ .expect("failed to execute cargo crate build");
+
+ if !command.status.success() {
+ return Err(anyhow!(
+ "Failed to compile {}:\n{}",
+ target.gn_target_name(),
+ String::from_utf8_lossy(&command.stderr)
+ ));
+ }
+
+ let stdout = String::from_utf8(command.stdout)?;
+ let messages: Vec<&str> = stdout.trim().split('\n').collect();
+ for message in messages {
+ let json: serde_json::Value =
+ serde_json::from_str(message).expect("JSON was not well-formatted");
+
+ let reason = json.get("reason").unwrap();
+ if reason != "build-script-executed" {
+ continue;
+ }
+
+ let package_id = json.get("package_id").unwrap().to_string().replace('"', "");
+ let package_elements: Vec<&str> = package_id.split(' ').collect();
+ let crate_name = package_elements[0];
+
+ // this build script execution was for the crate that we are expecting to have a
+ // build script (avoids issues where a dependency has a build script and we use
+ // that one by mistake)
+ if crate_name != target.target_name {
+ continue;
+ }
+
+ // cargo executes the build script for us and stores the output in its cache, we just
+ // need to parse it
+ let out_dir = json.get("out_dir").unwrap().as_str().unwrap();
+ let output = PathBuf::from_str(out_dir).unwrap().parent().unwrap().join("output");
+ return BuildScriptOutput::parse_from_file(output);
+ }
+
+ Err(anyhow!(
+ "We detected a build script in {} but cargo didn't compile it",
+ target.gn_target_name()
+ ))
+ }
+}
diff --git a/src/cfg.rs b/src/cfg.rs
new file mode 100644
index 0000000..75aa505
--- /dev/null
+++ b/src/cfg.rs
@@ -0,0 +1,259 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use anyhow::{anyhow, Result};
+
+/// Converts a cargo platform-specific dependency target into GN imperative control flow.
+///
+/// `Cargo.toml` files can define [platform-specific dependencies] in two ways:
+/// - Using `#[cfg]` syntax to match classes of targets.
+/// ```text
+/// [target.'cfg(unix)'.dependencies]
+/// foo = "0.1"
+/// ```
+///
+/// - Listing the full target the dependencies would apply to.
+/// ```text
+/// [target.aarch64-apple-darwin.dependencies]
+/// bar = "0.1"
+/// ```
+///
+/// This function is a wrapper around [`cfg_to_gn_conditional`] that converts full targets
+/// into `#[cfg]` syntax.
+///
+/// [platform-specific dependencies]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies
+pub fn target_to_gn_conditional(target: &str) -> Result<String> {
+ if target.starts_with("cfg") {
+ // This is #[cfg] syntax already.
+ cfg_to_gn_conditional(target)
+ } else if target.contains('-') {
+ // Handle this as a target triple. We only need to support targets in crates we
+ // depend on, so we use a simple exact match list that can be updated as needed.
+ let (target_arch, target_os) = match target {
+ "aarch64-apple-darwin" => ("aarch64", "macos"),
+ "riscv32i-unknown-none-elf" => ("riscv32i", "unknown"),
+ "riscv32imc-unknown-none-elf" => ("riscv32imc", "unknown"),
+ "thumbv6m-none-eabi" => ("thumbv6m", "unknown"),
+ // Return an error for unknown targets, to notify the dev that they should
+ // update this list.
+ _ => {
+ return Err(anyhow!(
+ "Unknown target: {}. Please add this to 'tools/cargo-gnaw/src/cfg.rs'.",
+ target
+ ))
+ }
+ };
+ cfg_to_gn_conditional(&format!(
+ "cfg(all(target_arch = \"{}\", target_os = \"{}\"))",
+ target_arch, target_os
+ ))
+ } else {
+ Err(anyhow!("Unknown platform-specific dependency target syntax: {}", target))
+ }
+}
+
+/// Convert a cargo cfg conditional into GN imperative control flow
+// TODO This should consume some information in the Cargo.toml file
+// to establish conventions for the carge -> GN build. This is hardcoded
+// to support Fuchsia at the moment.
+//
+// wow. an interview question in real life.
+pub fn cfg_to_gn_conditional(cfg: &str) -> Result<String> {
+ #[allow(clippy::if_same_then_else)]
+ if cfg.starts_with("cfg") {
+ Ok(cfg_to_gn_conditional(&cfg[4..cfg.len() - 1])?)
+ } else if cfg.starts_with("not") {
+ let section = &cfg[4..];
+ let mut paren_count = 1;
+ for (idx, c) in section.chars().enumerate() {
+ if c == ')' {
+ paren_count -= 1;
+ if paren_count == 0 {
+ return Ok(format!("!({})", cfg_to_gn_conditional(§ion[..idx])?));
+ }
+ } else if c == '(' {
+ paren_count += 1;
+ }
+ }
+ Err(anyhow!("bad not statement"))
+ } else if cfg == "any()" {
+ Ok(String::from("true"))
+ } else if cfg.starts_with("any") {
+ let section = &cfg[4..cfg.len()];
+ let mut accum = vec![];
+ let mut paren_count = 1;
+ let mut start_idx = 0;
+ for (idx, c) in section.chars().enumerate() {
+ if c == ')' {
+ paren_count -= 1;
+ if paren_count == 0 {
+ accum.push(cfg_to_gn_conditional(§ion[start_idx..idx])?);
+ }
+ } else if c == '(' {
+ paren_count += 1;
+ } else if c == ',' && paren_count <= 1 {
+ accum.push(cfg_to_gn_conditional(§ion[start_idx..idx])?);
+ start_idx = idx + 2; // skip ", "
+ }
+ }
+ Ok(format!("({})", accum.join(" || ")))
+ } else if cfg.starts_with("all") {
+ let section = &cfg[4..cfg.len()];
+ let mut accum = vec![];
+ let mut paren_count = 1;
+ let mut start_idx = 0;
+ for (idx, c) in section.chars().enumerate() {
+ if c == ')' {
+ paren_count -= 1;
+ if paren_count == 0 {
+ accum.push(cfg_to_gn_conditional(§ion[start_idx..idx])?);
+ }
+ } else if c == '(' {
+ paren_count += 1;
+ } else if c == ',' && paren_count <= 1 {
+ accum.push(cfg_to_gn_conditional(§ion[start_idx..idx])?);
+ start_idx = idx + 2; // skip ", "
+ }
+ }
+ Ok(format!("({})", accum.join(" && ")))
+ } else if cfg == "target_os = \"fuchsia\"" {
+ Ok(String::from("current_os == \"fuchsia\""))
+ } else if cfg == "target_os = \"macos\"" {
+ Ok(String::from("current_os == \"mac\""))
+ } else if cfg == "target_os = \"linux\"" {
+ Ok(String::from("current_os == \"linux\""))
+ } else if cfg == "target_os = \"unknown\"" {
+ Ok(String::from("current_os == \"unknown\""))
+ } else if cfg == "unix" {
+ // all our platforms are unix
+ Ok(String::from("true"))
+ } else if cfg == "feature = \"std\"" {
+ // need to detect std usage
+ Ok(String::from("true"))
+ } else if cfg == "target_arch = \"aarch64\"" {
+ Ok(String::from("current_cpu == \"arm64\""))
+ } else if cfg == "target_arch = \"x86_64\"" {
+ Ok(String::from("current_cpu == \"x64\""))
+ } else if cfg == "target_arch = \"wasm32\"" {
+ Ok(String::from("current_cpu == \"wasm32\""))
+ } else if cfg == "target_arch = \"riscv32i\"" {
+ Ok(String::from("current_cpu == \"riscv32\""))
+ } else if cfg == "target_arch = \"riscv32imc\"" {
+ Ok(String::from("current_cpu == \"riscv32\""))
+ } else if cfg == "target_arch = \"thumbv6m\"" {
+ Ok(String::from("current_cpu == \"arm\""))
+ } else if cfg == "windows" || cfg == "target_family = \"windows\"" {
+ // don't support host builds on windows right now
+ Ok(String::from("false"))
+
+ // Everything below is random cfgs that we don't know anything about
+ } else if cfg.starts_with("target_os") {
+ Ok(String::from("false"))
+ } else if cfg.starts_with("target_arch") {
+ Ok(String::from("false"))
+ } else if cfg.starts_with("target_env") {
+ Ok(String::from("false"))
+ } else if cfg.starts_with("target_feature") {
+ Ok(String::from("false"))
+ } else if cfg.starts_with("target_vendor") {
+ Ok(String::from("false"))
+ } else if cfg.starts_with("tokio_taskdump") {
+ Ok(String::from("false"))
+ } else if cfg.starts_with("tokio_unstable") {
+ Ok(String::from("false"))
+ } else if cfg.starts_with("tracing_unstable") {
+ Ok(String::from("false"))
+ } else if cfg.starts_with("docsrs") {
+ Ok(String::from("false"))
+ } else {
+ // TODO(https://fxbug.dev/42061225) better handling needed for these cases.
+ Err(anyhow!("Unknown cfg option used: {}", cfg))
+ }
+}
+
+#[test]
+fn basic_fuchsia() {
+ let cfg_str = r#"cfg(target_os = "fuchsia")"#;
+ let output = cfg_to_gn_conditional(cfg_str).unwrap();
+ assert_eq!(output, "current_os == \"fuchsia\"");
+}
+
+#[test]
+fn conditonal_empty_any() {
+ let cfg_str = r#"cfg(any())"#;
+ let output = cfg_to_gn_conditional(cfg_str).unwrap();
+ assert_eq!(output, "true");
+}
+
+#[test]
+fn conditonal_any() {
+ let cfg_str = r#"cfg(any(target_os = "fuchsia", target_os = "macos"))"#;
+ let output = cfg_to_gn_conditional(cfg_str).unwrap();
+ assert_eq!(output, "(current_os == \"fuchsia\" || current_os == \"mac\")");
+}
+
+#[test]
+fn conditonal_all() {
+ let cfg_str = r#"cfg(all(target_os = "fuchsia", target_os = "macos"))"#;
+ let output = cfg_to_gn_conditional(cfg_str).unwrap();
+ assert_eq!(output, "(current_os == \"fuchsia\" && current_os == \"mac\")");
+}
+
+#[test]
+fn conditonal_all_not() {
+ let cfg_str = r#"cfg(all(target_os = "fuchsia", not(target_os = "macos")))"#;
+ let output = cfg_to_gn_conditional(cfg_str).unwrap();
+ assert_eq!(output, "(current_os == \"fuchsia\" && !(current_os == \"mac\"))");
+}
+
+#[test]
+fn conditonal_fail() {
+ let cfg_str = r#"cfg(everything(target_os = "fuchsia"))"#;
+ let output = cfg_to_gn_conditional(cfg_str).unwrap_err();
+ assert_eq!(output.to_string(), r#"Unknown cfg option used: everything(target_os = "fuchsia")"#);
+}
+
+#[test]
+fn nested_cfgs() {
+ let cfg_str =
+ r#"cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_os = "hermit"))"#;
+ let output = cfg_to_gn_conditional(cfg_str).unwrap();
+ assert_eq!(
+ output.to_string(),
+ r#"((current_cpu == "x64" || current_cpu == "arm64") && false)"#
+ );
+}
+
+#[test]
+fn target_cfg() {
+ let target_str = r#"cfg(target_os = "fuchsia")"#;
+ let output = target_to_gn_conditional(target_str).unwrap();
+ assert_eq!(output, "current_os == \"fuchsia\"");
+}
+
+#[test]
+fn target_full() {
+ let target_str = r#"aarch64-apple-darwin"#;
+ let output = target_to_gn_conditional(target_str).unwrap();
+ assert_eq!(output, "(current_cpu == \"arm64\" && current_os == \"mac\")");
+}
+
+#[test]
+fn target_unknown_full() {
+ // We use a placeholder target "guaranteed" to never be used anywhere.
+ let target_str = r#"foo-bar-baz"#;
+ let err = target_to_gn_conditional(target_str).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Unknown target: foo-bar-baz. Please add this to 'tools/cargo-gnaw/src/cfg.rs'.",
+ );
+}
+
+#[test]
+fn target_unknown_syntax() {
+ // No leading "cfg", no hyphens.
+ let target_str = r#"fizzbuzz"#;
+ let err = target_to_gn_conditional(target_str).unwrap_err();
+ assert_eq!(err.to_string(), "Unknown platform-specific dependency target syntax: fizzbuzz");
+}
diff --git a/src/gn.rs b/src/gn.rs
new file mode 100644
index 0000000..d2479f4
--- /dev/null
+++ b/src/gn.rs
@@ -0,0 +1,887 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::cfg::{cfg_to_gn_conditional, target_to_gn_conditional};
+use crate::target::GnTarget;
+use crate::types::*;
+use crate::{
+ BinaryRenderOptions, CombinedTargetCfg, GlobalTargetCfgs, GroupVisibility, RuleRenaming,
+};
+use anyhow::{Context, Result};
+use cargo_metadata::Package;
+use std::borrow::Cow;
+use std::collections::BTreeMap;
+use std::fmt::Display;
+use std::io::{self, Write};
+use std::path::Path;
+use walkdir::WalkDir;
+
+/// Utility to add a version suffix to a GN target name.
+pub fn add_version_suffix(prefix: &str, version: &impl ToString) -> String {
+ let mut accum = String::new();
+ accum.push_str(&prefix);
+ accum.push_str("-v");
+ accum.push_str(version.to_string().replace('.', "_").as_str());
+ accum
+}
+
+/// Write a header for the output GN file
+pub fn write_header<W: io::Write>(output: &mut W, _cargo_file: &Path) -> Result<()> {
+ writeln!(
+ output,
+ include_str!("../templates/gn_header.template"),
+ // TODO set this, but in a way that tests don't fail on Jan 1st
+ year = "2020",
+ )
+ .map_err(Into::into)
+}
+
+pub fn write_fuchsia_sdk_metadata_header<W: io::Write>(output: &mut W) -> Result<()> {
+ writeln!(output, include_str!("../templates/gn_sdk_metadata_header.template"),)
+ .map_err(Into::into)
+}
+
+/// Write an import stament for the output GN file
+pub fn write_import<W: io::Write>(output: &mut W, file_name: &str) -> Result<()> {
+ writeln!(output, include_str!("../templates/gn_import.template"), file_name = file_name)
+ .map_err(Into::into)
+}
+
+/// Writes rules at the top of the GN file that don't have the version appended
+///
+/// Args:
+/// - `rule_renaming`: if set, the renaming rules will be used to generate
+/// appropriate group and rule names. See [RuleRenaming] for details.
+pub fn write_top_level_rule<W: io::Write>(
+ output: &mut W,
+ platform: Option<&str>,
+ pkg: &Package,
+ group_visibility: Option<&GroupVisibility>,
+ rule_renaming: Option<&RuleRenaming>,
+ testonly: bool,
+ has_tests: bool,
+) -> Result<()> {
+ let target_name = if pkg.is_proc_macro() {
+ format!("{}($host_toolchain)", pkg.gn_name())
+ } else {
+ pkg.gn_name()
+ };
+ if let Some(ref platform) = platform {
+ writeln!(
+ output,
+ "if ({conditional}) {{\n",
+ conditional = target_to_gn_conditional(platform)?
+ )?;
+ }
+
+ let group_rule = rule_renaming.and_then(|t| t.group_name.as_deref()).unwrap_or("group");
+ let optional_visibility =
+ group_visibility.map(|v| format!("visibility = {}", v.variable)).unwrap_or_default();
+ writeln!(
+ output,
+ include_str!("../templates/top_level_gn_rule.template"),
+ group_name = pkg.name,
+ dep_name = target_name,
+ group_rule_name = group_rule,
+ optional_visibility = optional_visibility,
+ optional_testonly = if testonly { "testonly = true" } else { "" },
+ )?;
+
+ if has_tests {
+ let name = pkg.name.clone() + "-test";
+ writeln!(
+ output,
+ include_str!("../templates/top_level_gn_rule.template"),
+ group_name = name,
+ group_rule_name = group_rule,
+ dep_name = target_name + "-test",
+ optional_visibility = optional_visibility,
+ optional_testonly = "testonly = true",
+ )?;
+ }
+
+ if platform.is_some() {
+ writeln!(output, "}}\n")?;
+ }
+ Ok(())
+}
+
+/// Writes Fuchsia SDK metadata for a top-level rule.
+pub fn write_fuchsia_sdk_metadata<W: io::Write>(
+ output: &mut W,
+ platform: Option<&str>,
+ pkg: &Package,
+ abs_dir: &Path,
+ rel_dir: &Path,
+) -> Result<()> {
+ // TODO: add features, and registry
+ std::fs::create_dir_all(abs_dir)
+ .with_context(|| format!("while making directories for {}", abs_dir.display()))?;
+
+ let file_name = format!("{}.sdk.meta.json", pkg.name);
+ let abs_path = abs_dir.join(&file_name);
+ let rel_path = rel_dir.join(&file_name);
+ let mut metadata_output = std::fs::File::create(&abs_path)
+ .with_context(|| format!("while writing {}", abs_path.display()))?;
+ writeln!(
+ metadata_output,
+ r#"{{
+ "type": "{sdk_atom_type}",
+ "name": "{group_name}",
+ "version": "{version}"
+}}"#,
+ sdk_atom_type = if pkg.is_proc_macro() { "rust_3p_proc_macro" } else { "rust_3p_library" },
+ group_name = pkg.name,
+ version = pkg.version,
+ )?;
+
+ let platform_constraint = if let Some(p) = platform {
+ format!(" && {}", target_to_gn_conditional(p)?)
+ } else {
+ "".to_owned()
+ };
+
+ writeln!(
+ output,
+ r#" if (_generating_sdk{constraint}) {{
+ sdk_atom("{group_name}_sdk") {{
+ id = "sdk://${{_sdk_prefix}}third_party/rust_crates/{group_name}"
+ category = "internal"
+ meta = {{
+ source = "{source}"
+ dest = "${{_sdk_prefix}}third_party/rust_crates/{group_name}/meta.json"
+ schema = "3p_rust_library"
+ }}
+ }}
+ }}
+ "#,
+ constraint = platform_constraint,
+ source = rel_path.display(),
+ group_name = pkg.name,
+ )?;
+
+ Ok(())
+}
+
+/// Writes rules at the top of the GN file that don't have the version appended
+pub fn write_binary_top_level_rule<'a, W: io::Write>(
+ output: &mut W,
+ platform: Option<String>,
+ target: &GnTarget<'a>,
+ options: &BinaryRenderOptions<'_>,
+) -> Result<()> {
+ if let Some(ref platform) = platform {
+ writeln!(
+ output,
+ "if ({conditional}) {{\n",
+ conditional = cfg_to_gn_conditional(platform)?
+ )?;
+ }
+ writeln!(
+ output,
+ include_str!("../templates/top_level_binary_gn_rule.template"),
+ group_name = options.binary_name,
+ dep_name = target.gn_target_name(),
+ optional_testonly = "",
+ )?;
+
+ if options.tests_enabled {
+ let name = options.binary_name.to_owned() + "-test";
+ let dep_name = target.gn_target_name() + "-test";
+ writeln!(
+ output,
+ include_str!("../templates/top_level_binary_gn_rule.template"),
+ group_name = name,
+ dep_name = dep_name,
+ optional_testonly = "testonly = true",
+ )?;
+ }
+
+ if platform.is_some() {
+ writeln!(output, "}}\n")?;
+ }
+ Ok(())
+}
+
+struct GnField {
+ ty: String,
+ exists: bool,
+ // Use BTreeMap so that iteration over platforms is stable.
+ add_fields: BTreeMap<Option<Platform>, Vec<String>>,
+ remove_fields: BTreeMap<Option<Platform>, Vec<String>>,
+}
+impl GnField {
+ /// If defining a new field in the template
+ pub fn new(ty: &str) -> GnField {
+ GnField {
+ ty: ty.to_string(),
+ exists: false,
+ add_fields: BTreeMap::new(),
+ remove_fields: BTreeMap::new(),
+ }
+ }
+
+ /// If the field already exists in the template
+ pub fn exists(ty: &str) -> GnField {
+ GnField { exists: true, ..Self::new(ty) }
+ }
+
+ pub fn add_platform_cfg<T: AsRef<str> + Display>(&mut self, platform: Option<String>, cfg: T) {
+ let field = self.add_fields.entry(platform).or_default();
+ field.push(format!("\"{}\"", cfg));
+ }
+
+ pub fn remove_platform_cfg<T: AsRef<str> + Display>(
+ &mut self,
+ platform: Option<String>,
+ cfg: T,
+ ) {
+ let field = self.remove_fields.entry(platform).or_default();
+ field.push(format!("\"{}\"", cfg));
+ }
+
+ pub fn add_cfg<T: AsRef<str> + Display>(&mut self, cfg: T) {
+ self.add_platform_cfg(None, cfg)
+ }
+
+ pub fn remove_cfg<T: AsRef<str> + Display>(&mut self, cfg: T) {
+ self.remove_platform_cfg(None, cfg)
+ }
+
+ pub fn render_gn(&self) -> String {
+ let mut output = if self.exists {
+ // We don't create an empty [] if the field already exists
+ match self.add_fields.get(&None) {
+ Some(add_fields) => format!("{} += [{}]\n", self.ty, add_fields.join(",")),
+ None => "".to_string(),
+ }
+ } else {
+ format!("{} = [{}]\n", self.ty, self.add_fields.get(&None).unwrap_or(&vec![]).join(","))
+ };
+
+ // remove platfrom independent configs
+ if let Some(rm_fields) = self.remove_fields.get(&None) {
+ output.push_str(format!("{} -= [{}]\n", self.ty, rm_fields.join(",")).as_str());
+ }
+
+ // Add logic for specific platforms
+ for platform in self.add_fields.keys().filter(|k| k.is_some()) {
+ output.push_str(
+ format!(
+ "if ({}) {{\n",
+ cfg_to_gn_conditional(platform.as_ref().unwrap()).expect("valid cfg")
+ )
+ .as_str(),
+ );
+ output.push_str(
+ format!(
+ "{} += [{}]",
+ self.ty,
+ self.add_fields.get(platform).unwrap_or(&vec![]).join(",")
+ )
+ .as_str(),
+ );
+ output.push_str("}\n");
+ }
+
+ // Remove logic for specific platforms
+ for platform in self.remove_fields.keys().filter(|k| k.is_some()) {
+ output.push_str(
+ format!(
+ "if ({}) {{\n",
+ cfg_to_gn_conditional(platform.as_ref().unwrap()).expect("valid cfg")
+ )
+ .as_str(),
+ );
+ output.push_str(
+ format!(
+ "{} -= [{}]",
+ self.ty,
+ self.remove_fields.get(platform).unwrap_or(&vec![]).join(",")
+ )
+ .as_str(),
+ );
+ output.push_str("}\n");
+ }
+ output
+ }
+}
+
+/// Write a Target to the GN file.
+///
+/// Includes information from the build script.
+///
+/// Args:
+/// - `renamed_rule`: if this is set, the rule that is written out is changed
+/// to be the content of this arg.
+pub fn write_rule<W: io::Write>(
+ output: &mut W,
+ target: &GnTarget<'_>,
+ project_root: &Path,
+ global_target_cfgs: Option<&GlobalTargetCfgs>,
+ custom_build: Option<&CombinedTargetCfg<'_>>,
+ output_name: Option<&str>,
+ is_testonly: bool,
+ is_test: bool,
+ renamed_rule: Option<&str>,
+ scan_for_licenses: bool,
+) -> Result<()> {
+ // Generate a section for dependencies that is paramaterized on toolchain
+ let mut dependencies = String::from("deps = []\n");
+ let mut aliased_deps = vec![];
+
+ // Stable output of platforms
+ let mut platform_deps: Vec<(
+ &Option<String>,
+ &Vec<(&cargo_metadata::Package, std::string::String)>,
+ )> = target.dependencies.iter().collect();
+ platform_deps.sort_by(|p, p2| p.0.cmp(p2.0));
+
+ for (platform, deps) in platform_deps {
+ // sort for stable output
+ let mut deps = deps.clone();
+ deps.sort_by(|a, b| (a.0).id.cmp(&(b.0).id));
+
+ // TODO(bwb) feed GN toolchain mapping in as a configuration to make more generic
+ match platform.as_ref().map(String::as_str) {
+ None => {
+ for pkg in deps {
+ dependencies.push_str(" deps += [");
+ if pkg.0.is_proc_macro() {
+ dependencies.push_str(
+ format!("\":{}($host_toolchain)\"", pkg.0.gn_name()).as_str(),
+ );
+ } else {
+ dependencies.push_str(format!("\":{}\"", pkg.0.gn_name()).as_str());
+ }
+ dependencies.push_str("]\n");
+ if pkg.0.name.replace("-", "_") != pkg.1 {
+ aliased_deps.push(format!("{} = \":{}\" ", pkg.1, pkg.0.gn_name()));
+ }
+ }
+ }
+ Some(platform) => {
+ dependencies.push_str(
+ format!("if ({}) {{\n", target_to_gn_conditional(platform)?).as_str(),
+ );
+ for pkg in deps {
+ dependencies.push_str(" deps += [");
+ if pkg.0.is_proc_macro() {
+ dependencies.push_str(
+ format!("\":{}($host_toolchain)\"", pkg.0.gn_name()).as_str(),
+ );
+ } else {
+ dependencies.push_str(format!("\":{}\"", pkg.0.gn_name()).as_str());
+ }
+ dependencies.push_str("]\n");
+
+ if pkg.0.name.replace("-", "_") != pkg.1 {
+ aliased_deps.push(format!("{} = \":{}\" ", pkg.1, pkg.0.gn_name()));
+ }
+ }
+ dependencies.push_str("}\n");
+ }
+ }
+ }
+
+ // write the features into the configs
+ let mut rustflags = GnField::new("rustflags");
+ let mut rustenv = GnField::new("rustenv");
+ let mut configs = GnField::exists("configs");
+ let mut require_licenses = false;
+
+ if let Some(global_cfg) = global_target_cfgs {
+ for cfg in &global_cfg.remove_cfgs {
+ configs.remove_cfg(cfg);
+ }
+ for cfg in &global_cfg.add_cfgs {
+ configs.add_cfg(cfg);
+ }
+ if let Some(ref require) = global_cfg.require_licenses {
+ require_licenses = *require;
+ }
+ }
+
+ // Associate unique metadata with this crate
+ rustflags.add_cfg("--cap-lints=allow");
+ rustflags.add_cfg(format!("--edition={}", target.edition));
+ rustflags.add_cfg(format!("-Cmetadata={}", target.metadata_hash()));
+ rustflags.add_cfg(format!("-Cextra-filename=-{}", target.metadata_hash()));
+ if is_test {
+ rustflags.add_cfg("--test");
+ }
+
+ // Aggregate feature flags
+ for feature in target.features {
+ rustflags.add_cfg(format!("--cfg=feature=\\\"{}\\\"", feature));
+ }
+
+ // From the gn custom configs, add flags, env vars, and visibility
+ let mut visibility = vec![];
+
+ let mut uses_fuchsia_license = false;
+
+ if let Some(custom_build) = custom_build {
+ for (platform, cfg) in custom_build {
+ if let Some(ref deps) = cfg.deps {
+ let build_deps = |dependencies: &mut String| {
+ for dep in deps {
+ dependencies.push_str(format!(" deps += [\"{}\"]", dep).as_str());
+ }
+ };
+ if let Some(platform) = platform {
+ dependencies.push_str(
+ format!("if ({}) {{\n", target_to_gn_conditional(platform)?).as_str(),
+ );
+ build_deps(&mut dependencies);
+ dependencies.push_str("}\n");
+ } else {
+ build_deps(&mut dependencies);
+ }
+ }
+ if let Some(ref flags) = cfg.rustflags {
+ for flag in flags {
+ rustflags.add_platform_cfg(platform.cloned(), flag.to_string());
+ }
+ }
+ if let Some(ref env_vars) = cfg.env_vars {
+ for flag in env_vars {
+ rustenv.add_platform_cfg(platform.cloned(), flag.to_string());
+ }
+ }
+ if let Some(ref crate_configs) = cfg.configs {
+ for config in crate_configs {
+ configs.add_platform_cfg(platform.cloned(), config);
+ }
+ }
+ if let Some(ref vis) = cfg.visibility {
+ visibility.extend(vis.iter().map(|v| format!(" visibility += [\"{}\"]", v)));
+ }
+ if let Some(ref uses) = cfg.uses_fuchsia_license {
+ uses_fuchsia_license = *uses;
+ }
+ }
+ }
+
+ let visibility = if visibility.is_empty() {
+ String::from("visibility = [\":*\"]\n")
+ } else {
+ let mut v = String::from("visibility = []\n");
+ v.extend(visibility);
+ v
+ };
+
+ // making the templates more readable.
+ let aliased_deps_str = if aliased_deps.is_empty() {
+ String::from("")
+ } else {
+ format!("aliased_deps = {{{}}}", aliased_deps.join("\n"))
+ };
+
+ // GN root relative path
+ let root_relative_path = format!(
+ "//{}",
+ target
+ .crate_root
+ .canonicalize_utf8()
+ .unwrap()
+ .strip_prefix(project_root)
+ .with_context(|| format!(
+ "{} is located outside of the project. Check your vendoring setup",
+ target.name()
+ ))?
+ .to_string()
+ );
+ let output_name = if is_test {
+ output_name.map_or_else(
+ || {
+ Cow::Owned(format!(
+ "{}-{}-test",
+ target.name().replace('-', "_"),
+ target.metadata_hash()
+ ))
+ },
+ |n| Cow::Owned(format!("{}-test", n)),
+ )
+ } else {
+ output_name.map_or_else(
+ || {
+ Cow::Owned(format!(
+ "{}-{}",
+ target.name().replace('-', "_"),
+ target.metadata_hash()
+ ))
+ },
+ Cow::Borrowed,
+ )
+ };
+ let mut target_name = target.gn_target_name();
+ if is_test {
+ target_name.push_str("-test");
+ }
+
+ let optional_testonly = if is_testonly || is_test { "testonly = true" } else { "" };
+
+ let gn_rule = if let Some(renamed_rule) = renamed_rule {
+ renamed_rule.to_owned()
+ } else if is_test {
+ "executable".to_owned()
+ } else {
+ target.gn_target_type()
+ };
+
+ let mut license_files_found = false;
+ let mut applicable_licenses = GnField::new("applicable_licenses");
+ let gn_crate_name = target.name().replace('-', "_");
+
+ if scan_for_licenses {
+ // Scan for LICENSE* files in the crate's root dir.
+ // Disabled in unit tests, where package_root always fails.
+ let mut license_files = vec![];
+
+ for entry in WalkDir::new(target.package_root()).follow_links(false).into_iter() {
+ let entry = entry.unwrap();
+ let file_name_lower = entry.file_name().to_string_lossy().to_lowercase();
+ if file_name_lower.starts_with("license")
+ || file_name_lower.starts_with("licence")
+ || file_name_lower.starts_with("copyright")
+ {
+ let license_file_label = format!(
+ "//{}",
+ entry
+ .path()
+ .canonicalize()
+ .unwrap()
+ .strip_prefix(project_root)
+ .unwrap()
+ .to_string_lossy()
+ );
+ license_files.push(license_file_label);
+ license_files_found = true;
+ }
+ }
+
+ if license_files_found {
+ if uses_fuchsia_license {
+ anyhow::bail!("ERROR: Crate {}.{} has license files but is set with uses_fuchsia_license = true", target.name(), target.version())
+ }
+
+ // Define a license target
+ let license_target_name = format!("{}.license", target_name);
+ // Directory iteration order is random, so sort alphabetically.
+ license_files.sort();
+ let mut license_files_param = GnField::new("license_files");
+ for entry in license_files {
+ license_files_param.add_cfg(entry);
+ }
+
+ writeln!(
+ output,
+ include_str!("../templates/gn_license.template"),
+ target_name = license_target_name,
+ public_package_name = gn_crate_name,
+ license_files = license_files_param.render_gn(),
+ )?;
+
+ applicable_licenses.add_cfg(format!(":{}", license_target_name));
+ } else if uses_fuchsia_license ||
+ // Empty crates are stubs crates authored by Fuchsia.
+ target.package_root().parent().unwrap().ends_with("third_party/rust_crates/empty")
+ {
+ applicable_licenses.add_cfg("//build/licenses:fuchsia_license");
+ } else if require_licenses {
+ anyhow::bail!(
+ "ERROR: Crate at {} must have LICENSE* or COPYRIGHT files.
+
+Make sure such files are placed at the crate's root folder.
+
+Alternatively, if the crate's license is the same as Fuchsia's,
+modify //third_party/rust_crates/Cargo.toml by adding:
+
+```
+[gn.package.{}.\"{}\"]
+uses_fuchsia_license = true
+```",
+ target.package_root(),
+ target.name(),
+ target.version()
+ );
+ }
+ }
+
+ writeln!(
+ output,
+ include_str!("../templates/gn_rule.template"),
+ gn_rule = gn_rule,
+ target_name = target_name,
+ crate_name = gn_crate_name,
+ output_name = output_name,
+ root_path = root_relative_path,
+ aliased_deps = aliased_deps_str,
+ dependencies = dependencies,
+ cfgs = configs.render_gn(),
+ rustenv = rustenv.render_gn(),
+ rustflags = rustflags.render_gn(),
+ visibility = visibility,
+ optional_testonly = optional_testonly,
+ applicable_licenses = applicable_licenses.render_gn(),
+ )
+ .map_err(Into::into)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use camino::Utf8Path;
+ use cargo_metadata::Edition;
+ use semver::Version;
+ use std::collections::HashMap;
+
+ // WARNING: the expected output tests below have non-printable artifacts
+ // due to the way the templates are generated. Do not remove extra spaces
+ // in the quoted strings.
+
+ #[test]
+ fn canonicalized_paths() {
+ let pkg_id = cargo_metadata::PackageId { repr: String::from("42") };
+ let version = Version::new(0, 1, 0);
+
+ let mut project_root = std::env::temp_dir();
+ project_root.push(Path::new("canonicalized_paths"));
+ std::fs::write(&project_root, "").expect("write to temp file");
+
+ let target = GnTarget::new(
+ &pkg_id,
+ "test_target",
+ "test_package",
+ Edition::E2018,
+ Utf8Path::from_path(project_root.as_path()).unwrap(),
+ &version,
+ GnRustType::Library,
+ &[],
+ false,
+ HashMap::new(),
+ );
+
+ let not_prefix = Path::new("/foo");
+ let prefix = std::env::temp_dir();
+
+ let mut output = vec![];
+ assert!(write_rule(
+ &mut output,
+ &target,
+ not_prefix,
+ None,
+ None,
+ None,
+ false,
+ false,
+ None,
+ false
+ )
+ .is_err());
+ assert!(write_rule(
+ &mut output,
+ &target,
+ prefix.as_path(),
+ None,
+ None,
+ None,
+ false,
+ false,
+ None,
+ false,
+ )
+ .is_ok());
+ }
+
+ #[test]
+ fn simple_target() {
+ let pkg_id = cargo_metadata::PackageId { repr: String::from("42") };
+ let version = Version::new(0, 1, 0);
+
+ let mut project_root = std::env::temp_dir();
+ project_root.push(Path::new("simple_target"));
+ std::fs::write(&project_root, "").expect("write to temp file");
+
+ let target = GnTarget::new(
+ &pkg_id,
+ "test_target",
+ "test_package",
+ Edition::E2018,
+ Utf8Path::from_path(project_root.as_path()).unwrap(),
+ &version,
+ GnRustType::Library,
+ &[],
+ false,
+ HashMap::new(),
+ );
+
+ let mut output = vec![];
+ write_rule(
+ &mut output,
+ &target,
+ std::env::temp_dir().as_path(),
+ None,
+ None,
+ None,
+ false,
+ false,
+ None,
+ false,
+ )
+ .unwrap();
+ let output = String::from_utf8(output).unwrap();
+ assert_eq!(
+ output,
+ r#"rust_library("test_package-v0_1_0") {
+ crate_name = "test_target"
+ crate_root = "//simple_target"
+ output_name = "test_target-c5bf97c44457465a"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = ["--cap-lints=allow","--edition=2018","-Cmetadata=c5bf97c44457465a","-Cextra-filename=-c5bf97c44457465a"]
+
+
+ visibility = [":*"]
+
+
+ applicable_licenses = []
+
+}
+
+"#
+ );
+ }
+
+ #[test]
+ fn binary_target() {
+ let pkg_id = cargo_metadata::PackageId { repr: String::from("42") };
+ let version = Version::new(0, 1, 0);
+
+ let mut project_root = std::env::temp_dir();
+ project_root.push(Path::new("binary_target"));
+ std::fs::write(&project_root, "").expect("write to temp file");
+
+ let target = GnTarget::new(
+ &pkg_id,
+ "test_target",
+ "test_package",
+ Edition::E2018,
+ Utf8Path::from_path(project_root.as_path()).unwrap(),
+ &version,
+ GnRustType::Binary,
+ &[],
+ false,
+ HashMap::new(),
+ );
+
+ let outname = Some("rainbow_binary");
+ let mut output = vec![];
+ write_rule(
+ &mut output,
+ &target,
+ std::env::temp_dir().as_path(),
+ None,
+ None,
+ outname,
+ false,
+ false,
+ None,
+ false,
+ )
+ .unwrap();
+
+ let output = String::from_utf8(output).unwrap();
+ assert_eq!(
+ output,
+ r#"executable("test_package-test_target-v0_1_0") {
+ crate_name = "test_target"
+ crate_root = "//binary_target"
+ output_name = "rainbow_binary"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = ["--cap-lints=allow","--edition=2018","-Cmetadata=bf8f4a806276c599","-Cextra-filename=-bf8f4a806276c599"]
+
+
+ visibility = [":*"]
+
+
+ applicable_licenses = []
+
+}
+
+"#
+ );
+ }
+
+ #[test]
+ fn renamed_target() {
+ let pkg_id = cargo_metadata::PackageId { repr: String::from("42") };
+ let version = Version::new(0, 1, 0);
+
+ let mut project_root = std::env::temp_dir();
+ project_root.push(Path::new("renamed_target"));
+ std::fs::write(&project_root, "").expect("write to temp file");
+
+ let target = GnTarget::new(
+ &pkg_id,
+ "test_target",
+ "test_package",
+ Edition::E2018,
+ Utf8Path::from_path(project_root.as_path()).unwrap(),
+ &version,
+ GnRustType::Binary,
+ &[],
+ false,
+ HashMap::new(),
+ );
+
+ let outname = Some("rainbow_binary");
+ let mut output = vec![];
+ write_rule(
+ &mut output,
+ &target,
+ std::env::temp_dir().as_path(),
+ None,
+ None,
+ outname,
+ false,
+ false,
+ Some("renamed_rule"),
+ false,
+ )
+ .unwrap();
+ let output = String::from_utf8(output).unwrap();
+ assert_eq!(
+ output,
+ r#"renamed_rule("test_package-test_target-v0_1_0") {
+ crate_name = "test_target"
+ crate_root = "//renamed_target"
+ output_name = "rainbow_binary"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = ["--cap-lints=allow","--edition=2018","-Cmetadata=bf8f4a806276c599","-Cextra-filename=-bf8f4a806276c599"]
+
+
+ visibility = [":*"]
+
+
+ applicable_licenses = []
+
+}
+
+"#
+ );
+ }
+}
diff --git a/src/graph.rs b/src/graph.rs
new file mode 100644
index 0000000..34e4e24
--- /dev/null
+++ b/src/graph.rs
@@ -0,0 +1,191 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::target::GnTarget;
+use crate::types::*;
+use anyhow::{anyhow, Context as _, Result};
+use cargo_metadata::{DependencyKind, Metadata, Package, PackageId};
+use std::collections::{HashMap, HashSet};
+use std::convert::TryFrom;
+
+pub struct GnBuildGraph<'a> {
+ metadata: &'a Metadata,
+ // hashset because the same target can get added multiple times
+ targets: HashSet<GnTarget<'a>>,
+}
+
+impl<'a> GnBuildGraph<'a> {
+ pub fn new(metadata: &'a Metadata) -> Self {
+ GnBuildGraph { metadata, targets: HashSet::new() }
+ }
+
+ pub fn targets(&'a self) -> impl Iterator<Item = &'a GnTarget<'_>> {
+ self.targets.iter()
+ }
+
+ pub fn find_library_target(&self, package: &str, version: &str) -> Option<&GnTarget<'_>> {
+ self.targets().find(|t| match t.target_type {
+ GnRustType::Library
+ | GnRustType::Rlib
+ | GnRustType::Staticlib
+ | GnRustType::Dylib
+ | GnRustType::Cdylib
+ | GnRustType::ProcMacro => t.pkg_name == package && t.version() == version,
+ _ => false,
+ })
+ }
+
+ pub fn find_binary_target(
+ &self,
+ package: &str,
+ version: &str,
+ target: &str,
+ ) -> Option<&GnTarget<'_>> {
+ self.targets().find(|t| match t.target_type {
+ GnRustType::Binary => {
+ t.pkg_name == package && t.version() == version && t.target_name == target
+ }
+ _ => false,
+ })
+ }
+
+ /// Add a cargo package to the target list. If the dependencies
+ /// are not already in the target graph, add them as well
+ pub fn add_cargo_package(&mut self, cargo_pkg_id: PackageId) -> Result<()> {
+ let package = &self.metadata[&cargo_pkg_id];
+ for node in self
+ .metadata
+ .resolve
+ .as_ref()
+ .unwrap()
+ .nodes
+ .iter()
+ .filter(|node| node.id == cargo_pkg_id)
+ {
+ let mut dependencies = HashMap::<Option<Platform>, Vec<(&'a Package, String)>>::new();
+
+ // collect the dependency edges for this node
+ for node_dep in node.deps.iter() {
+ let id = &node_dep.pkg;
+ if node_dep.dep_kinds.is_empty() {
+ return Err(anyhow!("Must use rustc 1.41+ to use cargo-gnaw. Needs dependency kind information."));
+ }
+ for kinds in &node_dep.dep_kinds {
+ let kind = &kinds.kind;
+ let target = &kinds.target;
+ match kind {
+ DependencyKind::Normal => {
+ self.add_cargo_package(id.clone())?;
+ let platform = target.as_ref().map(|x| format!("{}", x));
+ let platform_deps = dependencies.entry(platform).or_default();
+
+ // Somehow we get duplicates of the same dependency
+ // from cargo on some targets. Filter those out
+ // here. This is technically quadratic, but that's
+ // unlikely to matter for the number of deps on a
+ // single crate.
+ if !platform_deps.iter().any(|dep| dep.1 == node_dep.name.clone()) {
+ platform_deps.push((&self.metadata[id], node_dep.name.clone()));
+ }
+ }
+ DependencyKind::Build => {}
+ DependencyKind::Development => {}
+ err => {
+ return Err(anyhow!(
+ "Don't know how to handle this kind of dependency edge: {:?}",
+ err
+ ))
+ }
+ }
+ }
+ }
+
+ let has_build_script = package.targets.iter().any(|target| {
+ for kind in &target.kind {
+ let gn_rust_type = GnRustType::try_from(kind.as_str())
+ .with_context(|| {
+ format!("Failed to resolve GN target type for: {:?}", target)
+ })
+ .unwrap();
+
+ if gn_rust_type == GnRustType::BuildScript {
+ return true;
+ }
+ }
+
+ false
+ });
+
+ for rust_target in package.targets.iter() {
+ for kind in &rust_target.kind {
+ let target_type = GnRustType::try_from(kind.as_str())?;
+ match target_type {
+ GnRustType::Library | GnRustType::Rlib | GnRustType::ProcMacro => {
+ let gn_target = GnTarget::new(
+ &node.id,
+ &rust_target.name,
+ &package.name,
+ package.edition,
+ &rust_target.src_path,
+ &package.version,
+ target_type,
+ node.features.as_slice(),
+ has_build_script,
+ dependencies.clone(),
+ );
+ self.targets.insert(gn_target);
+ }
+ GnRustType::Binary => {
+ // If a crate contains both a library and binary targets,
+ // cargo implicitly adds a dependency from each binary to the library.
+ //
+ // This logic imitates that behaviour.
+ let mut deps = dependencies.clone();
+ let maybe_library = package.targets.iter().find(|v| {
+ v.kind.iter().any(|kind| {
+ matches!(
+ GnRustType::try_from(kind.as_str()),
+ Ok(GnRustType::Library)
+ )
+ })
+ });
+ if let Some(lib) = maybe_library {
+ deps.entry(None).or_default().push((
+ &self.metadata[&node.id],
+ lib.name.clone().replace('-', "_"),
+ ));
+ }
+
+ let gn_target = GnTarget::new(
+ &node.id,
+ &rust_target.name,
+ &package.name,
+ package.edition,
+ &rust_target.src_path,
+ &package.version,
+ target_type,
+ node.features.as_slice(),
+ has_build_script,
+ deps,
+ );
+ self.targets.insert(gn_target);
+ }
+
+ // FIXME(https://fxbug.dev/42173393): support staticlib, dylib, and
+ // cdylib crate types.
+ GnRustType::Staticlib | GnRustType::Dylib | GnRustType::Cdylib => (),
+
+ // BuildScripts are handled as part of the targets
+ GnRustType::BuildScript => (),
+
+ // TODO support building tests. Should integrate with whatever GN
+ // metadata collection system used by the main build.
+ GnRustType::Example | GnRustType::Bench | GnRustType::Test => (),
+ }
+ }
+ }
+ }
+ Ok(())
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..2570bb6
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,830 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::build::BuildScript;
+use crate::graph::GnBuildGraph;
+use crate::target::GnTarget;
+use crate::types::*;
+use anyhow::{Context, Result};
+use argh::FromArgs;
+use camino::Utf8PathBuf;
+use cargo_metadata::{CargoOpt, DependencyKind, Package};
+use serde_derive::{Deserialize, Serialize};
+use std::collections::{BTreeMap, HashMap, HashSet};
+use std::fs::File;
+use std::io::{self, Read, Write};
+use std::path::PathBuf;
+use std::process::Command;
+
+mod build;
+mod cfg;
+mod gn;
+mod graph;
+mod target;
+mod types;
+
+#[derive(FromArgs, Debug)]
+/// Generate a GN manifest for your vendored Cargo dependencies.
+pub struct Opt {
+ /// cargo manifest path
+ #[argh(option)]
+ manifest_path: PathBuf,
+
+ /// root of GN project
+ #[argh(option)]
+ project_root: PathBuf,
+
+ /// cargo binary to use (for vendored toolchains)
+ #[argh(option)]
+ cargo: Option<PathBuf>,
+
+ /// already generated configs from cargo build scripts
+ #[argh(option, short = 'p')]
+ // TODO(https://fxbug.dev/42165549)
+ #[allow(unused)]
+ cargo_configs: Option<PathBuf>,
+
+ /// location of GN file
+ #[argh(option, short = 'o')]
+ output: PathBuf,
+
+ /// location of JSON file with crate metadata
+ #[argh(option)]
+ emit_metadata: Option<PathBuf>,
+
+ /// location of GN binary to use for formating.
+ /// If no path is provided, no format will be run.
+ #[argh(option)]
+ gn_bin: Option<PathBuf>,
+
+ /// don't generate a target for the root crate
+ #[argh(switch)]
+ skip_root: bool,
+
+ /// run cargo with `--all-features`
+ #[argh(switch)]
+ all_features: bool,
+
+ /// run cargo with `--no-default-features`
+ #[argh(switch)]
+ no_default_features: bool,
+
+ /// run cargo with `--features <FEATURES>`
+ #[argh(option)]
+ features: Vec<String>,
+
+ /// directory to emit Fuchsia SDK metadata atoms into
+ #[argh(option)]
+ output_fuchsia_sdk_metadata: Option<PathBuf>,
+}
+
+type PackageName = String;
+type TargetName = String;
+type Version = String;
+
+/// Per-target metadata in the Cargo.toml for Rust crates that
+/// require extra information to in the BUILD.gn
+#[derive(Default, Clone, Serialize, Deserialize, Debug)]
+#[serde(deny_unknown_fields)]
+pub struct TargetCfg {
+ /// Config flags for rustc. Ex: --cfg=std
+ rustflags: Option<Vec<String>>,
+ /// Environment variables. These are usually from Cargo or the
+ /// build.rs file in the crate.
+ env_vars: Option<Vec<String>>,
+ /// GN Targets that this crate should depend on. Generally for
+ /// crates that build C libraries and link against.
+ deps: Option<Vec<String>>,
+ /// GN Configs that this crate should depend on. Used to add
+ /// crate-specific configs.
+ configs: Option<Vec<String>>,
+ /// GN Visibility that controls which targets can depend on this target.
+ visibility: Option<Vec<String>>,
+
+ // Whether the package uses the Fuchsia license.
+ uses_fuchsia_license: Option<bool>,
+}
+
+/// Configuration for a single GN executable target to generate from a Cargo binary target.
+#[derive(Clone, Serialize, Deserialize, Debug)]
+#[serde(deny_unknown_fields)]
+pub struct BinaryCfg {
+ /// Name to use as both the top-level GN group target and the executable's output name.
+ output_name: String,
+ /// Binary target configuration for all platforms.
+ #[serde(default, flatten)]
+ default_cfg: TargetCfg,
+ /// Per-platform binary target configuration.
+ #[serde(default)]
+ #[serde(rename = "platform")]
+ platform_cfg: HashMap<Platform, TargetCfg>,
+}
+
+/// Visibility list to use for a forwarding group
+#[derive(Clone, Serialize, Deserialize, Debug)]
+#[serde(deny_unknown_fields)]
+pub struct GroupVisibility {
+ /// .gni file to import which defines the variable
+ import: String,
+ /// Name of variable defined by the gni file containing the visibility list to use
+ variable: String,
+}
+
+/// Defines a per-target rule for rule renaming.
+///
+/// Some external crates require additional post-processing. For those we define custom rules,
+/// and force a rename of the rule and the group so that post-processing can be done by GN.
+#[derive(Clone, Serialize, Deserialize, Debug)]
+#[serde(deny_unknown_fields)]
+pub struct RuleRenaming {
+ /// The label of the `.gni` file used to make the group and target renaming
+ /// available.
+ ///
+ /// Only a single file can be imported. This is on purpose so as
+ /// not to pollute the generated file with many imports. The same file will
+ /// be imported only once.
+ import: String,
+ /// If set, this name will be used instead of `group` for the top level group.
+ group_name: Option<String>,
+ /// If set, this rule name will be used instead of the name suggested by
+ /// the target type (e.g. `my_rule` instead of `rust_library`).
+ rule_name: Option<String>,
+}
+
+/// Configuration for a Cargo package. Contains configuration for its (single) library target at the
+/// top level and optionally zero or more binaries to generate.
+#[derive(Default, Clone, Serialize, Deserialize, Debug)]
+#[serde(default, deny_unknown_fields)]
+pub struct PackageCfg {
+ /// Library target configuration for all platforms.
+ #[serde(flatten)]
+ default_cfg: TargetCfg,
+ /// Per-platform library target configuration.
+ #[serde(rename = "platform")]
+ platform_cfg: HashMap<Platform, TargetCfg>,
+ /// Configuration for GN binary targets to generate from one of the package's binary targets.
+ /// The map key identifies the cargo target name within this cargo package.
+ binary: HashMap<TargetName, BinaryCfg>,
+ /// List of cargo features which have been code reviewed for this cargo package
+ ///
+ /// Must be set if require_feature_reviews mentions this package.
+ reviewed_features: Option<Vec<String>>,
+ /// Visibility list to use for the forwarding group, for use with fixits which seek to remove
+ /// the use of a specific crate from the tree.
+ group_visibility: Option<GroupVisibility>,
+ /// Whether or not this target my only be used in tests.
+ testonly: Option<bool>,
+ /// Build tests for this target.
+ tests: bool,
+ /// Used to rename the group and target names used to bring this particular package
+ /// in. Normally, `cargo-gnaw` will use `group` and `rust_*` target name, but this
+ /// allows us to define custom replacement targets. An import is required, but the
+ /// feature is deliberately limited to just a single definition and a limited number
+ /// of renames, as it should be used in very special cases only.
+ target_renaming: Option<RuleRenaming>,
+}
+
+/// Configs added to all GN targets in the BUILD.gn
+#[derive(Serialize, Deserialize, Debug)]
+#[serde(deny_unknown_fields)]
+pub struct GlobalTargetCfgs {
+ remove_cfgs: Vec<String>,
+ add_cfgs: Vec<String>,
+ /// When true, crates must have license files.
+ require_licenses: Option<bool>,
+}
+
+/// Extra metadata in the Cargo.toml file that feeds into the
+/// BUILD.gn file.
+#[derive(Serialize, Deserialize, Debug)]
+#[serde(deny_unknown_fields)]
+struct GnBuildMetadata {
+ /// global configs
+ config: Option<GlobalTargetCfgs>,
+ /// packages for which only some features will be code reviewed
+ #[serde(default)]
+ require_feature_reviews: HashSet<PackageName>,
+ /// map of per-Cargo package configuration
+ package: HashMap<PackageName, HashMap<Version, PackageCfg>>,
+}
+
+impl GnBuildMetadata {
+ fn find_package<'a>(&'a self, pkg: &Package) -> Option<&'a PackageCfg> {
+ self.package.get(&pkg.name).and_then(|p| p.get(&pkg.version.to_string()))
+ }
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+struct BuildMetadata {
+ gn: Option<GnBuildMetadata>,
+}
+
+/// Used for identifying 3p owners via reverse dependencies. Ties together several pieces of
+/// metadata needed to associate a GN target with an OWNERS file and vice versa.
+#[derive(Clone, Debug, Deserialize, Eq, PartialEq, PartialOrd, Ord, Serialize)]
+#[serde(deny_unknown_fields)]
+pub struct CrateOutputMetadata {
+ /// Name of the crate as specified in Cargo.toml.
+ pub name: String,
+
+ /// Version of the crate, used for disambiguating between duplicated transitive deps.
+ pub version: String,
+
+ /// Full GN target for depending on the crate.
+ ///
+ /// For example, Rust targets all have a canonical target like
+ /// `//third_party/rust_crates:foo-v1_0_0`.
+ pub canonical_target: String,
+
+ /// Shorthand GN target for depending on the crate.
+ ///
+ /// For example, Rust targets listed in `third_party/rust_crates/Cargo.toml` have a
+ /// shortcut target like `//third_party/rust_crates:foo`.
+ pub shortcut_target: Option<String>,
+
+ /// Filesystem path to the directory containing `Cargo.toml`.
+ pub path: Utf8PathBuf,
+}
+
+// Use BTreeMap so that iteration over platforms is stable.
+type CombinedTargetCfg<'a> = BTreeMap<Option<&'a Platform>, &'a TargetCfg>;
+
+/// Render options for binary.
+pub struct BinaryRenderOptions<'a> {
+ /// Name of the binary.
+ binary_name: &'a str,
+ /// If true, this binary target is a test target.
+ tests_enabled: bool,
+}
+
+macro_rules! define_combined_cfg {
+ ($t:ty) => {
+ impl $t {
+ fn combined_target_cfg(&self) -> CombinedTargetCfg<'_> {
+ let mut combined: CombinedTargetCfg<'_> =
+ self.platform_cfg.iter().map(|(k, v)| (Some(k), v)).collect();
+ assert!(
+ combined.insert(None, &self.default_cfg).is_none(),
+ "Default platform (None) already present in combined cfg"
+ );
+ combined
+ }
+ }
+ };
+}
+define_combined_cfg!(PackageCfg);
+define_combined_cfg!(BinaryCfg);
+
+/// Writes out an import to the provided `output`.
+///
+/// The import is written out only the first time it appears. `imported_files`
+/// is updated so that the next appearance of the same import does not result
+/// in a repeated import.
+fn write_import_once<W: io::Write>(
+ mut output: &mut W,
+ imported_files: &mut HashSet<String>,
+ import: &String,
+) -> Result<()> {
+ if !imported_files.contains(import) {
+ gn::write_import(&mut output, import).with_context(|| "writing import")?;
+ imported_files.insert(import.clone());
+ }
+ Ok(())
+}
+
+pub fn generate_from_manifest<W: io::Write>(mut output: &mut W, opt: &Opt) -> Result<()> {
+ let manifest_path = &opt.manifest_path;
+ let path_from_root_to_generated = opt
+ .output
+ .parent()
+ .unwrap()
+ .strip_prefix(&opt.project_root)
+ .expect("--project-root must be a parent of --output");
+ let fuchsia_sdk_metadata_paths =
+ opt.output_fuchsia_sdk_metadata.as_ref().map(|output_fuchsia_sdk_metadata| {
+ let path_from_root_to_generated = output_fuchsia_sdk_metadata
+ .strip_prefix(opt.output.parent().unwrap())
+ .expect("--output_fuchsia_sdk_metadata must be in the same directory as --output");
+ (output_fuchsia_sdk_metadata, path_from_root_to_generated)
+ });
+ let mut emitted_metadata: Vec<CrateOutputMetadata> = Vec::new();
+ let mut top_level_metadata: HashSet<String> = HashSet::new();
+ let mut imported_files: HashSet<String> = HashSet::new();
+
+ // generate cargo metadata
+ let mut cmd = cargo_metadata::MetadataCommand::new();
+ let parent_dir = manifest_path
+ .parent()
+ .with_context(|| format!("while parsing parent path: {}", manifest_path.display()))?;
+ cmd.current_dir(parent_dir);
+ cmd.manifest_path(manifest_path);
+ if let Some(ref cargo_path) = opt.cargo {
+ cmd.cargo_path(cargo_path);
+ }
+ if opt.all_features {
+ cmd.features(CargoOpt::AllFeatures);
+ }
+ if opt.no_default_features {
+ cmd.features(CargoOpt::NoDefaultFeatures);
+ }
+ if !opt.features.is_empty() {
+ cmd.features(CargoOpt::SomeFeatures(opt.features.clone()));
+ }
+ cmd.other_options([String::from("--frozen")]);
+ let metadata = cmd.exec().with_context(|| {
+ format!("while running cargo metadata: supplied cargo binary: {:?}", &opt.cargo)
+ })?;
+
+ // read out custom gn commands from the toml file
+ let mut file = File::open(&manifest_path)
+ .with_context(|| format!("opening {}", manifest_path.display()))?;
+ let mut contents = String::new();
+ file.read_to_string(&mut contents)
+ .with_context(|| format!("while reading manifest: {}", manifest_path.display()))?;
+ let metadata_configs: BuildMetadata =
+ toml::from_str(&contents).context("parsing manifest toml")?;
+
+ gn::write_header(&mut output, manifest_path).context("writing header")?;
+
+ if opt.output_fuchsia_sdk_metadata.is_some() {
+ gn::write_fuchsia_sdk_metadata_header(&mut output)
+ .context("writing Fuchsia SDK metadata header")?;
+ }
+
+ // Construct a build graph of all the targets for GN
+ let mut build_graph = GnBuildGraph::new(&metadata);
+ match metadata.resolve.as_ref() {
+ Some(resolve) => {
+ let top_level_id =
+ resolve.root.as_ref().expect("the Cargo.toml file must define a package");
+ if opt.skip_root {
+ let top_level_node = resolve
+ .nodes
+ .iter()
+ .find(|node| node.id == *top_level_id)
+ .expect("top level node not in node graph");
+ for dep in &top_level_node.deps {
+ build_graph
+ .add_cargo_package(dep.pkg.clone())
+ .context("adding cargo package")?;
+ for kinds in dep.dep_kinds.iter() {
+ if kinds.kind == DependencyKind::Normal {
+ let platform = kinds.target.as_ref().map(|t| format!("{}", t));
+ let package = &metadata[&dep.pkg];
+ top_level_metadata.insert(package.name.to_owned());
+ let cfg = metadata_configs
+ .gn
+ .as_ref()
+ .and_then(|cfg| cfg.find_package(package));
+
+ let visibility = cfg.and_then(|cfg| cfg.group_visibility.as_ref());
+ if let Some(visibility) = visibility {
+ write_import_once(
+ &mut output,
+ &mut imported_files,
+ &visibility.import,
+ )?;
+ }
+
+ let target_renaming = cfg.and_then(|cfg| cfg.target_renaming.as_ref());
+ if let Some(target_renaming) = target_renaming {
+ write_import_once(
+ &mut output,
+ &mut imported_files,
+ &target_renaming.import,
+ )?;
+ }
+
+ gn::write_top_level_rule(
+ &mut output,
+ platform.as_deref(),
+ package,
+ visibility,
+ target_renaming,
+ cfg.and_then(|cfg| cfg.testonly).unwrap_or(false),
+ cfg.map(|c| c.tests).unwrap_or(false),
+ )
+ .with_context(|| {
+ format!("while writing top level rule for package: {}", &dep.pkg)
+ })
+ .context("writing top level rule")?;
+
+ if let Some((abs_path, rel_path)) = &fuchsia_sdk_metadata_paths {
+ gn::write_fuchsia_sdk_metadata(
+ &mut output,
+ platform.as_deref(),
+ package,
+ abs_path,
+ rel_path,
+ )
+ .with_context(|| {
+ format!(
+ "while writing top level rule for package: {}",
+ &dep.pkg
+ )
+ })
+ .context("writing Fuchsia SDK metadata for top level rule")?;
+ }
+ }
+ }
+ }
+ } else {
+ build_graph
+ .add_cargo_package(top_level_id.clone())
+ .with_context(|| "could not add cargo package")?;
+ let package = &metadata[&top_level_id];
+ top_level_metadata.insert(package.name.to_owned());
+ let cfg = metadata_configs.gn.as_ref().and_then(|cfg| cfg.find_package(package));
+
+ let visibility = cfg.and_then(|cfg| cfg.group_visibility.as_ref());
+ if let Some(visibility) = visibility {
+ write_import_once(&mut output, &mut imported_files, &visibility.import)?;
+ }
+
+ let target_renaming = cfg.and_then(|cfg| cfg.target_renaming.as_ref());
+ if let Some(target_renaming) = target_renaming {
+ write_import_once(&mut output, &mut imported_files, &target_renaming.import)?;
+ }
+
+ gn::write_top_level_rule(
+ &mut output,
+ None,
+ package,
+ visibility,
+ target_renaming,
+ cfg.and_then(|cfg| cfg.testonly).unwrap_or(false),
+ cfg.map(|c| c.tests).unwrap_or(false),
+ )
+ .with_context(|| "writing top level rule")?;
+
+ if let Some((abs_path, rel_path)) = &fuchsia_sdk_metadata_paths {
+ gn::write_fuchsia_sdk_metadata(&mut output, None, package, abs_path, rel_path)
+ .with_context(|| "writing Fuchsia SDK metadata for top level rule")?;
+ }
+ }
+ }
+ None => anyhow::bail!("Failed to resolve a build graph for the package tree"),
+ }
+
+ // Sort targets for stable output to minimize diff churn
+ let mut graph_targets: Vec<&GnTarget<'_>> = build_graph.targets().collect();
+ graph_targets.sort();
+
+ let global_config = match metadata_configs.gn {
+ Some(ref gn_configs) => gn_configs.config.as_ref(),
+ None => None,
+ };
+
+ let empty_hash_set = &HashSet::new();
+ let require_feature_reviews = metadata_configs
+ .gn
+ .as_ref()
+ .map(|gn| &gn.require_feature_reviews)
+ .unwrap_or(empty_hash_set);
+
+ // Grab the per-package configs.
+ let gn_pkg_cfgs = metadata_configs.gn.as_ref().map(|i| &i.package);
+
+ // Iterate through the target configs, verifying that the build graph contains the configured
+ // targets, then save off a mapping of GnTarget to the target config.
+ let mut target_cfgs = HashMap::<&GnTarget<'_>, CombinedTargetCfg<'_>>::new();
+ let mut target_binaries = HashMap::<&GnTarget<'_>, BinaryRenderOptions<'_>>::new();
+ let mut testonly_targets = HashSet::<&GnTarget<'_>>::new();
+ let mut targets_with_tests = HashSet::<&GnTarget<'_>>::new();
+ let mut reviewed_features_map = HashMap::<&GnTarget<'_>, Option<&[String]>>::new();
+ // An entry for each target that needs a renamed rule. There is no connection between
+ // PackageCfg and GnTarget, so to use the setting from PackageCfg, we need to build this map as
+ // we iterate through targets.
+ let mut renamed_rules = HashMap::<&GnTarget<'_>, &'_ str>::new();
+ let mut unused_configs = String::new();
+ if let Some(gn_pkg_cfgs) = gn_pkg_cfgs {
+ for (pkg_name, versions) in gn_pkg_cfgs {
+ for (pkg_version, pkg_cfg) in versions {
+ // Search the build graph for the library target.
+ if let Some(target) = build_graph.find_library_target(pkg_name, pkg_version) {
+ assert!(
+ target_cfgs.insert(target, pkg_cfg.combined_target_cfg()).is_none(),
+ "Duplicate library config somehow specified"
+ );
+ assert!(
+ reviewed_features_map
+ .insert(target, pkg_cfg.reviewed_features.as_deref())
+ .is_none(),
+ "Duplicate library config somehow specified"
+ );
+
+ if let Some(renamed_rule) = pkg_cfg
+ .target_renaming
+ .as_ref()
+ .and_then(|r| r.rule_name.as_ref())
+ .map(|x| x.as_str())
+ {
+ renamed_rules.insert(target, renamed_rule);
+ }
+
+ if pkg_cfg.testonly == Some(true) {
+ testonly_targets.insert(target);
+ }
+
+ if pkg_cfg.tests {
+ targets_with_tests.insert(target);
+ }
+ } else {
+ unused_configs.push_str(&format!(
+ "library crate, package {} version {}\n",
+ pkg_name, pkg_version
+ ));
+ }
+
+ // Handle binaries that should be built for this package, similarly searching the
+ // build graph for the binary targets.
+ for (bin_cargo_target, bin_cfg) in &pkg_cfg.binary {
+ if let Some(target) =
+ build_graph.find_binary_target(pkg_name, pkg_version, bin_cargo_target)
+ {
+ if let Some(old_options) = target_binaries.insert(
+ target,
+ BinaryRenderOptions {
+ binary_name: &bin_cfg.output_name,
+ tests_enabled: pkg_cfg.tests,
+ },
+ ) {
+ anyhow::bail!(
+ "A given binary target ({} in package {} version {}) can only be \
+ used for a single GN target, but multiple exist, including {} \
+ and {}",
+ bin_cargo_target,
+ pkg_name,
+ pkg_version,
+ &bin_cfg.output_name,
+ old_options.binary_name,
+ );
+ }
+ assert!(
+ target_cfgs.insert(target, bin_cfg.combined_target_cfg()).is_none(),
+ "Should have bailed above"
+ );
+
+ if pkg_cfg.tests {
+ targets_with_tests.insert(target);
+ }
+ } else {
+ unused_configs.push_str(&format!(
+ "binary crate {}, package {} version {}\n",
+ bin_cargo_target, pkg_name, pkg_version
+ ));
+ }
+ }
+ }
+ }
+ }
+ if !unused_configs.is_empty() {
+ anyhow::bail!(
+ "GNaw config exists for crates that were not found in the Cargo build graph:\n\n{}",
+ unused_configs
+ );
+ }
+
+ // Write the top-level GN rules for binaries. Verify that the names are unique, otherwise a
+ // build failure will result.
+ {
+ let mut names = HashSet::new();
+ for (target, options) in &target_binaries {
+ if !names.insert(options.binary_name) {
+ anyhow::bail!(
+ "Multiple targets are configured to generate executables named \"{}\"",
+ options.binary_name
+ );
+ }
+
+ emitted_metadata.push(CrateOutputMetadata {
+ name: options.binary_name.to_string(),
+ version: target.version(),
+ canonical_target: format!(
+ "//{}:{}",
+ path_from_root_to_generated.display(),
+ target.gn_target_name()
+ ),
+ shortcut_target: Some(format!(
+ "//{}:{}",
+ path_from_root_to_generated.display(),
+ options.binary_name
+ )),
+ path: target.package_root(),
+ });
+ gn::write_binary_top_level_rule(&mut output, None, target, options)
+ .context(format!("writing binary top level rule: {}", target.name()))?;
+ }
+ }
+
+ // Write out a GN rule for each target in the build graph
+ for target in graph_targets {
+ // Check whether we should generate a target if this is a binary.
+ let binary_name = if let GnRustType::Binary = target.target_type {
+ let name = target_binaries.get(target).map(|opt| opt.binary_name);
+ if name.is_none() {
+ continue;
+ }
+ name
+ } else {
+ None
+ };
+
+ let target_cfg = target_cfgs.get(target);
+ if target.has_build_script && target_cfg.is_none() {
+ let build_output = BuildScript::execute(target);
+ match build_output {
+ Ok(rules) => {
+ anyhow::bail!(
+ "Add this to your Cargo.toml located at {}:\n\
+ [gn.package.{}.\"{}\"]\n\
+ rustflags = [{}]\n\
+ rustenv = [{}]",
+ manifest_path.display(),
+ target.name(),
+ target.version(),
+ rules.rustflags.join(", "),
+ rules.rustenv.join(", ")
+ );
+ }
+ Err(err) => anyhow::bail!(
+ "{} {} uses a build script but no section defined in the GN section \
+ nor can we automatically generate the appropriate rules:\n{}",
+ target.name(),
+ target.version(),
+ err,
+ ),
+ }
+ }
+
+ // Check to see if we need to review individual features for this crate
+ let reviewed_features = reviewed_features_map.get(target);
+ if require_feature_reviews.contains(&target.name()) {
+ match reviewed_features {
+ Some(Some(_)) => {}
+ _ => {
+ anyhow::bail!(
+ "{name} {version} requires feature review but reviewed features not found.\n\n\
+ Make sure to conduct code review assuming the following features are enabled, \
+ and then add this to your Cargo.toml located at {manifest_path}:\n\
+ [gn.package.{name}.\"{version}\"]\n\
+ reviewed_features = {features}",
+ manifest_path = manifest_path.display(),
+ name = target.name(),
+ version = target.version(),
+ features = toml::to_string(target.features).unwrap()
+ );
+ }
+ }
+ } else if let Some(Some(_)) = reviewed_features {
+ anyhow::bail!(
+ "{name} {version} sets reviewed_features but {name} was not found in \
+ require_feature_reviews.\n\n\
+ Make sure to add it there so that reviewed_features is not accidentally \
+ removed during future crate version bumps.",
+ name = target.name(),
+ version = target.version(),
+ );
+ }
+
+ if let Some(&Some(reviewed_features)) = reviewed_features {
+ let reviewed_features_set =
+ reviewed_features.iter().map(|s| s.as_str()).collect::<HashSet<_>>();
+ let unreviewed_features = target
+ .features
+ .iter()
+ .filter(|f| !reviewed_features_set.contains(f.as_str()))
+ .collect::<Vec<_>>();
+ if !unreviewed_features.is_empty() {
+ anyhow::bail!(
+ "{name} {version} is included with unreviewed features {unreviewed:?}\n\n\
+ Make sure to additionally review code gated by these features, then add them \
+ to reviewed_features under [gn.package.{name}.\"{version}\"] in {manifest_path}",
+ name = target.name(),
+ version = target.version(),
+ unreviewed = unreviewed_features,
+ manifest_path = manifest_path.display(),
+ )
+ }
+ }
+
+ let package_root = target.package_root();
+ package_root.strip_prefix(&opt.project_root).unwrap_or_else(|e| {
+ panic!(
+ "{}: {} is not under the project_root ({})",
+ e,
+ target.package_root(),
+ opt.project_root.display()
+ )
+ });
+
+ let shortcut_target = if top_level_metadata.contains(target.pkg_name) {
+ Some(format!("//{}:{}", path_from_root_to_generated.display(), target.pkg_name))
+ } else {
+ None
+ };
+
+ emitted_metadata.push(CrateOutputMetadata {
+ name: target.name(),
+ version: target.version(),
+ canonical_target: format!(
+ "//{}:{}",
+ path_from_root_to_generated.display(),
+ target.gn_target_name()
+ ),
+ shortcut_target,
+ path: package_root.to_owned(),
+ });
+
+ gn::write_rule(
+ &mut output,
+ target,
+ &opt.project_root,
+ global_config,
+ target_cfg,
+ binary_name,
+ testonly_targets.contains(target),
+ false,
+ renamed_rules.get(target).copied(),
+ true,
+ )
+ .with_context(|| format!("writing rule for: {} {}", target.name(), target.version()))?;
+
+ if targets_with_tests.contains(target) {
+ gn::write_rule(
+ &mut output,
+ target,
+ &opt.project_root,
+ global_config,
+ target_cfg,
+ binary_name,
+ true,
+ true,
+ renamed_rules.get(&target).copied(),
+ true,
+ )
+ .with_context(|| format!("writing rule for: {} {}", target.name(), target.version()))?;
+ }
+ }
+
+ if let Some(metadata_path) = &opt.emit_metadata {
+ eprintln!("Emitting external crate metadata: {}", metadata_path.display());
+ emitted_metadata.sort();
+ let metadata_json = serde_json::to_string_pretty(&emitted_metadata)
+ .context("serializing metadata to json")?;
+ std::fs::create_dir_all(
+ metadata_path
+ .parent()
+ .expect("The metadata path must include a valid parent directory"),
+ )?;
+ std::fs::write(metadata_path, &metadata_json).context("writing metadata file")?;
+ }
+
+ Ok(())
+}
+
+pub fn run(args: &[impl AsRef<str>]) -> Result<()> {
+ // Check if running through cargo or stand-alone before arg parsing
+ let mut strs: Vec<&str> = args.iter().map(|s| s.as_ref()).collect();
+ if strs.get(1) == Some(&"gnaw") {
+ // If the second command is "gnaw" this likely invoked by `cargo gnaw`
+ // shift all args by one.
+ strs = strs[1..].to_vec()
+ }
+ let opt = match Opt::from_args(&[strs[0]], &strs[1..]) {
+ Ok(opt) => opt,
+ Err(e) if e.status.is_ok() => {
+ println!("{}", e.output);
+ return Ok(());
+ }
+ Err(e) => return Err(anyhow::Error::msg(e.output)),
+ };
+
+ eprintln!("Generating GN file from {}", opt.manifest_path.to_string_lossy());
+
+ // redirect to stdout if no GN output file specified
+ // Stores data in a buffer in-case to prevent creating bad BUILD.gn
+ let mut gn_output_buffer = vec![];
+ generate_from_manifest(&mut gn_output_buffer, &opt).context("generating manifest")?;
+
+ // Write the file buffer to an actual file
+ File::create(&opt.output)
+ .context("creating output file")?
+ .write_all(&gn_output_buffer)
+ .context("writing output contents")?;
+
+ if let Some(gn_bin) = opt.gn_bin {
+ eprintln!("Formatting output file: {}", opt.output.display());
+ let status = Command::new(&gn_bin)
+ .arg("format")
+ .arg(opt.output)
+ .status()
+ .with_context(|| format!("could not spawn GN: {}", gn_bin.display()))?;
+ if !status.success() {
+ anyhow::bail!("GN format command failed:{:?}", status);
+ }
+ }
+
+ Ok(())
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..4600346
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,10 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use anyhow::Result;
+
+fn main() -> Result<()> {
+ let args: Vec<String> = std::env::args().collect();
+ gnaw_lib::run(&args[..])
+}
diff --git a/src/target.rs b/src/target.rs
new file mode 100644
index 0000000..46d0777
--- /dev/null
+++ b/src/target.rs
@@ -0,0 +1,170 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::gn::add_version_suffix;
+use crate::types::*;
+use camino::{Utf8Path, Utf8PathBuf};
+use cargo_metadata::{Edition, Package, PackageId};
+use semver::Version;
+use std::borrow::Cow;
+use std::cmp::Ordering;
+use std::collections::hash_map::DefaultHasher;
+use std::collections::HashMap;
+use std::hash::{Hash, Hasher};
+
+pub struct GnTarget<'a> {
+ /// Package ID from the Cargo metadata
+ cargo_pkg_id: &'a PackageId,
+ /// Version of the Package from Cargo
+ version: &'a Version,
+ /// Name of the target given in Cargo.toml
+ pub target_name: &'a str,
+ /// Name of the package given in Cargo.toml
+ pub pkg_name: &'a str,
+ /// Path to the root of the crate
+ pub crate_root: &'a Utf8Path,
+ /// Rust Edition of the target
+ /// rustc: --edition
+ pub edition: Edition,
+ /// Type of crate
+ /// rustc: --crate-type
+ pub target_type: GnRustType,
+ /// Rust features enabled on this target
+ /// rustc: --cfg=feature=<string>
+ pub features: &'a [String],
+ /// Target depends on Cargo running a custom build-script
+ pub has_build_script: bool,
+ /// Target depends on Cargo running a custom build-script
+ pub dependencies: HashMap<Option<Platform>, Vec<(&'a Package, String)>>,
+}
+
+impl std::fmt::Debug for GnTarget<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let display_name = if self.target_name != self.pkg_name {
+ Cow::Owned(format!("{}.{}", self.pkg_name, self.target_name))
+ } else {
+ Cow::Borrowed(self.pkg_name)
+ };
+ if self.has_build_script {
+ write!(f, "{:?} with custom-build: {}", self.target_type, display_name)
+ } else {
+ write!(f, "{:?}: {}", self.target_type, display_name)
+ }
+ }
+}
+
+impl PartialEq for GnTarget<'_> {
+ fn eq(&self, other: &Self) -> bool {
+ self.cargo_pkg_id == other.cargo_pkg_id
+ && self.target_name == other.target_name
+ && self.target_type == other.target_type
+ }
+}
+impl Eq for GnTarget<'_> {}
+
+impl PartialOrd for GnTarget<'_> {
+ fn partial_cmp(&self, other: &GnTarget<'_>) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for GnTarget<'_> {
+ fn cmp(&self, other: &GnTarget<'_>) -> Ordering {
+ self.target_name
+ .cmp(other.target_name)
+ .then(self.target_type.cmp(&other.target_type))
+ .then(self.cargo_pkg_id.cmp(other.cargo_pkg_id))
+ }
+}
+
+impl Hash for GnTarget<'_> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.cargo_pkg_id.hash(state);
+ self.target_name.hash(state);
+ self.target_type.hash(state);
+ }
+}
+
+impl<'a> GnTarget<'a> {
+ pub fn new(
+ cargo_pkg_id: &'a PackageId,
+ target_name: &'a str,
+ pkg_name: &'a str,
+ edition: Edition,
+ crate_root: &'a Utf8Path,
+ version: &'a Version,
+ target_type: GnRustType,
+ features: &'a [Feature],
+ has_build_script: bool,
+ dependencies: HashMap<Option<Platform>, Vec<(&'a Package, String)>>,
+ ) -> Self {
+ GnTarget {
+ cargo_pkg_id,
+ target_name,
+ pkg_name,
+ edition,
+ crate_root,
+ version,
+ target_type,
+ features,
+ has_build_script,
+ dependencies,
+ }
+ }
+
+ /// Name of the target given in Cargo.toml
+ pub fn name(&self) -> String {
+ self.target_name.to_owned()
+ }
+
+ /// Version of the Package from Cargo
+ pub fn version(&self) -> String {
+ self.version.to_string()
+ }
+
+ pub fn metadata_hash(&self) -> String {
+ let mut hasher = DefaultHasher::new();
+ self.gn_target_name().hash(&mut hasher);
+ format!("{:x}", hasher.finish())
+ }
+
+ /// with version
+ pub fn gn_target_name(&self) -> String {
+ let prefix = match self.target_type {
+ GnRustType::Library | GnRustType::Rlib | GnRustType::ProcMacro => {
+ Cow::Borrowed(self.pkg_name)
+ }
+ GnRustType::Binary => Cow::Owned(format!("{}-{}", self.pkg_name, self.target_name)),
+ ty => panic!("Don't know how to represent this type \"{:?}\" in GN", ty),
+ };
+ add_version_suffix(&prefix, &self.version)
+ }
+
+ pub fn package_root(&self) -> Utf8PathBuf {
+ let mut package_root = self.crate_root.canonicalize_utf8().unwrap();
+
+ while !package_root.join("Cargo.toml").exists() {
+ package_root = package_root
+ .parent()
+ .expect("searching up from the crate root we must find a cargo.toml")
+ .to_path_buf();
+ }
+
+ assert!(
+ !package_root.ends_with("third_party/rust_crates"),
+ "must find a cargo.toml before the root one"
+ );
+
+ package_root.to_owned()
+ }
+
+ pub fn gn_target_type(&self) -> String {
+ match self.target_type {
+ GnRustType::Library | GnRustType::Rlib => String::from("rust_library"),
+ GnRustType::Binary => String::from("executable"),
+ GnRustType::ProcMacro => String::from("rust_proc_macro"),
+ ty => panic!("Don't know how to represent this type \"{:?}\" in GN", ty),
+ }
+ }
+}
diff --git a/src/types.rs b/src/types.rs
new file mode 100644
index 0000000..6a035d4
--- /dev/null
+++ b/src/types.rs
@@ -0,0 +1,69 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::gn::add_version_suffix;
+use anyhow::{anyhow, Error};
+use cargo_metadata::Package;
+use std::convert::TryFrom;
+
+pub type Feature = String;
+pub type Platform = String;
+
+#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub enum GnRustType {
+ ProcMacro,
+ Library,
+ Rlib,
+ Staticlib,
+ Dylib,
+ Cdylib,
+ Binary,
+ Example,
+ Test,
+ Bench,
+ BuildScript,
+}
+
+impl<'a> TryFrom<&'a str> for GnRustType {
+ type Error = Error;
+
+ fn try_from(value: &str) -> Result<Self, Self::Error> {
+ match value {
+ "bin" => Ok(GnRustType::Binary),
+ "lib" => Ok(GnRustType::Library),
+ "rlib" => Ok(GnRustType::Rlib),
+ "staticlib" => Ok(GnRustType::Staticlib),
+ "dylib" => Ok(GnRustType::Dylib),
+ "cdylib" => Ok(GnRustType::Cdylib),
+ "proc-macro" => Ok(GnRustType::ProcMacro),
+ "test" => Ok(GnRustType::Test),
+ "example" => Ok(GnRustType::Example),
+ "bench" => Ok(GnRustType::Bench),
+ "custom-build" => Ok(GnRustType::BuildScript),
+ value => Err(anyhow!("unknown crate type: {}", value)),
+ }
+ }
+}
+
+pub trait GnData {
+ fn gn_name(&self) -> String;
+ fn is_proc_macro(&self) -> bool;
+}
+
+impl GnData for Package {
+ fn gn_name(&self) -> String {
+ add_version_suffix(&self.name, &self.version)
+ }
+
+ fn is_proc_macro(&self) -> bool {
+ for target in &self.targets {
+ for kind in &target.kind {
+ if kind == "proc-macro" {
+ return true;
+ }
+ }
+ }
+ false
+ }
+}
diff --git a/templates/gn_header.template b/templates/gn_header.template
new file mode 100644
index 0000000..9edf89d
--- /dev/null
+++ b/templates/gn_header.template
@@ -0,0 +1,11 @@
+# Copyright {year} The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed(["gn_source_root"])
diff --git a/templates/gn_import.template b/templates/gn_import.template
new file mode 100644
index 0000000..f9d091f
--- /dev/null
+++ b/templates/gn_import.template
@@ -0,0 +1 @@
+import("{file_name}")
diff --git a/templates/gn_license.template b/templates/gn_license.template
new file mode 100644
index 0000000..d44999e
--- /dev/null
+++ b/templates/gn_license.template
@@ -0,0 +1,4 @@
+license("{target_name}") {{
+ public_package_name = "{public_package_name}"
+ {license_files}
+}}
diff --git a/templates/gn_rule.template b/templates/gn_rule.template
new file mode 100644
index 0000000..38040c3
--- /dev/null
+++ b/templates/gn_rule.template
@@ -0,0 +1,13 @@
+{gn_rule}("{target_name}") {{
+ crate_name = "{crate_name}"
+ crate_root = "{root_path}"
+ output_name = "{output_name}"
+ {cfgs}
+ {dependencies}
+ {rustenv}
+ {rustflags}
+ {aliased_deps}
+ {visibility}
+ {optional_testonly}
+ {applicable_licenses}
+}}
diff --git a/templates/gn_sdk_metadata_header.template b/templates/gn_sdk_metadata_header.template
new file mode 100644
index 0000000..7abba7c
--- /dev/null
+++ b/templates/gn_sdk_metadata_header.template
@@ -0,0 +1,14 @@
+import("//build/sdk/sdk_atom.gni")
+
+# True if the current GN toolchain instance can be used to create
+# Rust sdk_atom() targets.
+_generating_sdk = false
+
+# The SDK prefix for the current toolchain.
+if (current_toolchain == default_toolchain) {{
+ _generating_sdk = true
+ _sdk_prefix = ""
+}} else if (is_host) {{
+ _generating_sdk = true
+ _sdk_prefix = "rust_proc_macros/"
+}}
diff --git a/templates/top_level_binary_gn_rule.template b/templates/top_level_binary_gn_rule.template
new file mode 100644
index 0000000..f7a7ec5
--- /dev/null
+++ b/templates/top_level_binary_gn_rule.template
@@ -0,0 +1,4 @@
+group("{group_name}") {{
+ public_deps = [":{dep_name}"]
+ {optional_testonly}
+}}
diff --git a/templates/top_level_gn_rule.template b/templates/top_level_gn_rule.template
new file mode 100644
index 0000000..878377f
--- /dev/null
+++ b/templates/top_level_gn_rule.template
@@ -0,0 +1,5 @@
+{group_rule_name}("{group_name}") {{
+ public_deps = [":{dep_name}"]
+ {optional_testonly}
+ {optional_visibility}
+}}
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 0000000..e36b973
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1,3 @@
+!Cargo.toml
+!Cargo.lock
+!.cargo
diff --git a/tests/BUILD.gn b/tests/BUILD.gn
new file mode 100644
index 0000000..459c95b
--- /dev/null
+++ b/tests/BUILD.gn
@@ -0,0 +1,227 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/rust/rustc_binary.gni")
+import("//build/rust/rustc_library.gni")
+import("//build/rust/rustc_test.gni")
+import("//build/testing/host_test_data.gni")
+
+group("tests") {
+ testonly = true
+ deps = [ ":cargo_gnaw_golden_integration_host_test($host_toolchain)" ]
+}
+
+if (is_host) {
+ host_test("cargo_gnaw_golden_integration_host_test") {
+ binary_path = "$root_out_dir/cargo_gnaw_golden_integration_test"
+
+ args = [
+ "--test-base-dir",
+ rebase_path(".", root_build_dir),
+ "--rustc-binary-path",
+ "$out_rustc_prefix/bin/rustc",
+ "--gn-binary-path",
+ rebase_path("//prebuilt/third_party/gn/${host_platform}/gn",
+ root_build_dir),
+ "--cargo-binary-path",
+ "$out_rustc_prefix/bin/cargo",
+ "--lib-path",
+ "$out_rustc_prefix/lib",
+ ]
+
+ # Host tests are tested on different workers from those the artifacts were
+ # built on. This means, if we want to use binaries from the source tree,
+ # we need to add them to test dependencies explicitly.
+ deps = [
+ ":cargo_gnaw_golden_integration_test",
+ ":cargo_gnaw_prebuilt_bin_libs",
+ ":cargo_gnaw_tests",
+ "//build/rust:prebuilt_toolchain_host_test_data",
+ ]
+ }
+
+ rustc_binary("cargo_gnaw_golden_integration_test") {
+ edition = "2021"
+ source_root = "golden.rs"
+ sources = [ "golden.rs" ]
+ deps = [
+ "//third_party/rust_crates:anyhow",
+ "//third_party/rust_crates:argh",
+ "//third_party/rust_crates:pretty_assertions",
+ "//third_party/rust_crates:tempfile",
+ "//third_party/rust_crates:walkdir",
+ "//tools/cargo-gnaw:cargo-gnaw-lib",
+ ]
+ }
+
+ visibility = [ ":*" ]
+
+ host_test_data("cargo_gnaw_tests") {
+ sources = [
+ "binary/BUILD.gn",
+ "binary/Cargo.lock",
+ "binary/Cargo.toml",
+ "binary/src/lib.rs",
+ "binary/src/main.rs",
+ "binary_with_tests/BUILD.gn",
+ "binary_with_tests/Cargo.lock",
+ "binary_with_tests/Cargo.toml",
+ "binary_with_tests/src/lib.rs",
+ "binary_with_tests/src/main.rs",
+ "cargo_features/BUILD-all-features.gn",
+ "cargo_features/BUILD-default.gn",
+ "cargo_features/BUILD-featurefoo.gn",
+ "cargo_features/BUILD-no-default-features.gn",
+ "cargo_features/Cargo.lock",
+ "cargo_features/Cargo.toml",
+ "cargo_features/src/lib.rs",
+ "cargo_features/sub-crate-default/Cargo.toml",
+ "cargo_features/sub-crate-default/src/lib.rs",
+ "cargo_features/sub-crate-for-feature/Cargo.toml",
+ "cargo_features/sub-crate-for-feature/src/lib.rs",
+ "cargo_features/sub-crate-non-default/Cargo.toml",
+ "cargo_features/sub-crate-non-default/src/lib.rs",
+ "cargo_features/sub-crate-with-feature/Cargo.toml",
+ "cargo_features/sub-crate-with-feature/src/lib.rs",
+ "feature_review/BUILD.gn",
+ "feature_review/Cargo.lock",
+ "feature_review/Cargo.toml",
+ "feature_review/Cargo_extra_review.toml",
+ "feature_review/Cargo_missing_review.toml",
+ "feature_review/Cargo_unreviewed_feature.toml",
+ "feature_review/crate_with_features/Cargo.toml",
+ "feature_review/crate_with_features/src/lib.rs",
+ "feature_review/needs_feature/Cargo.toml",
+ "feature_review/needs_feature/src/lib.rs",
+ "feature_review/src/lib.rs",
+ "multiple_crate_types/BUILD.gn",
+ "multiple_crate_types/Cargo.lock",
+ "multiple_crate_types/Cargo.toml",
+ "multiple_crate_types/src/lib.rs",
+ "multiple_crate_types/sub-crate-with-cdylib/Cargo.toml",
+ "multiple_crate_types/sub-crate-with-cdylib/src/lib.rs",
+ "multiple_crate_types/sub-crate-with-dylib/Cargo.toml",
+ "multiple_crate_types/sub-crate-with-dylib/src/lib.rs",
+ "multiple_crate_types/sub-crate-with-rlib/Cargo.toml",
+ "multiple_crate_types/sub-crate-with-rlib/src/lib.rs",
+ "platform_deps/.cargo/config",
+ "platform_deps/BUILD.gn",
+ "platform_deps/Cargo.lock",
+ "platform_deps/Cargo.toml",
+ "platform_deps/src/lib.rs",
+ "platform_deps/vendor/anyhow/.cargo-checksum.json",
+ "platform_deps/vendor/anyhow/Cargo.toml",
+ "platform_deps/vendor/anyhow/LICENSE-APACHE",
+ "platform_deps/vendor/anyhow/LICENSE-MIT",
+ "platform_deps/vendor/anyhow/README.md",
+ "platform_deps/vendor/anyhow/build.rs",
+ "platform_deps/vendor/anyhow/src/backtrace.rs",
+ "platform_deps/vendor/anyhow/src/chain.rs",
+ "platform_deps/vendor/anyhow/src/context.rs",
+ "platform_deps/vendor/anyhow/src/error.rs",
+ "platform_deps/vendor/anyhow/src/fmt.rs",
+ "platform_deps/vendor/anyhow/src/kind.rs",
+ "platform_deps/vendor/anyhow/src/lib.rs",
+ "platform_deps/vendor/anyhow/src/macros.rs",
+ "platform_deps/vendor/anyhow/src/wrapper.rs",
+ "platform_deps/vendor/anyhow/tests/common/mod.rs",
+ "platform_deps/vendor/anyhow/tests/compiletest.rs",
+ "platform_deps/vendor/anyhow/tests/drop/mod.rs",
+ "platform_deps/vendor/anyhow/tests/test_autotrait.rs",
+ "platform_deps/vendor/anyhow/tests/test_backtrace.rs",
+ "platform_deps/vendor/anyhow/tests/test_boxed.rs",
+ "platform_deps/vendor/anyhow/tests/test_chain.rs",
+ "platform_deps/vendor/anyhow/tests/test_context.rs",
+ "platform_deps/vendor/anyhow/tests/test_convert.rs",
+ "platform_deps/vendor/anyhow/tests/test_downcast.rs",
+ "platform_deps/vendor/anyhow/tests/test_fmt.rs",
+ "platform_deps/vendor/anyhow/tests/test_macros.rs",
+ "platform_deps/vendor/anyhow/tests/test_repr.rs",
+ "platform_deps/vendor/anyhow/tests/test_source.rs",
+ "platform_deps/vendor/anyhow/tests/ui/no-impl.rs",
+ "platform_deps/vendor/anyhow/tests/ui/no-impl.stderr",
+ "platform_features/BUILD.gn",
+ "platform_features/Cargo.lock",
+ "platform_features/Cargo.toml",
+ "platform_features/a/Cargo.toml",
+ "platform_features/a/src/lib.rs",
+ "platform_features/b/Cargo.toml",
+ "platform_features/b/src/lib.rs",
+ "platform_features/src/lib.rs",
+ "sdk_metadata/BUILD.gn",
+ "sdk_metadata/Cargo.lock",
+ "sdk_metadata/Cargo.toml",
+ "sdk_metadata/sdk_metas/sdk_metadata.sdk.meta.json",
+ "sdk_metadata/src/lib.rs",
+ "sdk_metadata/sub-crate-proc-macro/Cargo.toml",
+ "sdk_metadata/sub-crate-proc-macro/src/lib.rs",
+ "sdk_metadata/sub-crate/Cargo.toml",
+ "sdk_metadata/sub-crate/src/lib.rs",
+ "simple/BUILD.gn",
+ "simple/Cargo.lock",
+ "simple/Cargo.toml",
+ "simple/src/lib.rs",
+ "simple_deps/.cargo/config",
+ "simple_deps/BUILD.gn",
+ "simple_deps/BUILD_WITH_NO_ROOT.gn",
+ "simple_deps/Cargo.lock",
+ "simple_deps/Cargo.toml",
+ "simple_deps/src/lib.rs",
+ "simple_deps/vendor/anyhow/.cargo-checksum.json",
+ "simple_deps/vendor/anyhow/Cargo.toml",
+ "simple_deps/vendor/anyhow/LICENSE-APACHE",
+ "simple_deps/vendor/anyhow/LICENSE-MIT",
+ "simple_deps/vendor/anyhow/README.md",
+ "simple_deps/vendor/anyhow/build.rs",
+ "simple_deps/vendor/anyhow/src/LICENSE-FOR-TESTING",
+ "simple_deps/vendor/anyhow/src/backtrace.rs",
+ "simple_deps/vendor/anyhow/src/chain.rs",
+ "simple_deps/vendor/anyhow/src/context.rs",
+ "simple_deps/vendor/anyhow/src/error.rs",
+ "simple_deps/vendor/anyhow/src/fmt.rs",
+ "simple_deps/vendor/anyhow/src/kind.rs",
+ "simple_deps/vendor/anyhow/src/lib.rs",
+ "simple_deps/vendor/anyhow/src/macros.rs",
+ "simple_deps/vendor/anyhow/src/wrapper.rs",
+ "simple_deps/vendor/anyhow/tests/common/mod.rs",
+ "simple_deps/vendor/anyhow/tests/compiletest.rs",
+ "simple_deps/vendor/anyhow/tests/drop/mod.rs",
+ "simple_deps/vendor/anyhow/tests/test_autotrait.rs",
+ "simple_deps/vendor/anyhow/tests/test_backtrace.rs",
+ "simple_deps/vendor/anyhow/tests/test_boxed.rs",
+ "simple_deps/vendor/anyhow/tests/test_chain.rs",
+ "simple_deps/vendor/anyhow/tests/test_context.rs",
+ "simple_deps/vendor/anyhow/tests/test_convert.rs",
+ "simple_deps/vendor/anyhow/tests/test_downcast.rs",
+ "simple_deps/vendor/anyhow/tests/test_fmt.rs",
+ "simple_deps/vendor/anyhow/tests/test_macros.rs",
+ "simple_deps/vendor/anyhow/tests/test_repr.rs",
+ "simple_deps/vendor/anyhow/tests/test_source.rs",
+ "simple_deps/vendor/anyhow/tests/ui/no-impl.rs",
+ "simple_deps/vendor/anyhow/tests/ui/no-impl.stderr",
+ "target_renaming/BUILD.gn",
+ "target_renaming/Cargo.lock",
+ "target_renaming/Cargo.toml",
+ "target_renaming/example_lib/Cargo.toml",
+ "target_renaming/example_lib/src/lib.rs",
+ "target_renaming/src/lib.rs",
+ "testonly/BUILD.gn",
+ "testonly/Cargo.lock",
+ "testonly/Cargo.toml",
+ "testonly/example_lib/Cargo.toml",
+ "testonly/example_lib/src/lib.rs",
+ "testonly/src/lib.rs",
+ "visibility/BUILD.gn",
+ "visibility/Cargo.lock",
+ "visibility/Cargo.toml",
+ "visibility/example_lib/Cargo.toml",
+ "visibility/example_lib/src/lib.rs",
+ "visibility/src/lib.rs",
+ ]
+ }
+
+ host_test_data("cargo_gnaw_prebuilt_bin_libs") {
+ sources = [ "//prebuilt/third_party/gn/${host_platform}/gn" ]
+ }
+}
diff --git a/tests/binary/BUILD.gn b/tests/binary/BUILD.gn
new file mode 100644
index 0000000..a8c2048
--- /dev/null
+++ b/tests/binary/BUILD.gn
@@ -0,0 +1,67 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+group("package-with-bin") {
+ public_deps = [ ":package-with-bin-v0_1_0" ]
+}
+
+group("foo-output") {
+ public_deps = [ ":package-with-bin-foo-binary-v0_1_0" ]
+}
+
+executable("package-with-bin-foo-binary-v0_1_0") {
+ crate_name = "foo_binary"
+ crate_root = "//binary/src/main.rs"
+ output_name = "foo-output"
+ if (current_os == "fuchsia") {
+ configs += [ "//extra/fuchsia/config" ]
+ }
+ if (true) {
+ configs += [ "//applied/to/all" ]
+ }
+
+ deps = []
+ deps += [ ":package-with-bin-v0_1_0" ]
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=afa00503dca78ba8",
+ "-Cextra-filename=-afa00503dca78ba8",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("package-with-bin-v0_1_0") {
+ crate_name = "package_with_bin"
+ crate_root = "//binary/src/lib.rs"
+ output_name = "package_with_bin-b6c5dfd7f886ecb4"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=b6c5dfd7f886ecb4",
+ "-Cextra-filename=-b6c5dfd7f886ecb4",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
diff --git a/tests/binary/Cargo.lock b/tests/binary/Cargo.lock
new file mode 100644
index 0000000..b254950
--- /dev/null
+++ b/tests/binary/Cargo.lock
@@ -0,0 +1,5 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "package-with-bin"
+version = "0.1.0"
diff --git a/tests/binary/Cargo.toml b/tests/binary/Cargo.toml
new file mode 100644
index 0000000..334e83b
--- /dev/null
+++ b/tests/binary/Cargo.toml
@@ -0,0 +1,23 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "package-with-bin"
+version = "0.1.0"
+authors = ["Bryan Henry <bryanhenry@google.com>"]
+edition = "2018"
+
+[[bin]]
+name = "foo-binary"
+path = "src/main.rs"
+
+[gn.package.package-with-bin."0.1.0".binary.foo-binary]
+output_name = "foo-output"
+
+[gn.package.package-with-bin."0.1.0".binary.foo-binary.platform."cfg(unix)"]
+configs = [ "//applied/to/all" ]
+
+[gn.package.package-with-bin."0.1.0".binary.foo-binary.platform."cfg(target_os = \"fuchsia\")"]
+configs = [ "//extra/fuchsia/config" ]
+
diff --git a/tests/binary/src/lib.rs b/tests/binary/src/lib.rs
new file mode 100644
index 0000000..5f482a8
--- /dev/null
+++ b/tests/binary/src/lib.rs
@@ -0,0 +1,7 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+fn some_fn() {
+ println!("Hello, world!");
+}
diff --git a/tests/binary/src/main.rs b/tests/binary/src/main.rs
new file mode 100644
index 0000000..8821b24
--- /dev/null
+++ b/tests/binary/src/main.rs
@@ -0,0 +1,5 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+fn main() {}
diff --git a/tests/binary_with_tests/BUILD.gn b/tests/binary_with_tests/BUILD.gn
new file mode 100644
index 0000000..43533b3
--- /dev/null
+++ b/tests/binary_with_tests/BUILD.gn
@@ -0,0 +1,130 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+group("package-with-bin") {
+ public_deps = [ ":package-with-bin-v0_1_0" ]
+}
+
+group("package-with-bin-test") {
+ public_deps = [ ":package-with-bin-v0_1_0-test" ]
+ testonly = true
+}
+
+group("foo-output") {
+ public_deps = [ ":package-with-bin-foo-binary-v0_1_0" ]
+}
+
+group("foo-output-test") {
+ public_deps = [ ":package-with-bin-foo-binary-v0_1_0-test" ]
+ testonly = true
+}
+
+executable("package-with-bin-foo-binary-v0_1_0") {
+ crate_name = "foo_binary"
+ crate_root = "//binary_with_tests/src/main.rs"
+ output_name = "foo-output"
+ if (current_os == "fuchsia") {
+ configs += [ "//extra/fuchsia/config" ]
+ }
+ if (true) {
+ configs += [ "//applied/to/all" ]
+ }
+
+ deps = []
+ deps += [ ":package-with-bin-v0_1_0" ]
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=afa00503dca78ba8",
+ "-Cextra-filename=-afa00503dca78ba8",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+executable("package-with-bin-foo-binary-v0_1_0-test") {
+ crate_name = "foo_binary"
+ crate_root = "//binary_with_tests/src/main.rs"
+ output_name = "foo-output-test"
+ if (current_os == "fuchsia") {
+ configs += [ "//extra/fuchsia/config" ]
+ }
+ if (true) {
+ configs += [ "//applied/to/all" ]
+ }
+
+ deps = []
+ deps += [ ":package-with-bin-v0_1_0" ]
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=afa00503dca78ba8",
+ "-Cextra-filename=-afa00503dca78ba8",
+ "--test",
+ ]
+
+ visibility = [ ":*" ]
+
+ testonly = true
+ applicable_licenses = []
+}
+
+rust_library("package-with-bin-v0_1_0") {
+ crate_name = "package_with_bin"
+ crate_root = "//binary_with_tests/src/lib.rs"
+ output_name = "package_with_bin-b6c5dfd7f886ecb4"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=b6c5dfd7f886ecb4",
+ "-Cextra-filename=-b6c5dfd7f886ecb4",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+executable("package-with-bin-v0_1_0-test") {
+ crate_name = "package_with_bin"
+ crate_root = "//binary_with_tests/src/lib.rs"
+ output_name = "package_with_bin-b6c5dfd7f886ecb4-test"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=b6c5dfd7f886ecb4",
+ "-Cextra-filename=-b6c5dfd7f886ecb4",
+ "--test",
+ ]
+
+ visibility = [ ":*" ]
+
+ testonly = true
+ applicable_licenses = []
+}
diff --git a/tests/binary_with_tests/Cargo.lock b/tests/binary_with_tests/Cargo.lock
new file mode 100644
index 0000000..b254950
--- /dev/null
+++ b/tests/binary_with_tests/Cargo.lock
@@ -0,0 +1,5 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "package-with-bin"
+version = "0.1.0"
diff --git a/tests/binary_with_tests/Cargo.toml b/tests/binary_with_tests/Cargo.toml
new file mode 100644
index 0000000..405a4a9
--- /dev/null
+++ b/tests/binary_with_tests/Cargo.toml
@@ -0,0 +1,26 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "package-with-bin"
+version = "0.1.0"
+authors = ["Bryan Henry <bryanhenry@google.com>"]
+edition = "2018"
+
+[[bin]]
+name = "foo-binary"
+path = "src/main.rs"
+
+[gn.package.package-with-bin."0.1.0"]
+tests = true
+
+[gn.package.package-with-bin."0.1.0".binary.foo-binary]
+output_name = "foo-output"
+
+[gn.package.package-with-bin."0.1.0".binary.foo-binary.platform."cfg(unix)"]
+configs = [ "//applied/to/all" ]
+
+[gn.package.package-with-bin."0.1.0".binary.foo-binary.platform."cfg(target_os = \"fuchsia\")"]
+configs = [ "//extra/fuchsia/config" ]
+
diff --git a/tests/binary_with_tests/src/lib.rs b/tests/binary_with_tests/src/lib.rs
new file mode 100644
index 0000000..5f482a8
--- /dev/null
+++ b/tests/binary_with_tests/src/lib.rs
@@ -0,0 +1,7 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+fn some_fn() {
+ println!("Hello, world!");
+}
diff --git a/tests/binary_with_tests/src/main.rs b/tests/binary_with_tests/src/main.rs
new file mode 100644
index 0000000..8821b24
--- /dev/null
+++ b/tests/binary_with_tests/src/main.rs
@@ -0,0 +1,5 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+fn main() {}
diff --git a/tests/cargo_features/BUILD-all-features.gn b/tests/cargo_features/BUILD-all-features.gn
new file mode 100644
index 0000000..e8476e8
--- /dev/null
+++ b/tests/cargo_features/BUILD-all-features.gn
@@ -0,0 +1,129 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+group("cargo_features") {
+ public_deps = [ ":cargo_features-v0_1_0" ]
+}
+
+rust_library("cargo_features-v0_1_0") {
+ crate_name = "cargo_features"
+ crate_root = "//cargo_features/src/lib.rs"
+ output_name = "cargo_features-4775242031656858"
+
+ deps = []
+ deps += [ ":sub-crate-default-v0_1_0" ]
+ deps += [ ":sub-crate-non-default-v0_1_0" ]
+ deps += [ ":sub-crate-with-feature-v0_1_0" ]
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=4775242031656858",
+ "-Cextra-filename=-4775242031656858",
+ "--cfg=feature=\"default\"",
+ "--cfg=feature=\"featurefoo\"",
+ "--cfg=feature=\"sub-crate-default\"",
+ "--cfg=feature=\"sub-crate-non-default\"",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("sub-crate-default-v0_1_0") {
+ crate_name = "sub_crate_default"
+ crate_root = "//cargo_features/sub-crate-default/src/lib.rs"
+ output_name = "sub_crate_default-907014e298fd4f87"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=907014e298fd4f87",
+ "-Cextra-filename=-907014e298fd4f87",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("sub-crate-for-feature-v0_1_0") {
+ crate_name = "sub_crate_for_feature"
+ crate_root = "//cargo_features/sub-crate-for-feature/src/lib.rs"
+ output_name = "sub_crate_for_feature-aa2f3f9b02f291a9"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=aa2f3f9b02f291a9",
+ "-Cextra-filename=-aa2f3f9b02f291a9",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("sub-crate-non-default-v0_1_0") {
+ crate_name = "sub_crate_non_default"
+ crate_root = "//cargo_features/sub-crate-non-default/src/lib.rs"
+ output_name = "sub_crate_non_default-d9f5c9139685fd00"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=d9f5c9139685fd00",
+ "-Cextra-filename=-d9f5c9139685fd00",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("sub-crate-with-feature-v0_1_0") {
+ crate_name = "sub_crate_with_feature"
+ crate_root = "//cargo_features/sub-crate-with-feature/src/lib.rs"
+ output_name = "sub_crate_with_feature-2daa1749812a946a"
+
+ deps = []
+ deps += [ ":sub-crate-for-feature-v0_1_0" ]
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=2daa1749812a946a",
+ "-Cextra-filename=-2daa1749812a946a",
+ "--cfg=feature=\"featurefoo\"",
+ "--cfg=feature=\"sub-crate-for-feature\"",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
diff --git a/tests/cargo_features/BUILD-default.gn b/tests/cargo_features/BUILD-default.gn
new file mode 100644
index 0000000..1990209
--- /dev/null
+++ b/tests/cargo_features/BUILD-default.gn
@@ -0,0 +1,81 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+group("cargo_features") {
+ public_deps = [ ":cargo_features-v0_1_0" ]
+}
+
+rust_library("cargo_features-v0_1_0") {
+ crate_name = "cargo_features"
+ crate_root = "//cargo_features/src/lib.rs"
+ output_name = "cargo_features-4775242031656858"
+
+ deps = []
+ deps += [ ":sub-crate-default-v0_1_0" ]
+ deps += [ ":sub-crate-with-feature-v0_1_0" ]
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=4775242031656858",
+ "-Cextra-filename=-4775242031656858",
+ "--cfg=feature=\"default\"",
+ "--cfg=feature=\"sub-crate-default\"",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("sub-crate-default-v0_1_0") {
+ crate_name = "sub_crate_default"
+ crate_root = "//cargo_features/sub-crate-default/src/lib.rs"
+ output_name = "sub_crate_default-907014e298fd4f87"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=907014e298fd4f87",
+ "-Cextra-filename=-907014e298fd4f87",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("sub-crate-with-feature-v0_1_0") {
+ crate_name = "sub_crate_with_feature"
+ crate_root = "//cargo_features/sub-crate-with-feature/src/lib.rs"
+ output_name = "sub_crate_with_feature-2daa1749812a946a"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=2daa1749812a946a",
+ "-Cextra-filename=-2daa1749812a946a",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
diff --git a/tests/cargo_features/BUILD-featurefoo.gn b/tests/cargo_features/BUILD-featurefoo.gn
new file mode 100644
index 0000000..7681de9
--- /dev/null
+++ b/tests/cargo_features/BUILD-featurefoo.gn
@@ -0,0 +1,106 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+group("cargo_features") {
+ public_deps = [ ":cargo_features-v0_1_0" ]
+}
+
+rust_library("cargo_features-v0_1_0") {
+ crate_name = "cargo_features"
+ crate_root = "//cargo_features/src/lib.rs"
+ output_name = "cargo_features-4775242031656858"
+
+ deps = []
+ deps += [ ":sub-crate-default-v0_1_0" ]
+ deps += [ ":sub-crate-with-feature-v0_1_0" ]
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=4775242031656858",
+ "-Cextra-filename=-4775242031656858",
+ "--cfg=feature=\"default\"",
+ "--cfg=feature=\"featurefoo\"",
+ "--cfg=feature=\"sub-crate-default\"",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("sub-crate-default-v0_1_0") {
+ crate_name = "sub_crate_default"
+ crate_root = "//cargo_features/sub-crate-default/src/lib.rs"
+ output_name = "sub_crate_default-907014e298fd4f87"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=907014e298fd4f87",
+ "-Cextra-filename=-907014e298fd4f87",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("sub-crate-for-feature-v0_1_0") {
+ crate_name = "sub_crate_for_feature"
+ crate_root = "//cargo_features/sub-crate-for-feature/src/lib.rs"
+ output_name = "sub_crate_for_feature-aa2f3f9b02f291a9"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=aa2f3f9b02f291a9",
+ "-Cextra-filename=-aa2f3f9b02f291a9",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("sub-crate-with-feature-v0_1_0") {
+ crate_name = "sub_crate_with_feature"
+ crate_root = "//cargo_features/sub-crate-with-feature/src/lib.rs"
+ output_name = "sub_crate_with_feature-2daa1749812a946a"
+
+ deps = []
+ deps += [ ":sub-crate-for-feature-v0_1_0" ]
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=2daa1749812a946a",
+ "-Cextra-filename=-2daa1749812a946a",
+ "--cfg=feature=\"featurefoo\"",
+ "--cfg=feature=\"sub-crate-for-feature\"",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
diff --git a/tests/cargo_features/BUILD-no-default-features.gn b/tests/cargo_features/BUILD-no-default-features.gn
new file mode 100644
index 0000000..118faaf
--- /dev/null
+++ b/tests/cargo_features/BUILD-no-default-features.gn
@@ -0,0 +1,57 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+group("cargo_features") {
+ public_deps = [ ":cargo_features-v0_1_0" ]
+}
+
+rust_library("cargo_features-v0_1_0") {
+ crate_name = "cargo_features"
+ crate_root = "//cargo_features/src/lib.rs"
+ output_name = "cargo_features-4775242031656858"
+
+ deps = []
+ deps += [ ":sub-crate-with-feature-v0_1_0" ]
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=4775242031656858",
+ "-Cextra-filename=-4775242031656858",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("sub-crate-with-feature-v0_1_0") {
+ crate_name = "sub_crate_with_feature"
+ crate_root = "//cargo_features/sub-crate-with-feature/src/lib.rs"
+ output_name = "sub_crate_with_feature-2daa1749812a946a"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=2daa1749812a946a",
+ "-Cextra-filename=-2daa1749812a946a",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
diff --git a/tests/cargo_features/Cargo.lock b/tests/cargo_features/Cargo.lock
new file mode 100644
index 0000000..b5075ed
--- /dev/null
+++ b/tests/cargo_features/Cargo.lock
@@ -0,0 +1,31 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "cargo_features"
+version = "0.1.0"
+dependencies = [
+ "sub-crate-default",
+ "sub-crate-non-default",
+ "sub-crate-with-feature",
+]
+
+[[package]]
+name = "sub-crate-default"
+version = "0.1.0"
+
+[[package]]
+name = "sub-crate-for-feature"
+version = "0.1.0"
+
+[[package]]
+name = "sub-crate-non-default"
+version = "0.1.0"
+
+[[package]]
+name = "sub-crate-with-feature"
+version = "0.1.0"
+dependencies = [
+ "sub-crate-for-feature",
+]
diff --git a/tests/cargo_features/Cargo.toml b/tests/cargo_features/Cargo.toml
new file mode 100644
index 0000000..47ba252
--- /dev/null
+++ b/tests/cargo_features/Cargo.toml
@@ -0,0 +1,19 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "cargo_features"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+sub-crate-default = { path = "./sub-crate-default", optional = true }
+sub-crate-non-default = { path = "./sub-crate-non-default", optional = true }
+sub-crate-with-feature = { path = "./sub-crate-with-feature" }
+
+[features]
+default = ["sub-crate-default"]
+featurefoo = ["sub-crate-with-feature/featurefoo"]
diff --git a/tests/cargo_features/src/lib.rs b/tests/cargo_features/src/lib.rs
new file mode 100644
index 0000000..a4eccd0
--- /dev/null
+++ b/tests/cargo_features/src/lib.rs
@@ -0,0 +1,12 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn it_works() {
+ let result = 2 + 2;
+ assert_eq!(result, 4);
+ }
+}
diff --git a/tests/cargo_features/sub-crate-default/Cargo.toml b/tests/cargo_features/sub-crate-default/Cargo.toml
new file mode 100644
index 0000000..8d9a004
--- /dev/null
+++ b/tests/cargo_features/sub-crate-default/Cargo.toml
@@ -0,0 +1,12 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "sub-crate-default"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/tests/cargo_features/sub-crate-default/src/lib.rs b/tests/cargo_features/sub-crate-default/src/lib.rs
new file mode 100644
index 0000000..a4eccd0
--- /dev/null
+++ b/tests/cargo_features/sub-crate-default/src/lib.rs
@@ -0,0 +1,12 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn it_works() {
+ let result = 2 + 2;
+ assert_eq!(result, 4);
+ }
+}
diff --git a/tests/cargo_features/sub-crate-for-feature/Cargo.toml b/tests/cargo_features/sub-crate-for-feature/Cargo.toml
new file mode 100644
index 0000000..9cf8fe7
--- /dev/null
+++ b/tests/cargo_features/sub-crate-for-feature/Cargo.toml
@@ -0,0 +1,12 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "sub-crate-for-feature"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/tests/cargo_features/sub-crate-for-feature/src/lib.rs b/tests/cargo_features/sub-crate-for-feature/src/lib.rs
new file mode 100644
index 0000000..a4eccd0
--- /dev/null
+++ b/tests/cargo_features/sub-crate-for-feature/src/lib.rs
@@ -0,0 +1,12 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn it_works() {
+ let result = 2 + 2;
+ assert_eq!(result, 4);
+ }
+}
diff --git a/tests/cargo_features/sub-crate-non-default/Cargo.toml b/tests/cargo_features/sub-crate-non-default/Cargo.toml
new file mode 100644
index 0000000..a8dc53b
--- /dev/null
+++ b/tests/cargo_features/sub-crate-non-default/Cargo.toml
@@ -0,0 +1,12 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "sub-crate-non-default"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/tests/cargo_features/sub-crate-non-default/src/lib.rs b/tests/cargo_features/sub-crate-non-default/src/lib.rs
new file mode 100644
index 0000000..a4eccd0
--- /dev/null
+++ b/tests/cargo_features/sub-crate-non-default/src/lib.rs
@@ -0,0 +1,12 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn it_works() {
+ let result = 2 + 2;
+ assert_eq!(result, 4);
+ }
+}
diff --git a/tests/cargo_features/sub-crate-with-feature/Cargo.toml b/tests/cargo_features/sub-crate-with-feature/Cargo.toml
new file mode 100644
index 0000000..35eee2b
--- /dev/null
+++ b/tests/cargo_features/sub-crate-with-feature/Cargo.toml
@@ -0,0 +1,16 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "sub-crate-with-feature"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+sub-crate-for-feature = { path = "../sub-crate-for-feature", optional = true }
+
+[features]
+featurefoo = ["sub-crate-for-feature"]
diff --git a/tests/cargo_features/sub-crate-with-feature/src/lib.rs b/tests/cargo_features/sub-crate-with-feature/src/lib.rs
new file mode 100644
index 0000000..a4eccd0
--- /dev/null
+++ b/tests/cargo_features/sub-crate-with-feature/src/lib.rs
@@ -0,0 +1,12 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn it_works() {
+ let result = 2 + 2;
+ assert_eq!(result, 4);
+ }
+}
diff --git a/tests/feature_review/BUILD.gn b/tests/feature_review/BUILD.gn
new file mode 100644
index 0000000..4403a59
--- /dev/null
+++ b/tests/feature_review/BUILD.gn
@@ -0,0 +1,80 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+group("feature_test") {
+ public_deps = [ ":feature_test-v0_1_0" ]
+}
+
+rust_library("crate_with_features-v0_1_0") {
+ crate_name = "crate_with_features"
+ crate_root = "//feature_review/crate_with_features/src/lib.rs"
+ output_name = "crate_with_features-7091b4706c607e57"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=7091b4706c607e57",
+ "-Cextra-filename=-7091b4706c607e57",
+ "--cfg=feature=\"feature1\"",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("feature_test-v0_1_0") {
+ crate_name = "feature_test"
+ crate_root = "//feature_review/src/lib.rs"
+ output_name = "feature_test-75712ce55243d0e0"
+
+ deps = []
+ deps += [ ":needs_feature-v0_1_0" ]
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=75712ce55243d0e0",
+ "-Cextra-filename=-75712ce55243d0e0",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("needs_feature-v0_1_0") {
+ crate_name = "needs_feature"
+ crate_root = "//feature_review/needs_feature/src/lib.rs"
+ output_name = "needs_feature-e99a212d2fc1fa1b"
+
+ deps = []
+ deps += [ ":crate_with_features-v0_1_0" ]
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=e99a212d2fc1fa1b",
+ "-Cextra-filename=-e99a212d2fc1fa1b",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
diff --git a/tests/feature_review/Cargo.lock b/tests/feature_review/Cargo.lock
new file mode 100644
index 0000000..c9a5795
--- /dev/null
+++ b/tests/feature_review/Cargo.lock
@@ -0,0 +1,21 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "crate_with_features"
+version = "0.1.0"
+
+[[package]]
+name = "feature_test"
+version = "0.1.0"
+dependencies = [
+ "needs_feature",
+]
+
+[[package]]
+name = "needs_feature"
+version = "0.1.0"
+dependencies = [
+ "crate_with_features",
+]
diff --git a/tests/feature_review/Cargo.toml b/tests/feature_review/Cargo.toml
new file mode 100644
index 0000000..09de031
--- /dev/null
+++ b/tests/feature_review/Cargo.toml
@@ -0,0 +1,17 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "feature_test"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+needs_feature = { path = "needs_feature" }
+
+[gn]
+require_feature_reviews = ["crate_with_features"]
+
+[gn.package.crate_with_features."0.1.0"]
+reviewed_features = ["feature1"]
diff --git a/tests/feature_review/Cargo_extra_review.toml b/tests/feature_review/Cargo_extra_review.toml
new file mode 100644
index 0000000..6f4c3e7
--- /dev/null
+++ b/tests/feature_review/Cargo_extra_review.toml
@@ -0,0 +1,10 @@
+[package]
+name = "feature_test"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+needs_feature = { path = "needs_feature" }
+
+[gn.package.crate_with_features."0.1.0"]
+reviewed_features = ["feature1"]
diff --git a/tests/feature_review/Cargo_missing_review.toml b/tests/feature_review/Cargo_missing_review.toml
new file mode 100644
index 0000000..deb7aad
--- /dev/null
+++ b/tests/feature_review/Cargo_missing_review.toml
@@ -0,0 +1,12 @@
+[package]
+name = "feature_test"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+needs_feature = { path = "needs_feature" }
+
+[gn]
+require_feature_reviews = ["crate_with_features"]
+
+[gn.package]
\ No newline at end of file
diff --git a/tests/feature_review/Cargo_unreviewed_feature.toml b/tests/feature_review/Cargo_unreviewed_feature.toml
new file mode 100644
index 0000000..e046195
--- /dev/null
+++ b/tests/feature_review/Cargo_unreviewed_feature.toml
@@ -0,0 +1,13 @@
+[package]
+name = "feature_test"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+needs_feature = { path = "needs_feature" }
+
+[gn]
+require_feature_reviews = ["crate_with_features"]
+
+[gn.package.crate_with_features."0.1.0"]
+reviewed_features = ["feature2"]
diff --git a/tests/feature_review/crate_with_features/Cargo.toml b/tests/feature_review/crate_with_features/Cargo.toml
new file mode 100644
index 0000000..c0fa58c
--- /dev/null
+++ b/tests/feature_review/crate_with_features/Cargo.toml
@@ -0,0 +1,12 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "crate_with_features"
+version = "0.1.0"
+edition = "2018"
+
+[features]
+feature1 = []
+feature2 = []
diff --git a/tests/feature_review/crate_with_features/src/lib.rs b/tests/feature_review/crate_with_features/src/lib.rs
new file mode 100644
index 0000000..70a39d4
--- /dev/null
+++ b/tests/feature_review/crate_with_features/src/lib.rs
@@ -0,0 +1,5 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Library intentionally left empty :)
diff --git a/tests/feature_review/needs_feature/Cargo.toml b/tests/feature_review/needs_feature/Cargo.toml
new file mode 100644
index 0000000..cbc217d
--- /dev/null
+++ b/tests/feature_review/needs_feature/Cargo.toml
@@ -0,0 +1,11 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "needs_feature"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+crate_with_features = { path = "../crate_with_features", features = ["feature1"] }
diff --git a/tests/feature_review/needs_feature/src/lib.rs b/tests/feature_review/needs_feature/src/lib.rs
new file mode 100644
index 0000000..70a39d4
--- /dev/null
+++ b/tests/feature_review/needs_feature/src/lib.rs
@@ -0,0 +1,5 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Library intentionally left empty :)
diff --git a/tests/feature_review/src/lib.rs b/tests/feature_review/src/lib.rs
new file mode 100644
index 0000000..70a39d4
--- /dev/null
+++ b/tests/feature_review/src/lib.rs
@@ -0,0 +1,5 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Library intentionally left empty :)
diff --git a/tests/golden.rs b/tests/golden.rs
new file mode 100644
index 0000000..99c9c5e
--- /dev/null
+++ b/tests/golden.rs
@@ -0,0 +1,355 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! This file contains "golden" tests, which compare the output of known sample
+//! `Cargo.toml` files with known fixed reference output files.
+//!
+//! TODO(https://fxbug.dev/42178193) move these golden specs into GN
+
+use {
+ anyhow::Context,
+ argh::FromArgs,
+ // Without this, the test diffs are impractical to debug.
+ pretty_assertions::assert_eq,
+ std::fmt::{Debug, Display},
+ std::path::{Path, PathBuf},
+};
+
+#[derive(FromArgs, Debug)]
+/// Paths to use in test. All paths are relative to where this test is executed.
+///
+/// These paths have to be relative when passed to this test on infra bots, so they are mapped
+/// correctly, otherwise they won't be available at test runtime. It is safe to convert these to
+/// absolute paths later in the test.
+struct Paths {
+ /// path to the directory where golden tests are placed.
+ #[argh(option)]
+ test_base_dir: String,
+ /// path to `rustc` binary to use in test.
+ #[argh(option)]
+ rustc_binary_path: String,
+ /// path to `gn` binary to use in test.
+ #[argh(option)]
+ gn_binary_path: String,
+ /// path to `cargo` binary to use in test.
+ #[argh(option)]
+ cargo_binary_path: String,
+ /// path to shared libraries directory to use in test.
+ #[argh(option)]
+ lib_path: String,
+}
+
+#[derive(PartialEq, Eq)]
+struct DisplayAsDebug<T: Display>(T);
+
+impl<T: Display> Debug for DisplayAsDebug<T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ Display::fmt(&self.0, f)
+ }
+}
+
+fn main() {
+ let paths: Paths = argh::from_env();
+ eprintln!("paths: {:?}", &paths);
+
+ // Shared library setup for Linux and Mac. Systems will ignore the settings
+ // that don't apply to them.
+ //
+ // These values need to be absolute so they work regardless of the current working directory.
+ std::env::set_var("LD_LIBRARY_PATH", Path::new(&paths.lib_path).canonicalize().unwrap());
+ std::env::set_var("DYLD_LIBRARY_PATH", Path::new(&paths.lib_path).canonicalize().unwrap());
+
+ // Cargo internally invokes rustc; but we must tell it to use the one from
+ // our sandbox, and this is configured using the env variable "RUSTC".
+ //
+ // This value needs to be absolute so it works regardless of the current working directory.
+ //
+ // See:
+ // https://doc.rust-lang.org/cargo/reference/environment-variables.html
+ std::env::set_var("RUSTC", Path::new(&paths.rustc_binary_path).canonicalize().unwrap());
+
+ #[derive(Debug, Default)]
+ struct Options {
+ /// Fuchsia SDK metadata output path; relative to the base test directory.
+ sdk_metadata_path: Option<Vec<&'static str>>,
+ /// Fuchsia SDK metadata golden path; relative to the golden files directory.
+ sdk_metadata_golden_path: Option<Vec<&'static str>>,
+ /// Extra arguments to pass to gnaw.
+ extra_args: Vec<&'static str>,
+ }
+ #[derive(Debug)]
+ struct TestCase {
+ /// Manifest file path (`Cargo.toml`); relative to the base test directory.
+ manifest_path: Vec<&'static str>,
+ /// Expected file (`BUILD.gn`); relative to the base test directory.
+ golden_expected_filename: Vec<&'static str>,
+ /// Extra stuff not needed for most tests.
+ options: Options,
+ }
+
+ let tests = vec![
+ TestCase {
+ manifest_path: vec!["simple", "Cargo.toml"],
+ golden_expected_filename: vec!["simple", "BUILD.gn"],
+ options: Default::default(),
+ },
+ TestCase {
+ manifest_path: vec!["simple_deps", "Cargo.toml"],
+ golden_expected_filename: vec!["simple_deps", "BUILD.gn"],
+ options: Default::default(),
+ },
+ TestCase {
+ manifest_path: vec!["simple_deps", "Cargo.toml"],
+ golden_expected_filename: vec!["simple_deps", "BUILD_WITH_NO_ROOT.gn"],
+ options: Options { extra_args: vec!["--skip-root"], ..Default::default() },
+ },
+ TestCase {
+ manifest_path: vec!["platform_deps", "Cargo.toml"],
+ golden_expected_filename: vec!["platform_deps", "BUILD.gn"],
+ options: Options { extra_args: vec!["--skip-root"], ..Default::default() },
+ },
+ TestCase {
+ manifest_path: vec!["platform_features", "Cargo.toml"],
+ golden_expected_filename: vec!["platform_features", "BUILD.gn"],
+ options: Options { extra_args: vec!["--skip-root"], ..Default::default() },
+ },
+ TestCase {
+ manifest_path: vec!["binary", "Cargo.toml"],
+ golden_expected_filename: vec!["binary", "BUILD.gn"],
+ options: Default::default(),
+ },
+ TestCase {
+ manifest_path: vec!["binary_with_tests", "Cargo.toml"],
+ golden_expected_filename: vec!["binary_with_tests", "BUILD.gn"],
+ options: Default::default(),
+ },
+ TestCase {
+ manifest_path: vec!["multiple_crate_types", "Cargo.toml"],
+ golden_expected_filename: vec!["multiple_crate_types", "BUILD.gn"],
+ options: Default::default(),
+ },
+ TestCase {
+ manifest_path: vec!["feature_review", "Cargo.toml"],
+ golden_expected_filename: vec!["feature_review", "BUILD.gn"],
+ options: Default::default(),
+ },
+ TestCase {
+ manifest_path: vec!["cargo_features", "Cargo.toml"],
+ golden_expected_filename: vec!["cargo_features", "BUILD-default.gn"],
+ options: Default::default(),
+ },
+ TestCase {
+ manifest_path: vec!["cargo_features", "Cargo.toml"],
+ golden_expected_filename: vec!["cargo_features", "BUILD-all-features.gn"],
+ options: Options { extra_args: vec!["--all-features"], ..Default::default() },
+ },
+ TestCase {
+ manifest_path: vec!["cargo_features", "Cargo.toml"],
+ golden_expected_filename: vec!["cargo_features", "BUILD-no-default-features.gn"],
+ options: Options { extra_args: vec!["--no-default-features"], ..Default::default() },
+ },
+ TestCase {
+ manifest_path: vec!["cargo_features", "Cargo.toml"],
+ golden_expected_filename: vec!["cargo_features", "BUILD-featurefoo.gn"],
+ options: Options { extra_args: vec!["--features", "featurefoo"], ..Default::default() },
+ },
+ TestCase {
+ manifest_path: vec!["visibility", "Cargo.toml"],
+ golden_expected_filename: vec!["visibility", "BUILD.gn"],
+ options: Options { extra_args: vec!["--skip-root"], ..Default::default() },
+ },
+ TestCase {
+ manifest_path: vec!["target_renaming", "Cargo.toml"],
+ golden_expected_filename: vec!["target_renaming", "BUILD.gn"],
+ options: Options { extra_args: vec!["--skip-root"], ..Default::default() },
+ },
+ TestCase {
+ manifest_path: vec!["sdk_metadata", "Cargo.toml"],
+ golden_expected_filename: vec!["sdk_metadata", "BUILD.gn"],
+ options: Options {
+ sdk_metadata_path: Some(vec!["sdk_metas", "sdk_metadata.sdk.meta.json"]),
+ sdk_metadata_golden_path: Some(vec![
+ "sdk_metadata",
+ "sdk_metas",
+ "sdk_metadata.sdk.meta.json",
+ ]),
+ ..Default::default()
+ },
+ },
+ TestCase {
+ manifest_path: vec!["testonly", "Cargo.toml"],
+ golden_expected_filename: vec!["testonly", "BUILD.gn"],
+ options: Options { extra_args: vec!["--skip-root"], ..Default::default() },
+ },
+ ];
+
+ let run_gnaw = |manifest_path: &[&str],
+ extra_args: &[&str],
+ sdk_metadata_path: Option<&[&str]>| {
+ let test_dir = tempfile::TempDir::new().unwrap();
+ let mut manifest_path: PathBuf =
+ test_dir.path().join(manifest_path.iter().collect::<PathBuf>());
+ let output = test_dir.path().join("BUILD.gn");
+ let output_sdk_metadata = sdk_metadata_path.map(|sdk_metadata_path| {
+ test_dir.path().join(sdk_metadata_path.iter().collect::<PathBuf>())
+ });
+
+ // we need the emitted file to be under the same path as the gn targets it references
+ let test_base_dir = PathBuf::from(&paths.test_base_dir);
+ copy_contents(&test_base_dir, test_dir.path());
+
+ if manifest_path.file_name().unwrap() != "Cargo.toml" {
+ // rename manifest so that `cargo metadata` is happy.
+ let manifest_dest_path =
+ manifest_path.parent().expect("getting Cargo.toml parent dir").join("Cargo.toml");
+ std::fs::copy(&manifest_path, &manifest_dest_path).expect("writing Cargo.toml");
+ manifest_path = manifest_dest_path;
+ }
+
+ let project_root = test_dir.path().to_str().unwrap().to_owned();
+ // Note: argh does not support "--flag=value" or "--bool-flag false".
+ let absolute_cargo_binary_path =
+ Path::new(&paths.cargo_binary_path).canonicalize().unwrap();
+ let mut args: Vec<&str> = vec![
+ // args[0] is not used in arg parsing, so this can be any string.
+ "fake_binary_name",
+ "--manifest-path",
+ manifest_path.to_str().unwrap(),
+ "--project-root",
+ &project_root,
+ "--output",
+ output.to_str().unwrap(),
+ "--gn-bin",
+ &paths.gn_binary_path,
+ "--cargo",
+ // Cargo is not executed in another working directory by gnaw_lib, so an absolute path
+ // is necessary here.
+ absolute_cargo_binary_path.to_str().unwrap(),
+ ];
+ if let Some(output_sdk_metadata) = &output_sdk_metadata {
+ args.extend(&[
+ "--output-fuchsia-sdk-metadata",
+ output_sdk_metadata.parent().unwrap().to_str().unwrap(),
+ ]);
+ }
+ args.extend(extra_args);
+ gnaw_lib::run(&args)
+ .with_context(|| format!("error running gnaw with args: {:?}\n\t", &args))?;
+ let output = std::fs::read_to_string(&output)
+ .with_context(|| format!("while reading tempfile: {}", output.display()))
+ .expect("tempfile read success");
+ let output_sdk_metadata = output_sdk_metadata
+ .as_ref()
+ .map(std::fs::read_to_string)
+ .transpose()
+ .with_context(|| {
+ format!("while reading sdk metadata: {}", output_sdk_metadata.unwrap().display())
+ })
+ .expect("sdk metadata read success");
+ Result::<_, anyhow::Error>::Ok((output, output_sdk_metadata))
+ };
+
+ for test in tests {
+ let (output, output_sdk_metadata) = run_gnaw(
+ &test.manifest_path,
+ &test.options.extra_args,
+ test.options.sdk_metadata_path.as_deref(),
+ )
+ .with_context(|| format!("\n\ttest was: {:?}", &test))
+ .expect("gnaw_lib::run should succeed");
+
+ let test_base_dir = PathBuf::from(&paths.test_base_dir);
+ let expected_path: PathBuf =
+ test_base_dir.join(test.golden_expected_filename.iter().collect::<PathBuf>());
+ let expected = std::fs::read_to_string(expected_path.to_string_lossy().to_string())
+ .with_context(|| {
+ format!("while reading expected: {:?}", &test.golden_expected_filename)
+ })
+ .expect("expected file read success");
+ assert_eq!(
+ DisplayAsDebug(&expected),
+ DisplayAsDebug(&output),
+ "left: expected; right: actual: {:?}\n\nGenerated content:\n----------\n{}\n----------\n",
+ &test,
+ &output
+ );
+
+ if let Some(output_sdk_metadata) = output_sdk_metadata {
+ let expected_sdk_metadata_path = test_base_dir.join(
+ test.options.sdk_metadata_golden_path.as_ref().unwrap().iter().collect::<PathBuf>(),
+ );
+ let expected_sdk_metadata = std::fs::read_to_string(&expected_sdk_metadata_path)
+ .with_context(|| {
+ format!("while reading sdk metadata: {}", expected_sdk_metadata_path.display())
+ })
+ .expect("sdk metadata read success");
+ assert_eq!(
+ DisplayAsDebug(&expected_sdk_metadata),
+ DisplayAsDebug(&output_sdk_metadata),
+ "left: expected; right: actual: {:?}\n\nGenerated content:\n----------\n{}\n----------\n",
+ &test,
+ &output_sdk_metadata,
+ );
+ }
+ }
+
+ #[derive(Debug)]
+ struct ExpectFailCase {
+ /// Manifest file path (`Cargo.toml`); relative to the base test directory.
+ manifest_path: Vec<&'static str>,
+ /// Expected string to search for in returned error.
+ expected_error_substring: &'static str,
+ /// Extra arguments to pass to gnaw.
+ extra_args: Vec<&'static str>,
+ }
+ let tests = vec![
+ ExpectFailCase {
+ manifest_path: vec!["feature_review", "Cargo_unreviewed_feature.toml"],
+ expected_error_substring:
+ "crate_with_features 0.1.0 is included with unreviewed features [\"feature1\"]",
+ extra_args: vec![],
+ },
+ ExpectFailCase {
+ manifest_path: vec!["feature_review", "Cargo_missing_review.toml"],
+ expected_error_substring:
+ "crate_with_features 0.1.0 requires feature review but reviewed features not found",
+ extra_args: vec![],
+ },
+ ExpectFailCase {
+ manifest_path: vec!["feature_review", "Cargo_extra_review.toml"],
+ expected_error_substring:
+ "crate_with_features 0.1.0 sets reviewed_features but crate_with_features was not found in require_feature_reviews",
+ extra_args: vec![],
+ },
+ ];
+ for test in tests {
+ let result = run_gnaw(&test.manifest_path, &test.extra_args, None);
+ let error = match result {
+ Ok(_) => panic!("gnaw unexpectedly succeeded for {:?}", test),
+ Err(e) => e,
+ };
+ if error.chain().find(|e| e.to_string().contains(test.expected_error_substring)).is_none() {
+ panic!(
+ "expected error to contain {:?}, was: {:?}",
+ test.expected_error_substring, error
+ );
+ }
+ }
+}
+
+fn copy_contents(original_test_dir: &Path, test_dir_path: &Path) {
+ // copy the contents of original test dir to test_dir
+ for entry in walkdir::WalkDir::new(&original_test_dir) {
+ let entry = entry.expect("walking original test directory to copy files to /tmp");
+ if !entry.file_type().is_file() {
+ continue;
+ }
+ let to_copy = entry.path();
+ let destination = test_dir_path.join(to_copy.strip_prefix(&original_test_dir).unwrap());
+ std::fs::create_dir_all(destination.parent().unwrap())
+ .expect("making parent of file to copy");
+ std::fs::copy(to_copy, destination).expect("copying file");
+ }
+ println!("done copying files");
+}
diff --git a/tests/multiple_crate_types/BUILD.gn b/tests/multiple_crate_types/BUILD.gn
new file mode 100644
index 0000000..5203c1a
--- /dev/null
+++ b/tests/multiple_crate_types/BUILD.gn
@@ -0,0 +1,105 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+group("multiple_crate_types") {
+ public_deps = [ ":multiple_crate_types-v1_0_25" ]
+}
+
+rust_library("multiple_crate_types-v1_0_25") {
+ crate_name = "multiple_crate_types"
+ crate_root = "//multiple_crate_types/src/lib.rs"
+ output_name = "multiple_crate_types-8be5e59704cbf5a4"
+
+ deps = []
+ deps += [ ":sub-crate-with-cdylib-v1_0_25" ]
+ deps += [ ":sub-crate-with-dylib-v1_0_25" ]
+ deps += [ ":sub-crate-with-rlib-v1_0_25" ]
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=8be5e59704cbf5a4",
+ "-Cextra-filename=-8be5e59704cbf5a4",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("sub-crate-with-cdylib-v1_0_25") {
+ crate_name = "sub_crate_with_cdylib"
+ crate_root = "//multiple_crate_types/sub-crate-with-cdylib/src/lib.rs"
+ output_name = "sub_crate_with_cdylib-2c19a9098651391d"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=2c19a9098651391d",
+ "-Cextra-filename=-2c19a9098651391d",
+ ]
+
+ visibility = []
+ visibility += [ ":*" ]
+ visibility += [ "//foo/bar/*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("sub-crate-with-dylib-v1_0_25") {
+ crate_name = "sub_crate_with_dylib"
+ crate_root = "//multiple_crate_types/sub-crate-with-dylib/src/lib.rs"
+ output_name = "sub_crate_with_dylib-cba782ed0389a004"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=cba782ed0389a004",
+ "-Cextra-filename=-cba782ed0389a004",
+ ]
+
+ visibility = []
+ visibility += [ ":*" ]
+ visibility += [ "//foo/bar/*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("sub-crate-with-rlib-v1_0_25") {
+ crate_name = "sub_crate_with_rlib"
+ crate_root = "//multiple_crate_types/sub-crate-with-rlib/src/lib.rs"
+ output_name = "sub_crate_with_rlib-5a8ce889cce06275"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=5a8ce889cce06275",
+ "-Cextra-filename=-5a8ce889cce06275",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
diff --git a/tests/multiple_crate_types/Cargo.lock b/tests/multiple_crate_types/Cargo.lock
new file mode 100644
index 0000000..dbea50f
--- /dev/null
+++ b/tests/multiple_crate_types/Cargo.lock
@@ -0,0 +1,24 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "multiple_crate_types"
+version = "1.0.25"
+dependencies = [
+ "sub-crate-with-cdylib",
+ "sub-crate-with-dylib",
+ "sub-crate-with-rlib",
+]
+
+[[package]]
+name = "sub-crate-with-cdylib"
+version = "1.0.25"
+
+[[package]]
+name = "sub-crate-with-dylib"
+version = "1.0.25"
+
+[[package]]
+name = "sub-crate-with-rlib"
+version = "1.0.25"
diff --git a/tests/multiple_crate_types/Cargo.toml b/tests/multiple_crate_types/Cargo.toml
new file mode 100644
index 0000000..44bed4f
--- /dev/null
+++ b/tests/multiple_crate_types/Cargo.toml
@@ -0,0 +1,20 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "multiple_crate_types"
+version = "1.0.25"
+authors = ["Erick Tryzelaar <etryzelaar@google.com>"]
+edition = "2018"
+
+[dependencies]
+sub-crate-with-cdylib = { version = "1.0.25", path = "sub-crate-with-cdylib" }
+sub-crate-with-dylib = { version = "1.0.25", path = "sub-crate-with-dylib" }
+sub-crate-with-rlib = { version = "1.0.25", path = "sub-crate-with-rlib" }
+
+[gn.package.sub-crate-with-cdylib."1.0.25"]
+visibility = [":*", "//foo/bar/*"]
+
+[gn.package.sub-crate-with-dylib."1.0.25"]
+visibility = [":*", "//foo/bar/*"]
diff --git a/tests/multiple_crate_types/src/lib.rs b/tests/multiple_crate_types/src/lib.rs
new file mode 100644
index 0000000..917264b
--- /dev/null
+++ b/tests/multiple_crate_types/src/lib.rs
@@ -0,0 +1,7 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+pub fn some_fn() {
+ println!("Hello, world!");
+}
diff --git a/tests/multiple_crate_types/sub-crate-with-cdylib/Cargo.toml b/tests/multiple_crate_types/sub-crate-with-cdylib/Cargo.toml
new file mode 100644
index 0000000..0083f47
--- /dev/null
+++ b/tests/multiple_crate_types/sub-crate-with-cdylib/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "sub-crate-with-cdylib"
+version = "1.0.25"
+authors = ["Erick Tryzelaar <etryzelaar@google.com>"]
+edition = "2018"
+
+[lib]
+crate-type = ["cdylib", "lib", "staticlib"]
diff --git a/tests/multiple_crate_types/sub-crate-with-cdylib/src/lib.rs b/tests/multiple_crate_types/sub-crate-with-cdylib/src/lib.rs
new file mode 100644
index 0000000..40b4304
--- /dev/null
+++ b/tests/multiple_crate_types/sub-crate-with-cdylib/src/lib.rs
@@ -0,0 +1,7 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// no-op
+// hello there
+pub const EXPORTED: u32 = 777;
diff --git a/tests/multiple_crate_types/sub-crate-with-dylib/Cargo.toml b/tests/multiple_crate_types/sub-crate-with-dylib/Cargo.toml
new file mode 100644
index 0000000..3aeefa6
--- /dev/null
+++ b/tests/multiple_crate_types/sub-crate-with-dylib/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "sub-crate-with-dylib"
+version = "1.0.25"
+authors = ["Erick Tryzelaar <etryzelaar@google.com>"]
+edition = "2018"
+
+[lib]
+crate-type = ["dylib", "lib", "staticlib"]
diff --git a/tests/multiple_crate_types/sub-crate-with-dylib/src/lib.rs b/tests/multiple_crate_types/sub-crate-with-dylib/src/lib.rs
new file mode 100644
index 0000000..40b4304
--- /dev/null
+++ b/tests/multiple_crate_types/sub-crate-with-dylib/src/lib.rs
@@ -0,0 +1,7 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// no-op
+// hello there
+pub const EXPORTED: u32 = 777;
diff --git a/tests/multiple_crate_types/sub-crate-with-rlib/Cargo.toml b/tests/multiple_crate_types/sub-crate-with-rlib/Cargo.toml
new file mode 100644
index 0000000..fa164e1
--- /dev/null
+++ b/tests/multiple_crate_types/sub-crate-with-rlib/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "sub-crate-with-rlib"
+version = "1.0.25"
+authors = ["Erick Tryzelaar <etryzelaar@google.com>"]
+edition = "2018"
+
+[lib]
+crate-type = ["rlib", "staticlib"]
diff --git a/tests/multiple_crate_types/sub-crate-with-rlib/src/lib.rs b/tests/multiple_crate_types/sub-crate-with-rlib/src/lib.rs
new file mode 100644
index 0000000..40b4304
--- /dev/null
+++ b/tests/multiple_crate_types/sub-crate-with-rlib/src/lib.rs
@@ -0,0 +1,7 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// no-op
+// hello there
+pub const EXPORTED: u32 = 777;
diff --git a/tests/platform_deps/.cargo/config b/tests/platform_deps/.cargo/config
new file mode 100644
index 0000000..0236928
--- /dev/null
+++ b/tests/platform_deps/.cargo/config
@@ -0,0 +1,5 @@
+[source.crates-io]
+replace-with = "vendored-sources"
+
+[source.vendored-sources]
+directory = "vendor"
diff --git a/tests/platform_deps/BUILD.gn b/tests/platform_deps/BUILD.gn
new file mode 100644
index 0000000..1196a9d
--- /dev/null
+++ b/tests/platform_deps/BUILD.gn
@@ -0,0 +1,57 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+if (current_cpu == "arm64" && current_os == "mac") {
+ group("anyhow") {
+ public_deps = [ ":anyhow-v1_0_27" ]
+ }
+}
+
+if (current_os == "fuchsia") {
+ group("anyhow") {
+ public_deps = [ ":anyhow-v1_0_27" ]
+ }
+}
+
+license("anyhow-v1_0_27.license") {
+ public_package_name = "anyhow"
+ license_files = [
+ "//platform_deps/vendor/anyhow/LICENSE-APACHE",
+ "//platform_deps/vendor/anyhow/LICENSE-MIT",
+ ]
+}
+
+rust_library("anyhow-v1_0_27") {
+ crate_name = "anyhow"
+ crate_root = "//platform_deps/vendor/anyhow/src/lib.rs"
+ output_name = "anyhow-d49cfcf8c47646f2"
+ if (current_os == "fuchsia") {
+ configs += [ "//build/config/fuchsia:libunwind" ]
+ }
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=d49cfcf8c47646f2",
+ "-Cextra-filename=-d49cfcf8c47646f2",
+ "--cfg=feature=\"default\"",
+ "--cfg=feature=\"std\"",
+ "--cfg=backtrace",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = [ ":anyhow-v1_0_27.license" ]
+}
diff --git a/tests/platform_deps/Cargo.lock b/tests/platform_deps/Cargo.lock
new file mode 100644
index 0000000..364b284
--- /dev/null
+++ b/tests/platform_deps/Cargo.lock
@@ -0,0 +1,14 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "anyhow"
+version = "1.0.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785"
+
+[[package]]
+name = "simple_deps"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+]
diff --git a/tests/platform_deps/Cargo.toml b/tests/platform_deps/Cargo.toml
new file mode 100644
index 0000000..7b234a5
--- /dev/null
+++ b/tests/platform_deps/Cargo.toml
@@ -0,0 +1,22 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "simple_deps"
+version = "0.1.0"
+authors = ["Benjamin Brittain <bwb@google.com>"]
+edition = "2018"
+
+[target.'cfg(target_os = "fuchsia")'.dependencies]
+anyhow = "1.0.27"
+
+[target.aarch64-apple-darwin.dependencies]
+anyhow = "1.0.27"
+
+# gn config
+[gn.package.anyhow."1.0.27"]
+rustflags = ["--cfg=backtrace"]
+
+[gn.package.anyhow."1.0.27".platform."cfg(target_os = \"fuchsia\")"]
+configs = [ "//build/config/fuchsia:libunwind" ]
diff --git a/tests/platform_deps/src/lib.rs b/tests/platform_deps/src/lib.rs
new file mode 100644
index 0000000..5f482a8
--- /dev/null
+++ b/tests/platform_deps/src/lib.rs
@@ -0,0 +1,7 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+fn some_fn() {
+ println!("Hello, world!");
+}
diff --git a/tests/platform_deps/vendor/anyhow/.cargo-checksum.json b/tests/platform_deps/vendor/anyhow/.cargo-checksum.json
new file mode 100644
index 0000000..5da7341
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"6a91884ae5c50562445d6a81d989a8ac76fe64e5fd6a6f3ce067e01f03772419","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"6f6d4c826e6be52ba5c29e6b1169b11e3789ab0d426d3e17ad1c19b1f646b631","build.rs":"42e4fff05dc773820ee1843aba8e36389a36386d6e6082e778d2203420bd72ab","src/backtrace.rs":"a82a8ffae2c68ee385dc78d8ec8cb6f3351234f0ad6af7e87df2371593e0f6aa","src/chain.rs":"1627608ce95c3484d26e1742a1b8b533b74c6159916b717bacffae3ae53731f9","src/context.rs":"f2be36af9588c924ed087bcb7bd681d330889e54b8f98cdc2aba93512a28762e","src/error.rs":"38ed38f1d3561685ff7167f3e706d86003952404e63f8ada5c5bf3e9ee32f76c","src/fmt.rs":"079d7b4faaa23f42423e0bb6b4e8a80d7d6d45c38c0d46bebd7d647c8679469f","src/kind.rs":"8481a8b7835eebb3859a8c32c217bf9c73543cfc62e3916b98d39af8b063125c","src/lib.rs":"e10c1dda3d1d40de3606176630885c0eaa0ac78fd58a169fed24004ccd4c37a8","src/macros.rs":"77722190b58a6106b21aefd3b5d4f136a076afcdbc0fae21562d99e2c22912e1","src/wrapper.rs":"1229beca67dbd95ca77c9ecce282272acc55276c267c58cb73a75388b4693dda","tests/common/mod.rs":"f9088c2d7afafa64ff730b629272045b776bfafc2f5957508242da630635f2e1","tests/compiletest.rs":"0a52a44786aea1c299c695bf948b2ed2081e4cc344e5c2cadceab4eb03d0010d","tests/drop/mod.rs":"464bc1ddeae307eac906928286ec3edb77057c5c1302e02150d3649e2b861f1a","tests/test_autotrait.rs":"981e792db353be2f14c7a1cabe43b5f1329c168cb7679077cc2be786a0920d48","tests/test_backtrace.rs":"0e50edbb33b6bd07ba89ff3db72fb7c688ba2a4371fccdbbb20309ab02948b6a","tests/test_boxed.rs":"98a45325b1e86d4c5d3094ab99cd1ada1f771c505d2d7322f0afcbe7bdb71cfa","tests/test_chain.rs":"f28efeae7395d1c395e6f1a647b4199c25a00410ade45248c145c6fcf2fb448a","tests/test_context.rs":"f82c915b182df1a604a4cd558a03b1a821414983d6f6af6822398104cea70676","tests/test_convert.rs":"62840be1ee8022ba5e8c0d3fc1752a1526b2c47d4cceecff2b86790524c3b3ea","tests/test_downcast.rs":"253d6f54e554965023b378b037827ec6289c4779a7a7c12706e19c2731d219fe","tests/test_fmt.rs":"17572596f257aac9aa2ec4620e292ca6a954128b94772bb948399fab53832e70","tests/test_macros.rs":"c7d3d5e0b756f59d4858035025fb341d031369c88486fd9f961ee16bae6c78bf","tests/test_repr.rs":"dbb9b04ddbe1ab31eb5331ea69f05bb3a147299da2275a3d4dcc92947b5591b9","tests/test_source.rs":"b80723cf635a4f8c4df21891b34bfab9ed2b2aa407e7a2f826d24e334cd5f88e","tests/ui/no-impl.rs":"fab6cbf2f6ea510b86f567dfb3b7c31250a9fd71ae5d110dbb9188be569ec593","tests/ui/no-impl.stderr":"7c2c3f46c266a437300591f10be330f937ac6a0a2213ed5030a9fbc895e2d100"},"package":"013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785"}
\ No newline at end of file
diff --git a/tests/platform_deps/vendor/anyhow/Cargo.toml b/tests/platform_deps/vendor/anyhow/Cargo.toml
new file mode 100644
index 0000000..9e2930c
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/Cargo.toml
@@ -0,0 +1,42 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "anyhow"
+version = "1.0.27"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+description = "Flexible concrete Error type built on std::error::Error"
+documentation = "https://docs.rs/anyhow"
+readme = "README.md"
+categories = ["rust-patterns"]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/dtolnay/anyhow"
+[dev-dependencies.futures]
+version = "0.3"
+default-features = false
+
+[dev-dependencies.rustversion]
+version = "1.0"
+
+[dev-dependencies.thiserror]
+version = "1.0"
+
+[dev-dependencies.trybuild]
+version = "1.0.19"
+features = ["diff"]
+
+[features]
+default = ["std"]
+std = []
+[badges.travis-ci]
+repository = "dtolnay/anyhow"
diff --git a/tests/platform_deps/vendor/anyhow/LICENSE-APACHE b/tests/platform_deps/vendor/anyhow/LICENSE-APACHE
new file mode 100644
index 0000000..16fe87b
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/LICENSE-APACHE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+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.
diff --git a/tests/platform_deps/vendor/anyhow/LICENSE-MIT b/tests/platform_deps/vendor/anyhow/LICENSE-MIT
new file mode 100644
index 0000000..31aa793
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/LICENSE-MIT
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/tests/platform_deps/vendor/anyhow/README.md b/tests/platform_deps/vendor/anyhow/README.md
new file mode 100644
index 0000000..0c2786b
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/README.md
@@ -0,0 +1,171 @@
+Anyhow ¯\\\_(ツ)\_/¯
+=========================
+
+[](https://travis-ci.com/dtolnay/anyhow)
+[](https://crates.io/crates/anyhow)
+[](https://docs.rs/anyhow)
+
+This library provides [`anyhow::Error`][Error], a trait object based error type
+for easy idiomatic error handling in Rust applications.
+
+[Error]: https://docs.rs/anyhow/1.0/anyhow/struct.Error.html
+
+```toml
+[dependencies]
+anyhow = "1.0"
+```
+
+*Compiler support: requires rustc 1.34+*
+
+<br>
+
+## Details
+
+- Use `Result<T, anyhow::Error>`, or equivalently `anyhow::Result<T>`, as the
+ return type of any fallible function.
+
+ Within the function, use `?` to easily propagate any error that implements the
+ `std::error::Error` trait.
+
+ ```rust
+ use anyhow::Result;
+
+ fn get_cluster_info() -> Result<ClusterMap> {
+ let config = std::fs::read_to_string("cluster.json")?;
+ let map: ClusterMap = serde_json::from_str(&config)?;
+ Ok(map)
+ }
+ ```
+
+- Attach context to help the person troubleshooting the error understand where
+ things went wrong. A low-level error like "No such file or directory" can be
+ annoying to debug without more context about what higher level step the
+ application was in the middle of.
+
+ ```rust
+ use anyhow::{Context, Result};
+
+ fn main() -> Result<()> {
+ ...
+ it.detach().context("Failed to detach the important thing")?;
+
+ let content = std::fs::read(path)
+ .with_context(|| format!("Failed to read instrs from {}", path))?;
+ ...
+ }
+ ```
+
+ ```console
+ Error: Failed to read instrs from ./path/to/instrs.json
+
+ Caused by:
+ No such file or directory (os error 2)
+ ```
+
+- Downcasting is supported and can be by value, by shared reference, or by
+ mutable reference as needed.
+
+ ```rust
+ // If the error was caused by redaction, then return a
+ // tombstone instead of the content.
+ match root_cause.downcast_ref::<DataStoreError>() {
+ Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)),
+ None => Err(error),
+ }
+ ```
+
+- A backtrace is captured and printed with the error if the underlying error
+ type does not already provide its own. In order to see backtraces, they must
+ be enabled through the environment variables described in [`std::backtrace`]:
+
+ - If you want panics and errors to both have backtraces, set
+ `RUST_BACKTRACE=1`;
+ - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`;
+ - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and
+ `RUST_LIB_BACKTRACE=0`.
+
+ [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables
+
+- Anyhow works with any error type that has an impl of `std::error::Error`,
+ including ones defined in your crate. We do not bundle a `derive(Error)` macro
+ but you can write the impls yourself or use a standalone macro like
+ [thiserror].
+
+ ```rust
+ use thiserror::Error;
+
+ #[derive(Error, Debug)]
+ pub enum FormatError {
+ #[error("Invalid header (expected {expected:?}, got {found:?})")]
+ InvalidHeader {
+ expected: String,
+ found: String,
+ },
+ #[error("Missing attribute: {0}")]
+ MissingAttribute(String),
+ }
+ ```
+
+- One-off error messages can be constructed using the `anyhow!` macro, which
+ supports string interpolation and produces an `anyhow::Error`.
+
+ ```rust
+ return Err(anyhow!("Missing attribute: {}", missing));
+ ```
+
+<br>
+
+## No-std support
+
+In no_std mode, the same API is almost all available and works the same way. To
+depend on Anyhow in no_std mode, disable our default enabled "std" feature in
+Cargo.toml. A global allocator is required.
+
+```toml
+[dependencies]
+anyhow = { version = "1.0", default-features = false }
+```
+
+Since the `?`-based error conversions would normally rely on the
+`std::error::Error` trait which is only available through std, no_std mode will
+require an explicit `.map_err(Error::msg)` when working with a non-Anyhow error
+type inside a function that returns Anyhow's error type.
+
+<br>
+
+## Comparison to failure
+
+The `anyhow::Error` type works something like `failure::Error`, but unlike
+failure ours is built around the standard library's `std::error::Error` trait
+rather than a separate trait `failure::Fail`. The standard library has adopted
+the necessary improvements for this to be possible as part of [RFC 2504].
+
+[RFC 2504]: https://github.com/rust-lang/rfcs/blob/HEAD/text/2504-fix-error.md
+
+<br>
+
+## Comparison to thiserror
+
+Use Anyhow if you don't care what error type your functions return, you just
+want it to be easy. This is common in application code. Use [thiserror] if you
+are a library that wants to design your own dedicated error type(s) so that on
+failures the caller gets exactly the information that you choose.
+
+[thiserror]: https://github.com/dtolnay/thiserror
+
+<br>
+
+#### License
+
+<sup>
+Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
+2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
+</sup>
+
+<br>
+
+<sub>
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
+be dual licensed as above, without any additional terms or conditions.
+</sub>
diff --git a/tests/platform_deps/vendor/anyhow/build.rs b/tests/platform_deps/vendor/anyhow/build.rs
new file mode 100644
index 0000000..293167e
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/build.rs
@@ -0,0 +1,61 @@
+use std::path::Path;
+use std::process::{Command, ExitStatus};
+use std::{env, fs};
+
+// This code exercises the surface area that we expect of the std Backtrace
+// type. If the current toolchain is able to compile it, we go ahead and use
+// backtrace in anyhow.
+const PROBE: &str = r#"
+ #![feature(backtrace)]
+ #![allow(dead_code)]
+
+ use std::backtrace::{Backtrace, BacktraceStatus};
+ use std::error::Error;
+ use std::fmt::{self, Display};
+
+ #[derive(Debug)]
+ struct E;
+
+ impl Display for E {
+ fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
+ unimplemented!()
+ }
+ }
+
+ impl Error for E {
+ fn backtrace(&self) -> Option<&Backtrace> {
+ let backtrace = Backtrace::capture();
+ match backtrace.status() {
+ BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {}
+ }
+ unimplemented!()
+ }
+ }
+"#;
+
+fn main() {
+ if !cfg!(feature = "std") {
+ return;
+ }
+ match compile_probe() {
+ Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"),
+ _ => {}
+ }
+}
+
+fn compile_probe() -> Option<ExitStatus> {
+ let rustc = env::var_os("RUSTC")?;
+ let out_dir = env::var_os("OUT_DIR")?;
+ let probefile = Path::new(&out_dir).join("probe.rs");
+ fs::write(&probefile, PROBE).ok()?;
+ Command::new(rustc)
+ .arg("--edition=2018")
+ .arg("--crate-name=anyhow_build")
+ .arg("--crate-type=lib")
+ .arg("--emit=metadata")
+ .arg("--out-dir")
+ .arg(out_dir)
+ .arg(probefile)
+ .status()
+ .ok()
+}
diff --git a/tests/platform_deps/vendor/anyhow/src/backtrace.rs b/tests/platform_deps/vendor/anyhow/src/backtrace.rs
new file mode 100644
index 0000000..01e33cb
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/src/backtrace.rs
@@ -0,0 +1,36 @@
+#[cfg(backtrace)]
+pub(crate) use std::backtrace::Backtrace;
+
+#[cfg(not(backtrace))]
+pub(crate) enum Backtrace {}
+
+#[cfg(backtrace)]
+macro_rules! backtrace {
+ () => {
+ Some(Backtrace::capture())
+ };
+}
+
+#[cfg(not(backtrace))]
+macro_rules! backtrace {
+ () => {
+ None
+ };
+}
+
+#[cfg(backtrace)]
+macro_rules! backtrace_if_absent {
+ ($err:expr) => {
+ match $err.backtrace() {
+ Some(_) => None,
+ None => Some(Backtrace::capture()),
+ }
+ };
+}
+
+#[cfg(all(feature = "std", not(backtrace)))]
+macro_rules! backtrace_if_absent {
+ ($err:expr) => {
+ None
+ };
+}
diff --git a/tests/platform_deps/vendor/anyhow/src/chain.rs b/tests/platform_deps/vendor/anyhow/src/chain.rs
new file mode 100644
index 0000000..97fd0e3
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/src/chain.rs
@@ -0,0 +1,95 @@
+use self::ChainState::*;
+use crate::StdError;
+
+#[cfg(feature = "std")]
+use std::vec;
+
+#[cfg(feature = "std")]
+pub(crate) use crate::Chain;
+
+#[cfg(not(feature = "std"))]
+pub(crate) struct Chain<'a> {
+ state: ChainState<'a>,
+}
+
+#[derive(Clone)]
+pub(crate) enum ChainState<'a> {
+ Linked {
+ next: Option<&'a (dyn StdError + 'static)>,
+ },
+ #[cfg(feature = "std")]
+ Buffered {
+ rest: vec::IntoIter<&'a (dyn StdError + 'static)>,
+ },
+}
+
+impl<'a> Chain<'a> {
+ pub fn new(head: &'a (dyn StdError + 'static)) -> Self {
+ Chain { state: ChainState::Linked { next: Some(head) } }
+ }
+}
+
+impl<'a> Iterator for Chain<'a> {
+ type Item = &'a (dyn StdError + 'static);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match &mut self.state {
+ Linked { next } => {
+ let error = (*next)?;
+ *next = error.source();
+ Some(error)
+ }
+ #[cfg(feature = "std")]
+ Buffered { rest } => rest.next(),
+ }
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let len = self.len();
+ (len, Some(len))
+ }
+}
+
+#[cfg(feature = "std")]
+impl DoubleEndedIterator for Chain<'_> {
+ fn next_back(&mut self) -> Option<Self::Item> {
+ match &mut self.state {
+ Linked { mut next } => {
+ let mut rest = Vec::new();
+ while let Some(cause) = next {
+ next = cause.source();
+ rest.push(cause);
+ }
+ let mut rest = rest.into_iter();
+ let last = rest.next_back();
+ self.state = Buffered { rest };
+ last
+ }
+ Buffered { rest } => rest.next_back(),
+ }
+ }
+}
+
+impl ExactSizeIterator for Chain<'_> {
+ fn len(&self) -> usize {
+ match &self.state {
+ Linked { mut next } => {
+ let mut len = 0;
+ while let Some(cause) = next {
+ next = cause.source();
+ len += 1;
+ }
+ len
+ }
+ #[cfg(feature = "std")]
+ Buffered { rest } => rest.len(),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl Default for Chain<'_> {
+ fn default() -> Self {
+ Chain { state: ChainState::Buffered { rest: Vec::new().into_iter() } }
+ }
+}
diff --git a/tests/platform_deps/vendor/anyhow/src/context.rs b/tests/platform_deps/vendor/anyhow/src/context.rs
new file mode 100644
index 0000000..25d3411
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/src/context.rs
@@ -0,0 +1,177 @@
+use crate::error::ContextError;
+use crate::{Context, Error, StdError};
+use core::convert::Infallible;
+use core::fmt::{self, Debug, Display, Write};
+
+#[cfg(backtrace)]
+use std::backtrace::Backtrace;
+
+mod ext {
+ use super::*;
+
+ pub trait StdError {
+ fn ext_context<C>(self, context: C) -> Error
+ where
+ C: Display + Send + Sync + 'static;
+ }
+
+ #[cfg(feature = "std")]
+ impl<E> StdError for E
+ where
+ E: std::error::Error + Send + Sync + 'static,
+ {
+ fn ext_context<C>(self, context: C) -> Error
+ where
+ C: Display + Send + Sync + 'static,
+ {
+ let backtrace = backtrace_if_absent!(self);
+ Error::from_context(context, self, backtrace)
+ }
+ }
+
+ impl StdError for Error {
+ fn ext_context<C>(self, context: C) -> Error
+ where
+ C: Display + Send + Sync + 'static,
+ {
+ self.context(context)
+ }
+ }
+}
+
+impl<T, E> Context<T, E> for Result<T, E>
+where
+ E: ext::StdError + Send + Sync + 'static,
+{
+ fn context<C>(self, context: C) -> Result<T, Error>
+ where
+ C: Display + Send + Sync + 'static,
+ {
+ self.map_err(|error| error.ext_context(context))
+ }
+
+ fn with_context<C, F>(self, context: F) -> Result<T, Error>
+ where
+ C: Display + Send + Sync + 'static,
+ F: FnOnce() -> C,
+ {
+ self.map_err(|error| error.ext_context(context()))
+ }
+}
+
+/// ```
+/// # type T = ();
+/// #
+/// use anyhow::{Context, Result};
+///
+/// fn maybe_get() -> Option<T> {
+/// # const IGNORE: &str = stringify! {
+/// ...
+/// # };
+/// # unimplemented!()
+/// }
+///
+/// fn demo() -> Result<()> {
+/// let t = maybe_get().context("there is no T")?;
+/// # const IGNORE: &str = stringify! {
+/// ...
+/// # };
+/// # unimplemented!()
+/// }
+/// ```
+impl<T> Context<T, Infallible> for Option<T> {
+ fn context<C>(self, context: C) -> Result<T, Error>
+ where
+ C: Display + Send + Sync + 'static,
+ {
+ self.ok_or_else(|| Error::from_display(context, backtrace!()))
+ }
+
+ fn with_context<C, F>(self, context: F) -> Result<T, Error>
+ where
+ C: Display + Send + Sync + 'static,
+ F: FnOnce() -> C,
+ {
+ self.ok_or_else(|| Error::from_display(context(), backtrace!()))
+ }
+}
+
+impl<C, E> Debug for ContextError<C, E>
+where
+ C: Display,
+ E: Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Error")
+ .field("context", &Quoted(&self.context))
+ .field("source", &self.error)
+ .finish()
+ }
+}
+
+impl<C, E> Display for ContextError<C, E>
+where
+ C: Display,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.context, f)
+ }
+}
+
+impl<C, E> StdError for ContextError<C, E>
+where
+ C: Display,
+ E: StdError + 'static,
+{
+ #[cfg(backtrace)]
+ fn backtrace(&self) -> Option<&Backtrace> {
+ self.error.backtrace()
+ }
+
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ Some(&self.error)
+ }
+}
+
+impl<C> StdError for ContextError<C, Error>
+where
+ C: Display,
+{
+ #[cfg(backtrace)]
+ fn backtrace(&self) -> Option<&Backtrace> {
+ Some(self.error.backtrace())
+ }
+
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ Some(self.error.inner.error())
+ }
+}
+
+struct Quoted<C>(C);
+
+impl<C> Debug for Quoted<C>
+where
+ C: Display,
+{
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_char('"')?;
+ Quoted(&mut *formatter).write_fmt(format_args!("{}", self.0))?;
+ formatter.write_char('"')?;
+ Ok(())
+ }
+}
+
+impl Write for Quoted<&mut fmt::Formatter<'_>> {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ Display::fmt(&s.escape_debug(), self.0)
+ }
+}
+
+pub(crate) mod private {
+ use super::*;
+
+ pub trait Sealed {}
+
+ impl<T, E> Sealed for Result<T, E> where E: ext::StdError {}
+ impl<T> Sealed for Option<T> {}
+}
diff --git a/tests/platform_deps/vendor/anyhow/src/error.rs b/tests/platform_deps/vendor/anyhow/src/error.rs
new file mode 100644
index 0000000..410a8f4
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/src/error.rs
@@ -0,0 +1,794 @@
+use crate::alloc::Box;
+use crate::backtrace::Backtrace;
+use crate::chain::Chain;
+use crate::{Error, StdError};
+use core::any::TypeId;
+use core::fmt::{self, Debug, Display};
+use core::mem::{self, ManuallyDrop};
+use core::ptr::{self, NonNull};
+
+#[cfg(feature = "std")]
+use core::ops::{Deref, DerefMut};
+
+impl Error {
+ /// Create a new error object from any error type.
+ ///
+ /// The error type must be threadsafe and `'static`, so that the `Error`
+ /// will be as well.
+ ///
+ /// If the error type does not provide a backtrace, a backtrace will be
+ /// created here to ensure that a backtrace exists.
+ #[cfg(feature = "std")]
+ pub fn new<E>(error: E) -> Self
+ where
+ E: StdError + Send + Sync + 'static,
+ {
+ let backtrace = backtrace_if_absent!(error);
+ Error::from_std(error, backtrace)
+ }
+
+ /// Create a new error object from a printable error message.
+ ///
+ /// If the argument implements std::error::Error, prefer `Error::new`
+ /// instead which preserves the underlying error's cause chain and
+ /// backtrace. If the argument may or may not implement std::error::Error
+ /// now or in the future, use `anyhow!(err)` which handles either way
+ /// correctly.
+ ///
+ /// `Error::msg("...")` is equivalent to `anyhow!("...")` but occasionally
+ /// convenient in places where a function is preferable over a macro, such
+ /// as iterator or stream combinators:
+ ///
+ /// ```
+ /// # mod ffi {
+ /// # pub struct Input;
+ /// # pub struct Output;
+ /// # pub async fn do_some_work(_: Input) -> Result<Output, &'static str> {
+ /// # unimplemented!()
+ /// # }
+ /// # }
+ /// #
+ /// # use ffi::{Input, Output};
+ /// #
+ /// use anyhow::{Error, Result};
+ /// use futures::stream::{Stream, StreamExt, TryStreamExt};
+ ///
+ /// async fn demo<S>(stream: S) -> Result<Vec<Output>>
+ /// where
+ /// S: Stream<Item = Input>,
+ /// {
+ /// stream
+ /// .then(ffi::do_some_work) // returns Result<Output, &str>
+ /// .map_err(Error::msg)
+ /// .try_collect()
+ /// .await
+ /// }
+ /// ```
+ pub fn msg<M>(message: M) -> Self
+ where
+ M: Display + Debug + Send + Sync + 'static,
+ {
+ Error::from_adhoc(message, backtrace!())
+ }
+
+ #[cfg(feature = "std")]
+ pub(crate) fn from_std<E>(error: E, backtrace: Option<Backtrace>) -> Self
+ where
+ E: StdError + Send + Sync + 'static,
+ {
+ let vtable = &ErrorVTable {
+ object_drop: object_drop::<E>,
+ object_ref: object_ref::<E>,
+ #[cfg(feature = "std")]
+ object_mut: object_mut::<E>,
+ object_boxed: object_boxed::<E>,
+ object_downcast: object_downcast::<E>,
+ object_drop_rest: object_drop_front::<E>,
+ };
+
+ // Safety: passing vtable that operates on the right type E.
+ unsafe { Error::construct(error, vtable, backtrace) }
+ }
+
+ pub(crate) fn from_adhoc<M>(message: M, backtrace: Option<Backtrace>) -> Self
+ where
+ M: Display + Debug + Send + Sync + 'static,
+ {
+ use crate::wrapper::MessageError;
+ let error: MessageError<M> = MessageError(message);
+ let vtable = &ErrorVTable {
+ object_drop: object_drop::<MessageError<M>>,
+ object_ref: object_ref::<MessageError<M>>,
+ #[cfg(feature = "std")]
+ object_mut: object_mut::<MessageError<M>>,
+ object_boxed: object_boxed::<MessageError<M>>,
+ object_downcast: object_downcast::<M>,
+ object_drop_rest: object_drop_front::<M>,
+ };
+
+ // Safety: MessageError is repr(transparent) so it is okay for the
+ // vtable to allow casting the MessageError<M> to M.
+ unsafe { Error::construct(error, vtable, backtrace) }
+ }
+
+ pub(crate) fn from_display<M>(message: M, backtrace: Option<Backtrace>) -> Self
+ where
+ M: Display + Send + Sync + 'static,
+ {
+ use crate::wrapper::DisplayError;
+ let error: DisplayError<M> = DisplayError(message);
+ let vtable = &ErrorVTable {
+ object_drop: object_drop::<DisplayError<M>>,
+ object_ref: object_ref::<DisplayError<M>>,
+ #[cfg(feature = "std")]
+ object_mut: object_mut::<DisplayError<M>>,
+ object_boxed: object_boxed::<DisplayError<M>>,
+ object_downcast: object_downcast::<M>,
+ object_drop_rest: object_drop_front::<M>,
+ };
+
+ // Safety: DisplayError is repr(transparent) so it is okay for the
+ // vtable to allow casting the DisplayError<M> to M.
+ unsafe { Error::construct(error, vtable, backtrace) }
+ }
+
+ #[cfg(feature = "std")]
+ pub(crate) fn from_context<C, E>(context: C, error: E, backtrace: Option<Backtrace>) -> Self
+ where
+ C: Display + Send + Sync + 'static,
+ E: StdError + Send + Sync + 'static,
+ {
+ let error: ContextError<C, E> = ContextError { context, error };
+
+ let vtable = &ErrorVTable {
+ object_drop: object_drop::<ContextError<C, E>>,
+ object_ref: object_ref::<ContextError<C, E>>,
+ #[cfg(feature = "std")]
+ object_mut: object_mut::<ContextError<C, E>>,
+ object_boxed: object_boxed::<ContextError<C, E>>,
+ object_downcast: context_downcast::<C, E>,
+ object_drop_rest: context_drop_rest::<C, E>,
+ };
+
+ // Safety: passing vtable that operates on the right type.
+ unsafe { Error::construct(error, vtable, backtrace) }
+ }
+
+ #[cfg(feature = "std")]
+ pub(crate) fn from_boxed(
+ error: Box<dyn StdError + Send + Sync>,
+ backtrace: Option<Backtrace>,
+ ) -> Self {
+ use crate::wrapper::BoxedError;
+ let error = BoxedError(error);
+ let vtable = &ErrorVTable {
+ object_drop: object_drop::<BoxedError>,
+ object_ref: object_ref::<BoxedError>,
+ #[cfg(feature = "std")]
+ object_mut: object_mut::<BoxedError>,
+ object_boxed: object_boxed::<BoxedError>,
+ object_downcast: object_downcast::<Box<dyn StdError + Send + Sync>>,
+ object_drop_rest: object_drop_front::<Box<dyn StdError + Send + Sync>>,
+ };
+
+ // Safety: BoxedError is repr(transparent) so it is okay for the vtable
+ // to allow casting to Box<dyn StdError + Send + Sync>.
+ unsafe { Error::construct(error, vtable, backtrace) }
+ }
+
+ // Takes backtrace as argument rather than capturing it here so that the
+ // user sees one fewer layer of wrapping noise in the backtrace.
+ //
+ // Unsafe because the given vtable must have sensible behavior on the error
+ // value of type E.
+ unsafe fn construct<E>(
+ error: E,
+ vtable: &'static ErrorVTable,
+ backtrace: Option<Backtrace>,
+ ) -> Self
+ where
+ E: StdError + Send + Sync + 'static,
+ {
+ let inner = Box::new(ErrorImpl { vtable, backtrace, _object: error });
+ // Erase the concrete type of E from the compile-time type system. This
+ // is equivalent to the safe unsize coersion from Box<ErrorImpl<E>> to
+ // Box<ErrorImpl<dyn StdError + Send + Sync + 'static>> except that the
+ // result is a thin pointer. The necessary behavior for manipulating the
+ // underlying ErrorImpl<E> is preserved in the vtable provided by the
+ // caller rather than a builtin fat pointer vtable.
+ let erased = mem::transmute::<Box<ErrorImpl<E>>, Box<ErrorImpl<()>>>(inner);
+ let inner = ManuallyDrop::new(erased);
+ Error { inner }
+ }
+
+ /// Wrap the error value with additional context.
+ ///
+ /// For attaching context to a `Result` as it is propagated, the
+ /// [`Context`][crate::Context] extension trait may be more convenient than
+ /// this function.
+ ///
+ /// The primary reason to use `error.context(...)` instead of
+ /// `result.context(...)` via the `Context` trait would be if the context
+ /// needs to depend on some data held by the underlying error:
+ ///
+ /// ```
+ /// # use std::fmt::{self, Debug, Display};
+ /// #
+ /// # type T = ();
+ /// #
+ /// # impl std::error::Error for ParseError {}
+ /// # impl Debug for ParseError {
+ /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ /// # unimplemented!()
+ /// # }
+ /// # }
+ /// # impl Display for ParseError {
+ /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ /// # unimplemented!()
+ /// # }
+ /// # }
+ /// #
+ /// use anyhow::Result;
+ /// use std::fs::File;
+ /// use std::path::Path;
+ ///
+ /// struct ParseError {
+ /// line: usize,
+ /// column: usize,
+ /// }
+ ///
+ /// fn parse_impl(file: File) -> Result<T, ParseError> {
+ /// # const IGNORE: &str = stringify! {
+ /// ...
+ /// # };
+ /// # unimplemented!()
+ /// }
+ ///
+ /// pub fn parse(path: impl AsRef<Path>) -> Result<T> {
+ /// let file = File::open(&path)?;
+ /// parse_impl(file).map_err(|error| {
+ /// let context = format!(
+ /// "only the first {} lines of {} are valid",
+ /// error.line, path.as_ref().display(),
+ /// );
+ /// anyhow::Error::new(error).context(context)
+ /// })
+ /// }
+ /// ```
+ pub fn context<C>(self, context: C) -> Self
+ where
+ C: Display + Send + Sync + 'static,
+ {
+ let error: ContextError<C, Error> = ContextError { context, error: self };
+
+ let vtable = &ErrorVTable {
+ object_drop: object_drop::<ContextError<C, Error>>,
+ object_ref: object_ref::<ContextError<C, Error>>,
+ #[cfg(feature = "std")]
+ object_mut: object_mut::<ContextError<C, Error>>,
+ object_boxed: object_boxed::<ContextError<C, Error>>,
+ object_downcast: context_chain_downcast::<C>,
+ object_drop_rest: context_chain_drop_rest::<C>,
+ };
+
+ // As the cause is anyhow::Error, we already have a backtrace for it.
+ let backtrace = None;
+
+ // Safety: passing vtable that operates on the right type.
+ unsafe { Error::construct(error, vtable, backtrace) }
+ }
+
+ /// Get the backtrace for this Error.
+ ///
+ /// Backtraces are only available on the nightly channel. Tracking issue:
+ /// [rust-lang/rust#53487][tracking].
+ ///
+ /// In order for the backtrace to be meaningful, one of the two environment
+ /// variables `RUST_LIB_BACKTRACE=1` or `RUST_BACKTRACE=1` must be defined
+ /// and `RUST_LIB_BACKTRACE` must not be `0`. Backtraces are somewhat
+ /// expensive to capture in Rust, so we don't necessarily want to be
+ /// capturing them all over the place all the time.
+ ///
+ /// - If you want panics and errors to both have backtraces, set
+ /// `RUST_BACKTRACE=1`;
+ /// - If you want only errors to have backtraces, set
+ /// `RUST_LIB_BACKTRACE=1`;
+ /// - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and
+ /// `RUST_LIB_BACKTRACE=0`.
+ ///
+ /// [tracking]: https://github.com/rust-lang/rust/issues/53487
+ #[cfg(backtrace)]
+ pub fn backtrace(&self) -> &Backtrace {
+ self.inner.backtrace()
+ }
+
+ /// An iterator of the chain of source errors contained by this Error.
+ ///
+ /// This iterator will visit every error in the cause chain of this error
+ /// object, beginning with the error that this error object was created
+ /// from.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use anyhow::Error;
+ /// use std::io;
+ ///
+ /// pub fn underlying_io_error_kind(error: &Error) -> Option<io::ErrorKind> {
+ /// for cause in error.chain() {
+ /// if let Some(io_error) = cause.downcast_ref::<io::Error>() {
+ /// return Some(io_error.kind());
+ /// }
+ /// }
+ /// None
+ /// }
+ /// ```
+ #[cfg(feature = "std")]
+ pub fn chain(&self) -> Chain {
+ self.inner.chain()
+ }
+
+ /// The lowest level cause of this error — this error's cause's
+ /// cause's cause etc.
+ ///
+ /// The root cause is the last error in the iterator produced by
+ /// [`chain()`][Error::chain].
+ #[cfg(feature = "std")]
+ pub fn root_cause(&self) -> &(dyn StdError + 'static) {
+ let mut chain = self.chain();
+ let mut root_cause = chain.next().unwrap();
+ for cause in chain {
+ root_cause = cause;
+ }
+ root_cause
+ }
+
+ /// Returns true if `E` is the type held by this error object.
+ ///
+ /// For errors with context, this method returns true if `E` matches the
+ /// type of the context `C` **or** the type of the error on which the
+ /// context has been attached. For details about the interaction between
+ /// context and downcasting, [see here].
+ ///
+ /// [see here]: trait.Context.html#effect-on-downcasting
+ pub fn is<E>(&self) -> bool
+ where
+ E: Display + Debug + Send + Sync + 'static,
+ {
+ self.downcast_ref::<E>().is_some()
+ }
+
+ /// Attempt to downcast the error object to a concrete type.
+ pub fn downcast<E>(self) -> Result<E, Self>
+ where
+ E: Display + Debug + Send + Sync + 'static,
+ {
+ let target = TypeId::of::<E>();
+ unsafe {
+ // Use vtable to find NonNull<()> which points to a value of type E
+ // somewhere inside the data structure.
+ let addr = match (self.inner.vtable.object_downcast)(&self.inner, target) {
+ Some(addr) => addr,
+ None => return Err(self),
+ };
+
+ // Prepare to read E out of the data structure. We'll drop the rest
+ // of the data structure separately so that E is not dropped.
+ let outer = ManuallyDrop::new(self);
+
+ // Read E from where the vtable found it.
+ let error = ptr::read(addr.cast::<E>().as_ptr());
+
+ // Read Box<ErrorImpl<()>> from self. Can't move it out because
+ // Error has a Drop impl which we want to not run.
+ let inner = ptr::read(&outer.inner);
+ let erased = ManuallyDrop::into_inner(inner);
+
+ // Drop rest of the data structure outside of E.
+ (erased.vtable.object_drop_rest)(erased, target);
+
+ Ok(error)
+ }
+ }
+
+ /// Downcast this error object by reference.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use anyhow::anyhow;
+ /// # use std::fmt::{self, Display};
+ /// # use std::task::Poll;
+ /// #
+ /// # #[derive(Debug)]
+ /// # enum DataStoreError {
+ /// # Censored(()),
+ /// # }
+ /// #
+ /// # impl Display for DataStoreError {
+ /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ /// # unimplemented!()
+ /// # }
+ /// # }
+ /// #
+ /// # impl std::error::Error for DataStoreError {}
+ /// #
+ /// # const REDACTED_CONTENT: () = ();
+ /// #
+ /// # let error = anyhow!("...");
+ /// # let root_cause = &error;
+ /// #
+ /// # let ret =
+ /// // If the error was caused by redaction, then return a tombstone instead
+ /// // of the content.
+ /// match root_cause.downcast_ref::<DataStoreError>() {
+ /// Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)),
+ /// None => Err(error),
+ /// }
+ /// # ;
+ /// ```
+ pub fn downcast_ref<E>(&self) -> Option<&E>
+ where
+ E: Display + Debug + Send + Sync + 'static,
+ {
+ let target = TypeId::of::<E>();
+ unsafe {
+ // Use vtable to find NonNull<()> which points to a value of type E
+ // somewhere inside the data structure.
+ let addr = (self.inner.vtable.object_downcast)(&self.inner, target)?;
+ Some(&*addr.cast::<E>().as_ptr())
+ }
+ }
+
+ /// Downcast this error object by mutable reference.
+ pub fn downcast_mut<E>(&mut self) -> Option<&mut E>
+ where
+ E: Display + Debug + Send + Sync + 'static,
+ {
+ let target = TypeId::of::<E>();
+ unsafe {
+ // Use vtable to find NonNull<()> which points to a value of type E
+ // somewhere inside the data structure.
+ let addr = (self.inner.vtable.object_downcast)(&self.inner, target)?;
+ Some(&mut *addr.cast::<E>().as_ptr())
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<E> From<E> for Error
+where
+ E: StdError + Send + Sync + 'static,
+{
+ fn from(error: E) -> Self {
+ let backtrace = backtrace_if_absent!(error);
+ Error::from_std(error, backtrace)
+ }
+}
+
+#[cfg(feature = "std")]
+impl Deref for Error {
+ type Target = dyn StdError + Send + Sync + 'static;
+
+ fn deref(&self) -> &Self::Target {
+ self.inner.error()
+ }
+}
+
+#[cfg(feature = "std")]
+impl DerefMut for Error {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.inner.error_mut()
+ }
+}
+
+impl Display for Error {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ self.inner.display(formatter)
+ }
+}
+
+impl Debug for Error {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ self.inner.debug(formatter)
+ }
+}
+
+impl Drop for Error {
+ fn drop(&mut self) {
+ unsafe {
+ // Read Box<ErrorImpl<()>> from self.
+ let inner = ptr::read(&self.inner);
+ let erased = ManuallyDrop::into_inner(inner);
+
+ // Invoke the vtable's drop behavior.
+ (erased.vtable.object_drop)(erased);
+ }
+ }
+}
+
+struct ErrorVTable {
+ object_drop: unsafe fn(Box<ErrorImpl<()>>),
+ object_ref: unsafe fn(&ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static),
+ #[cfg(feature = "std")]
+ object_mut: unsafe fn(&mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static),
+ object_boxed: unsafe fn(Box<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static>,
+ object_downcast: unsafe fn(&ErrorImpl<()>, TypeId) -> Option<NonNull<()>>,
+ object_drop_rest: unsafe fn(Box<ErrorImpl<()>>, TypeId),
+}
+
+// Safety: requires layout of *e to match ErrorImpl<E>.
+unsafe fn object_drop<E>(e: Box<ErrorImpl<()>>) {
+ // Cast back to ErrorImpl<E> so that the allocator receives the correct
+ // Layout to deallocate the Box's memory.
+ let unerased = mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<E>>>(e);
+ drop(unerased);
+}
+
+// Safety: requires layout of *e to match ErrorImpl<E>.
+unsafe fn object_drop_front<E>(e: Box<ErrorImpl<()>>, target: TypeId) {
+ // Drop the fields of ErrorImpl other than E as well as the Box allocation,
+ // without dropping E itself. This is used by downcast after doing a
+ // ptr::read to take ownership of the E.
+ let _ = target;
+ let unerased = mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<ManuallyDrop<E>>>>(e);
+ drop(unerased);
+}
+
+// Safety: requires layout of *e to match ErrorImpl<E>.
+unsafe fn object_ref<E>(e: &ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static)
+where
+ E: StdError + Send + Sync + 'static,
+{
+ // Attach E's native StdError vtable onto a pointer to self._object.
+ &(*(e as *const ErrorImpl<()> as *const ErrorImpl<E>))._object
+}
+
+// Safety: requires layout of *e to match ErrorImpl<E>.
+#[cfg(feature = "std")]
+unsafe fn object_mut<E>(e: &mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static)
+where
+ E: StdError + Send + Sync + 'static,
+{
+ // Attach E's native StdError vtable onto a pointer to self._object.
+ &mut (*(e as *mut ErrorImpl<()> as *mut ErrorImpl<E>))._object
+}
+
+// Safety: requires layout of *e to match ErrorImpl<E>.
+unsafe fn object_boxed<E>(e: Box<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static>
+where
+ E: StdError + Send + Sync + 'static,
+{
+ // Attach ErrorImpl<E>'s native StdError vtable. The StdError impl is below.
+ mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<E>>>(e)
+}
+
+// Safety: requires layout of *e to match ErrorImpl<E>.
+unsafe fn object_downcast<E>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>>
+where
+ E: 'static,
+{
+ if TypeId::of::<E>() == target {
+ // Caller is looking for an E pointer and e is ErrorImpl<E>, take a
+ // pointer to its E field.
+ let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<E>;
+ let addr = &(*unerased)._object as *const E as *mut ();
+ Some(NonNull::new_unchecked(addr))
+ } else {
+ None
+ }
+}
+
+// Safety: requires layout of *e to match ErrorImpl<ContextError<C, E>>.
+#[cfg(feature = "std")]
+unsafe fn context_downcast<C, E>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>>
+where
+ C: 'static,
+ E: 'static,
+{
+ if TypeId::of::<C>() == target {
+ let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, E>>;
+ let addr = &(*unerased)._object.context as *const C as *mut ();
+ Some(NonNull::new_unchecked(addr))
+ } else if TypeId::of::<E>() == target {
+ let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, E>>;
+ let addr = &(*unerased)._object.error as *const E as *mut ();
+ Some(NonNull::new_unchecked(addr))
+ } else {
+ None
+ }
+}
+
+// Safety: requires layout of *e to match ErrorImpl<ContextError<C, E>>.
+#[cfg(feature = "std")]
+unsafe fn context_drop_rest<C, E>(e: Box<ErrorImpl<()>>, target: TypeId)
+where
+ C: 'static,
+ E: 'static,
+{
+ // Called after downcasting by value to either the C or the E and doing a
+ // ptr::read to take ownership of that value.
+ if TypeId::of::<C>() == target {
+ let unerased = mem::transmute::<
+ Box<ErrorImpl<()>>,
+ Box<ErrorImpl<ContextError<ManuallyDrop<C>, E>>>,
+ >(e);
+ drop(unerased);
+ } else {
+ let unerased = mem::transmute::<
+ Box<ErrorImpl<()>>,
+ Box<ErrorImpl<ContextError<C, ManuallyDrop<E>>>>,
+ >(e);
+ drop(unerased);
+ }
+}
+
+// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>.
+unsafe fn context_chain_downcast<C>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>>
+where
+ C: 'static,
+{
+ if TypeId::of::<C>() == target {
+ let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, Error>>;
+ let addr = &(*unerased)._object.context as *const C as *mut ();
+ Some(NonNull::new_unchecked(addr))
+ } else {
+ // Recurse down the context chain per the inner error's vtable.
+ let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, Error>>;
+ let source = &(*unerased)._object.error;
+ (source.inner.vtable.object_downcast)(&source.inner, target)
+ }
+}
+
+// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>.
+unsafe fn context_chain_drop_rest<C>(e: Box<ErrorImpl<()>>, target: TypeId)
+where
+ C: 'static,
+{
+ // Called after downcasting by value to either the C or one of the causes
+ // and doing a ptr::read to take ownership of that value.
+ if TypeId::of::<C>() == target {
+ let unerased = mem::transmute::<
+ Box<ErrorImpl<()>>,
+ Box<ErrorImpl<ContextError<ManuallyDrop<C>, Error>>>,
+ >(e);
+ // Drop the entire rest of the data structure rooted in the next Error.
+ drop(unerased);
+ } else {
+ let unerased = mem::transmute::<
+ Box<ErrorImpl<()>>,
+ Box<ErrorImpl<ContextError<C, ManuallyDrop<Error>>>>,
+ >(e);
+ // Read out a ManuallyDrop<Box<ErrorImpl<()>>> from the next error.
+ let inner = ptr::read(&unerased._object.error.inner);
+ drop(unerased);
+ let erased = ManuallyDrop::into_inner(inner);
+ // Recursively drop the next error using the same target typeid.
+ (erased.vtable.object_drop_rest)(erased, target);
+ }
+}
+
+// repr C to ensure that E remains in the final position.
+#[repr(C)]
+pub(crate) struct ErrorImpl<E> {
+ vtable: &'static ErrorVTable,
+ backtrace: Option<Backtrace>,
+ // NOTE: Don't use directly. Use only through vtable. Erased type may have
+ // different alignment.
+ _object: E,
+}
+
+// repr C to ensure that ContextError<C, E> has the same layout as
+// ContextError<ManuallyDrop<C>, E> and ContextError<C, ManuallyDrop<E>>.
+#[repr(C)]
+pub(crate) struct ContextError<C, E> {
+ pub context: C,
+ pub error: E,
+}
+
+impl<E> ErrorImpl<E> {
+ fn erase(&self) -> &ErrorImpl<()> {
+ // Erase the concrete type of E but preserve the vtable in self.vtable
+ // for manipulating the resulting thin pointer. This is analogous to an
+ // unsize coersion.
+ unsafe { &*(self as *const ErrorImpl<E> as *const ErrorImpl<()>) }
+ }
+}
+
+impl ErrorImpl<()> {
+ pub(crate) fn error(&self) -> &(dyn StdError + Send + Sync + 'static) {
+ // Use vtable to attach E's native StdError vtable for the right
+ // original type E.
+ unsafe { &*(self.vtable.object_ref)(self) }
+ }
+
+ #[cfg(feature = "std")]
+ pub(crate) fn error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static) {
+ // Use vtable to attach E's native StdError vtable for the right
+ // original type E.
+ unsafe { &mut *(self.vtable.object_mut)(self) }
+ }
+
+ #[cfg(backtrace)]
+ pub(crate) fn backtrace(&self) -> &Backtrace {
+ // This unwrap can only panic if the underlying error's backtrace method
+ // is nondeterministic, which would only happen in maliciously
+ // constructed code.
+ self.backtrace
+ .as_ref()
+ .or_else(|| self.error().backtrace())
+ .expect("backtrace capture failed")
+ }
+
+ pub(crate) fn chain(&self) -> Chain {
+ Chain::new(self.error())
+ }
+}
+
+impl<E> StdError for ErrorImpl<E>
+where
+ E: StdError,
+{
+ #[cfg(backtrace)]
+ fn backtrace(&self) -> Option<&Backtrace> {
+ Some(self.erase().backtrace())
+ }
+
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ self.erase().error().source()
+ }
+}
+
+impl<E> Debug for ErrorImpl<E>
+where
+ E: Debug,
+{
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ self.erase().debug(formatter)
+ }
+}
+
+impl<E> Display for ErrorImpl<E>
+where
+ E: Display,
+{
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.erase().error(), formatter)
+ }
+}
+
+impl From<Error> for Box<dyn StdError + Send + Sync + 'static> {
+ fn from(error: Error) -> Self {
+ let outer = ManuallyDrop::new(error);
+ unsafe {
+ // Read Box<ErrorImpl<()>> from error. Can't move it out because
+ // Error has a Drop impl which we want to not run.
+ let inner = ptr::read(&outer.inner);
+ let erased = ManuallyDrop::into_inner(inner);
+
+ // Use vtable to attach ErrorImpl<E>'s native StdError vtable for
+ // the right original type E.
+ (erased.vtable.object_boxed)(erased)
+ }
+ }
+}
+
+impl From<Error> for Box<dyn StdError + 'static> {
+ fn from(error: Error) -> Self {
+ Box::<dyn StdError + Send + Sync>::from(error)
+ }
+}
+
+#[cfg(feature = "std")]
+impl AsRef<dyn StdError + Send + Sync> for Error {
+ fn as_ref(&self) -> &(dyn StdError + Send + Sync + 'static) {
+ &**self
+ }
+}
+
+#[cfg(feature = "std")]
+impl AsRef<dyn StdError> for Error {
+ fn as_ref(&self) -> &(dyn StdError + 'static) {
+ &**self
+ }
+}
diff --git a/tests/platform_deps/vendor/anyhow/src/fmt.rs b/tests/platform_deps/vendor/anyhow/src/fmt.rs
new file mode 100644
index 0000000..9579478
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/src/fmt.rs
@@ -0,0 +1,131 @@
+use crate::chain::Chain;
+use crate::error::ErrorImpl;
+use core::fmt::{self, Debug, Write};
+
+impl ErrorImpl<()> {
+ pub(crate) fn display(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.error())?;
+
+ if f.alternate() {
+ for cause in self.chain().skip(1) {
+ write!(f, ": {}", cause)?;
+ }
+ }
+
+ Ok(())
+ }
+
+ pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let error = self.error();
+
+ if f.alternate() {
+ return Debug::fmt(error, f);
+ }
+
+ write!(f, "{}", error)?;
+
+ if let Some(cause) = error.source() {
+ write!(f, "\n\nCaused by:")?;
+ let multiple = cause.source().is_some();
+ for (n, error) in Chain::new(cause).enumerate() {
+ writeln!(f)?;
+ let mut indented = Indented {
+ inner: f,
+ number: if multiple { Some(n) } else { None },
+ started: false,
+ };
+ write!(indented, "{}", error)?;
+ }
+ }
+
+ #[cfg(backtrace)]
+ {
+ use std::backtrace::BacktraceStatus;
+
+ let backtrace = self.backtrace();
+ if let BacktraceStatus::Captured = backtrace.status() {
+ let mut backtrace = backtrace.to_string();
+ if backtrace.starts_with("stack backtrace:") {
+ // Capitalize to match "Caused by:"
+ backtrace.replace_range(0..1, "S");
+ }
+ backtrace.truncate(backtrace.trim_end().len());
+ write!(f, "\n\n{}", backtrace)?;
+ }
+ }
+
+ Ok(())
+ }
+}
+
+struct Indented<'a, D> {
+ inner: &'a mut D,
+ number: Option<usize>,
+ started: bool,
+}
+
+impl<T> Write for Indented<'_, T>
+where
+ T: Write,
+{
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ for (i, line) in s.split('\n').enumerate() {
+ if !self.started {
+ self.started = true;
+ match self.number {
+ Some(number) => write!(self.inner, "{: >5}: ", number)?,
+ None => self.inner.write_str(" ")?,
+ }
+ } else if i > 0 {
+ self.inner.write_char('\n')?;
+ if self.number.is_some() {
+ self.inner.write_str(" ")?;
+ } else {
+ self.inner.write_str(" ")?;
+ }
+ }
+
+ self.inner.write_str(line)?;
+ }
+
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn one_digit() {
+ let input = "verify\nthis";
+ let expected = " 2: verify\n this";
+ let mut output = String::new();
+
+ Indented { inner: &mut output, number: Some(2), started: false }.write_str(input).unwrap();
+
+ assert_eq!(expected, output);
+ }
+
+ #[test]
+ fn two_digits() {
+ let input = "verify\nthis";
+ let expected = " 12: verify\n this";
+ let mut output = String::new();
+
+ Indented { inner: &mut output, number: Some(12), started: false }.write_str(input).unwrap();
+
+ assert_eq!(expected, output);
+ }
+
+ #[test]
+ fn no_digits() {
+ let input = "verify\nthis";
+ let expected = " verify\n this";
+ let mut output = String::new();
+
+ Indented { inner: &mut output, number: None, started: false }.write_str(input).unwrap();
+
+ assert_eq!(expected, output);
+ }
+}
diff --git a/tests/platform_deps/vendor/anyhow/src/kind.rs b/tests/platform_deps/vendor/anyhow/src/kind.rs
new file mode 100644
index 0000000..fdeb060
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/src/kind.rs
@@ -0,0 +1,116 @@
+// Tagged dispatch mechanism for resolving the behavior of `anyhow!($expr)`.
+//
+// When anyhow! is given a single expr argument to turn into anyhow::Error, we
+// want the resulting Error to pick up the input's implementation of source()
+// and backtrace() if it has a std::error::Error impl, otherwise require nothing
+// more than Display and Debug.
+//
+// Expressed in terms of specialization, we want something like:
+//
+// trait AnyhowNew {
+// fn new(self) -> Error;
+// }
+//
+// impl<T> AnyhowNew for T
+// where
+// T: Display + Debug + Send + Sync + 'static,
+// {
+// default fn new(self) -> Error {
+// /* no std error impl */
+// }
+// }
+//
+// impl<T> AnyhowNew for T
+// where
+// T: std::error::Error + Send + Sync + 'static,
+// {
+// fn new(self) -> Error {
+// /* use std error's source() and backtrace() */
+// }
+// }
+//
+// Since specialization is not stable yet, instead we rely on autoref behavior
+// of method resolution to perform tagged dispatch. Here we have two traits
+// AdhocKind and TraitKind that both have an anyhow_kind() method. AdhocKind is
+// implemented whether or not the caller's type has a std error impl, while
+// TraitKind is implemented only when a std error impl does exist. The ambiguity
+// is resolved by AdhocKind requiring an extra autoref so that it has lower
+// precedence.
+//
+// The anyhow! macro will set up the call in this form:
+//
+// #[allow(unused_imports)]
+// use $crate::private::{AdhocKind, TraitKind};
+// let error = $msg;
+// (&error).anyhow_kind().new(error)
+
+use crate::Error;
+use core::fmt::{Debug, Display};
+
+#[cfg(feature = "std")]
+use crate::StdError;
+
+#[cfg(backtrace)]
+use std::backtrace::Backtrace;
+
+pub struct Adhoc;
+
+pub trait AdhocKind: Sized {
+ #[inline]
+ fn anyhow_kind(&self) -> Adhoc {
+ Adhoc
+ }
+}
+
+impl<T> AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {}
+
+impl Adhoc {
+ pub fn new<M>(self, message: M) -> Error
+ where
+ M: Display + Debug + Send + Sync + 'static,
+ {
+ Error::from_adhoc(message, backtrace!())
+ }
+}
+
+pub struct Trait;
+
+pub trait TraitKind: Sized {
+ #[inline]
+ fn anyhow_kind(&self) -> Trait {
+ Trait
+ }
+}
+
+impl<E> TraitKind for E where E: Into<Error> {}
+
+impl Trait {
+ pub fn new<E>(self, error: E) -> Error
+ where
+ E: Into<Error>,
+ {
+ error.into()
+ }
+}
+
+#[cfg(feature = "std")]
+pub struct Boxed;
+
+#[cfg(feature = "std")]
+pub trait BoxedKind: Sized {
+ #[inline]
+ fn anyhow_kind(&self) -> Boxed {
+ Boxed
+ }
+}
+
+#[cfg(feature = "std")]
+impl BoxedKind for Box<dyn StdError + Send + Sync> {}
+
+#[cfg(feature = "std")]
+impl Boxed {
+ pub fn new(self, error: Box<dyn StdError + Send + Sync>) -> Error {
+ let backtrace = backtrace_if_absent!(error);
+ Error::from_boxed(error, backtrace)
+ }
+}
diff --git a/tests/platform_deps/vendor/anyhow/src/lib.rs b/tests/platform_deps/vendor/anyhow/src/lib.rs
new file mode 100644
index 0000000..3e49658
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/src/lib.rs
@@ -0,0 +1,587 @@
+//! This library provides [`anyhow::Error`][Error], a trait object based error
+//! type for easy idiomatic error handling in Rust applications.
+//!
+//! <br>
+//!
+//! # Details
+//!
+//! - Use `Result<T, anyhow::Error>`, or equivalently `anyhow::Result<T>`, as
+//! the return type of any fallible function.
+//!
+//! Within the function, use `?` to easily propagate any error that implements
+//! the `std::error::Error` trait.
+//!
+//! ```
+//! # pub trait Deserialize {}
+//! #
+//! # mod serde_json {
+//! # use super::Deserialize;
+//! # use std::io;
+//! #
+//! # pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> {
+//! # unimplemented!()
+//! # }
+//! # }
+//! #
+//! # struct ClusterMap;
+//! #
+//! # impl Deserialize for ClusterMap {}
+//! #
+//! use anyhow::Result;
+//!
+//! fn get_cluster_info() -> Result<ClusterMap> {
+//! let config = std::fs::read_to_string("cluster.json")?;
+//! let map: ClusterMap = serde_json::from_str(&config)?;
+//! Ok(map)
+//! }
+//! #
+//! # fn main() {}
+//! ```
+//!
+//! - Attach context to help the person troubleshooting the error understand
+//! where things went wrong. A low-level error like "No such file or
+//! directory" can be annoying to debug without more context about what higher
+//! level step the application was in the middle of.
+//!
+//! ```
+//! # struct It;
+//! #
+//! # impl It {
+//! # fn detach(&self) -> Result<()> {
+//! # unimplemented!()
+//! # }
+//! # }
+//! #
+//! use anyhow::{Context, Result};
+//!
+//! fn main() -> Result<()> {
+//! # return Ok(());
+//! #
+//! # const _: &str = stringify! {
+//! ...
+//! # };
+//! #
+//! # let it = It;
+//! # let path = "./path/to/instrs.json";
+//! #
+//! it.detach().context("Failed to detach the important thing")?;
+//!
+//! let content = std::fs::read(path)
+//! .with_context(|| format!("Failed to read instrs from {}", path))?;
+//! #
+//! # const _: &str = stringify! {
+//! ...
+//! # };
+//! #
+//! # Ok(())
+//! }
+//! ```
+//!
+//! ```console
+//! Error: Failed to read instrs from ./path/to/instrs.json
+//!
+//! Caused by:
+//! No such file or directory (os error 2)
+//! ```
+//!
+//! - Downcasting is supported and can be by value, by shared reference, or by
+//! mutable reference as needed.
+//!
+//! ```
+//! # use anyhow::anyhow;
+//! # use std::fmt::{self, Display};
+//! # use std::task::Poll;
+//! #
+//! # #[derive(Debug)]
+//! # enum DataStoreError {
+//! # Censored(()),
+//! # }
+//! #
+//! # impl Display for DataStoreError {
+//! # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+//! # unimplemented!()
+//! # }
+//! # }
+//! #
+//! # impl std::error::Error for DataStoreError {}
+//! #
+//! # const REDACTED_CONTENT: () = ();
+//! #
+//! # let error = anyhow!("...");
+//! # let root_cause = &error;
+//! #
+//! # let ret =
+//! // If the error was caused by redaction, then return a
+//! // tombstone instead of the content.
+//! match root_cause.downcast_ref::<DataStoreError>() {
+//! Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)),
+//! None => Err(error),
+//! }
+//! # ;
+//! ```
+//!
+//! - A backtrace is captured and printed with the error if the underlying error
+//! type does not already provide its own. In order to see backtraces, they
+//! must be enabled through the environment variables described in
+//! [`std::backtrace`]:
+//!
+//! - If you want panics and errors to both have backtraces, set
+//! `RUST_BACKTRACE=1`;
+//! - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`;
+//! - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and
+//! `RUST_LIB_BACKTRACE=0`.
+//!
+//! [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables
+//!
+//! - Anyhow works with any error type that has an impl of `std::error::Error`,
+//! including ones defined in your crate. We do not bundle a `derive(Error)`
+//! macro but you can write the impls yourself or use a standalone macro like
+//! [thiserror].
+//!
+//! [thiserror]: https://github.com/dtolnay/thiserror
+//!
+//! ```
+//! use thiserror::Error;
+//!
+//! #[derive(Error, Debug)]
+//! pub enum FormatError {
+//! #[error("Invalid header (expected {expected:?}, got {found:?})")]
+//! InvalidHeader {
+//! expected: String,
+//! found: String,
+//! },
+//! #[error("Missing attribute: {0}")]
+//! MissingAttribute(String),
+//! }
+//! ```
+//!
+//! - One-off error messages can be constructed using the `anyhow!` macro, which
+//! supports string interpolation and produces an `anyhow::Error`.
+//!
+//! ```
+//! # use anyhow::{anyhow, Result};
+//! #
+//! # fn demo() -> Result<()> {
+//! # let missing = "...";
+//! return Err(anyhow!("Missing attribute: {}", missing));
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! <br>
+//!
+//! # No-std support
+//!
+//! In no_std mode, the same API is almost all available and works the same way.
+//! To depend on Anyhow in no_std mode, disable our default enabled "std"
+//! feature in Cargo.toml. A global allocator is required.
+//!
+//! ```toml
+//! [dependencies]
+//! anyhow = { version = "1.0", default-features = false }
+//! ```
+//!
+//! Since the `?`-based error conversions would normally rely on the
+//! `std::error::Error` trait which is only available through std, no_std mode
+//! will require an explicit `.map_err(Error::msg)` when working with a
+//! non-Anyhow error type inside a function that returns Anyhow's error type.
+
+#![doc(html_root_url = "https://docs.rs/anyhow/1.0.27")]
+#![cfg_attr(backtrace, feature(backtrace))]
+#![cfg_attr(not(feature = "std"), no_std)]
+#![allow(clippy::needless_doctest_main, clippy::new_ret_no_self, clippy::wrong_self_convention)]
+
+mod alloc {
+ #[cfg(not(feature = "std"))]
+ extern crate alloc;
+
+ #[cfg(not(feature = "std"))]
+ pub use alloc::boxed::Box;
+
+ #[cfg(feature = "std")]
+ pub use std::boxed::Box;
+}
+
+#[macro_use]
+mod backtrace;
+mod chain;
+mod context;
+mod error;
+mod fmt;
+mod kind;
+mod macros;
+mod wrapper;
+
+use crate::alloc::Box;
+use crate::error::ErrorImpl;
+use core::fmt::Display;
+use core::mem::ManuallyDrop;
+
+#[cfg(not(feature = "std"))]
+use core::fmt::Debug;
+
+#[cfg(feature = "std")]
+use std::error::Error as StdError;
+
+#[cfg(not(feature = "std"))]
+trait StdError: Debug + Display {
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ None
+ }
+}
+
+pub use anyhow as format_err;
+
+/// The `Error` type, a wrapper around a dynamic error type.
+///
+/// `Error` works a lot like `Box<dyn std::error::Error>`, but with these
+/// differences:
+///
+/// - `Error` requires that the error is `Send`, `Sync`, and `'static`.
+/// - `Error` guarantees that a backtrace is available, even if the underlying
+/// error type does not provide one.
+/// - `Error` is represented as a narrow pointer — exactly one word in
+/// size instead of two.
+///
+/// <br>
+///
+/// # Display representations
+///
+/// When you print an error object using "{}" or to_string(), only the outermost
+/// underlying error or context is printed, not any of the lower level causes.
+/// This is exactly as if you had called the Display impl of the error from
+/// which you constructed your anyhow::Error.
+///
+/// ```console
+/// Failed to read instrs from ./path/to/instrs.json
+/// ```
+///
+/// To print causes as well using anyhow's default formatting of causes, use the
+/// alternate selector "{:#}".
+///
+/// ```console
+/// Failed to read instrs from ./path/to/instrs.json: No such file or directory (os error 2)
+/// ```
+///
+/// The Debug format "{:?}" includes your backtrace if one was captured. Note
+/// that this is the representation you get by default if you return an error
+/// from `fn main` instead of printing it explicitly yourself.
+///
+/// ```console
+/// Error: Failed to read instrs from ./path/to/instrs.json
+///
+/// Caused by:
+/// No such file or directory (os error 2)
+///
+/// Stack backtrace:
+/// 0: <E as anyhow::context::ext::StdError>::ext_context
+/// at /git/anyhow/src/backtrace.rs:26
+/// 1: core::result::Result<T,E>::map_err
+/// at /git/rustc/src/libcore/result.rs:596
+/// 2: anyhow::context::<impl anyhow::Context<T,E> for core::result::Result<T,E>>::with_context
+/// at /git/anyhow/src/context.rs:58
+/// 3: testing::main
+/// at src/main.rs:5
+/// 4: std::rt::lang_start
+/// at /git/rustc/src/libstd/rt.rs:61
+/// 5: main
+/// 6: __libc_start_main
+/// 7: _start
+/// ```
+///
+/// To see a conventional struct-style Debug representation, use "{:#?}".
+///
+/// ```console
+/// Error {
+/// context: "Failed to read instrs from ./path/to/instrs.json",
+/// source: Os {
+/// code: 2,
+/// kind: NotFound,
+/// message: "No such file or directory",
+/// },
+/// }
+/// ```
+///
+/// If none of the built-in representations are appropriate and you would prefer
+/// to render the error and its cause chain yourself, it can be done something
+/// like this:
+///
+/// ```
+/// use anyhow::{Context, Result};
+///
+/// fn main() {
+/// if let Err(err) = try_main() {
+/// eprintln!("ERROR: {}", err);
+/// err.chain().skip(1).for_each(|cause| eprintln!("because: {}", cause));
+/// std::process::exit(1);
+/// }
+/// }
+///
+/// fn try_main() -> Result<()> {
+/// # const IGNORE: &str = stringify! {
+/// ...
+/// # };
+/// # Ok(())
+/// }
+/// ```
+pub struct Error {
+ inner: ManuallyDrop<Box<ErrorImpl<()>>>,
+}
+
+/// Iterator of a chain of source errors.
+///
+/// This type is the iterator returned by [`Error::chain`].
+///
+/// # Example
+///
+/// ```
+/// use anyhow::Error;
+/// use std::io;
+///
+/// pub fn underlying_io_error_kind(error: &Error) -> Option<io::ErrorKind> {
+/// for cause in error.chain() {
+/// if let Some(io_error) = cause.downcast_ref::<io::Error>() {
+/// return Some(io_error.kind());
+/// }
+/// }
+/// None
+/// }
+/// ```
+#[cfg(feature = "std")]
+#[derive(Clone)]
+pub struct Chain<'a> {
+ state: crate::chain::ChainState<'a>,
+}
+
+/// `Result<T, Error>`
+///
+/// This is a reasonable return type to use throughout your application but also
+/// for `fn main`; if you do, failures will be printed along with any
+/// [context][Context] and a backtrace if one was captured.
+///
+/// `anyhow::Result` may be used with one *or* two type parameters.
+///
+/// ```rust
+/// use anyhow::Result;
+///
+/// # const IGNORE: &str = stringify! {
+/// fn demo1() -> Result<T> {...}
+/// // ^ equivalent to std::result::Result<T, anyhow::Error>
+///
+/// fn demo2() -> Result<T, OtherError> {...}
+/// // ^ equivalent to std::result::Result<T, OtherError>
+/// # };
+/// ```
+///
+/// # Example
+///
+/// ```
+/// # pub trait Deserialize {}
+/// #
+/// # mod serde_json {
+/// # use super::Deserialize;
+/// # use std::io;
+/// #
+/// # pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> {
+/// # unimplemented!()
+/// # }
+/// # }
+/// #
+/// # #[derive(Debug)]
+/// # struct ClusterMap;
+/// #
+/// # impl Deserialize for ClusterMap {}
+/// #
+/// use anyhow::Result;
+///
+/// fn main() -> Result<()> {
+/// # return Ok(());
+/// let config = std::fs::read_to_string("cluster.json")?;
+/// let map: ClusterMap = serde_json::from_str(&config)?;
+/// println!("cluster info: {:#?}", map);
+/// Ok(())
+/// }
+/// ```
+pub type Result<T, E = Error> = core::result::Result<T, E>;
+
+/// Provides the `context` method for `Result`.
+///
+/// This trait is sealed and cannot be implemented for types outside of
+/// `anyhow`.
+///
+/// <br>
+///
+/// # Example
+///
+/// ```
+/// use anyhow::{Context, Result};
+/// use std::fs;
+/// use std::path::PathBuf;
+///
+/// pub struct ImportantThing {
+/// path: PathBuf,
+/// }
+///
+/// impl ImportantThing {
+/// # const IGNORE: &'static str = stringify! {
+/// pub fn detach(&mut self) -> Result<()> {...}
+/// # };
+/// # fn detach(&mut self) -> Result<()> {
+/// # unimplemented!()
+/// # }
+/// }
+///
+/// pub fn do_it(mut it: ImportantThing) -> Result<Vec<u8>> {
+/// it.detach().context("Failed to detach the important thing")?;
+///
+/// let path = &it.path;
+/// let content = fs::read(path)
+/// .with_context(|| format!("Failed to read instrs from {}", path.display()))?;
+///
+/// Ok(content)
+/// }
+/// ```
+///
+/// When printed, the outermost context would be printed first and the lower
+/// level underlying causes would be enumerated below.
+///
+/// ```console
+/// Error: Failed to read instrs from ./path/to/instrs.json
+///
+/// Caused by:
+/// No such file or directory (os error 2)
+/// ```
+///
+/// <br>
+///
+/// # Effect on downcasting
+///
+/// After attaching context of type `C` onto an error of type `E`, the resulting
+/// `anyhow::Error` may be downcast to `C` **or** to `E`.
+///
+/// That is, in codebases that rely on downcasting, Anyhow's context supports
+/// both of the following use cases:
+///
+/// - **Attaching context whose type is insignificant onto errors whose type
+/// is used in downcasts.**
+///
+/// In other error libraries whose context is not designed this way, it can
+/// be risky to introduce context to existing code because new context might
+/// break existing working downcasts. In Anyhow, any downcast that worked
+/// before adding context will continue to work after you add a context, so
+/// you should freely add human-readable context to errors wherever it would
+/// be helpful.
+///
+/// ```
+/// # use anyhow::bail;
+/// # use thiserror::Error;
+/// #
+/// # #[derive(Error, Debug)]
+/// # #[error("???")]
+/// # struct SuspiciousError;
+/// #
+/// # fn helper() -> Result<()> {
+/// # bail!(SuspiciousError);
+/// # }
+/// #
+/// use anyhow::{Context, Result};
+///
+/// fn do_it() -> Result<()> {
+/// helper().context("Failed to complete the work")?;
+/// # const IGNORE: &str = stringify! {
+/// ...
+/// # };
+/// # unreachable!()
+/// }
+///
+/// fn main() {
+/// let err = do_it().unwrap_err();
+/// if let Some(e) = err.downcast_ref::<SuspiciousError>() {
+/// // If helper() returned SuspiciousError, this downcast will
+/// // correctly succeed even with the context in between.
+/// # return;
+/// }
+/// # panic!("expected downcast to succeed");
+/// }
+/// ```
+///
+/// - **Attaching context whose type is used in downcasts onto errors whose
+/// type is insignificant.**
+///
+/// Some codebases prefer to use machine-readable context to categorize
+/// lower level errors in a way that will be actionable to higher levels of
+/// the application.
+///
+/// ```
+/// # use anyhow::bail;
+/// # use thiserror::Error;
+/// #
+/// # #[derive(Error, Debug)]
+/// # #[error("???")]
+/// # struct HelperFailed;
+/// #
+/// # fn helper() -> Result<()> {
+/// # bail!("no such file or directory");
+/// # }
+/// #
+/// use anyhow::{Context, Result};
+///
+/// fn do_it() -> Result<()> {
+/// helper().context(HelperFailed)?;
+/// # const IGNORE: &str = stringify! {
+/// ...
+/// # };
+/// # unreachable!()
+/// }
+///
+/// fn main() {
+/// let err = do_it().unwrap_err();
+/// if let Some(e) = err.downcast_ref::<HelperFailed>() {
+/// // If helper failed, this downcast will succeed because
+/// // HelperFailed is the context that has been attached to
+/// // that error.
+/// # return;
+/// }
+/// # panic!("expected downcast to succeed");
+/// }
+/// ```
+pub trait Context<T, E>: context::private::Sealed {
+ /// Wrap the error value with additional context.
+ fn context<C>(self, context: C) -> Result<T, Error>
+ where
+ C: Display + Send + Sync + 'static;
+
+ /// Wrap the error value with additional context that is evaluated lazily
+ /// only once an error does occur.
+ fn with_context<C, F>(self, f: F) -> Result<T, Error>
+ where
+ C: Display + Send + Sync + 'static,
+ F: FnOnce() -> C;
+}
+
+// Not public API. Referenced by macro-generated code.
+#[doc(hidden)]
+pub mod private {
+ use crate::Error;
+ use core::fmt::{Debug, Display};
+
+ #[cfg(backtrace)]
+ use std::backtrace::Backtrace;
+
+ pub use core::result::Result::Err;
+
+ #[doc(hidden)]
+ pub mod kind {
+ pub use crate::kind::{AdhocKind, TraitKind};
+
+ #[cfg(feature = "std")]
+ pub use crate::kind::BoxedKind;
+ }
+
+ pub fn new_adhoc<M>(message: M) -> Error
+ where
+ M: Display + Debug + Send + Sync + 'static,
+ {
+ Error::from_adhoc(message, backtrace!())
+ }
+}
diff --git a/tests/platform_deps/vendor/anyhow/src/macros.rs b/tests/platform_deps/vendor/anyhow/src/macros.rs
new file mode 100644
index 0000000..15a9208
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/src/macros.rs
@@ -0,0 +1,163 @@
+/// Return early with an error.
+///
+/// This macro is equivalent to `return Err(From::from($err))`.
+///
+/// # Example
+///
+/// ```
+/// # use anyhow::{bail, Result};
+/// #
+/// # fn has_permission(user: usize, resource: usize) -> bool {
+/// # true
+/// # }
+/// #
+/// # fn main() -> Result<()> {
+/// # let user = 0;
+/// # let resource = 0;
+/// #
+/// if !has_permission(user, resource) {
+/// bail!("permission denied for accessing {}", resource);
+/// }
+/// # Ok(())
+/// # }
+/// ```
+///
+/// ```
+/// # use anyhow::{bail, Result};
+/// # use thiserror::Error;
+/// #
+/// # const MAX_DEPTH: usize = 1;
+/// #
+/// #[derive(Error, Debug)]
+/// enum ScienceError {
+/// #[error("recursion limit exceeded")]
+/// RecursionLimitExceeded,
+/// # #[error("...")]
+/// # More = (stringify! {
+/// ...
+/// # }, 1).1,
+/// }
+///
+/// # fn main() -> Result<()> {
+/// # let depth = 0;
+/// #
+/// if depth > MAX_DEPTH {
+/// bail!(ScienceError::RecursionLimitExceeded);
+/// }
+/// # Ok(())
+/// # }
+/// ```
+#[macro_export]
+macro_rules! bail {
+ ($msg:literal $(,)?) => {
+ return $crate::private::Err($crate::anyhow!($msg));
+ };
+ ($err:expr $(,)?) => {
+ return $crate::private::Err($crate::anyhow!($err));
+ };
+ ($fmt:expr, $($arg:tt)*) => {
+ return $crate::private::Err($crate::anyhow!($fmt, $($arg)*));
+ };
+}
+
+/// Return early with an error if a condition is not satisfied.
+///
+/// This macro is equivalent to `if !$cond { return Err(From::from($err)); }`.
+///
+/// Analogously to `assert!`, `ensure!` takes a condition and exits the function
+/// if the condition fails. Unlike `assert!`, `ensure!` returns an `Error`
+/// rather than panicking.
+///
+/// # Example
+///
+/// ```
+/// # use anyhow::{ensure, Result};
+/// #
+/// # fn main() -> Result<()> {
+/// # let user = 0;
+/// #
+/// ensure!(user == 0, "only user 0 is allowed");
+/// # Ok(())
+/// # }
+/// ```
+///
+/// ```
+/// # use anyhow::{ensure, Result};
+/// # use thiserror::Error;
+/// #
+/// # const MAX_DEPTH: usize = 1;
+/// #
+/// #[derive(Error, Debug)]
+/// enum ScienceError {
+/// #[error("recursion limit exceeded")]
+/// RecursionLimitExceeded,
+/// # #[error("...")]
+/// # More = (stringify! {
+/// ...
+/// # }, 1).1,
+/// }
+///
+/// # fn main() -> Result<()> {
+/// # let depth = 0;
+/// #
+/// ensure!(depth <= MAX_DEPTH, ScienceError::RecursionLimitExceeded);
+/// # Ok(())
+/// # }
+/// ```
+#[macro_export]
+macro_rules! ensure {
+ ($cond:expr, $msg:literal $(,)?) => {
+ if !$cond {
+ return $crate::private::Err($crate::anyhow!($msg));
+ }
+ };
+ ($cond:expr, $err:expr $(,)?) => {
+ if !$cond {
+ return $crate::private::Err($crate::anyhow!($err));
+ }
+ };
+ ($cond:expr, $fmt:expr, $($arg:tt)*) => {
+ if !$cond {
+ return $crate::private::Err($crate::anyhow!($fmt, $($arg)*));
+ }
+ };
+}
+
+/// Construct an ad-hoc error from a string.
+///
+/// This evaluates to an `Error`. It can take either just a string, or a format
+/// string with arguments. It also can take any custom type which implements
+/// `Debug` and `Display`.
+///
+/// # Example
+///
+/// ```
+/// # type V = ();
+/// #
+/// use anyhow::{anyhow, Result};
+///
+/// fn lookup(key: &str) -> Result<V> {
+/// if key.len() != 16 {
+/// return Err(anyhow!("key length must be 16 characters, got {:?}", key));
+/// }
+///
+/// // ...
+/// # Ok(())
+/// }
+/// ```
+#[macro_export]
+macro_rules! anyhow {
+ ($msg:literal $(,)?) => {
+ // Handle $:literal as a special case to make cargo-expanded code more
+ // concise in the common case.
+ $crate::private::new_adhoc($msg)
+ };
+ ($err:expr $(,)?) => ({
+ use $crate::private::kind::*;
+ let error = $err;
+ (&error).anyhow_kind().new(error)
+ });
+ ($fmt:expr, $($arg:tt)*) => {
+ $crate::private::new_adhoc(format!($fmt, $($arg)*))
+ };
+}
diff --git a/tests/platform_deps/vendor/anyhow/src/wrapper.rs b/tests/platform_deps/vendor/anyhow/src/wrapper.rs
new file mode 100644
index 0000000..3ebe51a
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/src/wrapper.rs
@@ -0,0 +1,78 @@
+use crate::StdError;
+use core::fmt::{self, Debug, Display};
+
+#[repr(transparent)]
+pub struct MessageError<M>(pub M);
+
+impl<M> Debug for MessageError<M>
+where
+ M: Display + Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Debug::fmt(&self.0, f)
+ }
+}
+
+impl<M> Display for MessageError<M>
+where
+ M: Display + Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.0, f)
+ }
+}
+
+impl<M> StdError for MessageError<M> where M: Display + Debug + 'static {}
+
+#[repr(transparent)]
+pub struct DisplayError<M>(pub M);
+
+impl<M> Debug for DisplayError<M>
+where
+ M: Display,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.0, f)
+ }
+}
+
+impl<M> Display for DisplayError<M>
+where
+ M: Display,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.0, f)
+ }
+}
+
+impl<M> StdError for DisplayError<M> where M: Display + 'static {}
+
+#[cfg(feature = "std")]
+#[repr(transparent)]
+pub struct BoxedError(pub Box<dyn StdError + Send + Sync>);
+
+#[cfg(feature = "std")]
+impl Debug for BoxedError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Debug::fmt(&self.0, f)
+ }
+}
+
+#[cfg(feature = "std")]
+impl Display for BoxedError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.0, f)
+ }
+}
+
+#[cfg(feature = "std")]
+impl StdError for BoxedError {
+ #[cfg(backtrace)]
+ fn backtrace(&self) -> Option<&crate::backtrace::Backtrace> {
+ self.0.backtrace()
+ }
+
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ self.0.source()
+ }
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/common/mod.rs b/tests/platform_deps/vendor/anyhow/tests/common/mod.rs
new file mode 100644
index 0000000..fc165a5
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/common/mod.rs
@@ -0,0 +1,14 @@
+use anyhow::{bail, Result};
+use std::io;
+
+pub fn bail_literal() -> Result<()> {
+ bail!("oh no!");
+}
+
+pub fn bail_fmt() -> Result<()> {
+ bail!("{} {}!", "oh", "no");
+}
+
+pub fn bail_error() -> Result<()> {
+ bail!(io::Error::new(io::ErrorKind::Other, "oh no!"));
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/compiletest.rs b/tests/platform_deps/vendor/anyhow/tests/compiletest.rs
new file mode 100644
index 0000000..f9aea23
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/compiletest.rs
@@ -0,0 +1,6 @@
+#[rustversion::attr(not(nightly), ignore)]
+#[test]
+fn ui() {
+ let t = trybuild::TestCases::new();
+ t.compile_fail("tests/ui/*.rs");
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/drop/mod.rs b/tests/platform_deps/vendor/anyhow/tests/drop/mod.rs
new file mode 100644
index 0000000..1204011
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/drop/mod.rs
@@ -0,0 +1,46 @@
+use std::error::Error as StdError;
+use std::fmt::{self, Display};
+use std::sync::atomic::AtomicBool;
+use std::sync::atomic::Ordering::SeqCst;
+use std::sync::Arc;
+
+#[derive(Debug)]
+pub struct Flag {
+ atomic: Arc<AtomicBool>,
+}
+
+impl Flag {
+ pub fn new() -> Self {
+ Flag { atomic: Arc::new(AtomicBool::new(false)) }
+ }
+
+ pub fn get(&self) -> bool {
+ self.atomic.load(SeqCst)
+ }
+}
+
+#[derive(Debug)]
+pub struct DetectDrop {
+ has_dropped: Flag,
+}
+
+impl DetectDrop {
+ pub fn new(has_dropped: &Flag) -> Self {
+ DetectDrop { has_dropped: Flag { atomic: Arc::clone(&has_dropped.atomic) } }
+ }
+}
+
+impl StdError for DetectDrop {}
+
+impl Display for DetectDrop {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "oh no!")
+ }
+}
+
+impl Drop for DetectDrop {
+ fn drop(&mut self) {
+ let already_dropped = self.has_dropped.atomic.swap(true, SeqCst);
+ assert!(!already_dropped);
+ }
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_autotrait.rs b/tests/platform_deps/vendor/anyhow/tests/test_autotrait.rs
new file mode 100644
index 0000000..0c9326d
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/test_autotrait.rs
@@ -0,0 +1,13 @@
+use anyhow::Error;
+
+#[test]
+fn test_send() {
+ fn assert_send<T: Send>() {}
+ assert_send::<Error>();
+}
+
+#[test]
+fn test_sync() {
+ fn assert_sync<T: Sync>() {}
+ assert_sync::<Error>();
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_backtrace.rs b/tests/platform_deps/vendor/anyhow/tests/test_backtrace.rs
new file mode 100644
index 0000000..ce385f5
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/test_backtrace.rs
@@ -0,0 +1,13 @@
+#[rustversion::not(nightly)]
+#[ignore]
+#[test]
+fn test_backtrace() {}
+
+#[rustversion::nightly]
+#[test]
+fn test_backtrace() {
+ use anyhow::anyhow;
+
+ let error = anyhow!("oh no!");
+ let _ = error.backtrace();
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_boxed.rs b/tests/platform_deps/vendor/anyhow/tests/test_boxed.rs
new file mode 100644
index 0000000..851dcbb
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/test_boxed.rs
@@ -0,0 +1,35 @@
+use anyhow::anyhow;
+use std::error::Error as StdError;
+use std::io;
+use thiserror::Error;
+
+#[derive(Error, Debug)]
+#[error("outer")]
+struct MyError {
+ source: io::Error,
+}
+
+#[test]
+fn test_boxed_str() {
+ let error = Box::<dyn StdError + Send + Sync>::from("oh no!");
+ let error = anyhow!(error);
+ assert_eq!("oh no!", error.to_string());
+ assert_eq!(
+ "oh no!",
+ error.downcast_ref::<Box<dyn StdError + Send + Sync>>().unwrap().to_string()
+ );
+}
+
+#[test]
+fn test_boxed_thiserror() {
+ let error = MyError { source: io::Error::new(io::ErrorKind::Other, "oh no!") };
+ let error = anyhow!(error);
+ assert_eq!("oh no!", error.source().unwrap().to_string());
+}
+
+#[test]
+fn test_boxed_anyhow() {
+ let error = anyhow!("oh no!").context("it failed");
+ let error = anyhow!(error);
+ assert_eq!("oh no!", error.source().unwrap().to_string());
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_chain.rs b/tests/platform_deps/vendor/anyhow/tests/test_chain.rs
new file mode 100644
index 0000000..b1c5a3d
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/test_chain.rs
@@ -0,0 +1,45 @@
+use anyhow::{anyhow, Error};
+
+fn error() -> Error {
+ anyhow!(0).context(1).context(2).context(3)
+}
+
+#[test]
+fn test_iter() {
+ let e = error();
+ let mut chain = e.chain();
+ assert_eq!("3", chain.next().unwrap().to_string());
+ assert_eq!("2", chain.next().unwrap().to_string());
+ assert_eq!("1", chain.next().unwrap().to_string());
+ assert_eq!("0", chain.next().unwrap().to_string());
+ assert!(chain.next().is_none());
+ assert!(chain.next_back().is_none());
+}
+
+#[test]
+fn test_rev() {
+ let e = error();
+ let mut chain = e.chain().rev();
+ assert_eq!("0", chain.next().unwrap().to_string());
+ assert_eq!("1", chain.next().unwrap().to_string());
+ assert_eq!("2", chain.next().unwrap().to_string());
+ assert_eq!("3", chain.next().unwrap().to_string());
+ assert!(chain.next().is_none());
+ assert!(chain.next_back().is_none());
+}
+
+#[test]
+fn test_len() {
+ let e = error();
+ let mut chain = e.chain();
+ assert_eq!(4, chain.len());
+ assert_eq!("3", chain.next().unwrap().to_string());
+ assert_eq!(3, chain.len());
+ assert_eq!("0", chain.next_back().unwrap().to_string());
+ assert_eq!(2, chain.len());
+ assert_eq!("2", chain.next().unwrap().to_string());
+ assert_eq!(1, chain.len());
+ assert_eq!("1", chain.next_back().unwrap().to_string());
+ assert_eq!(0, chain.len());
+ assert!(chain.next().is_none());
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_context.rs b/tests/platform_deps/vendor/anyhow/tests/test_context.rs
new file mode 100644
index 0000000..4bd1ffe
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/test_context.rs
@@ -0,0 +1,150 @@
+mod drop;
+
+use crate::drop::{DetectDrop, Flag};
+use anyhow::{Context, Error, Result};
+use std::fmt::{self, Display};
+use thiserror::Error;
+
+// https://github.com/dtolnay/anyhow/issues/18
+#[test]
+fn test_inference() -> Result<()> {
+ let x = "1";
+ let y: u32 = x.parse().context("...")?;
+ assert_eq!(y, 1);
+ Ok(())
+}
+
+macro_rules! context_type {
+ ($name:ident) => {
+ #[derive(Debug)]
+ struct $name {
+ message: &'static str,
+ drop: DetectDrop,
+ }
+
+ impl Display for $name {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(self.message)
+ }
+ }
+ };
+}
+
+context_type!(HighLevel);
+context_type!(MidLevel);
+
+#[derive(Error, Debug)]
+#[error("{message}")]
+struct LowLevel {
+ message: &'static str,
+ drop: DetectDrop,
+}
+
+struct Dropped {
+ low: Flag,
+ mid: Flag,
+ high: Flag,
+}
+
+impl Dropped {
+ fn none(&self) -> bool {
+ !self.low.get() && !self.mid.get() && !self.high.get()
+ }
+
+ fn all(&self) -> bool {
+ self.low.get() && self.mid.get() && self.high.get()
+ }
+}
+
+fn make_chain() -> (Error, Dropped) {
+ let dropped = Dropped { low: Flag::new(), mid: Flag::new(), high: Flag::new() };
+
+ let low =
+ LowLevel { message: "no such file or directory", drop: DetectDrop::new(&dropped.low) };
+
+ // impl Context for Result<T, E>
+ let mid = Err::<(), LowLevel>(low)
+ .context(MidLevel { message: "failed to load config", drop: DetectDrop::new(&dropped.mid) })
+ .unwrap_err();
+
+ // impl Context for Result<T, Error>
+ let high = Err::<(), Error>(mid)
+ .context(HighLevel {
+ message: "failed to start server",
+ drop: DetectDrop::new(&dropped.high),
+ })
+ .unwrap_err();
+
+ (high, dropped)
+}
+
+#[test]
+fn test_downcast_ref() {
+ let (err, dropped) = make_chain();
+
+ assert!(!err.is::<String>());
+ assert!(err.downcast_ref::<String>().is_none());
+
+ assert!(err.is::<HighLevel>());
+ let high = err.downcast_ref::<HighLevel>().unwrap();
+ assert_eq!(high.to_string(), "failed to start server");
+
+ assert!(err.is::<MidLevel>());
+ let mid = err.downcast_ref::<MidLevel>().unwrap();
+ assert_eq!(mid.to_string(), "failed to load config");
+
+ assert!(err.is::<LowLevel>());
+ let low = err.downcast_ref::<LowLevel>().unwrap();
+ assert_eq!(low.to_string(), "no such file or directory");
+
+ assert!(dropped.none());
+ drop(err);
+ assert!(dropped.all());
+}
+
+#[test]
+fn test_downcast_high() {
+ let (err, dropped) = make_chain();
+
+ let err = err.downcast::<HighLevel>().unwrap();
+ assert!(!dropped.high.get());
+ assert!(dropped.low.get() && dropped.mid.get());
+
+ drop(err);
+ assert!(dropped.all());
+}
+
+#[test]
+fn test_downcast_mid() {
+ let (err, dropped) = make_chain();
+
+ let err = err.downcast::<MidLevel>().unwrap();
+ assert!(!dropped.mid.get());
+ assert!(dropped.low.get() && dropped.high.get());
+
+ drop(err);
+ assert!(dropped.all());
+}
+
+#[test]
+fn test_downcast_low() {
+ let (err, dropped) = make_chain();
+
+ let err = err.downcast::<LowLevel>().unwrap();
+ assert!(!dropped.low.get());
+ assert!(dropped.mid.get() && dropped.high.get());
+
+ drop(err);
+ assert!(dropped.all());
+}
+
+#[test]
+fn test_unsuccessful_downcast() {
+ let (err, dropped) = make_chain();
+
+ let err = err.downcast::<String>().unwrap_err();
+ assert!(dropped.none());
+
+ drop(err);
+ assert!(dropped.all());
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_convert.rs b/tests/platform_deps/vendor/anyhow/tests/test_convert.rs
new file mode 100644
index 0000000..72da020
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/test_convert.rs
@@ -0,0 +1,24 @@
+mod drop;
+
+use self::drop::{DetectDrop, Flag};
+use anyhow::{Error, Result};
+use std::error::Error as StdError;
+
+#[test]
+fn test_convert() {
+ let has_dropped = Flag::new();
+ let error = Error::new(DetectDrop::new(&has_dropped));
+ let box_dyn = Box::<dyn StdError + Send + Sync>::from(error);
+ assert_eq!("oh no!", box_dyn.to_string());
+ drop(box_dyn);
+ assert!(has_dropped.get());
+}
+
+#[test]
+fn test_question_mark() -> Result<(), Box<dyn StdError>> {
+ fn f() -> Result<()> {
+ Ok(())
+ }
+ f()?;
+ Ok(())
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_downcast.rs b/tests/platform_deps/vendor/anyhow/tests/test_downcast.rs
new file mode 100644
index 0000000..c4393e8
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/test_downcast.rs
@@ -0,0 +1,70 @@
+mod common;
+mod drop;
+
+use self::common::*;
+use self::drop::{DetectDrop, Flag};
+use anyhow::Error;
+use std::error::Error as StdError;
+use std::fmt::{self, Display};
+use std::io;
+
+#[test]
+fn test_downcast() {
+ assert_eq!("oh no!", bail_literal().unwrap_err().downcast::<&str>().unwrap(),);
+ assert_eq!("oh no!", bail_fmt().unwrap_err().downcast::<String>().unwrap(),);
+ assert_eq!("oh no!", bail_error().unwrap_err().downcast::<io::Error>().unwrap().to_string(),);
+}
+
+#[test]
+fn test_downcast_ref() {
+ assert_eq!("oh no!", *bail_literal().unwrap_err().downcast_ref::<&str>().unwrap(),);
+ assert_eq!("oh no!", bail_fmt().unwrap_err().downcast_ref::<String>().unwrap(),);
+ assert_eq!(
+ "oh no!",
+ bail_error().unwrap_err().downcast_ref::<io::Error>().unwrap().to_string(),
+ );
+}
+
+#[test]
+fn test_downcast_mut() {
+ assert_eq!("oh no!", *bail_literal().unwrap_err().downcast_mut::<&str>().unwrap(),);
+ assert_eq!("oh no!", bail_fmt().unwrap_err().downcast_mut::<String>().unwrap(),);
+ assert_eq!(
+ "oh no!",
+ bail_error().unwrap_err().downcast_mut::<io::Error>().unwrap().to_string(),
+ );
+}
+
+#[test]
+fn test_drop() {
+ let has_dropped = Flag::new();
+ let error = Error::new(DetectDrop::new(&has_dropped));
+ drop(error.downcast::<DetectDrop>().unwrap());
+ assert!(has_dropped.get());
+}
+
+#[test]
+fn test_large_alignment() {
+ #[repr(align(64))]
+ #[derive(Debug)]
+ struct LargeAlignedError(&'static str);
+
+ impl Display for LargeAlignedError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(self.0)
+ }
+ }
+
+ impl StdError for LargeAlignedError {}
+
+ let error = Error::new(LargeAlignedError("oh no!"));
+ assert_eq!("oh no!", error.downcast_ref::<LargeAlignedError>().unwrap().0);
+}
+
+#[test]
+fn test_unsuccessful_downcast() {
+ let mut error = bail_error().unwrap_err();
+ assert!(error.downcast_ref::<&str>().is_none());
+ assert!(error.downcast_mut::<&str>().is_none());
+ assert!(error.downcast::<&str>().is_err());
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_fmt.rs b/tests/platform_deps/vendor/anyhow/tests/test_fmt.rs
new file mode 100644
index 0000000..cc49291
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/test_fmt.rs
@@ -0,0 +1,94 @@
+use anyhow::{bail, Context, Result};
+use std::io;
+
+fn f() -> Result<()> {
+ bail!(io::Error::new(io::ErrorKind::PermissionDenied, "oh no!"));
+}
+
+fn g() -> Result<()> {
+ f().context("f failed")
+}
+
+fn h() -> Result<()> {
+ g().context("g failed")
+}
+
+const EXPECTED_ALTDISPLAY_F: &str = "oh no!";
+
+const EXPECTED_ALTDISPLAY_G: &str = "f failed: oh no!";
+
+const EXPECTED_ALTDISPLAY_H: &str = "g failed: f failed: oh no!";
+
+const EXPECTED_DEBUG_F: &str = "oh no!";
+
+const EXPECTED_DEBUG_G: &str = "\
+f failed
+
+Caused by:
+ oh no!\
+";
+
+const EXPECTED_DEBUG_H: &str = "\
+g failed
+
+Caused by:
+ 0: f failed
+ 1: oh no!\
+";
+
+const EXPECTED_ALTDEBUG_F: &str = "\
+Custom {
+ kind: PermissionDenied,
+ error: \"oh no!\",
+}\
+";
+
+const EXPECTED_ALTDEBUG_G: &str = "\
+Error {
+ context: \"f failed\",
+ source: Custom {
+ kind: PermissionDenied,
+ error: \"oh no!\",
+ },
+}\
+";
+
+const EXPECTED_ALTDEBUG_H: &str = "\
+Error {
+ context: \"g failed\",
+ source: Error {
+ context: \"f failed\",
+ source: Custom {
+ kind: PermissionDenied,
+ error: \"oh no!\",
+ },
+ },
+}\
+";
+
+#[test]
+fn test_display() {
+ assert_eq!("g failed", h().unwrap_err().to_string());
+}
+
+#[test]
+fn test_altdisplay() {
+ assert_eq!(EXPECTED_ALTDISPLAY_F, format!("{:#}", f().unwrap_err()));
+ assert_eq!(EXPECTED_ALTDISPLAY_G, format!("{:#}", g().unwrap_err()));
+ assert_eq!(EXPECTED_ALTDISPLAY_H, format!("{:#}", h().unwrap_err()));
+}
+
+#[test]
+#[cfg_attr(not(backtrace), ignore)]
+fn test_debug() {
+ assert_eq!(EXPECTED_DEBUG_F, format!("{:?}", f().unwrap_err()));
+ assert_eq!(EXPECTED_DEBUG_G, format!("{:?}", g().unwrap_err()));
+ assert_eq!(EXPECTED_DEBUG_H, format!("{:?}", h().unwrap_err()));
+}
+
+#[test]
+fn test_altdebug() {
+ assert_eq!(EXPECTED_ALTDEBUG_F, format!("{:#?}", f().unwrap_err()));
+ assert_eq!(EXPECTED_ALTDEBUG_G, format!("{:#?}", g().unwrap_err()));
+ assert_eq!(EXPECTED_ALTDEBUG_H, format!("{:#?}", h().unwrap_err()));
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_macros.rs b/tests/platform_deps/vendor/anyhow/tests/test_macros.rs
new file mode 100644
index 0000000..c6888b6
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/test_macros.rs
@@ -0,0 +1,33 @@
+mod common;
+
+use self::common::*;
+use anyhow::ensure;
+
+#[test]
+fn test_messages() {
+ assert_eq!("oh no!", bail_literal().unwrap_err().to_string());
+ assert_eq!("oh no!", bail_fmt().unwrap_err().to_string());
+ assert_eq!("oh no!", bail_error().unwrap_err().to_string());
+}
+
+#[test]
+fn test_ensure() {
+ let f = || {
+ ensure!(1 + 1 == 2, "This is correct");
+ Ok(())
+ };
+ assert!(f().is_ok());
+
+ let v = 1;
+ let f = || {
+ ensure!(v + v == 2, "This is correct, v: {}", v);
+ Ok(())
+ };
+ assert!(f().is_ok());
+
+ let f = || {
+ ensure!(v + v == 1, "This is not correct, v: {}", v);
+ Ok(())
+ };
+ assert!(f().is_err());
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_repr.rs b/tests/platform_deps/vendor/anyhow/tests/test_repr.rs
new file mode 100644
index 0000000..72f5002
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/test_repr.rs
@@ -0,0 +1,29 @@
+mod drop;
+
+use self::drop::{DetectDrop, Flag};
+use anyhow::Error;
+use std::marker::Unpin;
+use std::mem;
+
+#[test]
+fn test_error_size() {
+ assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>());
+}
+
+#[test]
+fn test_null_pointer_optimization() {
+ assert_eq!(mem::size_of::<Result<(), Error>>(), mem::size_of::<usize>());
+}
+
+#[test]
+fn test_autotraits() {
+ fn assert<E: Unpin + Send + Sync + 'static>() {}
+ assert::<Error>();
+}
+
+#[test]
+fn test_drop() {
+ let has_dropped = Flag::new();
+ drop(Error::new(DetectDrop::new(&has_dropped)));
+ assert!(has_dropped.get());
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_source.rs b/tests/platform_deps/vendor/anyhow/tests/test_source.rs
new file mode 100644
index 0000000..018267d
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/test_source.rs
@@ -0,0 +1,62 @@
+use anyhow::anyhow;
+use std::error::Error as StdError;
+use std::fmt::{self, Display};
+use std::io;
+
+#[derive(Debug)]
+enum TestError {
+ Io(io::Error),
+}
+
+impl Display for TestError {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ TestError::Io(e) => Display::fmt(e, formatter),
+ }
+ }
+}
+
+impl StdError for TestError {
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ match self {
+ TestError::Io(io) => Some(io),
+ }
+ }
+}
+
+#[test]
+fn test_literal_source() {
+ let error = anyhow!("oh no!");
+ assert!(error.source().is_none());
+}
+
+#[test]
+fn test_variable_source() {
+ let msg = "oh no!";
+ let error = anyhow!(msg);
+ assert!(error.source().is_none());
+
+ let msg = msg.to_owned();
+ let error = anyhow!(msg);
+ assert!(error.source().is_none());
+}
+
+#[test]
+fn test_fmt_source() {
+ let error = anyhow!("{} {}!", "oh", "no");
+ assert!(error.source().is_none());
+}
+
+#[test]
+fn test_io_source() {
+ let io = io::Error::new(io::ErrorKind::Other, "oh no!");
+ let error = anyhow!(TestError::Io(io));
+ assert_eq!("oh no!", error.source().unwrap().to_string());
+}
+
+#[test]
+fn test_anyhow_from_anyhow() {
+ let error = anyhow!("oh no!").context("context");
+ let error = anyhow!(error);
+ assert_eq!("oh no!", error.source().unwrap().to_string());
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/ui/no-impl.rs b/tests/platform_deps/vendor/anyhow/tests/ui/no-impl.rs
new file mode 100644
index 0000000..d2e89af
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/ui/no-impl.rs
@@ -0,0 +1,8 @@
+use anyhow::anyhow;
+
+#[derive(Debug)]
+struct Error;
+
+fn main() {
+ let _ = anyhow!(Error);
+}
diff --git a/tests/platform_deps/vendor/anyhow/tests/ui/no-impl.stderr b/tests/platform_deps/vendor/anyhow/tests/ui/no-impl.stderr
new file mode 100644
index 0000000..be95737
--- /dev/null
+++ b/tests/platform_deps/vendor/anyhow/tests/ui/no-impl.stderr
@@ -0,0 +1,21 @@
+error[E0599]: no method named `anyhow_kind` found for reference `&Error` in the current scope
+ --> $DIR/no-impl.rs:7:13
+ |
+4 | struct Error;
+ | -------------
+ | |
+ | doesn't satisfy `Error: anyhow::kind::TraitKind`
+ | doesn't satisfy `Error: std::convert::Into<anyhow::Error>`
+ | doesn't satisfy `Error: std::fmt::Display`
+...
+7 | let _ = anyhow!(Error);
+ | ^^^^^^^^^^^^^^ method not found in `&Error`
+ |
+ = note: the method `anyhow_kind` exists but the following trait bounds were not satisfied:
+ `Error: std::convert::Into<anyhow::Error>`
+ which is required by `Error: anyhow::kind::TraitKind`
+ `Error: std::fmt::Display`
+ which is required by `&Error: anyhow::kind::AdhocKind`
+ `&Error: std::convert::Into<anyhow::Error>`
+ which is required by `&Error: anyhow::kind::TraitKind`
+ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/platform_features/BUILD.gn b/tests/platform_features/BUILD.gn
new file mode 100644
index 0000000..2d697e9
--- /dev/null
+++ b/tests/platform_features/BUILD.gn
@@ -0,0 +1,68 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+group("a") {
+ public_deps = [ ":a-v1_0_0" ]
+}
+
+if (!(current_os == "fuchsia")) {
+ group("b") {
+ public_deps = [ ":b-v1_0_0" ]
+ }
+}
+
+rust_library("a-v1_0_0") {
+ crate_name = "a"
+ crate_root = "//platform_features/a/src/lib.rs"
+ output_name = "a-537022e729cc9ca0"
+
+ deps = []
+ if (!(current_os == "fuchsia")) {
+ deps += [ ":b-v1_0_0" ]
+ }
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=537022e729cc9ca0",
+ "-Cextra-filename=-537022e729cc9ca0",
+ ]
+ if (!(current_os == "fuchsia")) {
+ rustflags += [ "--cfg=feature=\"b\"" ]
+ }
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("b-v1_0_0") {
+ crate_name = "b"
+ crate_root = "//platform_features/b/src/lib.rs"
+ output_name = "b-72d4e469bdf48536"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=72d4e469bdf48536",
+ "-Cextra-filename=-72d4e469bdf48536",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
diff --git a/tests/platform_features/Cargo.lock b/tests/platform_features/Cargo.lock
new file mode 100644
index 0000000..20913cf
--- /dev/null
+++ b/tests/platform_features/Cargo.lock
@@ -0,0 +1,19 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "a"
+version = "1.0.0"
+
+[[package]]
+name = "b"
+version = "1.0.0"
+
+[[package]]
+name = "platform_features"
+version = "0.1.0"
+dependencies = [
+ "a",
+ "b",
+]
diff --git a/tests/platform_features/Cargo.toml b/tests/platform_features/Cargo.toml
new file mode 100644
index 0000000..94b1e6e
--- /dev/null
+++ b/tests/platform_features/Cargo.toml
@@ -0,0 +1,20 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "platform_features"
+version = "0.1.0"
+authors = ["David Koloski <dkoloski@google.com>"]
+edition = "2021"
+
+[dependencies]
+a = { version = "1.0", path = "a" }
+
+[target."cfg(not(target_os = \"fuchsia\"))".dependencies]
+b = { version = "1.0", path = "b" }
+
+# Manually enable the 'b' feature on a and add a dependency on it
+[gn.package.a."1.0.0".platform."cfg(not(target_os = \"fuchsia\"))"]
+deps = [":b-v1_0_0"]
+rustflags = ["--cfg=feature=\\\"b\\\""]
diff --git a/tests/platform_features/a/Cargo.toml b/tests/platform_features/a/Cargo.toml
new file mode 100644
index 0000000..48a1677
--- /dev/null
+++ b/tests/platform_features/a/Cargo.toml
@@ -0,0 +1,13 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "a"
+version = "1.0.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+b = { path = "../b", optional = true }
diff --git a/tests/platform_features/a/src/lib.rs b/tests/platform_features/a/src/lib.rs
new file mode 100644
index 0000000..ff5b3e0
--- /dev/null
+++ b/tests/platform_features/a/src/lib.rs
@@ -0,0 +1,6 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#[cfg(all(target_os = "fuchsia", feature = "b"))]
+compile_error!("the 'b' feature is not supported on Fuchsia");
diff --git a/tests/platform_features/b/Cargo.toml b/tests/platform_features/b/Cargo.toml
new file mode 100644
index 0000000..d10394e
--- /dev/null
+++ b/tests/platform_features/b/Cargo.toml
@@ -0,0 +1,12 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "b"
+version = "1.0.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/tests/platform_features/b/src/lib.rs b/tests/platform_features/b/src/lib.rs
new file mode 100644
index 0000000..5c9d3b6
--- /dev/null
+++ b/tests/platform_features/b/src/lib.rs
@@ -0,0 +1,3 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
diff --git a/tests/platform_features/src/lib.rs b/tests/platform_features/src/lib.rs
new file mode 100644
index 0000000..5c9d3b6
--- /dev/null
+++ b/tests/platform_features/src/lib.rs
@@ -0,0 +1,3 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
diff --git a/tests/sdk_metadata/BUILD.gn b/tests/sdk_metadata/BUILD.gn
new file mode 100644
index 0000000..eb9b55f
--- /dev/null
+++ b/tests/sdk_metadata/BUILD.gn
@@ -0,0 +1,106 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+import("//build/sdk/sdk_atom.gni")
+
+# True if the current GN toolchain instance can be used to create
+# Rust sdk_atom() targets.
+_generating_sdk = false
+
+# The SDK prefix for the current toolchain.
+if (current_toolchain == default_toolchain) {
+ _generating_sdk = true
+ _sdk_prefix = ""
+} else if (is_host) {
+ _generating_sdk = true
+ _sdk_prefix = "rust_proc_macros/"
+}
+
+group("sdk_metadata") {
+ public_deps = [ ":sdk_metadata-v1_0_0" ]
+}
+
+if (_generating_sdk) {
+ sdk_atom("sdk_metadata_sdk") {
+ id = "sdk://${_sdk_prefix}third_party/rust_crates/sdk_metadata"
+ category = "internal"
+ meta = {
+ source = "sdk_metas/sdk_metadata.sdk.meta.json"
+ dest = "${_sdk_prefix}third_party/rust_crates/sdk_metadata/meta.json"
+ schema = "3p_rust_library"
+ }
+ }
+}
+
+rust_library("sdk_metadata-v1_0_0") {
+ crate_name = "sdk_metadata"
+ crate_root = "//sdk_metadata/src/lib.rs"
+ output_name = "sdk_metadata-e14a5f3dc8d9ca6"
+
+ deps = []
+ deps += [ ":sub-crate-v1_0_0" ]
+ deps += [ ":sub-crate-proc-macro-v1_0_0($host_toolchain)" ]
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=e14a5f3dc8d9ca6",
+ "-Cextra-filename=-e14a5f3dc8d9ca6",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_library("sub-crate-v1_0_0") {
+ crate_name = "sub_crate"
+ crate_root = "//sdk_metadata/sub-crate/src/lib.rs"
+ output_name = "sub_crate-1eba52a868057013"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=1eba52a868057013",
+ "-Cextra-filename=-1eba52a868057013",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
+
+rust_proc_macro("sub-crate-proc-macro-v1_0_0") {
+ crate_name = "sub_crate_proc_macro"
+ crate_root = "//sdk_metadata/sub-crate-proc-macro/src/lib.rs"
+ output_name = "sub_crate_proc_macro-bf52f82d4654f78c"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=bf52f82d4654f78c",
+ "-Cextra-filename=-bf52f82d4654f78c",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
diff --git a/tests/sdk_metadata/Cargo.lock b/tests/sdk_metadata/Cargo.lock
new file mode 100644
index 0000000..923c006
--- /dev/null
+++ b/tests/sdk_metadata/Cargo.lock
@@ -0,0 +1,19 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "sdk_metadata"
+version = "1.0.0"
+dependencies = [
+ "sub-crate",
+ "sub-crate-proc-macro",
+]
+
+[[package]]
+name = "sub-crate"
+version = "1.0.0"
+
+[[package]]
+name = "sub-crate-proc-macro"
+version = "1.0.0"
diff --git a/tests/sdk_metadata/Cargo.toml b/tests/sdk_metadata/Cargo.toml
new file mode 100644
index 0000000..f306dc1
--- /dev/null
+++ b/tests/sdk_metadata/Cargo.toml
@@ -0,0 +1,13 @@
+# Copyright 2023 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "sdk_metadata"
+version = "1.0.0"
+authors = ["David Koloski <dkoloski@google.com>"]
+edition = "2018"
+
+[dependencies]
+sub-crate = { version = "1.0.0", path = "sub-crate" }
+sub-crate-proc-macro = { version = "1.0.0", path = "sub-crate-proc-macro" }
diff --git a/tests/sdk_metadata/sdk_metas/sdk_metadata.sdk.meta.json b/tests/sdk_metadata/sdk_metas/sdk_metadata.sdk.meta.json
new file mode 100644
index 0000000..64d3f95
--- /dev/null
+++ b/tests/sdk_metadata/sdk_metas/sdk_metadata.sdk.meta.json
@@ -0,0 +1,5 @@
+{
+ "type": "rust_3p_library",
+ "name": "sdk_metadata",
+ "version": "1.0.0"
+}
diff --git a/tests/sdk_metadata/src/lib.rs b/tests/sdk_metadata/src/lib.rs
new file mode 100644
index 0000000..4cc0cd7
--- /dev/null
+++ b/tests/sdk_metadata/src/lib.rs
@@ -0,0 +1,5 @@
+// Copyright 2023 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+pub const EXPORTED: u32 = 777;
diff --git a/tests/sdk_metadata/sub-crate-proc-macro/Cargo.toml b/tests/sdk_metadata/sub-crate-proc-macro/Cargo.toml
new file mode 100644
index 0000000..c99e6bc
--- /dev/null
+++ b/tests/sdk_metadata/sub-crate-proc-macro/Cargo.toml
@@ -0,0 +1,12 @@
+# Copyright 2023 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "sub-crate-proc-macro"
+version = "1.0.0"
+authors = ["David Koloski <dkoloski@google.com>"]
+edition = "2018"
+
+[lib]
+proc-macro = true
diff --git a/tests/sdk_metadata/sub-crate-proc-macro/src/lib.rs b/tests/sdk_metadata/sub-crate-proc-macro/src/lib.rs
new file mode 100644
index 0000000..7874a8a
--- /dev/null
+++ b/tests/sdk_metadata/sub-crate-proc-macro/src/lib.rs
@@ -0,0 +1,5 @@
+// Copyright 2023 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+const PROC_MACRO: u32 = 777;
diff --git a/tests/sdk_metadata/sub-crate/Cargo.toml b/tests/sdk_metadata/sub-crate/Cargo.toml
new file mode 100644
index 0000000..9f35821
--- /dev/null
+++ b/tests/sdk_metadata/sub-crate/Cargo.toml
@@ -0,0 +1,9 @@
+# Copyright 2023 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "sub-crate"
+version = "1.0.0"
+authors = ["David Koloski <dkoloski@google.com>"]
+edition = "2018"
diff --git a/tests/sdk_metadata/sub-crate/src/lib.rs b/tests/sdk_metadata/sub-crate/src/lib.rs
new file mode 100644
index 0000000..7025df7
--- /dev/null
+++ b/tests/sdk_metadata/sub-crate/src/lib.rs
@@ -0,0 +1,5 @@
+// Copyright 2023 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+pub const SUB_CRATE: u32 = 777;
diff --git a/tests/simple/BUILD.gn b/tests/simple/BUILD.gn
new file mode 100644
index 0000000..6462d18
--- /dev/null
+++ b/tests/simple/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+group("simple") {
+ public_deps = [ ":simple-v1_0_25" ]
+}
+
+rust_library("simple-v1_0_25") {
+ crate_name = "simple"
+ crate_root = "//simple/src/lib.rs"
+ output_name = "simple-3b5ec47e038ac967"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=3b5ec47e038ac967",
+ "-Cextra-filename=-3b5ec47e038ac967",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
diff --git a/tests/simple/Cargo.lock b/tests/simple/Cargo.lock
new file mode 100644
index 0000000..6111785
--- /dev/null
+++ b/tests/simple/Cargo.lock
@@ -0,0 +1,6 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "simple"
+version = "1.0.25"
+
diff --git a/tests/simple/Cargo.toml b/tests/simple/Cargo.toml
new file mode 100644
index 0000000..2160613
--- /dev/null
+++ b/tests/simple/Cargo.toml
@@ -0,0 +1,11 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "simple"
+version = "1.0.25"
+authors = ["Benjamin Brittain <bwb@google.com>"]
+edition = "2018"
+
+[dependencies]
diff --git a/tests/simple/src/lib.rs b/tests/simple/src/lib.rs
new file mode 100644
index 0000000..40b4304
--- /dev/null
+++ b/tests/simple/src/lib.rs
@@ -0,0 +1,7 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// no-op
+// hello there
+pub const EXPORTED: u32 = 777;
diff --git a/tests/simple_deps/.cargo/config b/tests/simple_deps/.cargo/config
new file mode 100644
index 0000000..0236928
--- /dev/null
+++ b/tests/simple_deps/.cargo/config
@@ -0,0 +1,5 @@
+[source.crates-io]
+replace-with = "vendored-sources"
+
+[source.vendored-sources]
+directory = "vendor"
diff --git a/tests/simple_deps/BUILD.gn b/tests/simple_deps/BUILD.gn
new file mode 100644
index 0000000..5aac33d
--- /dev/null
+++ b/tests/simple_deps/BUILD.gn
@@ -0,0 +1,79 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+group("simple_deps") {
+ public_deps = [ ":simple_deps-v0_1_0" ]
+}
+
+license("anyhow-v1_0_27.license") {
+ public_package_name = "anyhow"
+ license_files = [
+ "//simple_deps/vendor/anyhow/LICENSE-APACHE",
+ "//simple_deps/vendor/anyhow/LICENSE-MIT",
+ "//simple_deps/vendor/anyhow/src/LICENSE-FOR-TESTING",
+ ]
+}
+
+rust_library("anyhow-v1_0_27") {
+ crate_name = "anyhow"
+ crate_root = "//simple_deps/vendor/anyhow/src/lib.rs"
+ output_name = "anyhow-d49cfcf8c47646f2"
+ configs += [ "//build/config:some_config" ]
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=d49cfcf8c47646f2",
+ "-Cextra-filename=-d49cfcf8c47646f2",
+ "--cfg=feature=\"default\"",
+ "--cfg=feature=\"std\"",
+ "--cfg=backtrace",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = [ ":anyhow-v1_0_27.license" ]
+}
+
+license("simple_deps-v0_1_0.license") {
+ public_package_name = "simple_deps"
+ license_files = [
+ "//simple_deps/vendor/anyhow/LICENSE-APACHE",
+ "//simple_deps/vendor/anyhow/LICENSE-MIT",
+ "//simple_deps/vendor/anyhow/src/LICENSE-FOR-TESTING",
+ ]
+}
+
+rust_library("simple_deps-v0_1_0") {
+ crate_name = "simple_deps"
+ crate_root = "//simple_deps/src/lib.rs"
+ output_name = "simple_deps-176a6857401f170f"
+
+ deps = []
+ deps += [ ":anyhow-v1_0_27" ]
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=176a6857401f170f",
+ "-Cextra-filename=-176a6857401f170f",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = [ ":simple_deps-v0_1_0.license" ]
+}
diff --git a/tests/simple_deps/BUILD_WITH_NO_ROOT.gn b/tests/simple_deps/BUILD_WITH_NO_ROOT.gn
new file mode 100644
index 0000000..7580145
--- /dev/null
+++ b/tests/simple_deps/BUILD_WITH_NO_ROOT.gn
@@ -0,0 +1,48 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+group("anyhow") {
+ public_deps = [ ":anyhow-v1_0_27" ]
+}
+
+license("anyhow-v1_0_27.license") {
+ public_package_name = "anyhow"
+ license_files = [
+ "//simple_deps/vendor/anyhow/LICENSE-APACHE",
+ "//simple_deps/vendor/anyhow/LICENSE-MIT",
+ "//simple_deps/vendor/anyhow/src/LICENSE-FOR-TESTING",
+ ]
+}
+
+rust_library("anyhow-v1_0_27") {
+ crate_name = "anyhow"
+ crate_root = "//simple_deps/vendor/anyhow/src/lib.rs"
+ output_name = "anyhow-d49cfcf8c47646f2"
+ configs += [ "//build/config:some_config" ]
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2018",
+ "-Cmetadata=d49cfcf8c47646f2",
+ "-Cextra-filename=-d49cfcf8c47646f2",
+ "--cfg=feature=\"default\"",
+ "--cfg=feature=\"std\"",
+ "--cfg=backtrace",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = [ ":anyhow-v1_0_27.license" ]
+}
diff --git a/tests/simple_deps/Cargo.lock b/tests/simple_deps/Cargo.lock
new file mode 100644
index 0000000..364b284
--- /dev/null
+++ b/tests/simple_deps/Cargo.lock
@@ -0,0 +1,14 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "anyhow"
+version = "1.0.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785"
+
+[[package]]
+name = "simple_deps"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+]
diff --git a/tests/simple_deps/Cargo.toml b/tests/simple_deps/Cargo.toml
new file mode 100644
index 0000000..9497227
--- /dev/null
+++ b/tests/simple_deps/Cargo.toml
@@ -0,0 +1,17 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "simple_deps"
+version = "0.1.0"
+authors = ["Benjamin Brittain <bwb@google.com>"]
+edition = "2018"
+
+[dependencies]
+anyhow = "1.0.27"
+
+# gn config
+[gn.package.anyhow."1.0.27"]
+rustflags = ["--cfg=backtrace"]
+configs = ["//build/config:some_config"]
diff --git a/tests/simple_deps/src/lib.rs b/tests/simple_deps/src/lib.rs
new file mode 100644
index 0000000..5f482a8
--- /dev/null
+++ b/tests/simple_deps/src/lib.rs
@@ -0,0 +1,7 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+fn some_fn() {
+ println!("Hello, world!");
+}
diff --git a/tests/simple_deps/vendor/anyhow/.cargo-checksum.json b/tests/simple_deps/vendor/anyhow/.cargo-checksum.json
new file mode 100644
index 0000000..5da7341
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"6a91884ae5c50562445d6a81d989a8ac76fe64e5fd6a6f3ce067e01f03772419","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"6f6d4c826e6be52ba5c29e6b1169b11e3789ab0d426d3e17ad1c19b1f646b631","build.rs":"42e4fff05dc773820ee1843aba8e36389a36386d6e6082e778d2203420bd72ab","src/backtrace.rs":"a82a8ffae2c68ee385dc78d8ec8cb6f3351234f0ad6af7e87df2371593e0f6aa","src/chain.rs":"1627608ce95c3484d26e1742a1b8b533b74c6159916b717bacffae3ae53731f9","src/context.rs":"f2be36af9588c924ed087bcb7bd681d330889e54b8f98cdc2aba93512a28762e","src/error.rs":"38ed38f1d3561685ff7167f3e706d86003952404e63f8ada5c5bf3e9ee32f76c","src/fmt.rs":"079d7b4faaa23f42423e0bb6b4e8a80d7d6d45c38c0d46bebd7d647c8679469f","src/kind.rs":"8481a8b7835eebb3859a8c32c217bf9c73543cfc62e3916b98d39af8b063125c","src/lib.rs":"e10c1dda3d1d40de3606176630885c0eaa0ac78fd58a169fed24004ccd4c37a8","src/macros.rs":"77722190b58a6106b21aefd3b5d4f136a076afcdbc0fae21562d99e2c22912e1","src/wrapper.rs":"1229beca67dbd95ca77c9ecce282272acc55276c267c58cb73a75388b4693dda","tests/common/mod.rs":"f9088c2d7afafa64ff730b629272045b776bfafc2f5957508242da630635f2e1","tests/compiletest.rs":"0a52a44786aea1c299c695bf948b2ed2081e4cc344e5c2cadceab4eb03d0010d","tests/drop/mod.rs":"464bc1ddeae307eac906928286ec3edb77057c5c1302e02150d3649e2b861f1a","tests/test_autotrait.rs":"981e792db353be2f14c7a1cabe43b5f1329c168cb7679077cc2be786a0920d48","tests/test_backtrace.rs":"0e50edbb33b6bd07ba89ff3db72fb7c688ba2a4371fccdbbb20309ab02948b6a","tests/test_boxed.rs":"98a45325b1e86d4c5d3094ab99cd1ada1f771c505d2d7322f0afcbe7bdb71cfa","tests/test_chain.rs":"f28efeae7395d1c395e6f1a647b4199c25a00410ade45248c145c6fcf2fb448a","tests/test_context.rs":"f82c915b182df1a604a4cd558a03b1a821414983d6f6af6822398104cea70676","tests/test_convert.rs":"62840be1ee8022ba5e8c0d3fc1752a1526b2c47d4cceecff2b86790524c3b3ea","tests/test_downcast.rs":"253d6f54e554965023b378b037827ec6289c4779a7a7c12706e19c2731d219fe","tests/test_fmt.rs":"17572596f257aac9aa2ec4620e292ca6a954128b94772bb948399fab53832e70","tests/test_macros.rs":"c7d3d5e0b756f59d4858035025fb341d031369c88486fd9f961ee16bae6c78bf","tests/test_repr.rs":"dbb9b04ddbe1ab31eb5331ea69f05bb3a147299da2275a3d4dcc92947b5591b9","tests/test_source.rs":"b80723cf635a4f8c4df21891b34bfab9ed2b2aa407e7a2f826d24e334cd5f88e","tests/ui/no-impl.rs":"fab6cbf2f6ea510b86f567dfb3b7c31250a9fd71ae5d110dbb9188be569ec593","tests/ui/no-impl.stderr":"7c2c3f46c266a437300591f10be330f937ac6a0a2213ed5030a9fbc895e2d100"},"package":"013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785"}
\ No newline at end of file
diff --git a/tests/simple_deps/vendor/anyhow/Cargo.toml b/tests/simple_deps/vendor/anyhow/Cargo.toml
new file mode 100644
index 0000000..9e2930c
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/Cargo.toml
@@ -0,0 +1,42 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "anyhow"
+version = "1.0.27"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+description = "Flexible concrete Error type built on std::error::Error"
+documentation = "https://docs.rs/anyhow"
+readme = "README.md"
+categories = ["rust-patterns"]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/dtolnay/anyhow"
+[dev-dependencies.futures]
+version = "0.3"
+default-features = false
+
+[dev-dependencies.rustversion]
+version = "1.0"
+
+[dev-dependencies.thiserror]
+version = "1.0"
+
+[dev-dependencies.trybuild]
+version = "1.0.19"
+features = ["diff"]
+
+[features]
+default = ["std"]
+std = []
+[badges.travis-ci]
+repository = "dtolnay/anyhow"
diff --git a/tests/simple_deps/vendor/anyhow/LICENSE-APACHE b/tests/simple_deps/vendor/anyhow/LICENSE-APACHE
new file mode 100644
index 0000000..16fe87b
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/LICENSE-APACHE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+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.
diff --git a/tests/simple_deps/vendor/anyhow/LICENSE-MIT b/tests/simple_deps/vendor/anyhow/LICENSE-MIT
new file mode 100644
index 0000000..31aa793
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/LICENSE-MIT
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/tests/simple_deps/vendor/anyhow/README.md b/tests/simple_deps/vendor/anyhow/README.md
new file mode 100644
index 0000000..0c2786b
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/README.md
@@ -0,0 +1,171 @@
+Anyhow ¯\\\_(ツ)\_/¯
+=========================
+
+[](https://travis-ci.com/dtolnay/anyhow)
+[](https://crates.io/crates/anyhow)
+[](https://docs.rs/anyhow)
+
+This library provides [`anyhow::Error`][Error], a trait object based error type
+for easy idiomatic error handling in Rust applications.
+
+[Error]: https://docs.rs/anyhow/1.0/anyhow/struct.Error.html
+
+```toml
+[dependencies]
+anyhow = "1.0"
+```
+
+*Compiler support: requires rustc 1.34+*
+
+<br>
+
+## Details
+
+- Use `Result<T, anyhow::Error>`, or equivalently `anyhow::Result<T>`, as the
+ return type of any fallible function.
+
+ Within the function, use `?` to easily propagate any error that implements the
+ `std::error::Error` trait.
+
+ ```rust
+ use anyhow::Result;
+
+ fn get_cluster_info() -> Result<ClusterMap> {
+ let config = std::fs::read_to_string("cluster.json")?;
+ let map: ClusterMap = serde_json::from_str(&config)?;
+ Ok(map)
+ }
+ ```
+
+- Attach context to help the person troubleshooting the error understand where
+ things went wrong. A low-level error like "No such file or directory" can be
+ annoying to debug without more context about what higher level step the
+ application was in the middle of.
+
+ ```rust
+ use anyhow::{Context, Result};
+
+ fn main() -> Result<()> {
+ ...
+ it.detach().context("Failed to detach the important thing")?;
+
+ let content = std::fs::read(path)
+ .with_context(|| format!("Failed to read instrs from {}", path))?;
+ ...
+ }
+ ```
+
+ ```console
+ Error: Failed to read instrs from ./path/to/instrs.json
+
+ Caused by:
+ No such file or directory (os error 2)
+ ```
+
+- Downcasting is supported and can be by value, by shared reference, or by
+ mutable reference as needed.
+
+ ```rust
+ // If the error was caused by redaction, then return a
+ // tombstone instead of the content.
+ match root_cause.downcast_ref::<DataStoreError>() {
+ Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)),
+ None => Err(error),
+ }
+ ```
+
+- A backtrace is captured and printed with the error if the underlying error
+ type does not already provide its own. In order to see backtraces, they must
+ be enabled through the environment variables described in [`std::backtrace`]:
+
+ - If you want panics and errors to both have backtraces, set
+ `RUST_BACKTRACE=1`;
+ - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`;
+ - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and
+ `RUST_LIB_BACKTRACE=0`.
+
+ [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables
+
+- Anyhow works with any error type that has an impl of `std::error::Error`,
+ including ones defined in your crate. We do not bundle a `derive(Error)` macro
+ but you can write the impls yourself or use a standalone macro like
+ [thiserror].
+
+ ```rust
+ use thiserror::Error;
+
+ #[derive(Error, Debug)]
+ pub enum FormatError {
+ #[error("Invalid header (expected {expected:?}, got {found:?})")]
+ InvalidHeader {
+ expected: String,
+ found: String,
+ },
+ #[error("Missing attribute: {0}")]
+ MissingAttribute(String),
+ }
+ ```
+
+- One-off error messages can be constructed using the `anyhow!` macro, which
+ supports string interpolation and produces an `anyhow::Error`.
+
+ ```rust
+ return Err(anyhow!("Missing attribute: {}", missing));
+ ```
+
+<br>
+
+## No-std support
+
+In no_std mode, the same API is almost all available and works the same way. To
+depend on Anyhow in no_std mode, disable our default enabled "std" feature in
+Cargo.toml. A global allocator is required.
+
+```toml
+[dependencies]
+anyhow = { version = "1.0", default-features = false }
+```
+
+Since the `?`-based error conversions would normally rely on the
+`std::error::Error` trait which is only available through std, no_std mode will
+require an explicit `.map_err(Error::msg)` when working with a non-Anyhow error
+type inside a function that returns Anyhow's error type.
+
+<br>
+
+## Comparison to failure
+
+The `anyhow::Error` type works something like `failure::Error`, but unlike
+failure ours is built around the standard library's `std::error::Error` trait
+rather than a separate trait `failure::Fail`. The standard library has adopted
+the necessary improvements for this to be possible as part of [RFC 2504].
+
+[RFC 2504]: https://github.com/rust-lang/rfcs/blob/HEAD/text/2504-fix-error.md
+
+<br>
+
+## Comparison to thiserror
+
+Use Anyhow if you don't care what error type your functions return, you just
+want it to be easy. This is common in application code. Use [thiserror] if you
+are a library that wants to design your own dedicated error type(s) so that on
+failures the caller gets exactly the information that you choose.
+
+[thiserror]: https://github.com/dtolnay/thiserror
+
+<br>
+
+#### License
+
+<sup>
+Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
+2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
+</sup>
+
+<br>
+
+<sub>
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
+be dual licensed as above, without any additional terms or conditions.
+</sub>
diff --git a/tests/simple_deps/vendor/anyhow/build.rs b/tests/simple_deps/vendor/anyhow/build.rs
new file mode 100644
index 0000000..293167e
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/build.rs
@@ -0,0 +1,61 @@
+use std::path::Path;
+use std::process::{Command, ExitStatus};
+use std::{env, fs};
+
+// This code exercises the surface area that we expect of the std Backtrace
+// type. If the current toolchain is able to compile it, we go ahead and use
+// backtrace in anyhow.
+const PROBE: &str = r#"
+ #![feature(backtrace)]
+ #![allow(dead_code)]
+
+ use std::backtrace::{Backtrace, BacktraceStatus};
+ use std::error::Error;
+ use std::fmt::{self, Display};
+
+ #[derive(Debug)]
+ struct E;
+
+ impl Display for E {
+ fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
+ unimplemented!()
+ }
+ }
+
+ impl Error for E {
+ fn backtrace(&self) -> Option<&Backtrace> {
+ let backtrace = Backtrace::capture();
+ match backtrace.status() {
+ BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {}
+ }
+ unimplemented!()
+ }
+ }
+"#;
+
+fn main() {
+ if !cfg!(feature = "std") {
+ return;
+ }
+ match compile_probe() {
+ Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"),
+ _ => {}
+ }
+}
+
+fn compile_probe() -> Option<ExitStatus> {
+ let rustc = env::var_os("RUSTC")?;
+ let out_dir = env::var_os("OUT_DIR")?;
+ let probefile = Path::new(&out_dir).join("probe.rs");
+ fs::write(&probefile, PROBE).ok()?;
+ Command::new(rustc)
+ .arg("--edition=2018")
+ .arg("--crate-name=anyhow_build")
+ .arg("--crate-type=lib")
+ .arg("--emit=metadata")
+ .arg("--out-dir")
+ .arg(out_dir)
+ .arg(probefile)
+ .status()
+ .ok()
+}
diff --git a/tests/simple_deps/vendor/anyhow/src/LICENSE-FOR-TESTING b/tests/simple_deps/vendor/anyhow/src/LICENSE-FOR-TESTING
new file mode 100644
index 0000000..f103256
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/src/LICENSE-FOR-TESTING
@@ -0,0 +1 @@
+Stub license for testing applicable_licenses.
\ No newline at end of file
diff --git a/tests/simple_deps/vendor/anyhow/src/backtrace.rs b/tests/simple_deps/vendor/anyhow/src/backtrace.rs
new file mode 100644
index 0000000..01e33cb
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/src/backtrace.rs
@@ -0,0 +1,36 @@
+#[cfg(backtrace)]
+pub(crate) use std::backtrace::Backtrace;
+
+#[cfg(not(backtrace))]
+pub(crate) enum Backtrace {}
+
+#[cfg(backtrace)]
+macro_rules! backtrace {
+ () => {
+ Some(Backtrace::capture())
+ };
+}
+
+#[cfg(not(backtrace))]
+macro_rules! backtrace {
+ () => {
+ None
+ };
+}
+
+#[cfg(backtrace)]
+macro_rules! backtrace_if_absent {
+ ($err:expr) => {
+ match $err.backtrace() {
+ Some(_) => None,
+ None => Some(Backtrace::capture()),
+ }
+ };
+}
+
+#[cfg(all(feature = "std", not(backtrace)))]
+macro_rules! backtrace_if_absent {
+ ($err:expr) => {
+ None
+ };
+}
diff --git a/tests/simple_deps/vendor/anyhow/src/chain.rs b/tests/simple_deps/vendor/anyhow/src/chain.rs
new file mode 100644
index 0000000..97fd0e3
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/src/chain.rs
@@ -0,0 +1,95 @@
+use self::ChainState::*;
+use crate::StdError;
+
+#[cfg(feature = "std")]
+use std::vec;
+
+#[cfg(feature = "std")]
+pub(crate) use crate::Chain;
+
+#[cfg(not(feature = "std"))]
+pub(crate) struct Chain<'a> {
+ state: ChainState<'a>,
+}
+
+#[derive(Clone)]
+pub(crate) enum ChainState<'a> {
+ Linked {
+ next: Option<&'a (dyn StdError + 'static)>,
+ },
+ #[cfg(feature = "std")]
+ Buffered {
+ rest: vec::IntoIter<&'a (dyn StdError + 'static)>,
+ },
+}
+
+impl<'a> Chain<'a> {
+ pub fn new(head: &'a (dyn StdError + 'static)) -> Self {
+ Chain { state: ChainState::Linked { next: Some(head) } }
+ }
+}
+
+impl<'a> Iterator for Chain<'a> {
+ type Item = &'a (dyn StdError + 'static);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match &mut self.state {
+ Linked { next } => {
+ let error = (*next)?;
+ *next = error.source();
+ Some(error)
+ }
+ #[cfg(feature = "std")]
+ Buffered { rest } => rest.next(),
+ }
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let len = self.len();
+ (len, Some(len))
+ }
+}
+
+#[cfg(feature = "std")]
+impl DoubleEndedIterator for Chain<'_> {
+ fn next_back(&mut self) -> Option<Self::Item> {
+ match &mut self.state {
+ Linked { mut next } => {
+ let mut rest = Vec::new();
+ while let Some(cause) = next {
+ next = cause.source();
+ rest.push(cause);
+ }
+ let mut rest = rest.into_iter();
+ let last = rest.next_back();
+ self.state = Buffered { rest };
+ last
+ }
+ Buffered { rest } => rest.next_back(),
+ }
+ }
+}
+
+impl ExactSizeIterator for Chain<'_> {
+ fn len(&self) -> usize {
+ match &self.state {
+ Linked { mut next } => {
+ let mut len = 0;
+ while let Some(cause) = next {
+ next = cause.source();
+ len += 1;
+ }
+ len
+ }
+ #[cfg(feature = "std")]
+ Buffered { rest } => rest.len(),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl Default for Chain<'_> {
+ fn default() -> Self {
+ Chain { state: ChainState::Buffered { rest: Vec::new().into_iter() } }
+ }
+}
diff --git a/tests/simple_deps/vendor/anyhow/src/context.rs b/tests/simple_deps/vendor/anyhow/src/context.rs
new file mode 100644
index 0000000..25d3411
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/src/context.rs
@@ -0,0 +1,177 @@
+use crate::error::ContextError;
+use crate::{Context, Error, StdError};
+use core::convert::Infallible;
+use core::fmt::{self, Debug, Display, Write};
+
+#[cfg(backtrace)]
+use std::backtrace::Backtrace;
+
+mod ext {
+ use super::*;
+
+ pub trait StdError {
+ fn ext_context<C>(self, context: C) -> Error
+ where
+ C: Display + Send + Sync + 'static;
+ }
+
+ #[cfg(feature = "std")]
+ impl<E> StdError for E
+ where
+ E: std::error::Error + Send + Sync + 'static,
+ {
+ fn ext_context<C>(self, context: C) -> Error
+ where
+ C: Display + Send + Sync + 'static,
+ {
+ let backtrace = backtrace_if_absent!(self);
+ Error::from_context(context, self, backtrace)
+ }
+ }
+
+ impl StdError for Error {
+ fn ext_context<C>(self, context: C) -> Error
+ where
+ C: Display + Send + Sync + 'static,
+ {
+ self.context(context)
+ }
+ }
+}
+
+impl<T, E> Context<T, E> for Result<T, E>
+where
+ E: ext::StdError + Send + Sync + 'static,
+{
+ fn context<C>(self, context: C) -> Result<T, Error>
+ where
+ C: Display + Send + Sync + 'static,
+ {
+ self.map_err(|error| error.ext_context(context))
+ }
+
+ fn with_context<C, F>(self, context: F) -> Result<T, Error>
+ where
+ C: Display + Send + Sync + 'static,
+ F: FnOnce() -> C,
+ {
+ self.map_err(|error| error.ext_context(context()))
+ }
+}
+
+/// ```
+/// # type T = ();
+/// #
+/// use anyhow::{Context, Result};
+///
+/// fn maybe_get() -> Option<T> {
+/// # const IGNORE: &str = stringify! {
+/// ...
+/// # };
+/// # unimplemented!()
+/// }
+///
+/// fn demo() -> Result<()> {
+/// let t = maybe_get().context("there is no T")?;
+/// # const IGNORE: &str = stringify! {
+/// ...
+/// # };
+/// # unimplemented!()
+/// }
+/// ```
+impl<T> Context<T, Infallible> for Option<T> {
+ fn context<C>(self, context: C) -> Result<T, Error>
+ where
+ C: Display + Send + Sync + 'static,
+ {
+ self.ok_or_else(|| Error::from_display(context, backtrace!()))
+ }
+
+ fn with_context<C, F>(self, context: F) -> Result<T, Error>
+ where
+ C: Display + Send + Sync + 'static,
+ F: FnOnce() -> C,
+ {
+ self.ok_or_else(|| Error::from_display(context(), backtrace!()))
+ }
+}
+
+impl<C, E> Debug for ContextError<C, E>
+where
+ C: Display,
+ E: Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Error")
+ .field("context", &Quoted(&self.context))
+ .field("source", &self.error)
+ .finish()
+ }
+}
+
+impl<C, E> Display for ContextError<C, E>
+where
+ C: Display,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.context, f)
+ }
+}
+
+impl<C, E> StdError for ContextError<C, E>
+where
+ C: Display,
+ E: StdError + 'static,
+{
+ #[cfg(backtrace)]
+ fn backtrace(&self) -> Option<&Backtrace> {
+ self.error.backtrace()
+ }
+
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ Some(&self.error)
+ }
+}
+
+impl<C> StdError for ContextError<C, Error>
+where
+ C: Display,
+{
+ #[cfg(backtrace)]
+ fn backtrace(&self) -> Option<&Backtrace> {
+ Some(self.error.backtrace())
+ }
+
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ Some(self.error.inner.error())
+ }
+}
+
+struct Quoted<C>(C);
+
+impl<C> Debug for Quoted<C>
+where
+ C: Display,
+{
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_char('"')?;
+ Quoted(&mut *formatter).write_fmt(format_args!("{}", self.0))?;
+ formatter.write_char('"')?;
+ Ok(())
+ }
+}
+
+impl Write for Quoted<&mut fmt::Formatter<'_>> {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ Display::fmt(&s.escape_debug(), self.0)
+ }
+}
+
+pub(crate) mod private {
+ use super::*;
+
+ pub trait Sealed {}
+
+ impl<T, E> Sealed for Result<T, E> where E: ext::StdError {}
+ impl<T> Sealed for Option<T> {}
+}
diff --git a/tests/simple_deps/vendor/anyhow/src/error.rs b/tests/simple_deps/vendor/anyhow/src/error.rs
new file mode 100644
index 0000000..410a8f4
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/src/error.rs
@@ -0,0 +1,794 @@
+use crate::alloc::Box;
+use crate::backtrace::Backtrace;
+use crate::chain::Chain;
+use crate::{Error, StdError};
+use core::any::TypeId;
+use core::fmt::{self, Debug, Display};
+use core::mem::{self, ManuallyDrop};
+use core::ptr::{self, NonNull};
+
+#[cfg(feature = "std")]
+use core::ops::{Deref, DerefMut};
+
+impl Error {
+ /// Create a new error object from any error type.
+ ///
+ /// The error type must be threadsafe and `'static`, so that the `Error`
+ /// will be as well.
+ ///
+ /// If the error type does not provide a backtrace, a backtrace will be
+ /// created here to ensure that a backtrace exists.
+ #[cfg(feature = "std")]
+ pub fn new<E>(error: E) -> Self
+ where
+ E: StdError + Send + Sync + 'static,
+ {
+ let backtrace = backtrace_if_absent!(error);
+ Error::from_std(error, backtrace)
+ }
+
+ /// Create a new error object from a printable error message.
+ ///
+ /// If the argument implements std::error::Error, prefer `Error::new`
+ /// instead which preserves the underlying error's cause chain and
+ /// backtrace. If the argument may or may not implement std::error::Error
+ /// now or in the future, use `anyhow!(err)` which handles either way
+ /// correctly.
+ ///
+ /// `Error::msg("...")` is equivalent to `anyhow!("...")` but occasionally
+ /// convenient in places where a function is preferable over a macro, such
+ /// as iterator or stream combinators:
+ ///
+ /// ```
+ /// # mod ffi {
+ /// # pub struct Input;
+ /// # pub struct Output;
+ /// # pub async fn do_some_work(_: Input) -> Result<Output, &'static str> {
+ /// # unimplemented!()
+ /// # }
+ /// # }
+ /// #
+ /// # use ffi::{Input, Output};
+ /// #
+ /// use anyhow::{Error, Result};
+ /// use futures::stream::{Stream, StreamExt, TryStreamExt};
+ ///
+ /// async fn demo<S>(stream: S) -> Result<Vec<Output>>
+ /// where
+ /// S: Stream<Item = Input>,
+ /// {
+ /// stream
+ /// .then(ffi::do_some_work) // returns Result<Output, &str>
+ /// .map_err(Error::msg)
+ /// .try_collect()
+ /// .await
+ /// }
+ /// ```
+ pub fn msg<M>(message: M) -> Self
+ where
+ M: Display + Debug + Send + Sync + 'static,
+ {
+ Error::from_adhoc(message, backtrace!())
+ }
+
+ #[cfg(feature = "std")]
+ pub(crate) fn from_std<E>(error: E, backtrace: Option<Backtrace>) -> Self
+ where
+ E: StdError + Send + Sync + 'static,
+ {
+ let vtable = &ErrorVTable {
+ object_drop: object_drop::<E>,
+ object_ref: object_ref::<E>,
+ #[cfg(feature = "std")]
+ object_mut: object_mut::<E>,
+ object_boxed: object_boxed::<E>,
+ object_downcast: object_downcast::<E>,
+ object_drop_rest: object_drop_front::<E>,
+ };
+
+ // Safety: passing vtable that operates on the right type E.
+ unsafe { Error::construct(error, vtable, backtrace) }
+ }
+
+ pub(crate) fn from_adhoc<M>(message: M, backtrace: Option<Backtrace>) -> Self
+ where
+ M: Display + Debug + Send + Sync + 'static,
+ {
+ use crate::wrapper::MessageError;
+ let error: MessageError<M> = MessageError(message);
+ let vtable = &ErrorVTable {
+ object_drop: object_drop::<MessageError<M>>,
+ object_ref: object_ref::<MessageError<M>>,
+ #[cfg(feature = "std")]
+ object_mut: object_mut::<MessageError<M>>,
+ object_boxed: object_boxed::<MessageError<M>>,
+ object_downcast: object_downcast::<M>,
+ object_drop_rest: object_drop_front::<M>,
+ };
+
+ // Safety: MessageError is repr(transparent) so it is okay for the
+ // vtable to allow casting the MessageError<M> to M.
+ unsafe { Error::construct(error, vtable, backtrace) }
+ }
+
+ pub(crate) fn from_display<M>(message: M, backtrace: Option<Backtrace>) -> Self
+ where
+ M: Display + Send + Sync + 'static,
+ {
+ use crate::wrapper::DisplayError;
+ let error: DisplayError<M> = DisplayError(message);
+ let vtable = &ErrorVTable {
+ object_drop: object_drop::<DisplayError<M>>,
+ object_ref: object_ref::<DisplayError<M>>,
+ #[cfg(feature = "std")]
+ object_mut: object_mut::<DisplayError<M>>,
+ object_boxed: object_boxed::<DisplayError<M>>,
+ object_downcast: object_downcast::<M>,
+ object_drop_rest: object_drop_front::<M>,
+ };
+
+ // Safety: DisplayError is repr(transparent) so it is okay for the
+ // vtable to allow casting the DisplayError<M> to M.
+ unsafe { Error::construct(error, vtable, backtrace) }
+ }
+
+ #[cfg(feature = "std")]
+ pub(crate) fn from_context<C, E>(context: C, error: E, backtrace: Option<Backtrace>) -> Self
+ where
+ C: Display + Send + Sync + 'static,
+ E: StdError + Send + Sync + 'static,
+ {
+ let error: ContextError<C, E> = ContextError { context, error };
+
+ let vtable = &ErrorVTable {
+ object_drop: object_drop::<ContextError<C, E>>,
+ object_ref: object_ref::<ContextError<C, E>>,
+ #[cfg(feature = "std")]
+ object_mut: object_mut::<ContextError<C, E>>,
+ object_boxed: object_boxed::<ContextError<C, E>>,
+ object_downcast: context_downcast::<C, E>,
+ object_drop_rest: context_drop_rest::<C, E>,
+ };
+
+ // Safety: passing vtable that operates on the right type.
+ unsafe { Error::construct(error, vtable, backtrace) }
+ }
+
+ #[cfg(feature = "std")]
+ pub(crate) fn from_boxed(
+ error: Box<dyn StdError + Send + Sync>,
+ backtrace: Option<Backtrace>,
+ ) -> Self {
+ use crate::wrapper::BoxedError;
+ let error = BoxedError(error);
+ let vtable = &ErrorVTable {
+ object_drop: object_drop::<BoxedError>,
+ object_ref: object_ref::<BoxedError>,
+ #[cfg(feature = "std")]
+ object_mut: object_mut::<BoxedError>,
+ object_boxed: object_boxed::<BoxedError>,
+ object_downcast: object_downcast::<Box<dyn StdError + Send + Sync>>,
+ object_drop_rest: object_drop_front::<Box<dyn StdError + Send + Sync>>,
+ };
+
+ // Safety: BoxedError is repr(transparent) so it is okay for the vtable
+ // to allow casting to Box<dyn StdError + Send + Sync>.
+ unsafe { Error::construct(error, vtable, backtrace) }
+ }
+
+ // Takes backtrace as argument rather than capturing it here so that the
+ // user sees one fewer layer of wrapping noise in the backtrace.
+ //
+ // Unsafe because the given vtable must have sensible behavior on the error
+ // value of type E.
+ unsafe fn construct<E>(
+ error: E,
+ vtable: &'static ErrorVTable,
+ backtrace: Option<Backtrace>,
+ ) -> Self
+ where
+ E: StdError + Send + Sync + 'static,
+ {
+ let inner = Box::new(ErrorImpl { vtable, backtrace, _object: error });
+ // Erase the concrete type of E from the compile-time type system. This
+ // is equivalent to the safe unsize coersion from Box<ErrorImpl<E>> to
+ // Box<ErrorImpl<dyn StdError + Send + Sync + 'static>> except that the
+ // result is a thin pointer. The necessary behavior for manipulating the
+ // underlying ErrorImpl<E> is preserved in the vtable provided by the
+ // caller rather than a builtin fat pointer vtable.
+ let erased = mem::transmute::<Box<ErrorImpl<E>>, Box<ErrorImpl<()>>>(inner);
+ let inner = ManuallyDrop::new(erased);
+ Error { inner }
+ }
+
+ /// Wrap the error value with additional context.
+ ///
+ /// For attaching context to a `Result` as it is propagated, the
+ /// [`Context`][crate::Context] extension trait may be more convenient than
+ /// this function.
+ ///
+ /// The primary reason to use `error.context(...)` instead of
+ /// `result.context(...)` via the `Context` trait would be if the context
+ /// needs to depend on some data held by the underlying error:
+ ///
+ /// ```
+ /// # use std::fmt::{self, Debug, Display};
+ /// #
+ /// # type T = ();
+ /// #
+ /// # impl std::error::Error for ParseError {}
+ /// # impl Debug for ParseError {
+ /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ /// # unimplemented!()
+ /// # }
+ /// # }
+ /// # impl Display for ParseError {
+ /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ /// # unimplemented!()
+ /// # }
+ /// # }
+ /// #
+ /// use anyhow::Result;
+ /// use std::fs::File;
+ /// use std::path::Path;
+ ///
+ /// struct ParseError {
+ /// line: usize,
+ /// column: usize,
+ /// }
+ ///
+ /// fn parse_impl(file: File) -> Result<T, ParseError> {
+ /// # const IGNORE: &str = stringify! {
+ /// ...
+ /// # };
+ /// # unimplemented!()
+ /// }
+ ///
+ /// pub fn parse(path: impl AsRef<Path>) -> Result<T> {
+ /// let file = File::open(&path)?;
+ /// parse_impl(file).map_err(|error| {
+ /// let context = format!(
+ /// "only the first {} lines of {} are valid",
+ /// error.line, path.as_ref().display(),
+ /// );
+ /// anyhow::Error::new(error).context(context)
+ /// })
+ /// }
+ /// ```
+ pub fn context<C>(self, context: C) -> Self
+ where
+ C: Display + Send + Sync + 'static,
+ {
+ let error: ContextError<C, Error> = ContextError { context, error: self };
+
+ let vtable = &ErrorVTable {
+ object_drop: object_drop::<ContextError<C, Error>>,
+ object_ref: object_ref::<ContextError<C, Error>>,
+ #[cfg(feature = "std")]
+ object_mut: object_mut::<ContextError<C, Error>>,
+ object_boxed: object_boxed::<ContextError<C, Error>>,
+ object_downcast: context_chain_downcast::<C>,
+ object_drop_rest: context_chain_drop_rest::<C>,
+ };
+
+ // As the cause is anyhow::Error, we already have a backtrace for it.
+ let backtrace = None;
+
+ // Safety: passing vtable that operates on the right type.
+ unsafe { Error::construct(error, vtable, backtrace) }
+ }
+
+ /// Get the backtrace for this Error.
+ ///
+ /// Backtraces are only available on the nightly channel. Tracking issue:
+ /// [rust-lang/rust#53487][tracking].
+ ///
+ /// In order for the backtrace to be meaningful, one of the two environment
+ /// variables `RUST_LIB_BACKTRACE=1` or `RUST_BACKTRACE=1` must be defined
+ /// and `RUST_LIB_BACKTRACE` must not be `0`. Backtraces are somewhat
+ /// expensive to capture in Rust, so we don't necessarily want to be
+ /// capturing them all over the place all the time.
+ ///
+ /// - If you want panics and errors to both have backtraces, set
+ /// `RUST_BACKTRACE=1`;
+ /// - If you want only errors to have backtraces, set
+ /// `RUST_LIB_BACKTRACE=1`;
+ /// - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and
+ /// `RUST_LIB_BACKTRACE=0`.
+ ///
+ /// [tracking]: https://github.com/rust-lang/rust/issues/53487
+ #[cfg(backtrace)]
+ pub fn backtrace(&self) -> &Backtrace {
+ self.inner.backtrace()
+ }
+
+ /// An iterator of the chain of source errors contained by this Error.
+ ///
+ /// This iterator will visit every error in the cause chain of this error
+ /// object, beginning with the error that this error object was created
+ /// from.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use anyhow::Error;
+ /// use std::io;
+ ///
+ /// pub fn underlying_io_error_kind(error: &Error) -> Option<io::ErrorKind> {
+ /// for cause in error.chain() {
+ /// if let Some(io_error) = cause.downcast_ref::<io::Error>() {
+ /// return Some(io_error.kind());
+ /// }
+ /// }
+ /// None
+ /// }
+ /// ```
+ #[cfg(feature = "std")]
+ pub fn chain(&self) -> Chain {
+ self.inner.chain()
+ }
+
+ /// The lowest level cause of this error — this error's cause's
+ /// cause's cause etc.
+ ///
+ /// The root cause is the last error in the iterator produced by
+ /// [`chain()`][Error::chain].
+ #[cfg(feature = "std")]
+ pub fn root_cause(&self) -> &(dyn StdError + 'static) {
+ let mut chain = self.chain();
+ let mut root_cause = chain.next().unwrap();
+ for cause in chain {
+ root_cause = cause;
+ }
+ root_cause
+ }
+
+ /// Returns true if `E` is the type held by this error object.
+ ///
+ /// For errors with context, this method returns true if `E` matches the
+ /// type of the context `C` **or** the type of the error on which the
+ /// context has been attached. For details about the interaction between
+ /// context and downcasting, [see here].
+ ///
+ /// [see here]: trait.Context.html#effect-on-downcasting
+ pub fn is<E>(&self) -> bool
+ where
+ E: Display + Debug + Send + Sync + 'static,
+ {
+ self.downcast_ref::<E>().is_some()
+ }
+
+ /// Attempt to downcast the error object to a concrete type.
+ pub fn downcast<E>(self) -> Result<E, Self>
+ where
+ E: Display + Debug + Send + Sync + 'static,
+ {
+ let target = TypeId::of::<E>();
+ unsafe {
+ // Use vtable to find NonNull<()> which points to a value of type E
+ // somewhere inside the data structure.
+ let addr = match (self.inner.vtable.object_downcast)(&self.inner, target) {
+ Some(addr) => addr,
+ None => return Err(self),
+ };
+
+ // Prepare to read E out of the data structure. We'll drop the rest
+ // of the data structure separately so that E is not dropped.
+ let outer = ManuallyDrop::new(self);
+
+ // Read E from where the vtable found it.
+ let error = ptr::read(addr.cast::<E>().as_ptr());
+
+ // Read Box<ErrorImpl<()>> from self. Can't move it out because
+ // Error has a Drop impl which we want to not run.
+ let inner = ptr::read(&outer.inner);
+ let erased = ManuallyDrop::into_inner(inner);
+
+ // Drop rest of the data structure outside of E.
+ (erased.vtable.object_drop_rest)(erased, target);
+
+ Ok(error)
+ }
+ }
+
+ /// Downcast this error object by reference.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use anyhow::anyhow;
+ /// # use std::fmt::{self, Display};
+ /// # use std::task::Poll;
+ /// #
+ /// # #[derive(Debug)]
+ /// # enum DataStoreError {
+ /// # Censored(()),
+ /// # }
+ /// #
+ /// # impl Display for DataStoreError {
+ /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ /// # unimplemented!()
+ /// # }
+ /// # }
+ /// #
+ /// # impl std::error::Error for DataStoreError {}
+ /// #
+ /// # const REDACTED_CONTENT: () = ();
+ /// #
+ /// # let error = anyhow!("...");
+ /// # let root_cause = &error;
+ /// #
+ /// # let ret =
+ /// // If the error was caused by redaction, then return a tombstone instead
+ /// // of the content.
+ /// match root_cause.downcast_ref::<DataStoreError>() {
+ /// Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)),
+ /// None => Err(error),
+ /// }
+ /// # ;
+ /// ```
+ pub fn downcast_ref<E>(&self) -> Option<&E>
+ where
+ E: Display + Debug + Send + Sync + 'static,
+ {
+ let target = TypeId::of::<E>();
+ unsafe {
+ // Use vtable to find NonNull<()> which points to a value of type E
+ // somewhere inside the data structure.
+ let addr = (self.inner.vtable.object_downcast)(&self.inner, target)?;
+ Some(&*addr.cast::<E>().as_ptr())
+ }
+ }
+
+ /// Downcast this error object by mutable reference.
+ pub fn downcast_mut<E>(&mut self) -> Option<&mut E>
+ where
+ E: Display + Debug + Send + Sync + 'static,
+ {
+ let target = TypeId::of::<E>();
+ unsafe {
+ // Use vtable to find NonNull<()> which points to a value of type E
+ // somewhere inside the data structure.
+ let addr = (self.inner.vtable.object_downcast)(&self.inner, target)?;
+ Some(&mut *addr.cast::<E>().as_ptr())
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<E> From<E> for Error
+where
+ E: StdError + Send + Sync + 'static,
+{
+ fn from(error: E) -> Self {
+ let backtrace = backtrace_if_absent!(error);
+ Error::from_std(error, backtrace)
+ }
+}
+
+#[cfg(feature = "std")]
+impl Deref for Error {
+ type Target = dyn StdError + Send + Sync + 'static;
+
+ fn deref(&self) -> &Self::Target {
+ self.inner.error()
+ }
+}
+
+#[cfg(feature = "std")]
+impl DerefMut for Error {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.inner.error_mut()
+ }
+}
+
+impl Display for Error {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ self.inner.display(formatter)
+ }
+}
+
+impl Debug for Error {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ self.inner.debug(formatter)
+ }
+}
+
+impl Drop for Error {
+ fn drop(&mut self) {
+ unsafe {
+ // Read Box<ErrorImpl<()>> from self.
+ let inner = ptr::read(&self.inner);
+ let erased = ManuallyDrop::into_inner(inner);
+
+ // Invoke the vtable's drop behavior.
+ (erased.vtable.object_drop)(erased);
+ }
+ }
+}
+
+struct ErrorVTable {
+ object_drop: unsafe fn(Box<ErrorImpl<()>>),
+ object_ref: unsafe fn(&ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static),
+ #[cfg(feature = "std")]
+ object_mut: unsafe fn(&mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static),
+ object_boxed: unsafe fn(Box<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static>,
+ object_downcast: unsafe fn(&ErrorImpl<()>, TypeId) -> Option<NonNull<()>>,
+ object_drop_rest: unsafe fn(Box<ErrorImpl<()>>, TypeId),
+}
+
+// Safety: requires layout of *e to match ErrorImpl<E>.
+unsafe fn object_drop<E>(e: Box<ErrorImpl<()>>) {
+ // Cast back to ErrorImpl<E> so that the allocator receives the correct
+ // Layout to deallocate the Box's memory.
+ let unerased = mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<E>>>(e);
+ drop(unerased);
+}
+
+// Safety: requires layout of *e to match ErrorImpl<E>.
+unsafe fn object_drop_front<E>(e: Box<ErrorImpl<()>>, target: TypeId) {
+ // Drop the fields of ErrorImpl other than E as well as the Box allocation,
+ // without dropping E itself. This is used by downcast after doing a
+ // ptr::read to take ownership of the E.
+ let _ = target;
+ let unerased = mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<ManuallyDrop<E>>>>(e);
+ drop(unerased);
+}
+
+// Safety: requires layout of *e to match ErrorImpl<E>.
+unsafe fn object_ref<E>(e: &ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static)
+where
+ E: StdError + Send + Sync + 'static,
+{
+ // Attach E's native StdError vtable onto a pointer to self._object.
+ &(*(e as *const ErrorImpl<()> as *const ErrorImpl<E>))._object
+}
+
+// Safety: requires layout of *e to match ErrorImpl<E>.
+#[cfg(feature = "std")]
+unsafe fn object_mut<E>(e: &mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static)
+where
+ E: StdError + Send + Sync + 'static,
+{
+ // Attach E's native StdError vtable onto a pointer to self._object.
+ &mut (*(e as *mut ErrorImpl<()> as *mut ErrorImpl<E>))._object
+}
+
+// Safety: requires layout of *e to match ErrorImpl<E>.
+unsafe fn object_boxed<E>(e: Box<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static>
+where
+ E: StdError + Send + Sync + 'static,
+{
+ // Attach ErrorImpl<E>'s native StdError vtable. The StdError impl is below.
+ mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<E>>>(e)
+}
+
+// Safety: requires layout of *e to match ErrorImpl<E>.
+unsafe fn object_downcast<E>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>>
+where
+ E: 'static,
+{
+ if TypeId::of::<E>() == target {
+ // Caller is looking for an E pointer and e is ErrorImpl<E>, take a
+ // pointer to its E field.
+ let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<E>;
+ let addr = &(*unerased)._object as *const E as *mut ();
+ Some(NonNull::new_unchecked(addr))
+ } else {
+ None
+ }
+}
+
+// Safety: requires layout of *e to match ErrorImpl<ContextError<C, E>>.
+#[cfg(feature = "std")]
+unsafe fn context_downcast<C, E>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>>
+where
+ C: 'static,
+ E: 'static,
+{
+ if TypeId::of::<C>() == target {
+ let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, E>>;
+ let addr = &(*unerased)._object.context as *const C as *mut ();
+ Some(NonNull::new_unchecked(addr))
+ } else if TypeId::of::<E>() == target {
+ let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, E>>;
+ let addr = &(*unerased)._object.error as *const E as *mut ();
+ Some(NonNull::new_unchecked(addr))
+ } else {
+ None
+ }
+}
+
+// Safety: requires layout of *e to match ErrorImpl<ContextError<C, E>>.
+#[cfg(feature = "std")]
+unsafe fn context_drop_rest<C, E>(e: Box<ErrorImpl<()>>, target: TypeId)
+where
+ C: 'static,
+ E: 'static,
+{
+ // Called after downcasting by value to either the C or the E and doing a
+ // ptr::read to take ownership of that value.
+ if TypeId::of::<C>() == target {
+ let unerased = mem::transmute::<
+ Box<ErrorImpl<()>>,
+ Box<ErrorImpl<ContextError<ManuallyDrop<C>, E>>>,
+ >(e);
+ drop(unerased);
+ } else {
+ let unerased = mem::transmute::<
+ Box<ErrorImpl<()>>,
+ Box<ErrorImpl<ContextError<C, ManuallyDrop<E>>>>,
+ >(e);
+ drop(unerased);
+ }
+}
+
+// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>.
+unsafe fn context_chain_downcast<C>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>>
+where
+ C: 'static,
+{
+ if TypeId::of::<C>() == target {
+ let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, Error>>;
+ let addr = &(*unerased)._object.context as *const C as *mut ();
+ Some(NonNull::new_unchecked(addr))
+ } else {
+ // Recurse down the context chain per the inner error's vtable.
+ let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, Error>>;
+ let source = &(*unerased)._object.error;
+ (source.inner.vtable.object_downcast)(&source.inner, target)
+ }
+}
+
+// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>.
+unsafe fn context_chain_drop_rest<C>(e: Box<ErrorImpl<()>>, target: TypeId)
+where
+ C: 'static,
+{
+ // Called after downcasting by value to either the C or one of the causes
+ // and doing a ptr::read to take ownership of that value.
+ if TypeId::of::<C>() == target {
+ let unerased = mem::transmute::<
+ Box<ErrorImpl<()>>,
+ Box<ErrorImpl<ContextError<ManuallyDrop<C>, Error>>>,
+ >(e);
+ // Drop the entire rest of the data structure rooted in the next Error.
+ drop(unerased);
+ } else {
+ let unerased = mem::transmute::<
+ Box<ErrorImpl<()>>,
+ Box<ErrorImpl<ContextError<C, ManuallyDrop<Error>>>>,
+ >(e);
+ // Read out a ManuallyDrop<Box<ErrorImpl<()>>> from the next error.
+ let inner = ptr::read(&unerased._object.error.inner);
+ drop(unerased);
+ let erased = ManuallyDrop::into_inner(inner);
+ // Recursively drop the next error using the same target typeid.
+ (erased.vtable.object_drop_rest)(erased, target);
+ }
+}
+
+// repr C to ensure that E remains in the final position.
+#[repr(C)]
+pub(crate) struct ErrorImpl<E> {
+ vtable: &'static ErrorVTable,
+ backtrace: Option<Backtrace>,
+ // NOTE: Don't use directly. Use only through vtable. Erased type may have
+ // different alignment.
+ _object: E,
+}
+
+// repr C to ensure that ContextError<C, E> has the same layout as
+// ContextError<ManuallyDrop<C>, E> and ContextError<C, ManuallyDrop<E>>.
+#[repr(C)]
+pub(crate) struct ContextError<C, E> {
+ pub context: C,
+ pub error: E,
+}
+
+impl<E> ErrorImpl<E> {
+ fn erase(&self) -> &ErrorImpl<()> {
+ // Erase the concrete type of E but preserve the vtable in self.vtable
+ // for manipulating the resulting thin pointer. This is analogous to an
+ // unsize coersion.
+ unsafe { &*(self as *const ErrorImpl<E> as *const ErrorImpl<()>) }
+ }
+}
+
+impl ErrorImpl<()> {
+ pub(crate) fn error(&self) -> &(dyn StdError + Send + Sync + 'static) {
+ // Use vtable to attach E's native StdError vtable for the right
+ // original type E.
+ unsafe { &*(self.vtable.object_ref)(self) }
+ }
+
+ #[cfg(feature = "std")]
+ pub(crate) fn error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static) {
+ // Use vtable to attach E's native StdError vtable for the right
+ // original type E.
+ unsafe { &mut *(self.vtable.object_mut)(self) }
+ }
+
+ #[cfg(backtrace)]
+ pub(crate) fn backtrace(&self) -> &Backtrace {
+ // This unwrap can only panic if the underlying error's backtrace method
+ // is nondeterministic, which would only happen in maliciously
+ // constructed code.
+ self.backtrace
+ .as_ref()
+ .or_else(|| self.error().backtrace())
+ .expect("backtrace capture failed")
+ }
+
+ pub(crate) fn chain(&self) -> Chain {
+ Chain::new(self.error())
+ }
+}
+
+impl<E> StdError for ErrorImpl<E>
+where
+ E: StdError,
+{
+ #[cfg(backtrace)]
+ fn backtrace(&self) -> Option<&Backtrace> {
+ Some(self.erase().backtrace())
+ }
+
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ self.erase().error().source()
+ }
+}
+
+impl<E> Debug for ErrorImpl<E>
+where
+ E: Debug,
+{
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ self.erase().debug(formatter)
+ }
+}
+
+impl<E> Display for ErrorImpl<E>
+where
+ E: Display,
+{
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.erase().error(), formatter)
+ }
+}
+
+impl From<Error> for Box<dyn StdError + Send + Sync + 'static> {
+ fn from(error: Error) -> Self {
+ let outer = ManuallyDrop::new(error);
+ unsafe {
+ // Read Box<ErrorImpl<()>> from error. Can't move it out because
+ // Error has a Drop impl which we want to not run.
+ let inner = ptr::read(&outer.inner);
+ let erased = ManuallyDrop::into_inner(inner);
+
+ // Use vtable to attach ErrorImpl<E>'s native StdError vtable for
+ // the right original type E.
+ (erased.vtable.object_boxed)(erased)
+ }
+ }
+}
+
+impl From<Error> for Box<dyn StdError + 'static> {
+ fn from(error: Error) -> Self {
+ Box::<dyn StdError + Send + Sync>::from(error)
+ }
+}
+
+#[cfg(feature = "std")]
+impl AsRef<dyn StdError + Send + Sync> for Error {
+ fn as_ref(&self) -> &(dyn StdError + Send + Sync + 'static) {
+ &**self
+ }
+}
+
+#[cfg(feature = "std")]
+impl AsRef<dyn StdError> for Error {
+ fn as_ref(&self) -> &(dyn StdError + 'static) {
+ &**self
+ }
+}
diff --git a/tests/simple_deps/vendor/anyhow/src/fmt.rs b/tests/simple_deps/vendor/anyhow/src/fmt.rs
new file mode 100644
index 0000000..9579478
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/src/fmt.rs
@@ -0,0 +1,131 @@
+use crate::chain::Chain;
+use crate::error::ErrorImpl;
+use core::fmt::{self, Debug, Write};
+
+impl ErrorImpl<()> {
+ pub(crate) fn display(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.error())?;
+
+ if f.alternate() {
+ for cause in self.chain().skip(1) {
+ write!(f, ": {}", cause)?;
+ }
+ }
+
+ Ok(())
+ }
+
+ pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let error = self.error();
+
+ if f.alternate() {
+ return Debug::fmt(error, f);
+ }
+
+ write!(f, "{}", error)?;
+
+ if let Some(cause) = error.source() {
+ write!(f, "\n\nCaused by:")?;
+ let multiple = cause.source().is_some();
+ for (n, error) in Chain::new(cause).enumerate() {
+ writeln!(f)?;
+ let mut indented = Indented {
+ inner: f,
+ number: if multiple { Some(n) } else { None },
+ started: false,
+ };
+ write!(indented, "{}", error)?;
+ }
+ }
+
+ #[cfg(backtrace)]
+ {
+ use std::backtrace::BacktraceStatus;
+
+ let backtrace = self.backtrace();
+ if let BacktraceStatus::Captured = backtrace.status() {
+ let mut backtrace = backtrace.to_string();
+ if backtrace.starts_with("stack backtrace:") {
+ // Capitalize to match "Caused by:"
+ backtrace.replace_range(0..1, "S");
+ }
+ backtrace.truncate(backtrace.trim_end().len());
+ write!(f, "\n\n{}", backtrace)?;
+ }
+ }
+
+ Ok(())
+ }
+}
+
+struct Indented<'a, D> {
+ inner: &'a mut D,
+ number: Option<usize>,
+ started: bool,
+}
+
+impl<T> Write for Indented<'_, T>
+where
+ T: Write,
+{
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ for (i, line) in s.split('\n').enumerate() {
+ if !self.started {
+ self.started = true;
+ match self.number {
+ Some(number) => write!(self.inner, "{: >5}: ", number)?,
+ None => self.inner.write_str(" ")?,
+ }
+ } else if i > 0 {
+ self.inner.write_char('\n')?;
+ if self.number.is_some() {
+ self.inner.write_str(" ")?;
+ } else {
+ self.inner.write_str(" ")?;
+ }
+ }
+
+ self.inner.write_str(line)?;
+ }
+
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn one_digit() {
+ let input = "verify\nthis";
+ let expected = " 2: verify\n this";
+ let mut output = String::new();
+
+ Indented { inner: &mut output, number: Some(2), started: false }.write_str(input).unwrap();
+
+ assert_eq!(expected, output);
+ }
+
+ #[test]
+ fn two_digits() {
+ let input = "verify\nthis";
+ let expected = " 12: verify\n this";
+ let mut output = String::new();
+
+ Indented { inner: &mut output, number: Some(12), started: false }.write_str(input).unwrap();
+
+ assert_eq!(expected, output);
+ }
+
+ #[test]
+ fn no_digits() {
+ let input = "verify\nthis";
+ let expected = " verify\n this";
+ let mut output = String::new();
+
+ Indented { inner: &mut output, number: None, started: false }.write_str(input).unwrap();
+
+ assert_eq!(expected, output);
+ }
+}
diff --git a/tests/simple_deps/vendor/anyhow/src/kind.rs b/tests/simple_deps/vendor/anyhow/src/kind.rs
new file mode 100644
index 0000000..fdeb060
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/src/kind.rs
@@ -0,0 +1,116 @@
+// Tagged dispatch mechanism for resolving the behavior of `anyhow!($expr)`.
+//
+// When anyhow! is given a single expr argument to turn into anyhow::Error, we
+// want the resulting Error to pick up the input's implementation of source()
+// and backtrace() if it has a std::error::Error impl, otherwise require nothing
+// more than Display and Debug.
+//
+// Expressed in terms of specialization, we want something like:
+//
+// trait AnyhowNew {
+// fn new(self) -> Error;
+// }
+//
+// impl<T> AnyhowNew for T
+// where
+// T: Display + Debug + Send + Sync + 'static,
+// {
+// default fn new(self) -> Error {
+// /* no std error impl */
+// }
+// }
+//
+// impl<T> AnyhowNew for T
+// where
+// T: std::error::Error + Send + Sync + 'static,
+// {
+// fn new(self) -> Error {
+// /* use std error's source() and backtrace() */
+// }
+// }
+//
+// Since specialization is not stable yet, instead we rely on autoref behavior
+// of method resolution to perform tagged dispatch. Here we have two traits
+// AdhocKind and TraitKind that both have an anyhow_kind() method. AdhocKind is
+// implemented whether or not the caller's type has a std error impl, while
+// TraitKind is implemented only when a std error impl does exist. The ambiguity
+// is resolved by AdhocKind requiring an extra autoref so that it has lower
+// precedence.
+//
+// The anyhow! macro will set up the call in this form:
+//
+// #[allow(unused_imports)]
+// use $crate::private::{AdhocKind, TraitKind};
+// let error = $msg;
+// (&error).anyhow_kind().new(error)
+
+use crate::Error;
+use core::fmt::{Debug, Display};
+
+#[cfg(feature = "std")]
+use crate::StdError;
+
+#[cfg(backtrace)]
+use std::backtrace::Backtrace;
+
+pub struct Adhoc;
+
+pub trait AdhocKind: Sized {
+ #[inline]
+ fn anyhow_kind(&self) -> Adhoc {
+ Adhoc
+ }
+}
+
+impl<T> AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {}
+
+impl Adhoc {
+ pub fn new<M>(self, message: M) -> Error
+ where
+ M: Display + Debug + Send + Sync + 'static,
+ {
+ Error::from_adhoc(message, backtrace!())
+ }
+}
+
+pub struct Trait;
+
+pub trait TraitKind: Sized {
+ #[inline]
+ fn anyhow_kind(&self) -> Trait {
+ Trait
+ }
+}
+
+impl<E> TraitKind for E where E: Into<Error> {}
+
+impl Trait {
+ pub fn new<E>(self, error: E) -> Error
+ where
+ E: Into<Error>,
+ {
+ error.into()
+ }
+}
+
+#[cfg(feature = "std")]
+pub struct Boxed;
+
+#[cfg(feature = "std")]
+pub trait BoxedKind: Sized {
+ #[inline]
+ fn anyhow_kind(&self) -> Boxed {
+ Boxed
+ }
+}
+
+#[cfg(feature = "std")]
+impl BoxedKind for Box<dyn StdError + Send + Sync> {}
+
+#[cfg(feature = "std")]
+impl Boxed {
+ pub fn new(self, error: Box<dyn StdError + Send + Sync>) -> Error {
+ let backtrace = backtrace_if_absent!(error);
+ Error::from_boxed(error, backtrace)
+ }
+}
diff --git a/tests/simple_deps/vendor/anyhow/src/lib.rs b/tests/simple_deps/vendor/anyhow/src/lib.rs
new file mode 100644
index 0000000..3e49658
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/src/lib.rs
@@ -0,0 +1,587 @@
+//! This library provides [`anyhow::Error`][Error], a trait object based error
+//! type for easy idiomatic error handling in Rust applications.
+//!
+//! <br>
+//!
+//! # Details
+//!
+//! - Use `Result<T, anyhow::Error>`, or equivalently `anyhow::Result<T>`, as
+//! the return type of any fallible function.
+//!
+//! Within the function, use `?` to easily propagate any error that implements
+//! the `std::error::Error` trait.
+//!
+//! ```
+//! # pub trait Deserialize {}
+//! #
+//! # mod serde_json {
+//! # use super::Deserialize;
+//! # use std::io;
+//! #
+//! # pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> {
+//! # unimplemented!()
+//! # }
+//! # }
+//! #
+//! # struct ClusterMap;
+//! #
+//! # impl Deserialize for ClusterMap {}
+//! #
+//! use anyhow::Result;
+//!
+//! fn get_cluster_info() -> Result<ClusterMap> {
+//! let config = std::fs::read_to_string("cluster.json")?;
+//! let map: ClusterMap = serde_json::from_str(&config)?;
+//! Ok(map)
+//! }
+//! #
+//! # fn main() {}
+//! ```
+//!
+//! - Attach context to help the person troubleshooting the error understand
+//! where things went wrong. A low-level error like "No such file or
+//! directory" can be annoying to debug without more context about what higher
+//! level step the application was in the middle of.
+//!
+//! ```
+//! # struct It;
+//! #
+//! # impl It {
+//! # fn detach(&self) -> Result<()> {
+//! # unimplemented!()
+//! # }
+//! # }
+//! #
+//! use anyhow::{Context, Result};
+//!
+//! fn main() -> Result<()> {
+//! # return Ok(());
+//! #
+//! # const _: &str = stringify! {
+//! ...
+//! # };
+//! #
+//! # let it = It;
+//! # let path = "./path/to/instrs.json";
+//! #
+//! it.detach().context("Failed to detach the important thing")?;
+//!
+//! let content = std::fs::read(path)
+//! .with_context(|| format!("Failed to read instrs from {}", path))?;
+//! #
+//! # const _: &str = stringify! {
+//! ...
+//! # };
+//! #
+//! # Ok(())
+//! }
+//! ```
+//!
+//! ```console
+//! Error: Failed to read instrs from ./path/to/instrs.json
+//!
+//! Caused by:
+//! No such file or directory (os error 2)
+//! ```
+//!
+//! - Downcasting is supported and can be by value, by shared reference, or by
+//! mutable reference as needed.
+//!
+//! ```
+//! # use anyhow::anyhow;
+//! # use std::fmt::{self, Display};
+//! # use std::task::Poll;
+//! #
+//! # #[derive(Debug)]
+//! # enum DataStoreError {
+//! # Censored(()),
+//! # }
+//! #
+//! # impl Display for DataStoreError {
+//! # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+//! # unimplemented!()
+//! # }
+//! # }
+//! #
+//! # impl std::error::Error for DataStoreError {}
+//! #
+//! # const REDACTED_CONTENT: () = ();
+//! #
+//! # let error = anyhow!("...");
+//! # let root_cause = &error;
+//! #
+//! # let ret =
+//! // If the error was caused by redaction, then return a
+//! // tombstone instead of the content.
+//! match root_cause.downcast_ref::<DataStoreError>() {
+//! Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)),
+//! None => Err(error),
+//! }
+//! # ;
+//! ```
+//!
+//! - A backtrace is captured and printed with the error if the underlying error
+//! type does not already provide its own. In order to see backtraces, they
+//! must be enabled through the environment variables described in
+//! [`std::backtrace`]:
+//!
+//! - If you want panics and errors to both have backtraces, set
+//! `RUST_BACKTRACE=1`;
+//! - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`;
+//! - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and
+//! `RUST_LIB_BACKTRACE=0`.
+//!
+//! [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables
+//!
+//! - Anyhow works with any error type that has an impl of `std::error::Error`,
+//! including ones defined in your crate. We do not bundle a `derive(Error)`
+//! macro but you can write the impls yourself or use a standalone macro like
+//! [thiserror].
+//!
+//! [thiserror]: https://github.com/dtolnay/thiserror
+//!
+//! ```
+//! use thiserror::Error;
+//!
+//! #[derive(Error, Debug)]
+//! pub enum FormatError {
+//! #[error("Invalid header (expected {expected:?}, got {found:?})")]
+//! InvalidHeader {
+//! expected: String,
+//! found: String,
+//! },
+//! #[error("Missing attribute: {0}")]
+//! MissingAttribute(String),
+//! }
+//! ```
+//!
+//! - One-off error messages can be constructed using the `anyhow!` macro, which
+//! supports string interpolation and produces an `anyhow::Error`.
+//!
+//! ```
+//! # use anyhow::{anyhow, Result};
+//! #
+//! # fn demo() -> Result<()> {
+//! # let missing = "...";
+//! return Err(anyhow!("Missing attribute: {}", missing));
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! <br>
+//!
+//! # No-std support
+//!
+//! In no_std mode, the same API is almost all available and works the same way.
+//! To depend on Anyhow in no_std mode, disable our default enabled "std"
+//! feature in Cargo.toml. A global allocator is required.
+//!
+//! ```toml
+//! [dependencies]
+//! anyhow = { version = "1.0", default-features = false }
+//! ```
+//!
+//! Since the `?`-based error conversions would normally rely on the
+//! `std::error::Error` trait which is only available through std, no_std mode
+//! will require an explicit `.map_err(Error::msg)` when working with a
+//! non-Anyhow error type inside a function that returns Anyhow's error type.
+
+#![doc(html_root_url = "https://docs.rs/anyhow/1.0.27")]
+#![cfg_attr(backtrace, feature(backtrace))]
+#![cfg_attr(not(feature = "std"), no_std)]
+#![allow(clippy::needless_doctest_main, clippy::new_ret_no_self, clippy::wrong_self_convention)]
+
+mod alloc {
+ #[cfg(not(feature = "std"))]
+ extern crate alloc;
+
+ #[cfg(not(feature = "std"))]
+ pub use alloc::boxed::Box;
+
+ #[cfg(feature = "std")]
+ pub use std::boxed::Box;
+}
+
+#[macro_use]
+mod backtrace;
+mod chain;
+mod context;
+mod error;
+mod fmt;
+mod kind;
+mod macros;
+mod wrapper;
+
+use crate::alloc::Box;
+use crate::error::ErrorImpl;
+use core::fmt::Display;
+use core::mem::ManuallyDrop;
+
+#[cfg(not(feature = "std"))]
+use core::fmt::Debug;
+
+#[cfg(feature = "std")]
+use std::error::Error as StdError;
+
+#[cfg(not(feature = "std"))]
+trait StdError: Debug + Display {
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ None
+ }
+}
+
+pub use anyhow as format_err;
+
+/// The `Error` type, a wrapper around a dynamic error type.
+///
+/// `Error` works a lot like `Box<dyn std::error::Error>`, but with these
+/// differences:
+///
+/// - `Error` requires that the error is `Send`, `Sync`, and `'static`.
+/// - `Error` guarantees that a backtrace is available, even if the underlying
+/// error type does not provide one.
+/// - `Error` is represented as a narrow pointer — exactly one word in
+/// size instead of two.
+///
+/// <br>
+///
+/// # Display representations
+///
+/// When you print an error object using "{}" or to_string(), only the outermost
+/// underlying error or context is printed, not any of the lower level causes.
+/// This is exactly as if you had called the Display impl of the error from
+/// which you constructed your anyhow::Error.
+///
+/// ```console
+/// Failed to read instrs from ./path/to/instrs.json
+/// ```
+///
+/// To print causes as well using anyhow's default formatting of causes, use the
+/// alternate selector "{:#}".
+///
+/// ```console
+/// Failed to read instrs from ./path/to/instrs.json: No such file or directory (os error 2)
+/// ```
+///
+/// The Debug format "{:?}" includes your backtrace if one was captured. Note
+/// that this is the representation you get by default if you return an error
+/// from `fn main` instead of printing it explicitly yourself.
+///
+/// ```console
+/// Error: Failed to read instrs from ./path/to/instrs.json
+///
+/// Caused by:
+/// No such file or directory (os error 2)
+///
+/// Stack backtrace:
+/// 0: <E as anyhow::context::ext::StdError>::ext_context
+/// at /git/anyhow/src/backtrace.rs:26
+/// 1: core::result::Result<T,E>::map_err
+/// at /git/rustc/src/libcore/result.rs:596
+/// 2: anyhow::context::<impl anyhow::Context<T,E> for core::result::Result<T,E>>::with_context
+/// at /git/anyhow/src/context.rs:58
+/// 3: testing::main
+/// at src/main.rs:5
+/// 4: std::rt::lang_start
+/// at /git/rustc/src/libstd/rt.rs:61
+/// 5: main
+/// 6: __libc_start_main
+/// 7: _start
+/// ```
+///
+/// To see a conventional struct-style Debug representation, use "{:#?}".
+///
+/// ```console
+/// Error {
+/// context: "Failed to read instrs from ./path/to/instrs.json",
+/// source: Os {
+/// code: 2,
+/// kind: NotFound,
+/// message: "No such file or directory",
+/// },
+/// }
+/// ```
+///
+/// If none of the built-in representations are appropriate and you would prefer
+/// to render the error and its cause chain yourself, it can be done something
+/// like this:
+///
+/// ```
+/// use anyhow::{Context, Result};
+///
+/// fn main() {
+/// if let Err(err) = try_main() {
+/// eprintln!("ERROR: {}", err);
+/// err.chain().skip(1).for_each(|cause| eprintln!("because: {}", cause));
+/// std::process::exit(1);
+/// }
+/// }
+///
+/// fn try_main() -> Result<()> {
+/// # const IGNORE: &str = stringify! {
+/// ...
+/// # };
+/// # Ok(())
+/// }
+/// ```
+pub struct Error {
+ inner: ManuallyDrop<Box<ErrorImpl<()>>>,
+}
+
+/// Iterator of a chain of source errors.
+///
+/// This type is the iterator returned by [`Error::chain`].
+///
+/// # Example
+///
+/// ```
+/// use anyhow::Error;
+/// use std::io;
+///
+/// pub fn underlying_io_error_kind(error: &Error) -> Option<io::ErrorKind> {
+/// for cause in error.chain() {
+/// if let Some(io_error) = cause.downcast_ref::<io::Error>() {
+/// return Some(io_error.kind());
+/// }
+/// }
+/// None
+/// }
+/// ```
+#[cfg(feature = "std")]
+#[derive(Clone)]
+pub struct Chain<'a> {
+ state: crate::chain::ChainState<'a>,
+}
+
+/// `Result<T, Error>`
+///
+/// This is a reasonable return type to use throughout your application but also
+/// for `fn main`; if you do, failures will be printed along with any
+/// [context][Context] and a backtrace if one was captured.
+///
+/// `anyhow::Result` may be used with one *or* two type parameters.
+///
+/// ```rust
+/// use anyhow::Result;
+///
+/// # const IGNORE: &str = stringify! {
+/// fn demo1() -> Result<T> {...}
+/// // ^ equivalent to std::result::Result<T, anyhow::Error>
+///
+/// fn demo2() -> Result<T, OtherError> {...}
+/// // ^ equivalent to std::result::Result<T, OtherError>
+/// # };
+/// ```
+///
+/// # Example
+///
+/// ```
+/// # pub trait Deserialize {}
+/// #
+/// # mod serde_json {
+/// # use super::Deserialize;
+/// # use std::io;
+/// #
+/// # pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> {
+/// # unimplemented!()
+/// # }
+/// # }
+/// #
+/// # #[derive(Debug)]
+/// # struct ClusterMap;
+/// #
+/// # impl Deserialize for ClusterMap {}
+/// #
+/// use anyhow::Result;
+///
+/// fn main() -> Result<()> {
+/// # return Ok(());
+/// let config = std::fs::read_to_string("cluster.json")?;
+/// let map: ClusterMap = serde_json::from_str(&config)?;
+/// println!("cluster info: {:#?}", map);
+/// Ok(())
+/// }
+/// ```
+pub type Result<T, E = Error> = core::result::Result<T, E>;
+
+/// Provides the `context` method for `Result`.
+///
+/// This trait is sealed and cannot be implemented for types outside of
+/// `anyhow`.
+///
+/// <br>
+///
+/// # Example
+///
+/// ```
+/// use anyhow::{Context, Result};
+/// use std::fs;
+/// use std::path::PathBuf;
+///
+/// pub struct ImportantThing {
+/// path: PathBuf,
+/// }
+///
+/// impl ImportantThing {
+/// # const IGNORE: &'static str = stringify! {
+/// pub fn detach(&mut self) -> Result<()> {...}
+/// # };
+/// # fn detach(&mut self) -> Result<()> {
+/// # unimplemented!()
+/// # }
+/// }
+///
+/// pub fn do_it(mut it: ImportantThing) -> Result<Vec<u8>> {
+/// it.detach().context("Failed to detach the important thing")?;
+///
+/// let path = &it.path;
+/// let content = fs::read(path)
+/// .with_context(|| format!("Failed to read instrs from {}", path.display()))?;
+///
+/// Ok(content)
+/// }
+/// ```
+///
+/// When printed, the outermost context would be printed first and the lower
+/// level underlying causes would be enumerated below.
+///
+/// ```console
+/// Error: Failed to read instrs from ./path/to/instrs.json
+///
+/// Caused by:
+/// No such file or directory (os error 2)
+/// ```
+///
+/// <br>
+///
+/// # Effect on downcasting
+///
+/// After attaching context of type `C` onto an error of type `E`, the resulting
+/// `anyhow::Error` may be downcast to `C` **or** to `E`.
+///
+/// That is, in codebases that rely on downcasting, Anyhow's context supports
+/// both of the following use cases:
+///
+/// - **Attaching context whose type is insignificant onto errors whose type
+/// is used in downcasts.**
+///
+/// In other error libraries whose context is not designed this way, it can
+/// be risky to introduce context to existing code because new context might
+/// break existing working downcasts. In Anyhow, any downcast that worked
+/// before adding context will continue to work after you add a context, so
+/// you should freely add human-readable context to errors wherever it would
+/// be helpful.
+///
+/// ```
+/// # use anyhow::bail;
+/// # use thiserror::Error;
+/// #
+/// # #[derive(Error, Debug)]
+/// # #[error("???")]
+/// # struct SuspiciousError;
+/// #
+/// # fn helper() -> Result<()> {
+/// # bail!(SuspiciousError);
+/// # }
+/// #
+/// use anyhow::{Context, Result};
+///
+/// fn do_it() -> Result<()> {
+/// helper().context("Failed to complete the work")?;
+/// # const IGNORE: &str = stringify! {
+/// ...
+/// # };
+/// # unreachable!()
+/// }
+///
+/// fn main() {
+/// let err = do_it().unwrap_err();
+/// if let Some(e) = err.downcast_ref::<SuspiciousError>() {
+/// // If helper() returned SuspiciousError, this downcast will
+/// // correctly succeed even with the context in between.
+/// # return;
+/// }
+/// # panic!("expected downcast to succeed");
+/// }
+/// ```
+///
+/// - **Attaching context whose type is used in downcasts onto errors whose
+/// type is insignificant.**
+///
+/// Some codebases prefer to use machine-readable context to categorize
+/// lower level errors in a way that will be actionable to higher levels of
+/// the application.
+///
+/// ```
+/// # use anyhow::bail;
+/// # use thiserror::Error;
+/// #
+/// # #[derive(Error, Debug)]
+/// # #[error("???")]
+/// # struct HelperFailed;
+/// #
+/// # fn helper() -> Result<()> {
+/// # bail!("no such file or directory");
+/// # }
+/// #
+/// use anyhow::{Context, Result};
+///
+/// fn do_it() -> Result<()> {
+/// helper().context(HelperFailed)?;
+/// # const IGNORE: &str = stringify! {
+/// ...
+/// # };
+/// # unreachable!()
+/// }
+///
+/// fn main() {
+/// let err = do_it().unwrap_err();
+/// if let Some(e) = err.downcast_ref::<HelperFailed>() {
+/// // If helper failed, this downcast will succeed because
+/// // HelperFailed is the context that has been attached to
+/// // that error.
+/// # return;
+/// }
+/// # panic!("expected downcast to succeed");
+/// }
+/// ```
+pub trait Context<T, E>: context::private::Sealed {
+ /// Wrap the error value with additional context.
+ fn context<C>(self, context: C) -> Result<T, Error>
+ where
+ C: Display + Send + Sync + 'static;
+
+ /// Wrap the error value with additional context that is evaluated lazily
+ /// only once an error does occur.
+ fn with_context<C, F>(self, f: F) -> Result<T, Error>
+ where
+ C: Display + Send + Sync + 'static,
+ F: FnOnce() -> C;
+}
+
+// Not public API. Referenced by macro-generated code.
+#[doc(hidden)]
+pub mod private {
+ use crate::Error;
+ use core::fmt::{Debug, Display};
+
+ #[cfg(backtrace)]
+ use std::backtrace::Backtrace;
+
+ pub use core::result::Result::Err;
+
+ #[doc(hidden)]
+ pub mod kind {
+ pub use crate::kind::{AdhocKind, TraitKind};
+
+ #[cfg(feature = "std")]
+ pub use crate::kind::BoxedKind;
+ }
+
+ pub fn new_adhoc<M>(message: M) -> Error
+ where
+ M: Display + Debug + Send + Sync + 'static,
+ {
+ Error::from_adhoc(message, backtrace!())
+ }
+}
diff --git a/tests/simple_deps/vendor/anyhow/src/macros.rs b/tests/simple_deps/vendor/anyhow/src/macros.rs
new file mode 100644
index 0000000..15a9208
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/src/macros.rs
@@ -0,0 +1,163 @@
+/// Return early with an error.
+///
+/// This macro is equivalent to `return Err(From::from($err))`.
+///
+/// # Example
+///
+/// ```
+/// # use anyhow::{bail, Result};
+/// #
+/// # fn has_permission(user: usize, resource: usize) -> bool {
+/// # true
+/// # }
+/// #
+/// # fn main() -> Result<()> {
+/// # let user = 0;
+/// # let resource = 0;
+/// #
+/// if !has_permission(user, resource) {
+/// bail!("permission denied for accessing {}", resource);
+/// }
+/// # Ok(())
+/// # }
+/// ```
+///
+/// ```
+/// # use anyhow::{bail, Result};
+/// # use thiserror::Error;
+/// #
+/// # const MAX_DEPTH: usize = 1;
+/// #
+/// #[derive(Error, Debug)]
+/// enum ScienceError {
+/// #[error("recursion limit exceeded")]
+/// RecursionLimitExceeded,
+/// # #[error("...")]
+/// # More = (stringify! {
+/// ...
+/// # }, 1).1,
+/// }
+///
+/// # fn main() -> Result<()> {
+/// # let depth = 0;
+/// #
+/// if depth > MAX_DEPTH {
+/// bail!(ScienceError::RecursionLimitExceeded);
+/// }
+/// # Ok(())
+/// # }
+/// ```
+#[macro_export]
+macro_rules! bail {
+ ($msg:literal $(,)?) => {
+ return $crate::private::Err($crate::anyhow!($msg));
+ };
+ ($err:expr $(,)?) => {
+ return $crate::private::Err($crate::anyhow!($err));
+ };
+ ($fmt:expr, $($arg:tt)*) => {
+ return $crate::private::Err($crate::anyhow!($fmt, $($arg)*));
+ };
+}
+
+/// Return early with an error if a condition is not satisfied.
+///
+/// This macro is equivalent to `if !$cond { return Err(From::from($err)); }`.
+///
+/// Analogously to `assert!`, `ensure!` takes a condition and exits the function
+/// if the condition fails. Unlike `assert!`, `ensure!` returns an `Error`
+/// rather than panicking.
+///
+/// # Example
+///
+/// ```
+/// # use anyhow::{ensure, Result};
+/// #
+/// # fn main() -> Result<()> {
+/// # let user = 0;
+/// #
+/// ensure!(user == 0, "only user 0 is allowed");
+/// # Ok(())
+/// # }
+/// ```
+///
+/// ```
+/// # use anyhow::{ensure, Result};
+/// # use thiserror::Error;
+/// #
+/// # const MAX_DEPTH: usize = 1;
+/// #
+/// #[derive(Error, Debug)]
+/// enum ScienceError {
+/// #[error("recursion limit exceeded")]
+/// RecursionLimitExceeded,
+/// # #[error("...")]
+/// # More = (stringify! {
+/// ...
+/// # }, 1).1,
+/// }
+///
+/// # fn main() -> Result<()> {
+/// # let depth = 0;
+/// #
+/// ensure!(depth <= MAX_DEPTH, ScienceError::RecursionLimitExceeded);
+/// # Ok(())
+/// # }
+/// ```
+#[macro_export]
+macro_rules! ensure {
+ ($cond:expr, $msg:literal $(,)?) => {
+ if !$cond {
+ return $crate::private::Err($crate::anyhow!($msg));
+ }
+ };
+ ($cond:expr, $err:expr $(,)?) => {
+ if !$cond {
+ return $crate::private::Err($crate::anyhow!($err));
+ }
+ };
+ ($cond:expr, $fmt:expr, $($arg:tt)*) => {
+ if !$cond {
+ return $crate::private::Err($crate::anyhow!($fmt, $($arg)*));
+ }
+ };
+}
+
+/// Construct an ad-hoc error from a string.
+///
+/// This evaluates to an `Error`. It can take either just a string, or a format
+/// string with arguments. It also can take any custom type which implements
+/// `Debug` and `Display`.
+///
+/// # Example
+///
+/// ```
+/// # type V = ();
+/// #
+/// use anyhow::{anyhow, Result};
+///
+/// fn lookup(key: &str) -> Result<V> {
+/// if key.len() != 16 {
+/// return Err(anyhow!("key length must be 16 characters, got {:?}", key));
+/// }
+///
+/// // ...
+/// # Ok(())
+/// }
+/// ```
+#[macro_export]
+macro_rules! anyhow {
+ ($msg:literal $(,)?) => {
+ // Handle $:literal as a special case to make cargo-expanded code more
+ // concise in the common case.
+ $crate::private::new_adhoc($msg)
+ };
+ ($err:expr $(,)?) => ({
+ use $crate::private::kind::*;
+ let error = $err;
+ (&error).anyhow_kind().new(error)
+ });
+ ($fmt:expr, $($arg:tt)*) => {
+ $crate::private::new_adhoc(format!($fmt, $($arg)*))
+ };
+}
diff --git a/tests/simple_deps/vendor/anyhow/src/wrapper.rs b/tests/simple_deps/vendor/anyhow/src/wrapper.rs
new file mode 100644
index 0000000..3ebe51a
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/src/wrapper.rs
@@ -0,0 +1,78 @@
+use crate::StdError;
+use core::fmt::{self, Debug, Display};
+
+#[repr(transparent)]
+pub struct MessageError<M>(pub M);
+
+impl<M> Debug for MessageError<M>
+where
+ M: Display + Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Debug::fmt(&self.0, f)
+ }
+}
+
+impl<M> Display for MessageError<M>
+where
+ M: Display + Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.0, f)
+ }
+}
+
+impl<M> StdError for MessageError<M> where M: Display + Debug + 'static {}
+
+#[repr(transparent)]
+pub struct DisplayError<M>(pub M);
+
+impl<M> Debug for DisplayError<M>
+where
+ M: Display,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.0, f)
+ }
+}
+
+impl<M> Display for DisplayError<M>
+where
+ M: Display,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.0, f)
+ }
+}
+
+impl<M> StdError for DisplayError<M> where M: Display + 'static {}
+
+#[cfg(feature = "std")]
+#[repr(transparent)]
+pub struct BoxedError(pub Box<dyn StdError + Send + Sync>);
+
+#[cfg(feature = "std")]
+impl Debug for BoxedError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Debug::fmt(&self.0, f)
+ }
+}
+
+#[cfg(feature = "std")]
+impl Display for BoxedError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.0, f)
+ }
+}
+
+#[cfg(feature = "std")]
+impl StdError for BoxedError {
+ #[cfg(backtrace)]
+ fn backtrace(&self) -> Option<&crate::backtrace::Backtrace> {
+ self.0.backtrace()
+ }
+
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ self.0.source()
+ }
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/common/mod.rs b/tests/simple_deps/vendor/anyhow/tests/common/mod.rs
new file mode 100644
index 0000000..fc165a5
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/common/mod.rs
@@ -0,0 +1,14 @@
+use anyhow::{bail, Result};
+use std::io;
+
+pub fn bail_literal() -> Result<()> {
+ bail!("oh no!");
+}
+
+pub fn bail_fmt() -> Result<()> {
+ bail!("{} {}!", "oh", "no");
+}
+
+pub fn bail_error() -> Result<()> {
+ bail!(io::Error::new(io::ErrorKind::Other, "oh no!"));
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/compiletest.rs b/tests/simple_deps/vendor/anyhow/tests/compiletest.rs
new file mode 100644
index 0000000..f9aea23
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/compiletest.rs
@@ -0,0 +1,6 @@
+#[rustversion::attr(not(nightly), ignore)]
+#[test]
+fn ui() {
+ let t = trybuild::TestCases::new();
+ t.compile_fail("tests/ui/*.rs");
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/drop/mod.rs b/tests/simple_deps/vendor/anyhow/tests/drop/mod.rs
new file mode 100644
index 0000000..1204011
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/drop/mod.rs
@@ -0,0 +1,46 @@
+use std::error::Error as StdError;
+use std::fmt::{self, Display};
+use std::sync::atomic::AtomicBool;
+use std::sync::atomic::Ordering::SeqCst;
+use std::sync::Arc;
+
+#[derive(Debug)]
+pub struct Flag {
+ atomic: Arc<AtomicBool>,
+}
+
+impl Flag {
+ pub fn new() -> Self {
+ Flag { atomic: Arc::new(AtomicBool::new(false)) }
+ }
+
+ pub fn get(&self) -> bool {
+ self.atomic.load(SeqCst)
+ }
+}
+
+#[derive(Debug)]
+pub struct DetectDrop {
+ has_dropped: Flag,
+}
+
+impl DetectDrop {
+ pub fn new(has_dropped: &Flag) -> Self {
+ DetectDrop { has_dropped: Flag { atomic: Arc::clone(&has_dropped.atomic) } }
+ }
+}
+
+impl StdError for DetectDrop {}
+
+impl Display for DetectDrop {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "oh no!")
+ }
+}
+
+impl Drop for DetectDrop {
+ fn drop(&mut self) {
+ let already_dropped = self.has_dropped.atomic.swap(true, SeqCst);
+ assert!(!already_dropped);
+ }
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_autotrait.rs b/tests/simple_deps/vendor/anyhow/tests/test_autotrait.rs
new file mode 100644
index 0000000..0c9326d
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/test_autotrait.rs
@@ -0,0 +1,13 @@
+use anyhow::Error;
+
+#[test]
+fn test_send() {
+ fn assert_send<T: Send>() {}
+ assert_send::<Error>();
+}
+
+#[test]
+fn test_sync() {
+ fn assert_sync<T: Sync>() {}
+ assert_sync::<Error>();
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_backtrace.rs b/tests/simple_deps/vendor/anyhow/tests/test_backtrace.rs
new file mode 100644
index 0000000..ce385f5
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/test_backtrace.rs
@@ -0,0 +1,13 @@
+#[rustversion::not(nightly)]
+#[ignore]
+#[test]
+fn test_backtrace() {}
+
+#[rustversion::nightly]
+#[test]
+fn test_backtrace() {
+ use anyhow::anyhow;
+
+ let error = anyhow!("oh no!");
+ let _ = error.backtrace();
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_boxed.rs b/tests/simple_deps/vendor/anyhow/tests/test_boxed.rs
new file mode 100644
index 0000000..851dcbb
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/test_boxed.rs
@@ -0,0 +1,35 @@
+use anyhow::anyhow;
+use std::error::Error as StdError;
+use std::io;
+use thiserror::Error;
+
+#[derive(Error, Debug)]
+#[error("outer")]
+struct MyError {
+ source: io::Error,
+}
+
+#[test]
+fn test_boxed_str() {
+ let error = Box::<dyn StdError + Send + Sync>::from("oh no!");
+ let error = anyhow!(error);
+ assert_eq!("oh no!", error.to_string());
+ assert_eq!(
+ "oh no!",
+ error.downcast_ref::<Box<dyn StdError + Send + Sync>>().unwrap().to_string()
+ );
+}
+
+#[test]
+fn test_boxed_thiserror() {
+ let error = MyError { source: io::Error::new(io::ErrorKind::Other, "oh no!") };
+ let error = anyhow!(error);
+ assert_eq!("oh no!", error.source().unwrap().to_string());
+}
+
+#[test]
+fn test_boxed_anyhow() {
+ let error = anyhow!("oh no!").context("it failed");
+ let error = anyhow!(error);
+ assert_eq!("oh no!", error.source().unwrap().to_string());
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_chain.rs b/tests/simple_deps/vendor/anyhow/tests/test_chain.rs
new file mode 100644
index 0000000..b1c5a3d
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/test_chain.rs
@@ -0,0 +1,45 @@
+use anyhow::{anyhow, Error};
+
+fn error() -> Error {
+ anyhow!(0).context(1).context(2).context(3)
+}
+
+#[test]
+fn test_iter() {
+ let e = error();
+ let mut chain = e.chain();
+ assert_eq!("3", chain.next().unwrap().to_string());
+ assert_eq!("2", chain.next().unwrap().to_string());
+ assert_eq!("1", chain.next().unwrap().to_string());
+ assert_eq!("0", chain.next().unwrap().to_string());
+ assert!(chain.next().is_none());
+ assert!(chain.next_back().is_none());
+}
+
+#[test]
+fn test_rev() {
+ let e = error();
+ let mut chain = e.chain().rev();
+ assert_eq!("0", chain.next().unwrap().to_string());
+ assert_eq!("1", chain.next().unwrap().to_string());
+ assert_eq!("2", chain.next().unwrap().to_string());
+ assert_eq!("3", chain.next().unwrap().to_string());
+ assert!(chain.next().is_none());
+ assert!(chain.next_back().is_none());
+}
+
+#[test]
+fn test_len() {
+ let e = error();
+ let mut chain = e.chain();
+ assert_eq!(4, chain.len());
+ assert_eq!("3", chain.next().unwrap().to_string());
+ assert_eq!(3, chain.len());
+ assert_eq!("0", chain.next_back().unwrap().to_string());
+ assert_eq!(2, chain.len());
+ assert_eq!("2", chain.next().unwrap().to_string());
+ assert_eq!(1, chain.len());
+ assert_eq!("1", chain.next_back().unwrap().to_string());
+ assert_eq!(0, chain.len());
+ assert!(chain.next().is_none());
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_context.rs b/tests/simple_deps/vendor/anyhow/tests/test_context.rs
new file mode 100644
index 0000000..4bd1ffe
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/test_context.rs
@@ -0,0 +1,150 @@
+mod drop;
+
+use crate::drop::{DetectDrop, Flag};
+use anyhow::{Context, Error, Result};
+use std::fmt::{self, Display};
+use thiserror::Error;
+
+// https://github.com/dtolnay/anyhow/issues/18
+#[test]
+fn test_inference() -> Result<()> {
+ let x = "1";
+ let y: u32 = x.parse().context("...")?;
+ assert_eq!(y, 1);
+ Ok(())
+}
+
+macro_rules! context_type {
+ ($name:ident) => {
+ #[derive(Debug)]
+ struct $name {
+ message: &'static str,
+ drop: DetectDrop,
+ }
+
+ impl Display for $name {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(self.message)
+ }
+ }
+ };
+}
+
+context_type!(HighLevel);
+context_type!(MidLevel);
+
+#[derive(Error, Debug)]
+#[error("{message}")]
+struct LowLevel {
+ message: &'static str,
+ drop: DetectDrop,
+}
+
+struct Dropped {
+ low: Flag,
+ mid: Flag,
+ high: Flag,
+}
+
+impl Dropped {
+ fn none(&self) -> bool {
+ !self.low.get() && !self.mid.get() && !self.high.get()
+ }
+
+ fn all(&self) -> bool {
+ self.low.get() && self.mid.get() && self.high.get()
+ }
+}
+
+fn make_chain() -> (Error, Dropped) {
+ let dropped = Dropped { low: Flag::new(), mid: Flag::new(), high: Flag::new() };
+
+ let low =
+ LowLevel { message: "no such file or directory", drop: DetectDrop::new(&dropped.low) };
+
+ // impl Context for Result<T, E>
+ let mid = Err::<(), LowLevel>(low)
+ .context(MidLevel { message: "failed to load config", drop: DetectDrop::new(&dropped.mid) })
+ .unwrap_err();
+
+ // impl Context for Result<T, Error>
+ let high = Err::<(), Error>(mid)
+ .context(HighLevel {
+ message: "failed to start server",
+ drop: DetectDrop::new(&dropped.high),
+ })
+ .unwrap_err();
+
+ (high, dropped)
+}
+
+#[test]
+fn test_downcast_ref() {
+ let (err, dropped) = make_chain();
+
+ assert!(!err.is::<String>());
+ assert!(err.downcast_ref::<String>().is_none());
+
+ assert!(err.is::<HighLevel>());
+ let high = err.downcast_ref::<HighLevel>().unwrap();
+ assert_eq!(high.to_string(), "failed to start server");
+
+ assert!(err.is::<MidLevel>());
+ let mid = err.downcast_ref::<MidLevel>().unwrap();
+ assert_eq!(mid.to_string(), "failed to load config");
+
+ assert!(err.is::<LowLevel>());
+ let low = err.downcast_ref::<LowLevel>().unwrap();
+ assert_eq!(low.to_string(), "no such file or directory");
+
+ assert!(dropped.none());
+ drop(err);
+ assert!(dropped.all());
+}
+
+#[test]
+fn test_downcast_high() {
+ let (err, dropped) = make_chain();
+
+ let err = err.downcast::<HighLevel>().unwrap();
+ assert!(!dropped.high.get());
+ assert!(dropped.low.get() && dropped.mid.get());
+
+ drop(err);
+ assert!(dropped.all());
+}
+
+#[test]
+fn test_downcast_mid() {
+ let (err, dropped) = make_chain();
+
+ let err = err.downcast::<MidLevel>().unwrap();
+ assert!(!dropped.mid.get());
+ assert!(dropped.low.get() && dropped.high.get());
+
+ drop(err);
+ assert!(dropped.all());
+}
+
+#[test]
+fn test_downcast_low() {
+ let (err, dropped) = make_chain();
+
+ let err = err.downcast::<LowLevel>().unwrap();
+ assert!(!dropped.low.get());
+ assert!(dropped.mid.get() && dropped.high.get());
+
+ drop(err);
+ assert!(dropped.all());
+}
+
+#[test]
+fn test_unsuccessful_downcast() {
+ let (err, dropped) = make_chain();
+
+ let err = err.downcast::<String>().unwrap_err();
+ assert!(dropped.none());
+
+ drop(err);
+ assert!(dropped.all());
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_convert.rs b/tests/simple_deps/vendor/anyhow/tests/test_convert.rs
new file mode 100644
index 0000000..72da020
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/test_convert.rs
@@ -0,0 +1,24 @@
+mod drop;
+
+use self::drop::{DetectDrop, Flag};
+use anyhow::{Error, Result};
+use std::error::Error as StdError;
+
+#[test]
+fn test_convert() {
+ let has_dropped = Flag::new();
+ let error = Error::new(DetectDrop::new(&has_dropped));
+ let box_dyn = Box::<dyn StdError + Send + Sync>::from(error);
+ assert_eq!("oh no!", box_dyn.to_string());
+ drop(box_dyn);
+ assert!(has_dropped.get());
+}
+
+#[test]
+fn test_question_mark() -> Result<(), Box<dyn StdError>> {
+ fn f() -> Result<()> {
+ Ok(())
+ }
+ f()?;
+ Ok(())
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_downcast.rs b/tests/simple_deps/vendor/anyhow/tests/test_downcast.rs
new file mode 100644
index 0000000..c4393e8
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/test_downcast.rs
@@ -0,0 +1,70 @@
+mod common;
+mod drop;
+
+use self::common::*;
+use self::drop::{DetectDrop, Flag};
+use anyhow::Error;
+use std::error::Error as StdError;
+use std::fmt::{self, Display};
+use std::io;
+
+#[test]
+fn test_downcast() {
+ assert_eq!("oh no!", bail_literal().unwrap_err().downcast::<&str>().unwrap(),);
+ assert_eq!("oh no!", bail_fmt().unwrap_err().downcast::<String>().unwrap(),);
+ assert_eq!("oh no!", bail_error().unwrap_err().downcast::<io::Error>().unwrap().to_string(),);
+}
+
+#[test]
+fn test_downcast_ref() {
+ assert_eq!("oh no!", *bail_literal().unwrap_err().downcast_ref::<&str>().unwrap(),);
+ assert_eq!("oh no!", bail_fmt().unwrap_err().downcast_ref::<String>().unwrap(),);
+ assert_eq!(
+ "oh no!",
+ bail_error().unwrap_err().downcast_ref::<io::Error>().unwrap().to_string(),
+ );
+}
+
+#[test]
+fn test_downcast_mut() {
+ assert_eq!("oh no!", *bail_literal().unwrap_err().downcast_mut::<&str>().unwrap(),);
+ assert_eq!("oh no!", bail_fmt().unwrap_err().downcast_mut::<String>().unwrap(),);
+ assert_eq!(
+ "oh no!",
+ bail_error().unwrap_err().downcast_mut::<io::Error>().unwrap().to_string(),
+ );
+}
+
+#[test]
+fn test_drop() {
+ let has_dropped = Flag::new();
+ let error = Error::new(DetectDrop::new(&has_dropped));
+ drop(error.downcast::<DetectDrop>().unwrap());
+ assert!(has_dropped.get());
+}
+
+#[test]
+fn test_large_alignment() {
+ #[repr(align(64))]
+ #[derive(Debug)]
+ struct LargeAlignedError(&'static str);
+
+ impl Display for LargeAlignedError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(self.0)
+ }
+ }
+
+ impl StdError for LargeAlignedError {}
+
+ let error = Error::new(LargeAlignedError("oh no!"));
+ assert_eq!("oh no!", error.downcast_ref::<LargeAlignedError>().unwrap().0);
+}
+
+#[test]
+fn test_unsuccessful_downcast() {
+ let mut error = bail_error().unwrap_err();
+ assert!(error.downcast_ref::<&str>().is_none());
+ assert!(error.downcast_mut::<&str>().is_none());
+ assert!(error.downcast::<&str>().is_err());
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_fmt.rs b/tests/simple_deps/vendor/anyhow/tests/test_fmt.rs
new file mode 100644
index 0000000..cc49291
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/test_fmt.rs
@@ -0,0 +1,94 @@
+use anyhow::{bail, Context, Result};
+use std::io;
+
+fn f() -> Result<()> {
+ bail!(io::Error::new(io::ErrorKind::PermissionDenied, "oh no!"));
+}
+
+fn g() -> Result<()> {
+ f().context("f failed")
+}
+
+fn h() -> Result<()> {
+ g().context("g failed")
+}
+
+const EXPECTED_ALTDISPLAY_F: &str = "oh no!";
+
+const EXPECTED_ALTDISPLAY_G: &str = "f failed: oh no!";
+
+const EXPECTED_ALTDISPLAY_H: &str = "g failed: f failed: oh no!";
+
+const EXPECTED_DEBUG_F: &str = "oh no!";
+
+const EXPECTED_DEBUG_G: &str = "\
+f failed
+
+Caused by:
+ oh no!\
+";
+
+const EXPECTED_DEBUG_H: &str = "\
+g failed
+
+Caused by:
+ 0: f failed
+ 1: oh no!\
+";
+
+const EXPECTED_ALTDEBUG_F: &str = "\
+Custom {
+ kind: PermissionDenied,
+ error: \"oh no!\",
+}\
+";
+
+const EXPECTED_ALTDEBUG_G: &str = "\
+Error {
+ context: \"f failed\",
+ source: Custom {
+ kind: PermissionDenied,
+ error: \"oh no!\",
+ },
+}\
+";
+
+const EXPECTED_ALTDEBUG_H: &str = "\
+Error {
+ context: \"g failed\",
+ source: Error {
+ context: \"f failed\",
+ source: Custom {
+ kind: PermissionDenied,
+ error: \"oh no!\",
+ },
+ },
+}\
+";
+
+#[test]
+fn test_display() {
+ assert_eq!("g failed", h().unwrap_err().to_string());
+}
+
+#[test]
+fn test_altdisplay() {
+ assert_eq!(EXPECTED_ALTDISPLAY_F, format!("{:#}", f().unwrap_err()));
+ assert_eq!(EXPECTED_ALTDISPLAY_G, format!("{:#}", g().unwrap_err()));
+ assert_eq!(EXPECTED_ALTDISPLAY_H, format!("{:#}", h().unwrap_err()));
+}
+
+#[test]
+#[cfg_attr(not(backtrace), ignore)]
+fn test_debug() {
+ assert_eq!(EXPECTED_DEBUG_F, format!("{:?}", f().unwrap_err()));
+ assert_eq!(EXPECTED_DEBUG_G, format!("{:?}", g().unwrap_err()));
+ assert_eq!(EXPECTED_DEBUG_H, format!("{:?}", h().unwrap_err()));
+}
+
+#[test]
+fn test_altdebug() {
+ assert_eq!(EXPECTED_ALTDEBUG_F, format!("{:#?}", f().unwrap_err()));
+ assert_eq!(EXPECTED_ALTDEBUG_G, format!("{:#?}", g().unwrap_err()));
+ assert_eq!(EXPECTED_ALTDEBUG_H, format!("{:#?}", h().unwrap_err()));
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_macros.rs b/tests/simple_deps/vendor/anyhow/tests/test_macros.rs
new file mode 100644
index 0000000..c6888b6
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/test_macros.rs
@@ -0,0 +1,33 @@
+mod common;
+
+use self::common::*;
+use anyhow::ensure;
+
+#[test]
+fn test_messages() {
+ assert_eq!("oh no!", bail_literal().unwrap_err().to_string());
+ assert_eq!("oh no!", bail_fmt().unwrap_err().to_string());
+ assert_eq!("oh no!", bail_error().unwrap_err().to_string());
+}
+
+#[test]
+fn test_ensure() {
+ let f = || {
+ ensure!(1 + 1 == 2, "This is correct");
+ Ok(())
+ };
+ assert!(f().is_ok());
+
+ let v = 1;
+ let f = || {
+ ensure!(v + v == 2, "This is correct, v: {}", v);
+ Ok(())
+ };
+ assert!(f().is_ok());
+
+ let f = || {
+ ensure!(v + v == 1, "This is not correct, v: {}", v);
+ Ok(())
+ };
+ assert!(f().is_err());
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_repr.rs b/tests/simple_deps/vendor/anyhow/tests/test_repr.rs
new file mode 100644
index 0000000..72f5002
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/test_repr.rs
@@ -0,0 +1,29 @@
+mod drop;
+
+use self::drop::{DetectDrop, Flag};
+use anyhow::Error;
+use std::marker::Unpin;
+use std::mem;
+
+#[test]
+fn test_error_size() {
+ assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>());
+}
+
+#[test]
+fn test_null_pointer_optimization() {
+ assert_eq!(mem::size_of::<Result<(), Error>>(), mem::size_of::<usize>());
+}
+
+#[test]
+fn test_autotraits() {
+ fn assert<E: Unpin + Send + Sync + 'static>() {}
+ assert::<Error>();
+}
+
+#[test]
+fn test_drop() {
+ let has_dropped = Flag::new();
+ drop(Error::new(DetectDrop::new(&has_dropped)));
+ assert!(has_dropped.get());
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_source.rs b/tests/simple_deps/vendor/anyhow/tests/test_source.rs
new file mode 100644
index 0000000..018267d
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/test_source.rs
@@ -0,0 +1,62 @@
+use anyhow::anyhow;
+use std::error::Error as StdError;
+use std::fmt::{self, Display};
+use std::io;
+
+#[derive(Debug)]
+enum TestError {
+ Io(io::Error),
+}
+
+impl Display for TestError {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ TestError::Io(e) => Display::fmt(e, formatter),
+ }
+ }
+}
+
+impl StdError for TestError {
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ match self {
+ TestError::Io(io) => Some(io),
+ }
+ }
+}
+
+#[test]
+fn test_literal_source() {
+ let error = anyhow!("oh no!");
+ assert!(error.source().is_none());
+}
+
+#[test]
+fn test_variable_source() {
+ let msg = "oh no!";
+ let error = anyhow!(msg);
+ assert!(error.source().is_none());
+
+ let msg = msg.to_owned();
+ let error = anyhow!(msg);
+ assert!(error.source().is_none());
+}
+
+#[test]
+fn test_fmt_source() {
+ let error = anyhow!("{} {}!", "oh", "no");
+ assert!(error.source().is_none());
+}
+
+#[test]
+fn test_io_source() {
+ let io = io::Error::new(io::ErrorKind::Other, "oh no!");
+ let error = anyhow!(TestError::Io(io));
+ assert_eq!("oh no!", error.source().unwrap().to_string());
+}
+
+#[test]
+fn test_anyhow_from_anyhow() {
+ let error = anyhow!("oh no!").context("context");
+ let error = anyhow!(error);
+ assert_eq!("oh no!", error.source().unwrap().to_string());
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/ui/no-impl.rs b/tests/simple_deps/vendor/anyhow/tests/ui/no-impl.rs
new file mode 100644
index 0000000..d2e89af
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/ui/no-impl.rs
@@ -0,0 +1,8 @@
+use anyhow::anyhow;
+
+#[derive(Debug)]
+struct Error;
+
+fn main() {
+ let _ = anyhow!(Error);
+}
diff --git a/tests/simple_deps/vendor/anyhow/tests/ui/no-impl.stderr b/tests/simple_deps/vendor/anyhow/tests/ui/no-impl.stderr
new file mode 100644
index 0000000..be95737
--- /dev/null
+++ b/tests/simple_deps/vendor/anyhow/tests/ui/no-impl.stderr
@@ -0,0 +1,21 @@
+error[E0599]: no method named `anyhow_kind` found for reference `&Error` in the current scope
+ --> $DIR/no-impl.rs:7:13
+ |
+4 | struct Error;
+ | -------------
+ | |
+ | doesn't satisfy `Error: anyhow::kind::TraitKind`
+ | doesn't satisfy `Error: std::convert::Into<anyhow::Error>`
+ | doesn't satisfy `Error: std::fmt::Display`
+...
+7 | let _ = anyhow!(Error);
+ | ^^^^^^^^^^^^^^ method not found in `&Error`
+ |
+ = note: the method `anyhow_kind` exists but the following trait bounds were not satisfied:
+ `Error: std::convert::Into<anyhow::Error>`
+ which is required by `Error: anyhow::kind::TraitKind`
+ `Error: std::fmt::Display`
+ which is required by `&Error: anyhow::kind::AdhocKind`
+ `&Error: std::convert::Into<anyhow::Error>`
+ which is required by `&Error: anyhow::kind::TraitKind`
+ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/target_renaming/BUILD.gn b/tests/target_renaming/BUILD.gn
new file mode 100644
index 0000000..fb498c9
--- /dev/null
+++ b/tests/target_renaming/BUILD.gn
@@ -0,0 +1,37 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+import("//example.gni")
+
+renamed_group("example_lib") {
+ public_deps = [ ":example_lib-v1_0_0" ]
+}
+
+renamed_rule("example_lib-v1_0_0") {
+ crate_name = "example_lib"
+ crate_root = "//target_renaming/example_lib/src/lib.rs"
+ output_name = "example_lib-4b9f22a13a4b11e0"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=4b9f22a13a4b11e0",
+ "-Cextra-filename=-4b9f22a13a4b11e0",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
diff --git a/tests/target_renaming/Cargo.lock b/tests/target_renaming/Cargo.lock
new file mode 100644
index 0000000..20221b9
--- /dev/null
+++ b/tests/target_renaming/Cargo.lock
@@ -0,0 +1,14 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "example_lib"
+version = "1.0.0"
+
+[[package]]
+name = "visibility_test"
+version = "0.1.0"
+dependencies = [
+ "example_lib",
+]
diff --git a/tests/target_renaming/Cargo.toml b/tests/target_renaming/Cargo.toml
new file mode 100644
index 0000000..3b90cad
--- /dev/null
+++ b/tests/target_renaming/Cargo.toml
@@ -0,0 +1,15 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "visibility_test"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+example_lib = { version = "1.0", path = "example_lib" }
+
+# gn config
+[gn.package.example_lib."1.0.0"]
+target_renaming = { import = "//example.gni", group_name = "renamed_group", rule_name = "renamed_rule" }
diff --git a/tests/target_renaming/example_lib/Cargo.toml b/tests/target_renaming/example_lib/Cargo.toml
new file mode 100644
index 0000000..12a5af3
--- /dev/null
+++ b/tests/target_renaming/example_lib/Cargo.toml
@@ -0,0 +1,4 @@
+[package]
+name = "example_lib"
+version = "1.0.0"
+edition = "2021"
diff --git a/tests/target_renaming/example_lib/src/lib.rs b/tests/target_renaming/example_lib/src/lib.rs
new file mode 100644
index 0000000..70a39d4
--- /dev/null
+++ b/tests/target_renaming/example_lib/src/lib.rs
@@ -0,0 +1,5 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Library intentionally left empty :)
diff --git a/tests/target_renaming/src/lib.rs b/tests/target_renaming/src/lib.rs
new file mode 100644
index 0000000..70a39d4
--- /dev/null
+++ b/tests/target_renaming/src/lib.rs
@@ -0,0 +1,5 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Library intentionally left empty :)
diff --git a/tests/testonly/BUILD.gn b/tests/testonly/BUILD.gn
new file mode 100644
index 0000000..696d03b
--- /dev/null
+++ b/tests/testonly/BUILD.gn
@@ -0,0 +1,37 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+group("example_lib") {
+ public_deps = [ ":example_lib-v1_0_0" ]
+ testonly = true
+}
+
+rust_library("example_lib-v1_0_0") {
+ crate_name = "example_lib"
+ crate_root = "//testonly/example_lib/src/lib.rs"
+ output_name = "example_lib-4b9f22a13a4b11e0"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=4b9f22a13a4b11e0",
+ "-Cextra-filename=-4b9f22a13a4b11e0",
+ ]
+
+ visibility = [ ":*" ]
+
+ testonly = true
+ applicable_licenses = []
+}
diff --git a/tests/testonly/Cargo.lock b/tests/testonly/Cargo.lock
new file mode 100644
index 0000000..17c74bd
--- /dev/null
+++ b/tests/testonly/Cargo.lock
@@ -0,0 +1,14 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "example_lib"
+version = "1.0.0"
+
+[[package]]
+name = "testonly_test"
+version = "0.1.0"
+dependencies = [
+ "example_lib",
+]
diff --git a/tests/testonly/Cargo.toml b/tests/testonly/Cargo.toml
new file mode 100644
index 0000000..fb7ef95
--- /dev/null
+++ b/tests/testonly/Cargo.toml
@@ -0,0 +1,15 @@
+# Copyright 2023 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "testonly_test"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+example_lib = { version = "1.0", path = "example_lib" }
+
+# gn config
+[gn.package.example_lib."1.0.0"]
+testonly = true
diff --git a/tests/testonly/example_lib/Cargo.lock b/tests/testonly/example_lib/Cargo.lock
new file mode 100644
index 0000000..c9d7300
--- /dev/null
+++ b/tests/testonly/example_lib/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "example_lib"
+version = "1.0.0"
diff --git a/tests/testonly/example_lib/Cargo.toml b/tests/testonly/example_lib/Cargo.toml
new file mode 100644
index 0000000..12a5af3
--- /dev/null
+++ b/tests/testonly/example_lib/Cargo.toml
@@ -0,0 +1,4 @@
+[package]
+name = "example_lib"
+version = "1.0.0"
+edition = "2021"
diff --git a/tests/testonly/example_lib/src/lib.rs b/tests/testonly/example_lib/src/lib.rs
new file mode 100644
index 0000000..b945863
--- /dev/null
+++ b/tests/testonly/example_lib/src/lib.rs
@@ -0,0 +1,5 @@
+// Copyright 2023 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Library intentionally left empty :)
diff --git a/tests/testonly/src/lib.rs b/tests/testonly/src/lib.rs
new file mode 100644
index 0000000..b945863
--- /dev/null
+++ b/tests/testonly/src/lib.rs
@@ -0,0 +1,5 @@
+// Copyright 2023 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Library intentionally left empty :)
diff --git a/tests/visibility/BUILD.gn b/tests/visibility/BUILD.gn
new file mode 100644
index 0000000..4e4db9b
--- /dev/null
+++ b/tests/visibility/BUILD.gn
@@ -0,0 +1,39 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE
+
+import("//build/licenses/license.gni")
+
+gn_source_root = rebase_path("//", root_build_dir)
+not_needed([ "gn_source_root" ])
+
+import("//example.gni")
+
+group("example_lib") {
+ public_deps = [ ":example_lib-v1_0_0" ]
+
+ visibility = example_visibility_var
+}
+
+rust_library("example_lib-v1_0_0") {
+ crate_name = "example_lib"
+ crate_root = "//visibility/example_lib/src/lib.rs"
+ output_name = "example_lib-4b9f22a13a4b11e0"
+
+ deps = []
+
+ rustenv = []
+
+ rustflags = [
+ "--cap-lints=allow",
+ "--edition=2021",
+ "-Cmetadata=4b9f22a13a4b11e0",
+ "-Cextra-filename=-4b9f22a13a4b11e0",
+ ]
+
+ visibility = [ ":*" ]
+
+ applicable_licenses = []
+}
diff --git a/tests/visibility/Cargo.lock b/tests/visibility/Cargo.lock
new file mode 100644
index 0000000..20221b9
--- /dev/null
+++ b/tests/visibility/Cargo.lock
@@ -0,0 +1,14 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "example_lib"
+version = "1.0.0"
+
+[[package]]
+name = "visibility_test"
+version = "0.1.0"
+dependencies = [
+ "example_lib",
+]
diff --git a/tests/visibility/Cargo.toml b/tests/visibility/Cargo.toml
new file mode 100644
index 0000000..d15e6e6
--- /dev/null
+++ b/tests/visibility/Cargo.toml
@@ -0,0 +1,15 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[package]
+name = "visibility_test"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+example_lib = { version = "1.0", path = "example_lib" }
+
+# gn config
+[gn.package.example_lib."1.0.0"]
+group_visibility = { import = "//example.gni", variable = "example_visibility_var" }
diff --git a/tests/visibility/example_lib/Cargo.toml b/tests/visibility/example_lib/Cargo.toml
new file mode 100644
index 0000000..12a5af3
--- /dev/null
+++ b/tests/visibility/example_lib/Cargo.toml
@@ -0,0 +1,4 @@
+[package]
+name = "example_lib"
+version = "1.0.0"
+edition = "2021"
diff --git a/tests/visibility/example_lib/src/lib.rs b/tests/visibility/example_lib/src/lib.rs
new file mode 100644
index 0000000..70a39d4
--- /dev/null
+++ b/tests/visibility/example_lib/src/lib.rs
@@ -0,0 +1,5 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Library intentionally left empty :)
diff --git a/tests/visibility/src/lib.rs b/tests/visibility/src/lib.rs
new file mode 100644
index 0000000..70a39d4
--- /dev/null
+++ b/tests/visibility/src/lib.rs
@@ -0,0 +1,5 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Library intentionally left empty :)