Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: add build system info; runnables to rust-project.json
Browse files Browse the repository at this point in the history
Wilfred authored and davidbarsky committed Jun 10, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 14a1f45 commit e855e9c
Showing 17 changed files with 575 additions and 226 deletions.
2 changes: 1 addition & 1 deletion crates/project-model/src/lib.rs
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ mod cargo_workspace;
mod cfg;
mod env;
mod manifest_path;
mod project_json;
pub mod project_json;
mod rustc_cfg;
mod sysroot;
pub mod target_data_layout;
123 changes: 120 additions & 3 deletions crates/project-model/src/project_json.rs
Original file line number Diff line number Diff line change
@@ -54,9 +54,9 @@ use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
use rustc_hash::FxHashMap;
use serde::{de, Deserialize, Serialize};
use span::Edition;
use std::path::PathBuf;

use crate::cfg::CfgFlag;
use crate::ManifestPath;
use crate::{cfg::CfgFlag, ManifestPath, TargetKind};

/// Roots and crates that compose this Rust project.
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -68,6 +68,9 @@ pub struct ProjectJson {
project_root: AbsPathBuf,
manifest: Option<ManifestPath>,
crates: Vec<Crate>,
/// Configuration for commands, such as CLI invocations for
/// a check build or a test run.
runnables: Vec<Runnable>,
}

/// A crate points to the root module of a crate and lists the dependencies of the crate. This is
@@ -88,6 +91,35 @@ pub struct Crate {
pub(crate) exclude: Vec<AbsPathBuf>,
pub(crate) is_proc_macro: bool,
pub(crate) repository: Option<String>,
pub build: Option<Build>,
}

