Skip to content

Commit e855e9c

Browse files
Wilfreddavidbarsky
authored andcommitted
feature: add build system info; runnables to rust-project.json
1 parent 14a1f45 commit e855e9c

File tree

17 files changed

+575
-226
lines changed

17 files changed

+575
-226
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: 120 additions & 3 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)]
@@ -68,6 +68,9 @@ pub struct ProjectJson {
6868
project_root: AbsPathBuf,
6969
manifest: Option<ManifestPath>,
7070
crates: Vec<Crate>,
71+
/// Configuration for commands, such as CLI invocations for
72+
/// a check build or a test run.
73+
runnables: Vec<Runnable>,
7174
}
7275

7376
/// 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 {
8891
pub(crate) exclude: Vec<AbsPathBuf>,
8992
pub(crate) is_proc_macro: bool,
9093
pub(crate) repository: Option<String>,
94+
pub build: Option<Build>,
95+
}
96+
97+
/// Additional metadata about a crate, used to configure runnables.
98+
#[derive(Clone, Debug, Eq, PartialEq)]
99+
pub struct Build {
100+
/// The name associated with this crate, according to the custom
101+
/// build system being used.
102+
pub label: String,
103+
/// Path corresponding to the build system-specific file defining the crate.
104+
pub build_file: PathBuf,
105+
/// What kind of target is this crate? For example, we don't want
106+
/// to offer a 'run' button for library crates.
107+
pub target_kind: TargetKind,
108+
}
109+
110+
#[derive(Debug, Clone, PartialEq, Eq)]
111+
pub struct Runnable {
112+
pub program: String,
113+
pub args: Vec<String>,
114+
pub cwd: PathBuf,
115+
pub kind: RunnableKind,
116+
}
117+
118+
#[derive(Debug, Clone, PartialEq, Eq)]
119+
pub enum RunnableKind {
120+
Check,
121+
Run,
122+
TestOne,
91123
}
92124

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

163+
let build = match crate_data.build {
164+
Some(build) => Some(Build {
165+
label: build.label,
166+
build_file: build.build_file,
167+
target_kind: build.target_kind.into(),
168+
}),
169+
None => None,
170+
};
171+
130172
Crate {
131173
display_name: crate_data
132174
.display_name
@@ -146,6 +188,7 @@ impl ProjectJson {
146188
exclude,
147189
is_proc_macro: crate_data.is_proc_macro,
148190
repository: crate_data.repository,
191+
build,
149192
}
150193
})
151194
.collect(),
@@ -167,7 +210,15 @@ impl ProjectJson {
167210
&self.project_root
168211
}
169212

170-
/// Returns the path to the project's manifest file, if it exists.
213+
pub fn crate_by_root(&self, root: &AbsPath) -> Option<Crate> {
214+
self.crates
215+
.iter()
216+
.filter(|krate| krate.is_workspace_member)
217+
.find(|krate| krate.root_module == root)
218+
.cloned()
219+
}
220+
221+
/// Returns the path to the project's manifest, if it exists.
171222
pub fn manifest(&self) -> Option<&ManifestPath> {
172223
self.manifest.as_ref()
173224
}
@@ -176,13 +227,19 @@ impl ProjectJson {
176227
pub fn manifest_or_root(&self) -> &AbsPath {
177228
self.manifest.as_ref().map_or(&self.project_root, |manifest| manifest.as_ref())
178229
}
230+
231+
pub fn runnables(&self) -> &[Runnable] {
232+
&self.runnables
233+
}
179234
}
180235

181236
#[derive(Serialize, Deserialize, Debug, Clone)]
182237
pub struct ProjectJsonData {
183238
sysroot: Option<Utf8PathBuf>,
184239
sysroot_src: Option<Utf8PathBuf>,
185240
crates: Vec<CrateData>,
241+
#[serde(default)]
242+
runnables: Vec<RunnableData>,
186243
}
187244

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

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

