//! In rust-analyzer, we maintain a strict separation between pure abstract //! semantic project model and a concrete model of a particular build system. //! //! Pure model is represented by the `base_db::CrateGraph` from another crate. //! //! In this crate, we are concerned with "real world" project models. //! //! Specifically, here we have a representation for a Cargo project //! ([`CargoWorkspace`]) and for manually specified layout ([`ProjectJson`]). //! //! Roughly, the things we do here are: //! //! * Project discovery (where's the relevant Cargo.toml for the current dir). //! * Custom build steps (`build.rs` code generation and compilation of //! procedural macros). //! * Lowering of concrete model to a `base_db::CrateGraph` // It's useful to refer to code that is private in doc comments. #![allow(rustdoc::private_intra_doc_links)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; pub mod project_json; pub mod toolchain_info { pub mod rustc_cfg; pub mod target_data; pub mod target_tuple; pub mod version; use std::path::Path; use crate::{ManifestPath, Sysroot, cargo_config_file::CargoConfigFile}; #[derive(Copy, Clone)] pub enum QueryConfig<'a> { /// Directly invoke `rustc` to query the desired information. Rustc(&'a Sysroot, &'a Path), /// Attempt to use cargo to query the desired information, honoring cargo configurations. /// If this fails, falls back to invoking `rustc` directly. Cargo(&'a Sysroot, &'a ManifestPath, &'a Option), } } mod build_dependencies; mod cargo_config_file; mod cargo_workspace; mod env; mod manifest_path; mod sysroot; mod workspace; #[cfg(test)] mod tests; use std::{ fmt, fs::{self, ReadDir, read_dir}, io, process::Command, }; use anyhow::{Context, bail, format_err}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashSet; pub use crate::{ build_dependencies::{ProcMacroDylibPath, WorkspaceBuildScripts}, cargo_workspace::{ CargoConfig, CargoFeatures, CargoMetadataConfig, CargoWorkspace, Package, PackageData, PackageDependency, RustLibSource, Target, TargetData, TargetDirectoryConfig, TargetKind, }, manifest_path::ManifestPath, project_json::{ProjectJson, ProjectJsonData}, sysroot::Sysroot, workspace::{FileLoader, PackageRoot, ProjectWorkspace, ProjectWorkspaceKind}, }; pub use cargo_metadata::Metadata; #[derive(Debug, Clone, PartialEq, Eq)] pub struct ProjectJsonFromCommand { /// The data describing this project, such as its dependencies. pub data: ProjectJsonData, /// The build system specific file that describes this project, /// such as a `my-project/BUCK` file. pub buildfile: AbsPathBuf, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub enum ProjectManifest { ProjectJson(ManifestPath), CargoToml(ManifestPath), CargoScript(ManifestPath), } impl ProjectManifest { pub fn from_manifest_file(path: AbsPathBuf) -> anyhow::Result { let path = ManifestPath::try_from(path) .map_err(|path| format_err!("bad manifest path: {path}"))?; if path.file_name().unwrap_or_default() == "rust-project.json" { return Ok(ProjectManifest::ProjectJson(path)); } if path.file_name().unwrap_or_default() == ".rust-project.json" { return Ok(ProjectManifest::ProjectJson(path)); } if path.file_name().unwrap_or_default() == "Cargo.toml" { return Ok(ProjectManifest::CargoToml(path)); } if path.extension().unwrap_or_default() == "rs" { return Ok(ProjectManifest::CargoScript(path)); } bail!( "project root must point to a Cargo.toml, rust-project.json or