/// Additional metadata about a crate, used to configure runnables.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Build {
/// The name associated with this crate, according to the custom
/// build system being used.
pub label: String,
/// Path corresponding to the build system-specific file defining the crate.
pub build_file: PathBuf,
/// What kind of target is this crate? For example, we don't want
/// to offer a 'run' button for library crates.
pub target_kind: TargetKind,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Runnable {
pub program: String,
pub args: Vec<String>,
pub cwd: PathBuf,
pub kind: RunnableKind,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RunnableKind {
Check,
Run,
TestOne,
}

impl ProjectJson {
@@ -109,6 +141,7 @@ impl ProjectJson {
sysroot_src: data.sysroot_src.map(absolutize_on_base),
project_root: base.to_path_buf(),
manifest,
runnables: data.runnables.into_iter().map(Runnable::from).collect(),
crates: data
.crates
.into_iter()
@@ -127,6 +160,15 @@ impl ProjectJson {
None => (vec![root_module.parent().unwrap().to_path_buf()], Vec::new()),
};

let build = match crate_data.build {
Some(build) => Some(Build {
label: build.label,
build_file: build.build_file,
target_kind: build.target_kind.into(),
}),
None => None,
};

Crate {
display_name: crate_data
.display_name
@@ -146,6 +188,7 @@ impl ProjectJson {
exclude,
is_proc_macro: crate_data.is_proc_macro,
repository: crate_data.repository,
build,
}
})
.collect(),
@@ -167,7 +210,15 @@ impl ProjectJson {
&self.project_root
}

/// Returns the path to the project's manifest file, if it exists.
pub fn crate_by_root(&self, root: &AbsPath) -> Option<Crate> {
self.crates
.iter()
.filter(|krate| krate.is_workspace_member)
.find(|krate| krate.root_module == root)
.cloned()
}

/// Returns the path to the project's manifest, if it exists.
pub fn manifest(&self) -> Option<&ManifestPath> {
self.manifest.as_ref()
}
@@ -176,13 +227,19 @@ impl ProjectJson {
pub fn manifest_or_root(&self) -> &AbsPath {
self.manifest.as_ref().map_or(&self.project_root, |manifest| manifest.as_ref())
}

pub fn runnables(&self) -> &[Runnable] {
&self.runnables
}
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ProjectJsonData {
sysroot: Option<Utf8PathBuf>,
sysroot_src: Option<Utf8PathBuf>,
crates: Vec<CrateData>,
#[serde(default)]
runnables: Vec<RunnableData>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
@@ -205,6 +262,8 @@ struct CrateData {
is_proc_macro: bool,
#[serde(default)]
repository: Option<String>,
#[serde(default)]
build: Option<BuildData>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
@@ -220,6 +279,48 @@ enum EditionData {
Edition2024,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BuildData {
label: String,
build_file: PathBuf,
target_kind: TargetKindData,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RunnableData {
pub program: String,
pub args: Vec<String>,
pub cwd: PathBuf,
pub kind: RunnableKindData,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum RunnableKindData {
Check,
Run,
TestOne,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum TargetKindData {
Bin,
/// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...).
Lib,
Test,
}

impl From<TargetKindData> for TargetKind {
fn from(data: TargetKindData) -> Self {
match data {
TargetKindData::Bin => TargetKind::Bin,
TargetKindData::Lib => TargetKind::Lib { is_proc_macro: false },
TargetKindData::Test => TargetKind::Test,
}
}
}

impl From<EditionData> for Edition {
fn from(data: EditionData) -> Self {
match data {
@@ -231,6 +332,22 @@ impl From<EditionData> for Edition {
}
}

impl From<RunnableData> for Runnable {
fn from(data: RunnableData) -> Self {
Runnable { program: data.program, args: data.args, cwd: data.cwd, kind: data.kind.into() }
}
}

impl From<RunnableKindData> for RunnableKind {
fn from(data: RunnableKindData) -> Self {
match data {
RunnableKindData::Check => RunnableKind::Check,
RunnableKindData::Run => RunnableKind::Run,
RunnableKindData::TestOne => RunnableKind::TestOne,
}
}
}

/// Identifies a crate by position in the crates array.
///
/// This will differ from `CrateId` when multiple `ProjectJson`
2 changes: 1 addition & 1 deletion crates/project-model/src/workspace.rs
Original file line number Diff line number Diff line change
@@ -76,7 +76,7 @@ pub enum ProjectWorkspaceKind {
/// Environment variables set in the `.cargo/config` file.
cargo_config_extra_env: FxHashMap<String, String>,
},
/// Project workspace was manually specified using a `rust-project.json` file.
/// Project workspace was specified using a `rust-project.json` file.
Json(ProjectJson),
// FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
// That's not the end user experience we should strive for.
61 changes: 45 additions & 16 deletions crates/rust-analyzer/src/global_state.rs
Original file line number Diff line number Diff line change
@@ -18,10 +18,7 @@ use parking_lot::{
RwLockWriteGuard,
};
use proc_macro_api::ProcMacroServer;
use project_model::{
CargoWorkspace, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, Target,
WorkspaceBuildScripts,
};
use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts};
use rustc_hash::{FxHashMap, FxHashSet};
use tracing::{span, Level};
use triomphe::Arc;
@@ -40,6 +37,7 @@ use crate::{
mem_docs::MemDocs,
op_queue::OpQueue,
reload,
target_spec::{CargoTargetSpec, ProjectJsonTargetSpec, TargetSpec},
task_pool::{TaskPool, TaskQueue},
};

@@ -556,21 +554,52 @@ impl GlobalStateSnapshot {
self.vfs_read().file_path(file_id).clone()
}

pub(crate) fn cargo_target_for_crate_root(
&self,
crate_id: CrateId,
) -> Option<(&CargoWorkspace, Target)> {
pub(crate) fn target_spec_for_crate(&self, crate_id: CrateId) -> Option<TargetSpec> {
let file_id = self.analysis.crate_root(crate_id).ok()?;
let path = self.vfs_read().file_path(file_id).clone();
let path = path.as_path()?;
self.workspaces.iter().find_map(|ws| match &ws.kind {
ProjectWorkspaceKind::Cargo { cargo, .. }
| ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => {
cargo.target_by_root(path).map(|it| (cargo, it))
}
ProjectWorkspaceKind::Json { .. } => None,
ProjectWorkspaceKind::DetachedFile { .. } => None,
})

for workspace in self.workspaces.iter() {
match &workspace.kind {
ProjectWorkspaceKind::Cargo { cargo, .. }
| ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => {
let Some(target_idx) = cargo.target_by_root(path) else {
continue;
};

let target_data = &cargo[target_idx];
let package_data = &cargo[target_data.package];

return Some(TargetSpec::Cargo(CargoTargetSpec {
workspace_root: cargo.workspace_root().to_path_buf(),
cargo_toml: package_data.manifest.clone(),
crate_id,
package: cargo.package_flag(package_data),
target: target_data.name.clone(),
target_kind: target_data.kind,
required_features: target_data.required_features.clone(),
features: package_data.features.keys().cloned().collect(),
}));
}
ProjectWorkspaceKind::Json(project) => {
let Some(krate) = project.crate_by_root(path) else {
continue;
};
let Some(build) = krate.build else {
continue;
};

return Some(TargetSpec::ProjectJson(ProjectJsonTargetSpec {
label: build.label,
target_kind: build.target_kind,
shell_runnables: project.runnables().to_owned(),
}));
}
ProjectWorkspaceKind::DetachedFile { .. } => {}
};
}

None
}

pub(crate) fn file_exists(&self, file_id: FileId) -> bool {
Loading

0 comments on commit e855e9c

Please sign in to comment.