Skip to content

Commit 0d0ef0a

Browse files
Wilfreddavidbarsky
authored andcommitted
feature: add build system info; runnables to rust-project.json
1 parent f28f15a commit 0d0ef0a

File tree

17 files changed

+570
-254
lines changed

17 files changed

+570
-254
lines changed

crates/project-model/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ mod cargo_workspace;
2222
mod cfg;
2323
mod env;
2424
mod manifest_path;
25-
mod project_json;
25+
pub mod project_json;
2626
mod rustc_cfg;
2727
mod sysroot;
2828
pub mod target_data_layout;

crates/project-model/src/project_json.rs

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
5454
use rustc_hash::FxHashMap;
5555
use serde::{de, Deserialize, Serialize};
5656
use span::Edition;
57+
use std::path::PathBuf;
5758

58-
use crate::cfg::CfgFlag;
59-
use crate::ManifestPath;
59+
use crate::{cfg::CfgFlag, ManifestPath, TargetKind};
6060

6161
/// Roots and crates that compose this Rust project.
6262
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -88,6 +88,23 @@ pub struct Crate {
8888
pub(crate) exclude: Vec<AbsPathBuf>,
8989
pub(crate) is_proc_macro: bool,
9090
pub(crate) repository: Option<String>,
91+
pub build: Option<Build>,
92+
}
93+
94+
/// Additional metadata about a crate, used to configure runnables.
95+
#[derive(Clone, Debug, Eq, PartialEq)]
96+
pub struct Build {
97+
/// The name associated with this crate, according to the custom
98+
/// build system being used.
99+
pub label: String,
100+
/// Path corresponding to the build-system-specific file defining the crate.
101+
pub build_file: PathBuf,
102+
/// What kind of target is this crate? For example, we don't want
103+
/// to offer a 'run' button for library crates.
104+
pub target_kind: TargetKind,
105+
/// Configuration for shell commands, such as CLI invocations for
106+
/// a check build or a test run.
107+
pub runnables: Vec<Runnables>,
91108
}
92109

93110
impl ProjectJson {
@@ -127,6 +144,16 @@ impl ProjectJson {
127144
None => (vec![root_module.parent().unwrap().to_path_buf()], Vec::new()),
128145
};
129146

147+
let build = match crate_data.build {
148+
Some(build) => Some(Build {
149+
label: build.label,
150+
build_file: build.build_file,
151+
target_kind: build.target_kind.into(),
152+
runnables: build.runnables,
153+
}),
154+
None => None,
155+
};
156+
130157
Crate {
131158
display_name: crate_data
132159
.display_name
@@ -146,6 +173,7 @@ impl ProjectJson {
146173
exclude,
147174
is_proc_macro: crate_data.is_proc_macro,
148175
repository: crate_data.repository,
176+
build,
149177
}
150178
})
151179
.collect(),
@@ -167,10 +195,23 @@ impl ProjectJson {
167195
&self.project_root
168196
}
169197

198+
pub fn crate_by_root(&self, root: &AbsPath) -> Option<Crate> {
199+
self.crates
200+
.iter()
201+
.filter(|krate| krate.is_workspace_member)
202+
.find(|krate| krate.root_module == root)
203+
.cloned()
204+
}
205+
170206
/// Returns the path to the project's manifest or root folder, if no manifest exists.
171207
pub fn manifest_or_root(&self) -> &AbsPath {
172208
self.manifest.as_ref().map_or(&self.project_root, |manifest| manifest.as_ref())
173209
}
210+
211+
/// Returns the manifest
212+
pub fn manifest(&self) -> Option<&ManifestPath> {
213+
self.manifest.as_ref()
214+
}
174215
}
175216

176217
#[derive(Serialize, Deserialize, Debug, Clone)]
@@ -200,6 +241,8 @@ struct CrateData {
200241
is_proc_macro: bool,
201242
#[serde(default)]
202243
repository: Option<String>,
244+
#[serde(default)]
245+
build: Option<BuildData>,
203246
}
204247

205248
#[derive(Serialize, Deserialize, Debug, Clone)]
@@ -215,6 +258,50 @@ enum EditionData {
215258
Edition2024,
216259
}
217260

