From eac24d52e672c0a9c118e8969bf1b839c3e7f1f3 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Sunkara Date: Thu, 13 Aug 2020 12:05:30 +0200 Subject: Rename ra_project_model -> project_model --- crates/ra_project_model/src/cargo_workspace.rs | 362 ---------------- crates/ra_project_model/src/cfg_flag.rs | 51 --- crates/ra_project_model/src/lib.rs | 544 ------------------------- crates/ra_project_model/src/project_json.rs | 143 ------- crates/ra_project_model/src/sysroot.rs | 173 -------- 5 files changed, 1273 deletions(-) delete mode 100644 crates/ra_project_model/src/cargo_workspace.rs delete mode 100644 crates/ra_project_model/src/cfg_flag.rs delete mode 100644 crates/ra_project_model/src/lib.rs delete mode 100644 crates/ra_project_model/src/project_json.rs delete mode 100644 crates/ra_project_model/src/sysroot.rs (limited to 'crates/ra_project_model/src') diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs deleted file mode 100644 index abf8dca96..000000000 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ /dev/null @@ -1,362 +0,0 @@ -//! FIXME: write short doc here - -use std::{ - ffi::OsStr, - ops, - path::{Path, PathBuf}, - process::Command, -}; - -use anyhow::{Context, Result}; -use arena::{Arena, Idx}; -use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; -use paths::{AbsPath, AbsPathBuf}; -use ra_db::Edition; -use rustc_hash::FxHashMap; - -use crate::cfg_flag::CfgFlag; - -/// `CargoWorkspace` represents the logical structure of, well, a Cargo -/// workspace. It pretty closely mirrors `cargo metadata` output. -/// -/// Note that internally, rust analyzer uses a different structure: -/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates, -/// while this knows about `Packages` & `Targets`: purely cargo-related -/// concepts. -/// -/// We use absolute paths here, `cargo metadata` guarantees to always produce -/// abs paths. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct CargoWorkspace { - packages: Arena, - targets: Arena, - workspace_root: AbsPathBuf, -} - -impl ops::Index for CargoWorkspace { - type Output = PackageData; - fn index(&self, index: Package) -> &PackageData { - &self.packages[index] - } -} - -impl ops::Index for CargoWorkspace { - type Output = TargetData; - fn index(&self, index: Target) -> &TargetData { - &self.targets[index] - } -} - -#[derive(Default, Clone, Debug, PartialEq, Eq)] -pub struct CargoConfig { - /// Do not activate the `default` feature. - pub no_default_features: bool, - - /// Activate all available features - pub all_features: bool, - - /// List of features to activate. - /// This will be ignored if `cargo_all_features` is true. - pub features: Vec, - - /// Runs cargo check on launch to figure out the correct values of OUT_DIR - pub load_out_dirs_from_check: bool, - - /// rustc target - pub target: Option, -} - -pub type Package = Idx; - -pub type Target = Idx; - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct PackageData { - pub version: String, - pub name: String, - pub manifest: AbsPathBuf, - pub targets: Vec, - pub is_member: bool, - pub dependencies: Vec, - pub edition: Edition, - pub features: Vec, - pub cfgs: Vec, - pub out_dir: Option, - pub proc_macro_dylib_path: Option, -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct PackageDependency { - pub pkg: Package, - pub name: String, -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct TargetData { - pub package: Package, - pub name: String, - pub root: AbsPathBuf, - pub kind: TargetKind, - pub is_proc_macro: bool, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TargetKind { - Bin, - /// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...). - Lib, - Example, - Test, - Bench, - Other, -} - -impl TargetKind { - fn new(kinds: &[String]) -> TargetKind { - for kind in kinds { - return match kind.as_str() { - "bin" => TargetKind::Bin, - "test" => TargetKind::Test, - "bench" => TargetKind::Bench, - "example" => TargetKind::Example, - "proc-macro" => TargetKind::Lib, - _ if kind.contains("lib") => TargetKind::Lib, - _ => continue, - }; - } - TargetKind::Other - } -} - -impl PackageData { - pub fn root(&self) -> &AbsPath { - self.manifest.parent().unwrap() - } -} - -impl CargoWorkspace { - pub fn from_cargo_metadata( - cargo_toml: &AbsPath, - cargo_features: &CargoConfig, - ) -> Result { - let mut meta = MetadataCommand::new(); - meta.cargo_path(toolchain::cargo()); - meta.manifest_path(cargo_toml.to_path_buf()); - if cargo_features.all_features { - meta.features(CargoOpt::AllFeatures); - } else { - if cargo_features.no_default_features { - // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` - // https://github.com/oli-obk/cargo_metadata/issues/79 - meta.features(CargoOpt::NoDefaultFeatures); - } - if !cargo_features.features.is_empty() { - meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone())); - } - } - if let Some(parent) = cargo_toml.parent() { - meta.current_dir(parent.to_path_buf()); - } - if let Some(target) = cargo_features.target.as_ref() { - meta.other_options(vec![String::from("--filter-platform"), target.clone()]); - } - let mut meta = meta.exec().with_context(|| { - format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) - })?; - - let mut out_dir_by_id = FxHashMap::default(); - let mut cfgs = FxHashMap::default(); - let mut proc_macro_dylib_paths = FxHashMap::default(); - if cargo_features.load_out_dirs_from_check { - let resources = load_extern_resources(cargo_toml, cargo_features)?; - out_dir_by_id = resources.out_dirs; - cfgs = resources.cfgs; - proc_macro_dylib_paths = resources.proc_dylib_paths; - } - - let mut pkg_by_id = FxHashMap::default(); - let mut packages = Arena::default(); - let mut targets = Arena::default(); - - let ws_members = &meta.workspace_members; - - 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, .. } = - meta_pkg; - let is_member = ws_members.contains(&id); - let edition = edition - .parse::() - .with_context(|| format!("Failed to parse edition {}", edition))?; - let pkg = packages.alloc(PackageData { - name, - version: version.to_string(), - manifest: AbsPathBuf::assert(manifest_path), - targets: Vec::new(), - is_member, - edition, - dependencies: Vec::new(), - features: Vec::new(), - cfgs: cfgs.get(&id).cloned().unwrap_or_default(), - out_dir: out_dir_by_id.get(&id).cloned(), - proc_macro_dylib_path: proc_macro_dylib_paths.get(&id).cloned(), - }); - let pkg_data = &mut packages[pkg]; - pkg_by_id.insert(id, pkg); - for meta_tgt in meta_pkg.targets { - let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"]; - let tgt = targets.alloc(TargetData { - package: pkg, - name: meta_tgt.name, - root: AbsPathBuf::assert(meta_tgt.src_path.clone()), - kind: TargetKind::new(meta_tgt.kind.as_slice()), - is_proc_macro, - }); - pkg_data.targets.push(tgt); - } - } - let resolve = meta.resolve.expect("metadata executed with deps"); - for mut node in resolve.nodes { - let source = match pkg_by_id.get(&node.id) { - Some(&src) => src, - // FIXME: replace this and a similar branch below with `.unwrap`, once - // https://github.com/rust-lang/cargo/issues/7841 - // is fixed and hits stable (around 1.43-is probably?). - None => { - log::error!("Node id do not match in cargo metadata, ignoring {}", node.id); - continue; - } - }; - node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg)); - for dep_node in node.deps { - let pkg = match pkg_by_id.get(&dep_node.pkg) { - Some(&pkg) => pkg, - None => { - log::error!( - "Dep node id do not match in cargo metadata, ignoring {}", - dep_node.pkg - ); - continue; - } - }; - let dep = PackageDependency { name: dep_node.name, pkg }; - packages[source].dependencies.push(dep); - } - packages[source].features.extend(node.features); - } - - let workspace_root = AbsPathBuf::assert(meta.workspace_root); - Ok(CargoWorkspace { packages, targets, workspace_root: workspace_root }) - } - - pub fn packages<'a>(&'a self) -> impl Iterator + ExactSizeIterator + 'a { - self.packages.iter().map(|(id, _pkg)| id) - } - - pub fn target_by_root(&self, root: &AbsPath) -> Option { - self.packages() - .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root)) - .next() - .copied() - } - - pub fn workspace_root(&self) -> &AbsPath { - &self.workspace_root - } - - pub fn package_flag(&self, package: &PackageData) -> String { - if self.is_unique(&*package.name) { - package.name.clone() - } else { - format!("{}:{}", package.name, package.version) - } - } - - fn is_unique(&self, name: &str) -> bool { - self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 - } -} - -#[derive(Debug, Clone, Default)] -pub struct ExternResources { - out_dirs: FxHashMap, - proc_dylib_paths: FxHashMap, - cfgs: FxHashMap>, -} - -pub fn load_extern_resources( - cargo_toml: &Path, - cargo_features: &CargoConfig, -) -> Result { - let mut cmd = Command::new(toolchain::cargo()); - cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); - if cargo_features.all_features { - cmd.arg("--all-features"); - } else { - if cargo_features.no_default_features { - // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` - // https://github.com/oli-obk/cargo_metadata/issues/79 - cmd.arg("--no-default-features"); - } - if !cargo_features.features.is_empty() { - cmd.arg("--features"); - cmd.arg(cargo_features.features.join(" ")); - } - } - - let output = cmd.output()?; - - let mut res = ExternResources::default(); - - for message in cargo_metadata::Message::parse_stream(output.stdout.as_slice()) { - if let Ok(message) = message { - match message { - Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { - let cfgs = { - let mut acc = Vec::new(); - for cfg in cfgs { - match cfg.parse::() { - Ok(it) => acc.push(it), - Err(err) => { - anyhow::bail!("invalid cfg from cargo-metadata: {}", err) - } - }; - } - acc - }; - // cargo_metadata crate returns default (empty) path for - // older cargos, which is not absolute, so work around that. - if out_dir != PathBuf::default() { - let out_dir = AbsPathBuf::assert(out_dir); - res.out_dirs.insert(package_id.clone(), out_dir); - res.cfgs.insert(package_id, cfgs); - } - } - Message::CompilerArtifact(message) => { - if message.target.kind.contains(&"proc-macro".to_string()) { - let package_id = message.package_id; - // Skip rmeta file - if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) - { - let filename = AbsPathBuf::assert(filename.clone()); - res.proc_dylib_paths.insert(package_id, filename); - } - } - } - Message::CompilerMessage(_) => (), - Message::Unknown => (), - Message::BuildFinished(_) => {} - Message::TextLine(_) => {} - } - } - } - Ok(res) -} - -// FIXME: File a better way to know if it is a dylib -fn is_dylib(path: &Path) -> bool { - match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) { - None => false, - Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"), - } -} diff --git a/crates/ra_project_model/src/cfg_flag.rs b/crates/ra_project_model/src/cfg_flag.rs deleted file mode 100644 index e92962cf6..000000000 --- a/crates/ra_project_model/src/cfg_flag.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! Parsing of CfgFlags as command line arguments, as in -//! -//! rustc main.rs --cfg foo --cfg 'feature="bar"' -use std::str::FromStr; - -use cfg::CfgOptions; -use stdx::split_once; - -#[derive(Clone, Eq, PartialEq, Debug)] -pub enum CfgFlag { - Atom(String), - KeyValue { key: String, value: String }, -} - -impl FromStr for CfgFlag { - type Err = String; - fn from_str(s: &str) -> Result { - let res = match split_once(s, '=') { - Some((key, value)) => { - if !(value.starts_with('"') && value.ends_with('"')) { - return Err(format!("Invalid cfg ({:?}), value should be in quotes", s)); - } - let key = key.to_string(); - let value = value[1..value.len() - 1].to_string(); - CfgFlag::KeyValue { key, value } - } - None => CfgFlag::Atom(s.into()), - }; - Ok(res) - } -} - -impl<'de> serde::Deserialize<'de> for CfgFlag { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom) - } -} - -impl Extend for CfgOptions { - fn extend>(&mut self, iter: T) { - for cfg_flag in iter { - match cfg_flag { - CfgFlag::Atom(it) => self.insert_atom(it.into()), - CfgFlag::KeyValue { key, value } => self.insert_key_value(key.into(), value.into()), - } - } - } -} diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs deleted file mode 100644 index ee42198f3..000000000 --- a/crates/ra_project_model/src/lib.rs +++ /dev/null @@ -1,544 +0,0 @@ -//! FIXME: write short doc here - -mod cargo_workspace; -mod project_json; -mod sysroot; -mod cfg_flag; - -use std::{ - fs::{self, read_dir, ReadDir}, - io, - process::Command, -}; - -use anyhow::{bail, Context, Result}; -use cfg::CfgOptions; -use paths::{AbsPath, AbsPathBuf}; -use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; -use rustc_hash::{FxHashMap, FxHashSet}; - -use crate::cfg_flag::CfgFlag; - -pub use crate::{ - cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, - project_json::{ProjectJson, ProjectJsonData}, - sysroot::Sysroot, -}; - -pub use ra_proc_macro::ProcMacroClient; - -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum ProjectWorkspace { - /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. - Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, - /// Project workspace was manually specified using a `rust-project.json` file. - Json { project: ProjectJson }, -} - -/// `PackageRoot` describes a package root folder. -/// Which may be an external dependency, or a member of -/// the current workspace. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct PackageRoot { - /// Is a member of the current workspace - pub is_member: bool, - pub include: Vec, - pub exclude: Vec, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub enum ProjectManifest { - ProjectJson(AbsPathBuf), - CargoToml(AbsPathBuf), -} - -impl ProjectManifest { - pub fn from_manifest_file(path: AbsPathBuf) -> Result { - if path.ends_with("rust-project.json") { - return Ok(ProjectManifest::ProjectJson(path)); - } - if path.ends_with("Cargo.toml") { - return Ok(ProjectManifest::CargoToml(path)); - } - bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) - } - - pub fn discover_single(path: &AbsPath) -> Result { - let mut candidates = ProjectManifest::discover(path)?; - let res = match candidates.pop() { - None => bail!("no projects"), - Some(it) => it, - }; - - if !candidates.is_empty() { - bail!("more than one project") - } - Ok(res) - } - - pub fn discover(path: &AbsPath) -> io::Result> { - if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { - return Ok(vec![ProjectManifest::ProjectJson(project_json)]); - } - return find_cargo_toml(path) - .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect()); - - fn find_cargo_toml(path: &AbsPath) -> io::Result> { - match find_in_parent_dirs(path, "Cargo.toml") { - Some(it) => Ok(vec![it]), - None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)), - } - } - - fn find_in_parent_dirs(path: &AbsPath, target_file_name: &str) -> Option { - if path.ends_with(target_file_name) { - return Some(path.to_path_buf()); - } - - let mut curr = Some(path); - - while let Some(path) = curr { - let candidate = path.join(target_file_name); - if candidate.exists() { - return Some(candidate); - } - curr = path.parent(); - } - - None - } - - fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec { - // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects - entities - .filter_map(Result::ok) - .map(|it| it.path().join("Cargo.toml")) - .filter(|it| it.exists()) - .map(AbsPathBuf::assert) - .collect() - } - } - - pub fn discover_all(paths: &[impl AsRef]) -> Vec { - let mut res = paths - .iter() - .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok()) - .flatten() - .collect::>() - .into_iter() - .collect::>(); - res.sort(); - res - } -} - -impl ProjectWorkspace { - pub fn load( - manifest: ProjectManifest, - cargo_config: &CargoConfig, - with_sysroot: bool, - ) -> Result { - let res = match manifest { - ProjectManifest::ProjectJson(project_json) => { - let file = fs::read_to_string(&project_json).with_context(|| { - format!("Failed to read json file {}", project_json.display()) - })?; - let data = serde_json::from_str(&file).with_context(|| { - format!("Failed to deserialize json file {}", project_json.display()) - })?; - let project_location = project_json.parent().unwrap().to_path_buf(); - let project = ProjectJson::new(&project_location, data); - ProjectWorkspace::Json { project } - } - ProjectManifest::CargoToml(cargo_toml) => { - let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_config) - .with_context(|| { - format!( - "Failed to read Cargo metadata from Cargo.toml file {}", - cargo_toml.display() - ) - })?; - let sysroot = if with_sysroot { - Sysroot::discover(&cargo_toml).with_context(|| { - format!( - "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?", - cargo_toml.display() - ) - })? - } else { - Sysroot::default() - }; - ProjectWorkspace::Cargo { cargo, sysroot } - } - }; - - Ok(res) - } - - /// Returns the roots for the current `ProjectWorkspace` - /// The return type contains the path and whether or not - /// the root is a member of the current workspace - pub fn to_roots(&self) -> Vec { - match self { - ProjectWorkspace::Json { project } => project - .crates - .iter() - .map(|krate| PackageRoot { - is_member: krate.is_workspace_member, - include: krate.include.clone(), - exclude: krate.exclude.clone(), - }) - .collect::>() - .into_iter() - .collect::>(), - ProjectWorkspace::Cargo { cargo, sysroot } => cargo - .packages() - .map(|pkg| { - let is_member = cargo[pkg].is_member; - let pkg_root = cargo[pkg].root().to_path_buf(); - - let mut include = vec![pkg_root.clone()]; - include.extend(cargo[pkg].out_dir.clone()); - - let mut exclude = vec![pkg_root.join(".git")]; - if is_member { - exclude.push(pkg_root.join("target")); - } else { - exclude.push(pkg_root.join("tests")); - exclude.push(pkg_root.join("examples")); - exclude.push(pkg_root.join("benches")); - } - PackageRoot { is_member, include, exclude } - }) - .chain(sysroot.crates().map(|krate| PackageRoot { - is_member: false, - include: vec![sysroot[krate].root_dir().to_path_buf()], - exclude: Vec::new(), - })) - .collect(), - } - } - - pub fn proc_macro_dylib_paths(&self) -> Vec { - match self { - ProjectWorkspace::Json { project } => project - .crates - .iter() - .filter_map(|krate| krate.proc_macro_dylib_path.as_ref()) - .cloned() - .collect(), - ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo - .packages() - .filter_map(|pkg| cargo[pkg].proc_macro_dylib_path.as_ref()) - .cloned() - .collect(), - } - } - - pub fn n_packages(&self) -> usize { - match self { - ProjectWorkspace::Json { project, .. } => project.crates.len(), - ProjectWorkspace::Cargo { cargo, sysroot } => { - cargo.packages().len() + sysroot.crates().len() - } - } - } - - pub fn to_crate_graph( - &self, - target: Option<&str>, - proc_macro_client: &ProcMacroClient, - load: &mut dyn FnMut(&AbsPath) -> Option, - ) -> CrateGraph { - let mut crate_graph = CrateGraph::default(); - match self { - ProjectWorkspace::Json { project } => { - let mut cfg_cache: FxHashMap, Vec> = FxHashMap::default(); - let crates: FxHashMap<_, _> = project - .crates - .iter() - .enumerate() - .filter_map(|(seq_index, krate)| { - let file_path = &krate.root_module; - let file_id = load(&file_path)?; - - let env = krate.env.clone().into_iter().collect(); - let proc_macro = krate - .proc_macro_dylib_path - .clone() - .map(|it| proc_macro_client.by_dylib_path(&it)); - - let target = krate.target.as_deref().or(target); - let target_cfgs = cfg_cache - .entry(target) - .or_insert_with(|| get_rustc_cfg_options(target)); - - let mut cfg_options = CfgOptions::default(); - cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); - - // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env - Some(( - CrateId(seq_index as u32), - crate_graph.add_crate_root( - file_id, - krate.edition, - // FIXME json definitions can store the crate name - None, - cfg_options, - env, - proc_macro.unwrap_or_default(), - ), - )) - }) - .collect(); - - for (id, krate) in project.crates.iter().enumerate() { - for dep in &krate.deps { - let from_crate_id = CrateId(id as u32); - let to_crate_id = dep.crate_id; - if let (Some(&from), Some(&to)) = - (crates.get(&from_crate_id), crates.get(&to_crate_id)) - { - if crate_graph.add_dep(from, dep.name.clone(), to).is_err() { - log::error!( - "cyclic dependency {:?} -> {:?}", - from_crate_id, - to_crate_id - ); - } - } - } - } - } - ProjectWorkspace::Cargo { cargo, sysroot } => { - let mut cfg_options = CfgOptions::default(); - cfg_options.extend(get_rustc_cfg_options(target)); - - let sysroot_crates: FxHashMap<_, _> = sysroot - .crates() - .filter_map(|krate| { - let file_id = load(&sysroot[krate].root)?; - - let env = Env::default(); - let proc_macro = vec![]; - let name = sysroot[krate].name.clone(); - let crate_id = crate_graph.add_crate_root( - file_id, - Edition::Edition2018, - Some(name), - cfg_options.clone(), - env, - proc_macro, - ); - Some((krate, crate_id)) - }) - .collect(); - - for from in sysroot.crates() { - for &to in sysroot[from].deps.iter() { - let name = &sysroot[to].name; - if let (Some(&from), Some(&to)) = - (sysroot_crates.get(&from), sysroot_crates.get(&to)) - { - if crate_graph.add_dep(from, CrateName::new(name).unwrap(), to).is_err() - { - log::error!("cyclic dependency between sysroot crates") - } - } - } - } - - let libcore = sysroot.core().and_then(|it| sysroot_crates.get(&it).copied()); - let liballoc = sysroot.alloc().and_then(|it| sysroot_crates.get(&it).copied()); - let libstd = sysroot.std().and_then(|it| sysroot_crates.get(&it).copied()); - let libproc_macro = - sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied()); - - let mut pkg_to_lib_crate = FxHashMap::default(); - let mut pkg_crates = FxHashMap::default(); - - // Add test cfg for non-sysroot crates - cfg_options.insert_atom("test".into()); - cfg_options.insert_atom("debug_assertions".into()); - - // Next, create crates for each package, target pair - for pkg in cargo.packages() { - let mut lib_tgt = None; - for &tgt in cargo[pkg].targets.iter() { - let root = cargo[tgt].root.as_path(); - if let Some(file_id) = load(root) { - let edition = cargo[pkg].edition; - let cfg_options = { - let mut opts = cfg_options.clone(); - for feature in cargo[pkg].features.iter() { - opts.insert_key_value("feature".into(), feature.into()); - } - opts.extend(cargo[pkg].cfgs.iter().cloned()); - opts - }; - let mut env = Env::default(); - if let Some(out_dir) = &cargo[pkg].out_dir { - // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() - if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { - env.set("OUT_DIR", out_dir); - } - } - let proc_macro = cargo[pkg] - .proc_macro_dylib_path - .as_ref() - .map(|it| proc_macro_client.by_dylib_path(&it)) - .unwrap_or_default(); - - let crate_id = crate_graph.add_crate_root( - file_id, - edition, - Some(cargo[pkg].name.clone()), - cfg_options, - env, - proc_macro.clone(), - ); - if cargo[tgt].kind == TargetKind::Lib { - lib_tgt = Some((crate_id, cargo[tgt].name.clone())); - pkg_to_lib_crate.insert(pkg, crate_id); - } - if cargo[tgt].is_proc_macro { - if let Some(proc_macro) = libproc_macro { - if crate_graph - .add_dep( - crate_id, - CrateName::new("proc_macro").unwrap(), - proc_macro, - ) - .is_err() - { - log::error!( - "cyclic dependency on proc_macro for {}", - &cargo[pkg].name - ) - } - } - } - - pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); - } - } - - // Set deps to the core, std and to the lib target of the current package - for &from in pkg_crates.get(&pkg).into_iter().flatten() { - if let Some((to, name)) = lib_tgt.clone() { - if to != from - && crate_graph - .add_dep( - from, - // For root projects with dashes in their name, - // cargo metadata does not do any normalization, - // so we do it ourselves currently - CrateName::normalize_dashes(&name), - to, - ) - .is_err() - { - { - log::error!( - "cyclic dependency between targets of {}", - &cargo[pkg].name - ) - } - } - } - // core is added as a dependency before std in order to - // mimic rustcs dependency order - if let Some(core) = libcore { - if crate_graph - .add_dep(from, CrateName::new("core").unwrap(), core) - .is_err() - { - log::error!("cyclic dependency on core for {}", &cargo[pkg].name) - } - } - if let Some(alloc) = liballoc { - if crate_graph - .add_dep(from, CrateName::new("alloc").unwrap(), alloc) - .is_err() - { - log::error!("cyclic dependency on alloc for {}", &cargo[pkg].name) - } - } - if let Some(std) = libstd { - if crate_graph - .add_dep(from, CrateName::new("std").unwrap(), std) - .is_err() - { - log::error!("cyclic dependency on std for {}", &cargo[pkg].name) - } - } - } - } - - // Now add a dep edge from all targets of upstream to the lib - // target of downstream. - for pkg in cargo.packages() { - for dep in cargo[pkg].dependencies.iter() { - if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { - for &from in pkg_crates.get(&pkg).into_iter().flatten() { - if crate_graph - .add_dep(from, CrateName::new(&dep.name).unwrap(), to) - .is_err() - { - log::error!( - "cyclic dependency {} -> {}", - &cargo[pkg].name, - &cargo[dep.pkg].name - ) - } - } - } - } - } - } - } - crate_graph - } -} - -fn get_rustc_cfg_options(target: Option<&str>) -> Vec { - let mut res = Vec::new(); - - // Some nightly-only cfgs, which are required for stdlib - res.push(CfgFlag::Atom("target_thread_local".into())); - for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() { - for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() { - res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() }); - } - } - - let rustc_cfgs = { - let mut cmd = Command::new(toolchain::rustc()); - cmd.args(&["--print", "cfg", "-O"]); - if let Some(target) = target { - cmd.args(&["--target", target]); - } - utf8_stdout(cmd) - }; - - match rustc_cfgs { - Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())), - Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), - } - - res -} - -fn utf8_stdout(mut cmd: Command) -> Result { - let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; - if !output.status.success() { - match String::from_utf8(output.stderr) { - Ok(stderr) if !stderr.is_empty() => { - bail!("{:?} failed, {}\nstderr:\n{}", cmd, output.status, stderr) - } - _ => bail!("{:?} failed, {}", cmd, output.status), - } - } - let stdout = String::from_utf8(output.stdout)?; - Ok(stdout) -} diff --git a/crates/ra_project_model/src/project_json.rs b/crates/ra_project_model/src/project_json.rs deleted file mode 100644 index e3f3163f6..000000000 --- a/crates/ra_project_model/src/project_json.rs +++ /dev/null @@ -1,143 +0,0 @@ -//! FIXME: write short doc here - -use std::path::PathBuf; - -use paths::{AbsPath, AbsPathBuf}; -use ra_db::{CrateId, CrateName, Dependency, Edition}; -use rustc_hash::FxHashMap; -use serde::{de, Deserialize}; - -use crate::cfg_flag::CfgFlag; - -/// Roots and crates that compose this Rust project. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ProjectJson { - pub(crate) crates: Vec, -} - -/// A crate points to the root module of a crate and lists the dependencies of the crate. This is -/// useful in creating the crate graph. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Crate { - pub(crate) root_module: AbsPathBuf, - pub(crate) edition: Edition, - pub(crate) deps: Vec, - pub(crate) cfg: Vec, - pub(crate) target: Option, - pub(crate) env: FxHashMap, - pub(crate) proc_macro_dylib_path: Option, - pub(crate) is_workspace_member: bool, - pub(crate) include: Vec, - pub(crate) exclude: Vec, -} - -impl ProjectJson { - pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { - ProjectJson { - crates: data - .crates - .into_iter() - .map(|crate_data| { - let is_workspace_member = crate_data.is_workspace_member.unwrap_or_else(|| { - crate_data.root_module.is_relative() - && !crate_data.root_module.starts_with("..") - || crate_data.root_module.starts_with(base) - }); - let root_module = base.join(crate_data.root_module); - let (include, exclude) = match crate_data.source { - Some(src) => { - let absolutize = |dirs: Vec| { - dirs.into_iter().map(|it| base.join(it)).collect::>() - }; - (absolutize(src.include_dirs), absolutize(src.exclude_dirs)) - } - None => (vec![root_module.parent().unwrap().to_path_buf()], Vec::new()), - }; - - Crate { - root_module, - edition: crate_data.edition.into(), - deps: crate_data - .deps - .into_iter() - .map(|dep_data| Dependency { - crate_id: CrateId(dep_data.krate as u32), - name: dep_data.name, - }) - .collect::>(), - cfg: crate_data.cfg, - target: crate_data.target, - env: crate_data.env, - proc_macro_dylib_path: crate_data - .proc_macro_dylib_path - .map(|it| base.join(it)), - is_workspace_member, - include, - exclude, - } - }) - .collect::>(), - } - } -} - -#[derive(Deserialize)] -pub struct ProjectJsonData { - crates: Vec, -} - -#[derive(Deserialize)] -struct CrateData { - root_module: PathBuf, - edition: EditionData, - deps: Vec, - #[serde(default)] - cfg: Vec, - target: Option, - #[serde(default)] - env: FxHashMap, - proc_macro_dylib_path: Option, - is_workspace_member: Option, - source: Option, -} - -#[derive(Deserialize)] -#[serde(rename = "edition")] -enum EditionData { - #[serde(rename = "2015")] - Edition2015, - #[serde(rename = "2018")] - Edition2018, -} - -impl From for Edition { - fn from(data: EditionData) -> Self { - match data { - EditionData::Edition2015 => Edition::Edition2015, - EditionData::Edition2018 => Edition::Edition2018, - } - } -} - -#[derive(Deserialize)] -struct DepData { - /// Identifies a crate by position in the crates array. - #[serde(rename = "crate")] - krate: usize, - #[serde(deserialize_with = "deserialize_crate_name")] - name: CrateName, -} - -#[derive(Deserialize)] -struct CrateSource { - include_dirs: Vec, - exclude_dirs: Vec, -} - -fn deserialize_crate_name<'de, D>(de: D) -> Result -where - D: de::Deserializer<'de>, -{ - let name = String::deserialize(de)?; - CrateName::new(&name).map_err(|err| de::Error::custom(format!("invalid crate name: {:?}", err))) -} diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs deleted file mode 100644 index 8239797b6..000000000 --- a/crates/ra_project_model/src/sysroot.rs +++ /dev/null @@ -1,173 +0,0 @@ -//! FIXME: write short doc here - -use std::{convert::TryFrom, env, ops, path::Path, process::Command}; - -use anyhow::{bail, format_err, Result}; -use arena::{Arena, Idx}; -use paths::{AbsPath, AbsPathBuf}; - -use crate::utf8_stdout; - -#[derive(Default, Debug, Clone, Eq, PartialEq)] -pub struct Sysroot { - crates: Arena, -} - -pub type SysrootCrate = Idx; - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct SysrootCrateData { - pub name: String, - pub root: AbsPathBuf, - pub deps: Vec, -} - -impl ops::Index for Sysroot { - type Output = SysrootCrateData; - fn index(&self, index: SysrootCrate) -> &SysrootCrateData { - &self.crates[index] - } -} - -impl Sysroot { - pub fn core(&self) -> Option { - self.by_name("core") - } - - pub fn alloc(&self) -> Option { - self.by_name("alloc") - } - - pub fn std(&self) -> Option { - self.by_name("std") - } - - pub fn proc_macro(&self) -> Option { - self.by_name("proc_macro") - } - - pub fn crates<'a>(&'a self) -> impl Iterator + ExactSizeIterator + 'a { - self.crates.iter().map(|(id, _data)| id) - } - - pub fn discover(cargo_toml: &AbsPath) -> Result { - let src = get_or_install_rust_src(cargo_toml)?; - let mut sysroot = Sysroot { crates: Arena::default() }; - for name in SYSROOT_CRATES.trim().lines() { - // FIXME: remove this path when 1.47 comes out - // https://github.com/rust-lang/rust/pull/73265 - let root = src.join(format!("lib{}", name)).join("lib.rs"); - if root.exists() { - sysroot.crates.alloc(SysrootCrateData { - name: name.into(), - root, - deps: Vec::new(), - }); - } else { - let root = src.join(name).join("src/lib.rs"); - if root.exists() { - sysroot.crates.alloc(SysrootCrateData { - name: name.into(), - root, - deps: Vec::new(), - }); - } - } - } - if let Some(std) = sysroot.std() { - for dep in STD_DEPS.trim().lines() { - if let Some(dep) = sysroot.by_name(dep) { - sysroot.crates[std].deps.push(dep) - } - } - } - if let Some(alloc) = sysroot.alloc() { - if let Some(core) = sysroot.core() { - sysroot.crates[alloc].deps.push(core); - } - } - Ok(sysroot) - } - - fn by_name(&self, name: &str) -> Option { - self.crates.iter().find(|(_id, data)| data.name == name).map(|(id, _data)| id) - } -} - -fn get_or_install_rust_src(cargo_toml: &AbsPath) -> Result { - if let Ok(path) = env::var("RUST_SRC_PATH") { - let path = AbsPathBuf::try_from(path.as_str()) - .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?; - return Ok(path); - } - let current_dir = cargo_toml.parent().unwrap(); - let mut rustc = Command::new(toolchain::rustc()); - rustc.current_dir(current_dir).args(&["--print", "sysroot"]); - let stdout = utf8_stdout(rustc)?; - let sysroot_path = AbsPath::assert(Path::new(stdout.trim())); - let mut src = get_rust_src(sysroot_path); - if src.is_none() { - let mut rustup = Command::new(toolchain::rustup()); - rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]); - utf8_stdout(rustup)?; - src = get_rust_src(sysroot_path); - } - match src { - Some(r) => Ok(r), - None => bail!( - "can't load standard library from sysroot\n\ - {}\n\ - (discovered via `rustc --print sysroot`)\n\ - try running `rustup component add rust-src` or set `RUST_SRC_PATH`", - sysroot_path.display(), - ), - } -} - -fn get_rust_src(sysroot_path: &AbsPath) -> Option { - // try the new path first since the old one still exists - let mut src_path = sysroot_path.join("lib/rustlib/src/rust/library"); - if !src_path.exists() { - // FIXME: remove this path when 1.47 comes out - // https://github.com/rust-lang/rust/pull/73265 - src_path = sysroot_path.join("lib/rustlib/src/rust/src"); - } - if src_path.exists() { - Some(src_path) - } else { - None - } -} - -impl SysrootCrateData { - pub fn root_dir(&self) -> &AbsPath { - self.root.parent().unwrap() - } -} - -const SYSROOT_CRATES: &str = " -alloc -core -panic_abort -panic_unwind -proc_macro -profiler_builtins -rtstartup -std -stdarch -term -test -unwind"; - -const STD_DEPS: &str = " -alloc -core -panic_abort -panic_unwind -profiler_builtins -rtstartup -proc_macro -stdarch -term -test -unwind"; -- cgit v1.2.3