//! See [`CargoWorkspace`].
-use std::convert::TryInto;
use std::iter;
use std::path::PathBuf;
use std::{ops, process::Command};
Discover,
}
+/// Crates to disable `#[cfg(test)]` on.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum UnsetTestCrates {
+ None,
+ Only(Vec<String>),
+ All,
+}
+
+impl Default for UnsetTestCrates {
+ fn default() -> Self {
+ Self::None
+ }
+}
+
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CargoConfig {
/// Do not activate the `default` feature.
pub rustc_source: Option<RustcSource>,
/// crates to disable `#[cfg(test)]` on
- pub unset_test_crates: Vec<String>,
+ pub unset_test_crates: UnsetTestCrates,
pub wrap_rustc_in_build_scripts: bool,
}
impl CargoConfig {
pub fn cfg_overrides(&self) -> CfgOverrides {
- self.unset_test_crates
- .iter()
- .cloned()
- .zip(iter::repeat_with(|| {
- cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]).unwrap()
- }))
- .collect()
+ match &self.unset_test_crates {
+ UnsetTestCrates::None => CfgOverrides::Selective(iter::empty().collect()),
+ UnsetTestCrates::Only(unset_test_crates) => CfgOverrides::Selective(
+ unset_test_crates
+ .iter()
+ .cloned()
+ .zip(iter::repeat_with(|| {
+ cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())])
+ .unwrap()
+ }))
+ .collect(),
+ ),
+ UnsetTestCrates::All => CfgOverrides::Wildcard(
+ cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]).unwrap(),
+ ),
+ }
}
}
pub version: semver::Version,
/// Name as given in the `Cargo.toml`
pub name: String,
+ /// Repository as given in the `Cargo.toml`
+ pub repository: Option<String>,
/// Path containing the `Cargo.toml`
pub manifest: ManifestPath,
/// Targets provided by the crate (lib, bin, example, test, ...)
pub targets: Vec<Target>,
- /// Is this package a member of the current workspace
+ /// Does this package come from the local filesystem (and is editable)?
+ pub is_local: bool,
+ // Whether this package is a member of the workspace
pub is_member: bool,
/// List of packages this package depends on
pub dependencies: Vec<PackageDependency>,
pub features: FxHashMap<String, Vec<String>>,
/// List of features enabled on this package
pub active_features: Vec<String>,
- // String representation of package id
+ /// String representation of package id
pub id: String,
- // The contents of [package.metadata.rust-analyzer]
+ /// The contents of [package.metadata.rust-analyzer]
pub metadata: RustAnalyzerPackageMetaData,
}
pub kind: TargetKind,
/// Is this target a proc-macro
pub is_proc_macro: bool,
+ /// Required features of the target without which it won't build
+ pub required_features: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
impl CargoWorkspace {
pub fn fetch_metadata(
cargo_toml: &ManifestPath,
+ current_dir: &AbsPath,
config: &CargoConfig,
progress: &dyn Fn(String),
) -> Result<cargo_metadata::Metadata> {
meta.features(CargoOpt::SomeFeatures(config.features.clone()));
}
}
- meta.current_dir(cargo_toml.parent().as_os_str());
+ meta.current_dir(current_dir.as_os_str());
if let Some(target) = target {
meta.other_options(vec![String::from("--filter-platform"), target]);
// unclear whether cargo itself supports it.
progress("metadata".to_string());
- let meta = meta.exec().with_context(|| {
- format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display(),)
- })?;
+ let meta =
+ meta.exec().with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()))?;
Ok(meta)
}
meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
for meta_pkg in &meta.packages {
let cargo_metadata::Package {
- id, edition, name, manifest_path, version, metadata, ..
+ id,
+ edition,
+ name,
+ manifest_path,
+ version,
+ metadata,
+ repository,
+ ..
} = meta_pkg;
let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default();
- let is_member = ws_members.contains(id);
let edition = edition.parse::<Edition>().unwrap_or_else(|err| {
tracing::error!("Failed to parse edition {}", err);
Edition::CURRENT
});
+ // We treat packages without source as "local" packages. That includes all members of
+ // the current workspace, as well as any path dependency outside the workspace.
+ let is_local = meta_pkg.source.is_none();
+ let is_member = ws_members.contains(id);
let pkg = packages.alloc(PackageData {
id: id.repr.clone(),
version: version.clone(),
manifest: AbsPathBuf::assert(PathBuf::from(&manifest_path)).try_into().unwrap(),
targets: Vec::new(),
+ is_local,
is_member,
edition,
+ repository: repository.clone(),
dependencies: Vec::new(),
features: meta_pkg.features.clone().into_iter().collect(),
active_features: Vec::new(),
root: AbsPathBuf::assert(PathBuf::from(&meta_tgt.src_path)),
kind: TargetKind::new(meta_tgt.kind.as_slice()),
is_proc_macro,
+ required_features: meta_tgt.required_features.clone(),
});
pkg_data.targets.push(tgt);
}
pub fn target_by_root(&self, root: &AbsPath) -> Option<Target> {
self.packages()
- .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root))
- .next()
+ .filter(|&pkg| self[pkg].is_member)
+ .find_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root))
.copied()
}
}
}
+ pub fn parent_manifests(&self, manifest_path: &ManifestPath) -> Option<Vec<ManifestPath>> {
+ let mut found = false;
+ let parent_manifests = self
+ .packages()
+ .filter_map(|pkg| {
+ if !found && &self[pkg].manifest == manifest_path {
+ found = true
+ }
+ self[pkg].dependencies.iter().find_map(|dep| {
+ if &self[dep.pkg].manifest == manifest_path {
+ return Some(self[pkg].manifest.clone());
+ }
+ None
+ })
+ })
+ .collect::<Vec<ManifestPath>>();
+
+ // some packages has this pkg as dep. return their manifests
+ if parent_manifests.len() > 0 {
+ return Some(parent_manifests);
+ }
+
+ // this pkg is inside this cargo workspace, fallback to workspace root
+ if found {
+ return Some(vec![
+ ManifestPath::try_from(self.workspace_root().join("Cargo.toml")).ok()?
+ ]);
+ }
+
+ // not in this workspace
+ None
+ }
+
fn is_unique(&self, name: &str) -> bool {
self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
}