blob: 9a9371dd8c03f9b866cf55c7bc5a6c46f2c2c1ea [file]
use std::collections::{BTreeMap, BTreeSet};
use serde::Deserialize;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CrateSpec {
pub aliases: BTreeMap<String, String>,
pub crate_id: String,
pub display_name: String,
pub edition: String,
pub root_module: String,
pub is_workspace_member: bool,
pub deps: BTreeSet<String>,
pub proc_macro_dylib_path: Option<String>,
pub source: Option<CrateSpecSource>,
pub cfg: Vec<String>,
pub env: BTreeMap<String, String>,
pub target: String,
pub crate_type: CrateType,
pub is_test: bool,
pub build: Option<CrateSpecBuild>,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CrateSpecBuild {
pub label: String,
pub build_file: String,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CrateSpecSource {
pub exclude_dirs: Vec<String>,
pub include_dirs: Vec<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum CrateType {
Bin,
Rlib,
Lib,
Dylib,
Cdylib,
Staticlib,
ProcMacro,
}
/// Read all crate specs, deduplicating crates with the same ID. This happens when
/// a rust_test depends on a rust_library, for example.
pub fn consolidate_crate_specs(crate_specs: Vec<CrateSpec>) -> anyhow::Result<BTreeSet<CrateSpec>> {
let mut consolidated_specs: BTreeMap<String, CrateSpec> = BTreeMap::new();
for mut spec in crate_specs.into_iter() {
log::debug!("{:?}", spec);
if let Some(existing) = consolidated_specs.get_mut(&spec.crate_id) {
existing.deps.extend(spec.deps);
existing.env.extend(spec.env);
existing.aliases.extend(spec.aliases);
if let Some(source) = &mut existing.source {
if let Some(mut new_source) = spec.source {
new_source
.exclude_dirs
.retain(|src| !source.exclude_dirs.contains(src));
new_source
.include_dirs
.retain(|src| !source.include_dirs.contains(src));
source.exclude_dirs.extend(new_source.exclude_dirs);
source.include_dirs.extend(new_source.include_dirs);
}
} else {
existing.source = spec.source;
}
spec.cfg.retain(|cfg| !existing.cfg.contains(cfg));
existing.cfg.extend(spec.cfg);
// display_name should match the library's crate name because Rust Analyzer
// seems to use display_name for matching crate entries in rust-project.json
// against symbols in source files. For more details, see
// https://github.com/bazelbuild/rules_rust/issues/1032
if spec.crate_type == CrateType::Rlib {
existing.display_name = spec.display_name;
existing.crate_type = CrateType::Rlib;
existing.is_test = spec.is_test;
}
// We want to use the test target's build label to provide
// unit tests codelens actions for library crates in IDEs.
if spec.is_test {
if let Some(build) = spec.build {
existing.build = Some(build);
}
}
// For proc-macro crates that exist within the workspace, there will be a
// generated crate-spec in both the fastbuild and opt-exec configuration.
// Prefer proc macro paths with an opt-exec component in the path.
if let Some(dylib_path) = spec.proc_macro_dylib_path.as_ref() {
const OPT_PATH_COMPONENT: &str = "-opt-exec-";
if dylib_path.contains(OPT_PATH_COMPONENT) {
existing.proc_macro_dylib_path.replace(dylib_path.clone());
}
}
} else {
consolidated_specs.insert(spec.crate_id.clone(), spec);
}
}
Ok(consolidated_specs.into_values().collect())
}
#[cfg(test)]
mod test {
use super::*;
use itertools::Itertools;
#[test]
fn consolidate_lib_then_test_specs() {
let crate_specs = vec![
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::from(["ID-lib_dep.rs".into()]),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: Some(CrateSpecBuild {
label: "//:mylib".to_owned(),
build_file: "BUILD.bazel".to_owned(),
}),
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-extra_test_dep.rs".into(),
display_name: "extra_test_dep".into(),
edition: "2018".into(),
root_module: "extra_test_dep.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: None,
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-lib_dep.rs".into(),
display_name: "lib_dep".into(),
edition: "2018".into(),
root_module: "lib_dep.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: None,
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib_test".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::from(["ID-extra_test_dep.rs".into()]),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Bin,
is_test: true,
build: None,
},
];
assert_eq!(
consolidate_crate_specs(crate_specs).unwrap(),
BTreeSet::from([
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::from(["ID-lib_dep.rs".into(), "ID-extra_test_dep.rs".into()]),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: Some(CrateSpecBuild {
label: "//:mylib".to_owned(),
build_file: "BUILD.bazel".to_owned(),
}),
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-extra_test_dep.rs".into(),
display_name: "extra_test_dep".into(),
edition: "2018".into(),
root_module: "extra_test_dep.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: None
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-lib_dep.rs".into(),
display_name: "lib_dep".into(),
edition: "2018".into(),
root_module: "lib_dep.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: None
},
])
);
}
#[test]
fn consolidate_test_then_lib_specs() {
let crate_specs = vec![
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib_test".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::from(["ID-extra_test_dep.rs".into()]),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Bin,
is_test: true,
build: None,
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::from(["ID-lib_dep.rs".into()]),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: None,
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-extra_test_dep.rs".into(),
display_name: "extra_test_dep".into(),
edition: "2018".into(),
root_module: "extra_test_dep.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: None,
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-lib_dep.rs".into(),
display_name: "lib_dep".into(),
edition: "2018".into(),
root_module: "lib_dep.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: None,
},
];
assert_eq!(
consolidate_crate_specs(crate_specs).unwrap(),
BTreeSet::from([
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::from(["ID-lib_dep.rs".into(), "ID-extra_test_dep.rs".into()]),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: None
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-extra_test_dep.rs".into(),
display_name: "extra_test_dep".into(),
edition: "2018".into(),
root_module: "extra_test_dep.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: None
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-lib_dep.rs".into(),
display_name: "lib_dep".into(),
edition: "2018".into(),
root_module: "lib_dep.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: None
},
])
);
}
#[test]
fn consolidate_lib_test_main_specs() {
// mylib.rs is a library but has tests and an entry point, and mylib2.rs
// depends on mylib.rs. The display_name of the library target mylib.rs
// should be "mylib" no matter what order the crate specs is in.
// Otherwise Rust Analyzer will not be able to resolve references to
// mylib in mylib2.rs.
let crate_specs = vec![
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: None,
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib_test".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Bin,
is_test: true,
build: None,
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib_main".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Bin,
is_test: false,
build: None,
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib2.rs".into(),
display_name: "mylib2".into(),
edition: "2018".into(),
root_module: "mylib2.rs".into(),
is_workspace_member: true,
deps: BTreeSet::from(["ID-mylib.rs".into()]),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: None,
},
];
for perm in crate_specs.into_iter().permutations(4) {
assert_eq!(
consolidate_crate_specs(perm).unwrap(),
BTreeSet::from([
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::from([]),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: None,
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib2.rs".into(),
display_name: "mylib2".into(),
edition: "2018".into(),
root_module: "mylib2.rs".into(),
is_workspace_member: true,
deps: BTreeSet::from(["ID-mylib.rs".into()]),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: false,
build: None
},
])
);
}
}
#[test]
fn consolidate_proc_macro_prefer_exec() {
// proc macro crates should prefer the -opt-exec- path which is always generated
// during builds where it is used, while the fastbuild version would only be built
// when explicitly building that target.
let crate_specs = vec![
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-myproc_macro.rs".into(),
display_name: "myproc_macro".into(),
edition: "2018".into(),
root_module: "myproc_macro.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: Some(
"bazel-out/k8-opt-exec-F005BA11/bin/myproc_macro/libmyproc_macro-12345.so"
.into(),
),
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::ProcMacro,
is_test: false,
build: None,
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-myproc_macro.rs".into(),
display_name: "myproc_macro".into(),
edition: "2018".into(),
root_module: "myproc_macro.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: Some(
"bazel-out/k8-fastbuild/bin/myproc_macro/libmyproc_macro-12345.so".into(),
),
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::ProcMacro,
is_test: false,
build: None,
},
];
for perm in crate_specs.into_iter().permutations(2) {
assert_eq!(
consolidate_crate_specs(perm).unwrap(),
BTreeSet::from([CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-myproc_macro.rs".into(),
display_name: "myproc_macro".into(),
edition: "2018".into(),
root_module: "myproc_macro.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: Some(
"bazel-out/k8-opt-exec-F005BA11/bin/myproc_macro/libmyproc_macro-12345.so"
.into()
),
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::ProcMacro,
is_test: false,
build: None,
},])
);
}
}
#[test]
fn consolidate_spec_with_aliases() {
let crate_specs = vec![
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: true,
build: None,
},
CrateSpec {
aliases: BTreeMap::from([("ID-mylib_dep.rs".into(), "aliased_name".into())]),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib_test".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Bin,
is_test: true,
build: None,
},
];
for perm in crate_specs.into_iter().permutations(2) {
assert_eq!(
consolidate_crate_specs(perm).unwrap(),
BTreeSet::from([CrateSpec {
aliases: BTreeMap::from([("ID-mylib_dep.rs".into(), "aliased_name".into())]),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::from([]),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: true,
build: None,
}])
);
}
}
#[test]
fn consolidate_spec_with_sources() {
let crate_specs = vec![
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: None,
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: true,
build: None,
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib_test".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: Some(CrateSpecSource {
exclude_dirs: vec!["exclude".into()],
include_dirs: vec!["include".into()],
}),
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Bin,
is_test: true,
build: None,
},
];
for perm in crate_specs.into_iter().permutations(2) {
assert_eq!(
consolidate_crate_specs(perm).unwrap(),
BTreeSet::from([CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::from([]),
proc_macro_dylib_path: None,
source: Some(CrateSpecSource {
exclude_dirs: vec!["exclude".into()],
include_dirs: vec!["include".into()],
}),
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: true,
build: None,
}])
);
}
}
#[test]
fn consolidate_spec_with_duplicate_sources() {
let crate_specs = vec![
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: Some(CrateSpecSource {
exclude_dirs: vec!["exclude".into()],
include_dirs: vec!["include".into()],
}),
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: true,
build: None,
},
CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib_test".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::new(),
proc_macro_dylib_path: None,
source: Some(CrateSpecSource {
exclude_dirs: vec!["exclude".into()],
include_dirs: vec!["include".into()],
}),
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Bin,
is_test: true,
build: None,
},
];
for perm in crate_specs.into_iter().permutations(2) {
assert_eq!(
consolidate_crate_specs(perm).unwrap(),
BTreeSet::from([CrateSpec {
aliases: BTreeMap::new(),
crate_id: "ID-mylib.rs".into(),
display_name: "mylib".into(),
edition: "2018".into(),
root_module: "mylib.rs".into(),
is_workspace_member: true,
deps: BTreeSet::from([]),
proc_macro_dylib_path: None,
source: Some(CrateSpecSource {
exclude_dirs: vec!["exclude".into()],
include_dirs: vec!["include".into()],
}),
cfg: vec!["test".into(), "debug_assertions".into()],
env: BTreeMap::new(),
target: "x86_64-unknown-linux-gnu".into(),
crate_type: CrateType::Rlib,
is_test: true,
build: None,
}])
);
}
}
}