261+
#[derive(Serialize, Deserialize, Debug, Clone)]
262+
pub struct BuildData {
263+
label: String,
264+
build_file: PathBuf,
265+
target_kind: TargetKindData,
266+
runnables: Vec<Runnables>,
267+
}
268+
269+
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
270+
#[serde(rename_all = "camelCase")]
271+
pub struct Runnables {
272+
pub program: String,
273+
pub args: Vec<String>,
274+
pub cwd: PathBuf,
275+
pub kind: RunnableKind,
276+
}
277+
278+
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
279+
#[serde(rename_all = "camelCase")]
280+
pub enum RunnableKind {
281+
Check,
282+
Run,
283+
TestOne,
284+
}
285+
286+
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
287+
#[serde(rename_all = "camelCase")]
288+
pub enum TargetKindData {
289+
Bin,
290+
/// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...).
291+
Lib,
292+
Test,
293+
}
294+
295+
impl From<TargetKindData> for TargetKind {
296+
fn from(value: TargetKindData) -> Self {
297+
match value {
298+
TargetKindData::Bin => TargetKind::Bin,
299+
TargetKindData::Lib => TargetKind::Lib { is_proc_macro: false },
300+
TargetKindData::Test => TargetKind::Test,
301+
}
302+
}
303+
}
304+
218305
impl From<EditionData> for Edition {
219306
fn from(data: EditionData) -> Self {
220307
match data {

crates/project-model/src/workspace.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ pub enum ProjectWorkspaceKind {
7676
/// Environment variables set in the `.cargo/config` file.
7777
cargo_config_extra_env: FxHashMap<String, String>,
7878
},
79-
/// Project workspace was manually specified using a `rust-project.json` file.
79+
/// Project workspace was specified using a `rust-project.json` file.
8080
Json(ProjectJson),
8181
// FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
8282
// That's not the end user experience we should strive for.

crates/rust-analyzer/src/global_state.rs

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ use parking_lot::{
1818
RwLockWriteGuard,
1919
};
2020
use proc_macro_api::ProcMacroServer;
21-
use project_model::{
22-
CargoWorkspace, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, Target,
23-
WorkspaceBuildScripts,
24-
};
21+
use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts};
2522
use rustc_hash::{FxHashMap, FxHashSet};
2623
use tracing::{span, Level};
2724
use triomphe::Arc;
@@ -37,6 +34,7 @@ use crate::{
3734
mem_docs::MemDocs,
3835
op_queue::OpQueue,
3936
reload,
37+
target_spec::{CargoTargetSpec, ProjectJsonTargetSpec, TargetSpec},
4038
task_pool::{TaskPool, TaskQueue},
4139
};
4240

@@ -478,21 +476,52 @@ impl GlobalStateSnapshot {
478476
self.vfs_read().file_path(file_id).clone()
479477
}
480478

481-
pub(crate) fn cargo_target_for_crate_root(
482-
&self,
483-
crate_id: CrateId,
484-
) -> Option<(&CargoWorkspace, Target)> {
479+
pub(crate) fn target_spec_for_crate(&self, crate_id: CrateId) -> Option<TargetSpec> {
485480
let file_id = self.analysis.crate_root(crate_id).ok()?;
486481
let path = self.vfs_read().file_path(file_id).clone();
487482
let path = path.as_path()?;
488-
self.workspaces.iter().find_map(|ws| match &ws.kind {
489-
ProjectWorkspaceKind::Cargo { cargo, .. }
490-
| ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => {
491-
cargo.target_by_root(path).map(|it| (cargo, it))
492-
}
493-
ProjectWorkspaceKind::Json { .. } => None,
494-
ProjectWorkspaceKind::DetachedFile { .. } => None,
495-
})
483+
484+
for workspace in self.workspaces.iter() {
485+
match &workspace.kind {
486+
ProjectWorkspaceKind::Cargo { cargo, .. }
487+
| ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => {
488+
let Some(target_idx) = cargo.target_by_root(path) else {
489+
continue;
490+
};
491+
492+
let target_data = &cargo[target_idx];
493+
let package_data = &cargo[target_data.package];
494+
495+
return Some(TargetSpec::Cargo(CargoTargetSpec {
496+
workspace_root: cargo.workspace_root().to_path_buf(),
497+
cargo_toml: package_data.manifest.clone(),
498+
crate_id,
499+
package: cargo.package_flag(package_data),
500+
target: target_data.name.clone(),
501+
target_kind: target_data.kind,
502+
required_features: target_data.required_features.clone(),
503+
features: package_data.features.keys().cloned().collect(),
504+
}));
505+
}
506+
ProjectWorkspaceKind::Json(project) => {
507+
let Some(krate) = project.crate_by_root(path) else {
508+
continue;
509+
};
510+
let Some(build) = krate.build else {
511+
continue;
512+
};
513+
514+
return Some(TargetSpec::ProjectJson(ProjectJsonTargetSpec {
515+
target_kind: build.target_kind,
516+
label: build.label,
517+
shell_runnables: build.runnables,
518+
}));
519+
}
520+
ProjectWorkspaceKind::DetachedFile { .. } => {}
521+
};
522+
}
523+
524+
None
496525
}
497526

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

0 commit comments

Comments
 (0)