Skip to content
This repository was archived by the owner on Dec 29, 2022. It is now read-only.

Commit b92fc57

Browse files
authored
Merge pull request #441 from Xanewok/resolve-dep-graph
Create simple dep-graph from Cargo metadata
2 parents ecdbf0c + af093f9 commit b92fc57

File tree

3 files changed

+163
-12
lines changed

3 files changed

+163
-12
lines changed

src/build/cargo.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ use serde_json;
1717
use data::Analysis;
1818
use build::{Internals, BufWriter, BuildResult, CompilationContext};
1919
use build::environment::{self, Environment, EnvironmentLock};
20+
use super::plan::create_plan;
2021
use config::Config;
2122
use vfs::Vfs;
2223

2324
use std::collections::{HashMap, HashSet, BTreeMap};
2425
use std::env;
2526
use std::ffi::OsString;
2627
use std::fs::{read_dir, remove_file};
28+
use std::mem;
2729
use std::path::{Path};
2830
use std::process::Command;
2931
use std::sync::{Arc, Mutex};
@@ -79,18 +81,18 @@ fn run_cargo(compilation_cx: Arc<Mutex<CompilationContext>>,
7981
// guarantee consistent environment variables.
8082
let (lock_guard, inner_lock) = env_lock.lock();
8183

82-
let build_dir = {
83-
let compilation_cx = compilation_cx.lock().unwrap();
84-
compilation_cx.build_dir.as_ref().unwrap().clone()
85-
};
86-
87-
let exec = RlsExecutor::new(compilation_cx,
84+
let exec = RlsExecutor::new(compilation_cx.clone(),
8885
rls_config.clone(),
8986
inner_lock,
9087
vfs,
9188
compiler_messages,
9289
analyses);
9390

91+
// Hold the lock until we create `Workspace`, which is needed to create
92+
// a build plan.
93+
let mut compilation_cx_access = compilation_cx.lock().unwrap();
94+
let build_dir = compilation_cx_access.build_dir.as_ref().unwrap().clone();
95+
9496
// Note that this may not be equal build_dir when inside a workspace member
9597
let manifest_path = important_paths::find_root_manifest_for_wd(None, &build_dir)
9698
.expect(&format!("Couldn't find a root manifest for cwd: {:?}", &build_dir));
@@ -105,9 +107,15 @@ fn run_cargo(compilation_cx: Arc<Mutex<CompilationContext>>,
105107

106108
let ws = Workspace::new(&manifest_path, &config).expect("could not create cargo workspace");
107109

110+
// Create and store a build plan for the created Workspace
111+
let build_plan = create_plan(&ws).unwrap();
112+
compilation_cx_access.build_plan = Some(build_plan);
113+
// Release the lock on the compilation_cx
114+
mem::drop(compilation_cx_access);
115+
108116
// TODO: It might be feasible to keep this CargoOptions structure cached and regenerate
109117
// it on every relevant configuration change
110-
let (opts, rustflags) = {
118+
let (opts, rustflags, clear_env_rust_log) = {
111119
// We mustn't lock configuration for the whole build process
112120
let rls_config = rls_config.lock().unwrap();
113121

@@ -134,7 +142,7 @@ fn run_cargo(compilation_cx: Arc<Mutex<CompilationContext>>,
134142
}
135143
}
136144

137-
(opts, rustflags)
145+
(opts, rustflags, rls_config.clear_env_rust_log)
138146
};
139147

140148
let spec = Packages::from_flags(ws.is_virtual(), opts.all, &opts.exclude, &opts.package)
@@ -154,7 +162,7 @@ fn run_cargo(compilation_cx: Arc<Mutex<CompilationContext>>,
154162
let mut env: HashMap<String, Option<OsString>> = HashMap::new();
155163
env.insert("RUSTFLAGS".to_owned(), Some(rustflags.into()));
156164

157-
if rls_config.lock().unwrap().clear_env_rust_log {
165+
if clear_env_rust_log {
158166
env.insert("RUST_LOG".to_owned(), None);
159167
}
160168

src/build/mod.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ use std::time::Duration;
2828
mod environment;
2929
mod cargo;
3030
mod rustc;
31+
pub mod plan;
32+
33+
use self::plan::Plan as BuildPlan;
3134

3235
/// Manages builds.
3336
///
@@ -99,14 +102,17 @@ pub enum BuildPriority {
99102
Normal,
100103
}
101104

102-
// Information passed to Cargo/rustc to build.
105+
/// Information passed to Cargo/rustc to build.
103106
#[derive(Debug)]
104107
struct CompilationContext {
105-
// args and envs are saved from Cargo and passed to rustc.
108+
/// args and envs are saved from Cargo and passed to rustc.
106109
args: Vec<String>,
107110
envs: HashMap<String, Option<OsString>>,
108-
// The build directory is supplied by the client and passed to Cargo.
111+
/// The build directory is supplied by the client and passed to Cargo.
109112
build_dir: Option<PathBuf>,
113+
/// Build plan, which should know all the inter-package/target dependencies
114+
/// along with args/envs. Only contains inter-package dep-graph for now.
115+
build_plan: Option<BuildPlan>,
110116
}
111117

112118
impl CompilationContext {
@@ -115,6 +121,7 @@ impl CompilationContext {
115121
args: vec![],
116122
envs: HashMap::new(),
117123
build_dir: None,
124+
build_plan: None,
118125
}
119126
}
120127
}

src/build/plan.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::collections::HashMap;
12+
13+
use cargo::core::{Package as CargoPackage, PackageId, Resolve, Target, Workspace};
14+
use cargo::ops::{self, OutputMetadataOptions, Packages};
15+
use cargo::util::CargoResult;
16+
17+
/// Metadata version copied from `cargo_output_metadata.rs`. TODO: Remove
18+
/// when Cargo API will expose more information regarding output metadata.
19+
const VERSION: u32 = 1;
20+
21+
/// Holds the information how exactly the build will be performed for a given
22+
/// workspace with given, specified features.
23+
/// **TODO:** Use it to schedule an analysis build instead of relying on Cargo
24+
/// invocations.
25+
#[derive(Debug)]
26+
pub struct Plan {
27+
// TODO: Implement/add inter-(package) target dep queue
28+
// with args/envs per-target/package
29+
pub metadata: Metadata
30+
}
31+
32+
pub fn create_plan(ws: &Workspace) -> CargoResult<Plan> {
33+
// TODO: Fill appropriately
34+
let options = OutputMetadataOptions {
35+
features: vec![],
36+
no_default_features: false,
37+
all_features: false,
38+
no_deps: false,
39+
version: VERSION,
40+
};
41+
42+
let metadata = metadata_full(ws, &options)?;
43+
Ok(Plan { metadata: metadata.into() })
44+
}
45+
46+
/// Targets and features for a given package in the dep graph.
47+
#[derive(Debug)]
48+
pub struct Package {
49+
pub id: PackageId,
50+
pub targets: Vec<Target>,
51+
pub features: HashMap<String, Vec<String>>,
52+
}
53+
54+
impl From<CargoPackage> for Package {
55+
fn from(pkg: CargoPackage) -> Package {
56+
Package {
57+
id: pkg.package_id().clone(),
58+
targets: pkg.targets().iter().map(|x| x.clone()).collect(),
59+
features: pkg.summary().features().clone()
60+
}
61+
}
62+
}
63+
64+
/// Provides inter-package dependency graph and available packages' info in the
65+
/// workspace scope, along with workspace members and root package ids.
66+
#[derive(Debug)]
67+
pub struct Metadata {
68+
packages: HashMap<PackageId, Package>,
69+
resolve: Resolve,
70+
members: Vec<PackageId>,
71+
root: Option<PackageId>
72+
}
73+
74+
impl From<ExportInfo> for Metadata {
75+
fn from(info: ExportInfo) -> Metadata {
76+
// ExportInfo with deps information will always have `Some` resolve
77+
let MetadataResolve { resolve, root } = info.resolve.unwrap();
78+
79+
let packages: HashMap<PackageId, Package> = info.packages
80+
.iter()
81+
.map(|x| x.to_owned().into())
82+
.map(|pkg: Package| (pkg.id.clone(), pkg)) // TODO: Can I borrow key from member of value?
83+
.collect();
84+
85+
Metadata {
86+
packages,
87+
resolve,
88+
members: info.workspace_members,
89+
root,
90+
}
91+
}
92+
}
93+
94+
// TODO: Copied for now from Cargo, since it's not fully exposed in the API.
95+
// Remove when appropriate members are exposed.
96+
#[derive(Debug)]
97+
pub struct ExportInfo {
98+
pub packages: Vec<CargoPackage>,
99+
pub workspace_members: Vec<PackageId>,
100+
pub resolve: Option<MetadataResolve>,
101+
pub target_directory: String,
102+
pub version: u32,
103+
}
104+
105+
#[derive(Debug)]
106+
pub struct MetadataResolve {
107+
pub resolve: Resolve,
108+
pub root: Option<PackageId>,
109+
}
110+
111+
fn metadata_full(ws: &Workspace,
112+
opt: &OutputMetadataOptions) -> CargoResult<ExportInfo> {
113+
let specs = Packages::All.into_package_id_specs(ws)?;
114+
let deps = ops::resolve_ws_precisely(ws,
115+
None,
116+
&opt.features,
117+
opt.all_features,
118+
opt.no_default_features,
119+
&specs)?;
120+
let (packages, resolve) = deps;
121+
122+
let packages = packages.package_ids()
123+
.map(|i| packages.get(i).map(|p| p.clone()))
124+
.collect::<CargoResult<Vec<_>>>()?;
125+
126+
Ok(ExportInfo {
127+
packages: packages,
128+
workspace_members: ws.members().map(|pkg| pkg.package_id().clone()).collect(),
129+
resolve: Some(MetadataResolve{
130+
resolve: resolve,
131+
root: ws.current_opt().map(|pkg| pkg.package_id().clone()),
132+
}),
133+
target_directory: ws.target_dir().display().to_string(),
134+
version: VERSION,
135+
})
136+
}

0 commit comments

Comments
 (0)