282+
#[derive(Serialize, Deserialize, Debug, Clone)]
283+
pub struct BuildData {
284+
label: String,
285+
build_file: PathBuf,
286+
target_kind: TargetKindData,
287+
}
288+
289+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
290+
pub struct RunnableData {
291+
pub program: String,
292+
pub args: Vec<String>,
293+
pub cwd: PathBuf,
294+
pub kind: RunnableKindData,
295+
}
296+
297+
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
298+
#[serde(rename_all = "camelCase")]
299+
pub enum RunnableKindData {
300+
Check,
301+
Run,
302+
TestOne,
303+
}
304+
305+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
306+
#[serde(rename_all = "camelCase")]
307+
pub enum TargetKindData {
308+
Bin,
309+
/// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...).
310+
Lib,
311+
Test,
312+
}
313+
314+
impl From<TargetKindData> for TargetKind {
315+
fn from(data: TargetKindData) -> Self {
316+
match data {
317+
TargetKindData::Bin => TargetKind::Bin,
318+
TargetKindData::Lib => TargetKind::Lib { is_proc_macro: false },
319+
TargetKindData::Test => TargetKind::Test,
320+
}
321+
}
322+
}
323+
223324
impl From<EditionData> for Edition {
224325
fn from(data: EditionData) -> Self {
225326
match data {
@@ -231,6 +332,22 @@ impl From<EditionData> for Edition {
231332
}
232333
}
233334

335+
impl From<RunnableData> for Runnable {
336+
fn from(data: RunnableData) -> Self {
337+
Runnable { program: data.program, args: data.args, cwd: data.cwd, kind: data.kind.into() }
338+
}
339+
}
340+
341+
impl From<RunnableKindData> for RunnableKind {
342+
fn from(data: RunnableKindData) -> Self {
343+
match data {
344+
RunnableKindData::Check => RunnableKind::Check,
345+
RunnableKindData::Run => RunnableKind::Run,
346+
RunnableKindData::TestOne => RunnableKind::TestOne,
347+
}
348+
}
349+
}
350+
234351
/// Identifies a crate by position in the crates array.
235352
///
236353
/// This will differ from `CrateId` when multiple `ProjectJson`

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;
@@ -40,6 +37,7 @@ use crate::{
4037
mem_docs::MemDocs,
4138
op_queue::OpQueue,
4239
reload,
40+
target_spec::{CargoTargetSpec, ProjectJsonTargetSpec, TargetSpec},
4341
task_pool::{TaskPool, TaskQueue},
4442
};
4543

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

559-
pub(crate) fn cargo_target_for_crate_root(
560-
&self,
561-
crate_id: CrateId,
562-
) -> Option<(&CargoWorkspace, Target)> {
557+
pub(crate) fn target_spec_for_crate(&self, crate_id: CrateId) -> Option<TargetSpec> {
563558
let file_id = self.analysis.crate_root(crate_id).ok()?;
564559
let path = self.vfs_read().file_path(file_id).clone();
565560
let path = path.as_path()?;
566-
self.workspaces.iter().find_map(|ws| match &ws.kind {
567-
ProjectWorkspaceKind::Cargo { cargo, .. }
568-
| ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => {
569-
cargo.target_by_root(path).map(|it| (cargo, it))
570-
}
571-
ProjectWorkspaceKind::Json { .. } => None,
572-
ProjectWorkspaceKind::DetachedFile { .. } => None,
573-
})
561+
562+
for workspace in self.workspaces.iter() {
563+
match &workspace.kind {
564+
ProjectWorkspaceKind::Cargo { cargo, .. }
565+
| ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => {
566+
let Some(target_idx) = cargo.target_by_root(path) else {
567+
continue;
568+
};
569+
570+
let target_data = &cargo[target_idx];
571+
let package_data = &cargo[target_data.package];
572+
573+
return Some(TargetSpec::Cargo(CargoTargetSpec {
574+
workspace_root: cargo.workspace_root().to_path_buf(),
575+
cargo_toml: package_data.manifest.clone(),
576+
crate_id,
577+
package: cargo.package_flag(package_data),
578+
target: target_data.name.clone(),
579+
target_kind: target_data.kind,
580+
required_features: target_data.required_features.clone(),
581+
features: package_data.features.keys().cloned().collect(),
582+
}));
583+
}
584+
ProjectWorkspaceKind::Json(project) => {
585+
let Some(krate) = project.crate_by_root(path) else {
586+
continue;
587+
};
588+
let Some(build) = krate.build else {
589+
continue;
590+
};
591+
592+
return Some(TargetSpec::ProjectJson(ProjectJsonTargetSpec {
593+
label: build.label,
594+
target_kind: build.target_kind,
595+
shell_runnables: project.runnables().to_owned(),
596+
}));
597+
}
598+
ProjectWorkspaceKind::DetachedFile { .. } => {}
599+
};
600+
}
601+
602+
None
574603
}
575604

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

0 commit comments

Comments
 (0)