aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_project_model
diff options
context:
space:
mode:
authorZac Pullar-Strecker <[email protected]>2020-07-31 03:12:44 +0100
committerZac Pullar-Strecker <[email protected]>2020-07-31 03:12:44 +0100
commitf05d7b41a719d848844b054a16477b29d0f063c6 (patch)
tree0a8a0946e8aef2ce64d4c13d0035ba41cce2daf3 /crates/ra_project_model
parent73ff610e41959e3e7c78a2b4b25b086883132956 (diff)
parent6b7cb8b5ab539fc4333ce34bc29bf77c976f232a (diff)
Merge remote-tracking branch 'upstream/master' into 503-hover-doc-links
Hasn't fixed tests yet.
Diffstat (limited to 'crates/ra_project_model')
-rw-r--r--crates/ra_project_model/Cargo.toml3
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs98
-rw-r--r--crates/ra_project_model/src/cfg_flag.rs51
-rw-r--r--crates/ra_project_model/src/lib.rs182
-rw-r--r--crates/ra_project_model/src/project_json.rs118
-rw-r--r--crates/ra_project_model/src/sysroot.rs102
6 files changed, 317 insertions, 237 deletions
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml
index b1b44dcf7..99adea8e4 100644
--- a/crates/ra_project_model/Cargo.toml
+++ b/crates/ra_project_model/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2018"
3name = "ra_project_model" 3name = "ra_project_model"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
@@ -11,7 +12,7 @@ doctest = false
11log = "0.4.8" 12log = "0.4.8"
12rustc-hash = "1.1.0" 13rustc-hash = "1.1.0"
13 14
14cargo_metadata = "0.10.0" 15cargo_metadata = "0.11.1"
15 16
16ra_arena = { path = "../ra_arena" } 17ra_arena = { path = "../ra_arena" }
17ra_cfg = { path = "../ra_cfg" } 18ra_cfg = { path = "../ra_cfg" }
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index 3b124020d..10513542e 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -1,6 +1,11 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{ffi::OsStr, ops, path::Path, process::Command}; 3use std::{
4 ffi::OsStr,
5 ops,
6 path::{Path, PathBuf},
7 process::Command,
8};
4 9
5use anyhow::{Context, Result}; 10use anyhow::{Context, Result};
6use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; 11use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
@@ -9,6 +14,8 @@ use ra_arena::{Arena, Idx};
9use ra_db::Edition; 14use ra_db::Edition;
10use rustc_hash::FxHashMap; 15use rustc_hash::FxHashMap;
11 16
17use crate::cfg_flag::CfgFlag;
18
12/// `CargoWorkspace` represents the logical structure of, well, a Cargo 19/// `CargoWorkspace` represents the logical structure of, well, a Cargo
13/// workspace. It pretty closely mirrors `cargo metadata` output. 20/// workspace. It pretty closely mirrors `cargo metadata` output.
14/// 21///
@@ -19,7 +26,7 @@ use rustc_hash::FxHashMap;
19/// 26///
20/// We use absolute paths here, `cargo metadata` guarantees to always produce 27/// We use absolute paths here, `cargo metadata` guarantees to always produce
21/// abs paths. 28/// abs paths.
22#[derive(Debug, Clone)] 29#[derive(Debug, Clone, Eq, PartialEq)]
23pub struct CargoWorkspace { 30pub struct CargoWorkspace {
24 packages: Arena<PackageData>, 31 packages: Arena<PackageData>,
25 targets: Arena<TargetData>, 32 targets: Arena<TargetData>,
@@ -40,7 +47,7 @@ impl ops::Index<Target> for CargoWorkspace {
40 } 47 }
41} 48}
42 49
43#[derive(Clone, Debug, PartialEq, Eq)] 50#[derive(Default, Clone, Debug, PartialEq, Eq)]
44pub struct CargoConfig { 51pub struct CargoConfig {
45 /// Do not activate the `default` feature. 52 /// Do not activate the `default` feature.
46 pub no_default_features: bool, 53 pub no_default_features: bool,
@@ -59,23 +66,11 @@ pub struct CargoConfig {
59 pub target: Option<String>, 66 pub target: Option<String>,
60} 67}
61 68
62impl Default for CargoConfig {
63 fn default() -> Self {
64 CargoConfig {
65 no_default_features: false,
66 all_features: false,
67 features: Vec::new(),
68 load_out_dirs_from_check: false,
69 target: None,
70 }
71 }
72}
73
74pub type Package = Idx<PackageData>; 69pub type Package = Idx<PackageData>;
75 70
76pub type Target = Idx<TargetData>; 71pub type Target = Idx<TargetData>;
77 72
78#[derive(Debug, Clone)] 73#[derive(Debug, Clone, Eq, PartialEq)]
79pub struct PackageData { 74pub struct PackageData {
80 pub version: String, 75 pub version: String,
81 pub name: String, 76 pub name: String,
@@ -85,18 +80,18 @@ pub struct PackageData {
85 pub dependencies: Vec<PackageDependency>, 80 pub dependencies: Vec<PackageDependency>,
86 pub edition: Edition, 81 pub edition: Edition,
87 pub features: Vec<String>, 82 pub features: Vec<String>,
88 pub cfgs: Vec<String>, 83 pub cfgs: Vec<CfgFlag>,
89 pub out_dir: Option<AbsPathBuf>, 84 pub out_dir: Option<AbsPathBuf>,
90 pub proc_macro_dylib_path: Option<AbsPathBuf>, 85 pub proc_macro_dylib_path: Option<AbsPathBuf>,
91} 86}
92 87
93#[derive(Debug, Clone)] 88#[derive(Debug, Clone, Eq, PartialEq)]
94pub struct PackageDependency { 89pub struct PackageDependency {
95 pub pkg: Package, 90 pub pkg: Package,
96 pub name: String, 91 pub name: String,
97} 92}
98 93
99#[derive(Debug, Clone)] 94#[derive(Debug, Clone, Eq, PartialEq)]
100pub struct TargetData { 95pub struct TargetData {
101 pub package: Package, 96 pub package: Package,
102 pub name: String, 97 pub name: String,
@@ -141,28 +136,31 @@ impl PackageData {
141 136
142impl CargoWorkspace { 137impl CargoWorkspace {
143 pub fn from_cargo_metadata( 138 pub fn from_cargo_metadata(
144 cargo_toml: &Path, 139 cargo_toml: &AbsPath,
145 cargo_features: &CargoConfig, 140 cargo_features: &CargoConfig,
146 ) -> Result<CargoWorkspace> { 141 ) -> Result<CargoWorkspace> {
147 let mut meta = MetadataCommand::new(); 142 let mut meta = MetadataCommand::new();
148 meta.cargo_path(ra_toolchain::cargo()); 143 meta.cargo_path(ra_toolchain::cargo());
149 meta.manifest_path(cargo_toml); 144 meta.manifest_path(cargo_toml.to_path_buf());
150 if cargo_features.all_features { 145 if cargo_features.all_features {
151 meta.features(CargoOpt::AllFeatures); 146 meta.features(CargoOpt::AllFeatures);
152 } else if cargo_features.no_default_features { 147 } else {
153 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` 148 if cargo_features.no_default_features {
154 // https://github.com/oli-obk/cargo_metadata/issues/79 149 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
155 meta.features(CargoOpt::NoDefaultFeatures); 150 // https://github.com/oli-obk/cargo_metadata/issues/79
156 } else if !cargo_features.features.is_empty() { 151 meta.features(CargoOpt::NoDefaultFeatures);
157 meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone())); 152 }
153 if !cargo_features.features.is_empty() {
154 meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone()));
155 }
158 } 156 }
159 if let Some(parent) = cargo_toml.parent() { 157 if let Some(parent) = cargo_toml.parent() {
160 meta.current_dir(parent); 158 meta.current_dir(parent.to_path_buf());
161 } 159 }
162 if let Some(target) = cargo_features.target.as_ref() { 160 if let Some(target) = cargo_features.target.as_ref() {
163 meta.other_options(vec![String::from("--filter-platform"), target.clone()]); 161 meta.other_options(vec![String::from("--filter-platform"), target.clone()]);
164 } 162 }
165 let meta = meta.exec().with_context(|| { 163 let mut meta = meta.exec().with_context(|| {
166 format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) 164 format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display())
167 })?; 165 })?;
168 166
@@ -182,6 +180,7 @@ impl CargoWorkspace {
182 180
183 let ws_members = &meta.workspace_members; 181 let ws_members = &meta.workspace_members;
184 182
183 meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
185 for meta_pkg in meta.packages { 184 for meta_pkg in meta.packages {
186 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = 185 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } =
187 meta_pkg; 186 meta_pkg;
@@ -217,7 +216,7 @@ impl CargoWorkspace {
217 } 216 }
218 } 217 }
219 let resolve = meta.resolve.expect("metadata executed with deps"); 218 let resolve = meta.resolve.expect("metadata executed with deps");
220 for node in resolve.nodes { 219 for mut node in resolve.nodes {
221 let source = match pkg_by_id.get(&node.id) { 220 let source = match pkg_by_id.get(&node.id) {
222 Some(&src) => src, 221 Some(&src) => src,
223 // FIXME: replace this and a similar branch below with `.unwrap`, once 222 // FIXME: replace this and a similar branch below with `.unwrap`, once
@@ -228,6 +227,7 @@ impl CargoWorkspace {
228 continue; 227 continue;
229 } 228 }
230 }; 229 };
230 node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg));
231 for dep_node in node.deps { 231 for dep_node in node.deps {
232 let pkg = match pkg_by_id.get(&dep_node.pkg) { 232 let pkg = match pkg_by_id.get(&dep_node.pkg) {
233 Some(&pkg) => pkg, 233 Some(&pkg) => pkg,
@@ -281,7 +281,7 @@ impl CargoWorkspace {
281pub struct ExternResources { 281pub struct ExternResources {
282 out_dirs: FxHashMap<PackageId, AbsPathBuf>, 282 out_dirs: FxHashMap<PackageId, AbsPathBuf>,
283 proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>, 283 proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>,
284 cfgs: FxHashMap<PackageId, Vec<String>>, 284 cfgs: FxHashMap<PackageId, Vec<CfgFlag>>,
285} 285}
286 286
287pub fn load_extern_resources( 287pub fn load_extern_resources(
@@ -292,12 +292,16 @@ pub fn load_extern_resources(
292 cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); 292 cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
293 if cargo_features.all_features { 293 if cargo_features.all_features {
294 cmd.arg("--all-features"); 294 cmd.arg("--all-features");
295 } else if cargo_features.no_default_features {
296 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
297 // https://github.com/oli-obk/cargo_metadata/issues/79
298 cmd.arg("--no-default-features");
299 } else { 295 } else {
300 cmd.args(&cargo_features.features); 296 if cargo_features.no_default_features {
297 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
298 // https://github.com/oli-obk/cargo_metadata/issues/79
299 cmd.arg("--no-default-features");
300 }
301 if !cargo_features.features.is_empty() {
302 cmd.arg("--features");
303 cmd.arg(cargo_features.features.join(" "));
304 }
301 } 305 }
302 306
303 let output = cmd.output()?; 307 let output = cmd.output()?;
@@ -308,9 +312,25 @@ pub fn load_extern_resources(
308 if let Ok(message) = message { 312 if let Ok(message) = message {
309 match message { 313 match message {
310 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { 314 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => {
311 let out_dir = AbsPathBuf::assert(out_dir); 315 let cfgs = {
312 res.out_dirs.insert(package_id.clone(), out_dir); 316 let mut acc = Vec::new();
313 res.cfgs.insert(package_id, cfgs); 317 for cfg in cfgs {
318 match cfg.parse::<CfgFlag>() {
319 Ok(it) => acc.push(it),
320 Err(err) => {
321 anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
322 }
323 };
324 }
325 acc
326 };
327 // cargo_metadata crate returns default (empty) path for
328 // older cargos, which is not absolute, so work around that.
329 if out_dir != PathBuf::default() {
330 let out_dir = AbsPathBuf::assert(out_dir);
331 res.out_dirs.insert(package_id.clone(), out_dir);
332 res.cfgs.insert(package_id, cfgs);
333 }
314 } 334 }
315 Message::CompilerArtifact(message) => { 335 Message::CompilerArtifact(message) => {
316 if message.target.kind.contains(&"proc-macro".to_string()) { 336 if message.target.kind.contains(&"proc-macro".to_string()) {
diff --git a/crates/ra_project_model/src/cfg_flag.rs b/crates/ra_project_model/src/cfg_flag.rs
new file mode 100644
index 000000000..bd50056c6
--- /dev/null
+++ b/crates/ra_project_model/src/cfg_flag.rs
@@ -0,0 +1,51 @@
1//! Parsing of CfgFlags as command line arguments, as in
2//!
3//! rustc main.rs --cfg foo --cfg 'feature="bar"'
4use std::str::FromStr;
5
6use ra_cfg::CfgOptions;
7use stdx::split_once;
8
9#[derive(Clone, Eq, PartialEq, Debug)]
10pub enum CfgFlag {
11 Atom(String),
12 KeyValue { key: String, value: String },
13}
14
15impl FromStr for CfgFlag {
16 type Err = String;
17 fn from_str(s: &str) -> Result<Self, Self::Err> {
18 let res = match split_once(s, '=') {
19 Some((key, value)) => {
20 if !(value.starts_with('"') && value.ends_with('"')) {
21 return Err(format!("Invalid cfg ({:?}), value should be in quotes", s));
22 }
23 let key = key.to_string();
24 let value = value[1..value.len() - 1].to_string();
25 CfgFlag::KeyValue { key, value }
26 }
27 None => CfgFlag::Atom(s.into()),
28 };
29 Ok(res)
30 }
31}
32
33impl<'de> serde::Deserialize<'de> for CfgFlag {
34 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
35 where
36 D: serde::Deserializer<'de>,
37 {
38 String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
39 }
40}
41
42impl Extend<CfgFlag> for CfgOptions {
43 fn extend<T: IntoIterator<Item = CfgFlag>>(&mut self, iter: T) {
44 for cfg_flag in iter {
45 match cfg_flag {
46 CfgFlag::Atom(it) => self.insert_atom(it.into()),
47 CfgFlag::KeyValue { key, value } => self.insert_key_value(key.into(), value.into()),
48 }
49 }
50 }
51}
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 8b85b4831..300e75135 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -3,12 +3,12 @@
3mod cargo_workspace; 3mod cargo_workspace;
4mod project_json; 4mod project_json;
5mod sysroot; 5mod sysroot;
6mod cfg_flag;
6 7
7use std::{ 8use std::{
8 fs::{self, read_dir, ReadDir}, 9 fs::{self, read_dir, ReadDir},
9 io, 10 io,
10 path::Path, 11 process::Command,
11 process::{Command, Output},
12}; 12};
13 13
14use anyhow::{bail, Context, Result}; 14use anyhow::{bail, Context, Result};
@@ -17,14 +17,17 @@ use ra_cfg::CfgOptions;
17use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; 17use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId};
18use rustc_hash::{FxHashMap, FxHashSet}; 18use rustc_hash::{FxHashMap, FxHashSet};
19 19
20use crate::cfg_flag::CfgFlag;
21
20pub use crate::{ 22pub use crate::{
21 cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, 23 cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind},
22 project_json::{ProjectJson, ProjectJsonData}, 24 project_json::{ProjectJson, ProjectJsonData},
23 sysroot::Sysroot, 25 sysroot::Sysroot,
24}; 26};
27
25pub use ra_proc_macro::ProcMacroClient; 28pub use ra_proc_macro::ProcMacroClient;
26 29
27#[derive(Debug, Clone)] 30#[derive(Debug, Clone, Eq, PartialEq)]
28pub enum ProjectWorkspace { 31pub enum ProjectWorkspace {
29 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 32 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
30 Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, 33 Cargo { cargo: CargoWorkspace, sysroot: Sysroot },
@@ -35,30 +38,12 @@ pub enum ProjectWorkspace {
35/// `PackageRoot` describes a package root folder. 38/// `PackageRoot` describes a package root folder.
36/// Which may be an external dependency, or a member of 39/// Which may be an external dependency, or a member of
37/// the current workspace. 40/// the current workspace.
38#[derive(Debug, Clone)] 41#[derive(Debug, Clone, Eq, PartialEq, Hash)]
39pub struct PackageRoot { 42pub struct PackageRoot {
40 /// Path to the root folder
41 path: AbsPathBuf,
42 /// Is a member of the current workspace 43 /// Is a member of the current workspace
43 is_member: bool, 44 pub is_member: bool,
44 out_dir: Option<AbsPathBuf>, 45 pub include: Vec<AbsPathBuf>,
45} 46 pub exclude: Vec<AbsPathBuf>,
46impl PackageRoot {
47 pub fn new_member(path: AbsPathBuf) -> PackageRoot {
48 Self { path, is_member: true, out_dir: None }
49 }
50 pub fn new_non_member(path: AbsPathBuf) -> PackageRoot {
51 Self { path, is_member: false, out_dir: None }
52 }
53 pub fn path(&self) -> &AbsPath {
54 &self.path
55 }
56 pub fn out_dir(&self) -> Option<&AbsPath> {
57 self.out_dir.as_deref()
58 }
59 pub fn is_member(&self) -> bool {
60 self.is_member
61 }
62} 47}
63 48
64#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] 49#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
@@ -150,7 +135,7 @@ impl ProjectManifest {
150impl ProjectWorkspace { 135impl ProjectWorkspace {
151 pub fn load( 136 pub fn load(
152 manifest: ProjectManifest, 137 manifest: ProjectManifest,
153 cargo_features: &CargoConfig, 138 cargo_config: &CargoConfig,
154 with_sysroot: bool, 139 with_sysroot: bool,
155 ) -> Result<ProjectWorkspace> { 140 ) -> Result<ProjectWorkspace> {
156 let res = match manifest { 141 let res = match manifest {
@@ -166,7 +151,7 @@ impl ProjectWorkspace {
166 ProjectWorkspace::Json { project } 151 ProjectWorkspace::Json { project }
167 } 152 }
168 ProjectManifest::CargoToml(cargo_toml) => { 153 ProjectManifest::CargoToml(cargo_toml) => {
169 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) 154 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_config)
170 .with_context(|| { 155 .with_context(|| {
171 format!( 156 format!(
172 "Failed to read Cargo metadata from Cargo.toml file {}", 157 "Failed to read Cargo metadata from Cargo.toml file {}",
@@ -195,18 +180,40 @@ impl ProjectWorkspace {
195 /// the root is a member of the current workspace 180 /// the root is a member of the current workspace
196 pub fn to_roots(&self) -> Vec<PackageRoot> { 181 pub fn to_roots(&self) -> Vec<PackageRoot> {
197 match self { 182 match self {
198 ProjectWorkspace::Json { project } => { 183 ProjectWorkspace::Json { project } => project
199 project.roots.iter().map(|r| PackageRoot::new_member(r.path.clone())).collect() 184 .crates
200 } 185 .iter()
186 .map(|krate| PackageRoot {
187 is_member: krate.is_workspace_member,
188 include: krate.include.clone(),
189 exclude: krate.exclude.clone(),
190 })
191 .collect::<FxHashSet<_>>()
192 .into_iter()
193 .collect::<Vec<_>>(),
201 ProjectWorkspace::Cargo { cargo, sysroot } => cargo 194 ProjectWorkspace::Cargo { cargo, sysroot } => cargo
202 .packages() 195 .packages()
203 .map(|pkg| PackageRoot { 196 .map(|pkg| {
204 path: cargo[pkg].root().to_path_buf(), 197 let is_member = cargo[pkg].is_member;
205 is_member: cargo[pkg].is_member, 198 let pkg_root = cargo[pkg].root().to_path_buf();
206 out_dir: cargo[pkg].out_dir.clone(), 199
200 let mut include = vec![pkg_root.clone()];
201 include.extend(cargo[pkg].out_dir.clone());
202
203 let mut exclude = vec![pkg_root.join(".git")];
204 if is_member {
205 exclude.push(pkg_root.join("target"));
206 } else {
207 exclude.push(pkg_root.join("tests"));
208 exclude.push(pkg_root.join("examples"));
209 exclude.push(pkg_root.join("benches"));
210 }
211 PackageRoot { is_member, include, exclude }
207 }) 212 })
208 .chain(sysroot.crates().map(|krate| { 213 .chain(sysroot.crates().map(|krate| PackageRoot {
209 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) 214 is_member: false,
215 include: vec![sysroot[krate].root_dir().to_path_buf()],
216 exclude: Vec::new(),
210 })) 217 }))
211 .collect(), 218 .collect(),
212 } 219 }
@@ -246,6 +253,7 @@ impl ProjectWorkspace {
246 let mut crate_graph = CrateGraph::default(); 253 let mut crate_graph = CrateGraph::default();
247 match self { 254 match self {
248 ProjectWorkspace::Json { project } => { 255 ProjectWorkspace::Json { project } => {
256 let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default();
249 let crates: FxHashMap<_, _> = project 257 let crates: FxHashMap<_, _> = project
250 .crates 258 .crates
251 .iter() 259 .iter()
@@ -254,17 +262,20 @@ impl ProjectWorkspace {
254 let file_path = &krate.root_module; 262 let file_path = &krate.root_module;
255 let file_id = load(&file_path)?; 263 let file_id = load(&file_path)?;
256 264
257 let mut env = Env::default(); 265 let env = krate.env.clone().into_iter().collect();
258 if let Some(out_dir) = &krate.out_dir {
259 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
260 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
261 env.set("OUT_DIR", out_dir);
262 }
263 }
264 let proc_macro = krate 266 let proc_macro = krate
265 .proc_macro_dylib_path 267 .proc_macro_dylib_path
266 .clone() 268 .clone()
267 .map(|it| proc_macro_client.by_dylib_path(&it)); 269 .map(|it| proc_macro_client.by_dylib_path(&it));
270
271 let target = krate.target.as_deref().or(target);
272 let target_cfgs = cfg_cache
273 .entry(target)
274 .or_insert_with(|| get_rustc_cfg_options(target));
275
276 let mut cfg_options = CfgOptions::default();
277 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
278
268 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env 279 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env
269 Some(( 280 Some((
270 CrateId(seq_index as u32), 281 CrateId(seq_index as u32),
@@ -273,7 +284,7 @@ impl ProjectWorkspace {
273 krate.edition, 284 krate.edition,
274 // FIXME json definitions can store the crate name 285 // FIXME json definitions can store the crate name
275 None, 286 None,
276 krate.cfg.clone(), 287 cfg_options,
277 env, 288 env,
278 proc_macro.unwrap_or_default(), 289 proc_macro.unwrap_or_default(),
279 ), 290 ),
@@ -288,10 +299,7 @@ impl ProjectWorkspace {
288 if let (Some(&from), Some(&to)) = 299 if let (Some(&from), Some(&to)) =
289 (crates.get(&from_crate_id), crates.get(&to_crate_id)) 300 (crates.get(&from_crate_id), crates.get(&to_crate_id))
290 { 301 {
291 if crate_graph 302 if crate_graph.add_dep(from, dep.name.clone(), to).is_err() {
292 .add_dep(from, CrateName::new(&dep.name).unwrap(), to)
293 .is_err()
294 {
295 log::error!( 303 log::error!(
296 "cyclic dependency {:?} -> {:?}", 304 "cyclic dependency {:?} -> {:?}",
297 from_crate_id, 305 from_crate_id,
@@ -303,7 +311,8 @@ impl ProjectWorkspace {
303 } 311 }
304 } 312 }
305 ProjectWorkspace::Cargo { cargo, sysroot } => { 313 ProjectWorkspace::Cargo { cargo, sysroot } => {
306 let mut cfg_options = get_rustc_cfg_options(target); 314 let mut cfg_options = CfgOptions::default();
315 cfg_options.extend(get_rustc_cfg_options(target));
307 316
308 let sysroot_crates: FxHashMap<_, _> = sysroot 317 let sysroot_crates: FxHashMap<_, _> = sysroot
309 .crates() 318 .crates()
@@ -312,13 +321,11 @@ impl ProjectWorkspace {
312 321
313 let env = Env::default(); 322 let env = Env::default();
314 let proc_macro = vec![]; 323 let proc_macro = vec![];
315 let crate_name = CrateName::new(&sysroot[krate].name) 324 let name = sysroot[krate].name.clone();
316 .expect("Sysroot crate names should not contain dashes");
317
318 let crate_id = crate_graph.add_crate_root( 325 let crate_id = crate_graph.add_crate_root(
319 file_id, 326 file_id,
320 Edition::Edition2018, 327 Edition::Edition2018,
321 Some(crate_name), 328 Some(name),
322 cfg_options.clone(), 329 cfg_options.clone(),
323 env, 330 env,
324 proc_macro, 331 proc_macro,
@@ -352,6 +359,7 @@ impl ProjectWorkspace {
352 359
353 // Add test cfg for non-sysroot crates 360 // Add test cfg for non-sysroot crates
354 cfg_options.insert_atom("test".into()); 361 cfg_options.insert_atom("test".into());
362 cfg_options.insert_atom("debug_assertions".into());
355 363
356 // Next, create crates for each package, target pair 364 // Next, create crates for each package, target pair
357 for pkg in cargo.packages() { 365 for pkg in cargo.packages() {
@@ -365,15 +373,7 @@ impl ProjectWorkspace {
365 for feature in cargo[pkg].features.iter() { 373 for feature in cargo[pkg].features.iter() {
366 opts.insert_key_value("feature".into(), feature.into()); 374 opts.insert_key_value("feature".into(), feature.into());
367 } 375 }
368 for cfg in cargo[pkg].cfgs.iter() { 376 opts.extend(cargo[pkg].cfgs.iter().cloned());
369 match cfg.find('=') {
370 Some(split) => opts.insert_key_value(
371 cfg[..split].into(),
372 cfg[split + 1..].trim_matches('"').into(),
373 ),
374 None => opts.insert_atom(cfg.into()),
375 };
376 }
377 opts 377 opts
378 }; 378 };
379 let mut env = Env::default(); 379 let mut env = Env::default();
@@ -392,7 +392,7 @@ impl ProjectWorkspace {
392 let crate_id = crate_graph.add_crate_root( 392 let crate_id = crate_graph.add_crate_root(
393 file_id, 393 file_id,
394 edition, 394 edition,
395 Some(CrateName::normalize_dashes(&cargo[pkg].name)), 395 Some(cargo[pkg].name.clone()),
396 cfg_options, 396 cfg_options,
397 env, 397 env,
398 proc_macro.clone(), 398 proc_macro.clone(),
@@ -499,66 +499,37 @@ impl ProjectWorkspace {
499 } 499 }
500 crate_graph 500 crate_graph
501 } 501 }
502
503 pub fn workspace_root_for(&self, path: &Path) -> Option<&AbsPath> {
504 match self {
505 ProjectWorkspace::Cargo { cargo, .. } => {
506 Some(cargo.workspace_root()).filter(|root| path.starts_with(root))
507 }
508 ProjectWorkspace::Json { project: ProjectJson { roots, .. }, .. } => roots
509 .iter()
510 .find(|root| path.starts_with(&root.path))
511 .map(|root| root.path.as_path()),
512 }
513 }
514} 502}
515 503
516fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions { 504fn get_rustc_cfg_options(target: Option<&str>) -> Vec<CfgFlag> {
517 let mut cfg_options = CfgOptions::default(); 505 let mut res = Vec::new();
518 506
519 // Some nightly-only cfgs, which are required for stdlib 507 // Some nightly-only cfgs, which are required for stdlib
520 { 508 res.push(CfgFlag::Atom("target_thread_local".into()));
521 cfg_options.insert_atom("target_thread_local".into()); 509 for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() {
522 for &target_has_atomic in ["8", "16", "32", "64", "cas", "ptr"].iter() { 510 for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() {
523 cfg_options.insert_key_value("target_has_atomic".into(), target_has_atomic.into()); 511 res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() });
524 cfg_options
525 .insert_key_value("target_has_atomic_load_store".into(), target_has_atomic.into());
526 } 512 }
527 } 513 }
528 514
529 let rustc_cfgs = || -> Result<String> { 515 let rustc_cfgs = {
530 // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here.
531 let mut cmd = Command::new(ra_toolchain::rustc()); 516 let mut cmd = Command::new(ra_toolchain::rustc());
532 cmd.args(&["--print", "cfg", "-O"]); 517 cmd.args(&["--print", "cfg", "-O"]);
533 if let Some(target) = target { 518 if let Some(target) = target {
534 cmd.args(&["--target", target]); 519 cmd.args(&["--target", target]);
535 } 520 }
536 let output = output(cmd)?; 521 utf8_stdout(cmd)
537 Ok(String::from_utf8(output.stdout)?) 522 };
538 }();
539 523
540 match rustc_cfgs { 524 match rustc_cfgs {
541 Ok(rustc_cfgs) => { 525 Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())),
542 for line in rustc_cfgs.lines() {
543 match line.find('=') {
544 None => cfg_options.insert_atom(line.into()),
545 Some(pos) => {
546 let key = &line[..pos];
547 let value = line[pos + 1..].trim_matches('"');
548 cfg_options.insert_key_value(key.into(), value.into());
549 }
550 }
551 }
552 }
553 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), 526 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
554 } 527 }
555 528
556 cfg_options.insert_atom("debug_assertions".into()); 529 res
557
558 cfg_options
559} 530}
560 531
561fn output(mut cmd: Command) -> Result<Output> { 532fn utf8_stdout(mut cmd: Command) -> Result<String> {
562 let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; 533 let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?;
563 if !output.status.success() { 534 if !output.status.success() {
564 match String::from_utf8(output.stderr) { 535 match String::from_utf8(output.stderr) {
@@ -568,5 +539,6 @@ fn output(mut cmd: Command) -> Result<Output> {
568 _ => bail!("{:?} failed, {}", cmd, output.status), 539 _ => bail!("{:?} failed, {}", cmd, output.status),
569 } 540 }
570 } 541 }
571 Ok(output) 542 let stdout = String::from_utf8(output.stdout)?;
543 Ok(stdout)
572} 544}
diff --git a/crates/ra_project_model/src/project_json.rs b/crates/ra_project_model/src/project_json.rs
index 4b5dcd634..e3f3163f6 100644
--- a/crates/ra_project_model/src/project_json.rs
+++ b/crates/ra_project_model/src/project_json.rs
@@ -3,70 +3,78 @@
3use std::path::PathBuf; 3use std::path::PathBuf;
4 4
5use paths::{AbsPath, AbsPathBuf}; 5use paths::{AbsPath, AbsPathBuf};
6use ra_cfg::CfgOptions; 6use ra_db::{CrateId, CrateName, Dependency, Edition};
7use ra_db::{CrateId, Dependency, Edition}; 7use rustc_hash::FxHashMap;
8use rustc_hash::FxHashSet; 8use serde::{de, Deserialize};
9use serde::Deserialize; 9
10use stdx::split_delim; 10use crate::cfg_flag::CfgFlag;
11 11
12/// Roots and crates that compose this Rust project. 12/// Roots and crates that compose this Rust project.
13#[derive(Clone, Debug)] 13#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct ProjectJson { 14pub struct ProjectJson {
15 pub(crate) roots: Vec<Root>,
16 pub(crate) crates: Vec<Crate>, 15 pub(crate) crates: Vec<Crate>,
17} 16}
18 17
19/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in
20/// all roots. Roots might be nested.
21#[derive(Clone, Debug)]
22pub struct Root {
23 pub(crate) path: AbsPathBuf,
24}
25
26/// A crate points to the root module of a crate and lists the dependencies of the crate. This is 18/// A crate points to the root module of a crate and lists the dependencies of the crate. This is
27/// useful in creating the crate graph. 19/// useful in creating the crate graph.
28#[derive(Clone, Debug)] 20#[derive(Clone, Debug, Eq, PartialEq)]
29pub struct Crate { 21pub struct Crate {
30 pub(crate) root_module: AbsPathBuf, 22 pub(crate) root_module: AbsPathBuf,
31 pub(crate) edition: Edition, 23 pub(crate) edition: Edition,
32 pub(crate) deps: Vec<Dependency>, 24 pub(crate) deps: Vec<Dependency>,
33 pub(crate) cfg: CfgOptions, 25 pub(crate) cfg: Vec<CfgFlag>,
34 pub(crate) out_dir: Option<AbsPathBuf>, 26 pub(crate) target: Option<String>,
27 pub(crate) env: FxHashMap<String, String>,
35 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, 28 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
29 pub(crate) is_workspace_member: bool,
30 pub(crate) include: Vec<AbsPathBuf>,
31 pub(crate) exclude: Vec<AbsPathBuf>,
36} 32}
37 33
38impl ProjectJson { 34impl ProjectJson {
39 pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { 35 pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson {
40 ProjectJson { 36 ProjectJson {
41 roots: data.roots.into_iter().map(|path| Root { path: base.join(path) }).collect(),
42 crates: data 37 crates: data
43 .crates 38 .crates
44 .into_iter() 39 .into_iter()
45 .map(|crate_data| Crate { 40 .map(|crate_data| {
46 root_module: base.join(crate_data.root_module), 41 let is_workspace_member = crate_data.is_workspace_member.unwrap_or_else(|| {
47 edition: crate_data.edition.into(), 42 crate_data.root_module.is_relative()
48 deps: crate_data 43 && !crate_data.root_module.starts_with("..")
49 .deps 44 || crate_data.root_module.starts_with(base)
50 .into_iter() 45 });
51 .map(|dep_data| Dependency { 46 let root_module = base.join(crate_data.root_module);
52 crate_id: CrateId(dep_data.krate as u32), 47 let (include, exclude) = match crate_data.source {
53 name: dep_data.name.into(), 48 Some(src) => {
54 }) 49 let absolutize = |dirs: Vec<PathBuf>| {
55 .collect::<Vec<_>>(), 50 dirs.into_iter().map(|it| base.join(it)).collect::<Vec<_>>()
56 cfg: { 51 };
57 let mut cfg = CfgOptions::default(); 52 (absolutize(src.include_dirs), absolutize(src.exclude_dirs))
58 for entry in &crate_data.cfg {
59 match split_delim(entry, '=') {
60 Some((key, value)) => {
61 cfg.insert_key_value(key.into(), value.into());
62 }
63 None => cfg.insert_atom(entry.into()),
64 }
65 } 53 }
66 cfg 54 None => (vec![root_module.parent().unwrap().to_path_buf()], Vec::new()),
67 }, 55 };
68 out_dir: crate_data.out_dir.map(|it| base.join(it)), 56
69 proc_macro_dylib_path: crate_data.proc_macro_dylib_path.map(|it| base.join(it)), 57 Crate {
58 root_module,
59 edition: crate_data.edition.into(),
60 deps: crate_data
61 .deps
62 .into_iter()
63 .map(|dep_data| Dependency {
64 crate_id: CrateId(dep_data.krate as u32),
65 name: dep_data.name,
66 })
67 .collect::<Vec<_>>(),
68 cfg: crate_data.cfg,
69 target: crate_data.target,
70 env: crate_data.env,
71 proc_macro_dylib_path: crate_data
72 .proc_macro_dylib_path
73 .map(|it| base.join(it)),
74 is_workspace_member,
75 include,
76 exclude,
77 }
70 }) 78 })
71 .collect::<Vec<_>>(), 79 .collect::<Vec<_>>(),
72 } 80 }
@@ -75,7 +83,6 @@ impl ProjectJson {
75 83
76#[derive(Deserialize)] 84#[derive(Deserialize)]
77pub struct ProjectJsonData { 85pub struct ProjectJsonData {
78 roots: Vec<PathBuf>,
79 crates: Vec<CrateData>, 86 crates: Vec<CrateData>,
80} 87}
81 88
@@ -85,9 +92,13 @@ struct CrateData {
85 edition: EditionData, 92 edition: EditionData,
86 deps: Vec<DepData>, 93 deps: Vec<DepData>,
87 #[serde(default)] 94 #[serde(default)]
88 cfg: FxHashSet<String>, 95 cfg: Vec<CfgFlag>,
89 out_dir: Option<PathBuf>, 96 target: Option<String>,
97 #[serde(default)]
98 env: FxHashMap<String, String>,
90 proc_macro_dylib_path: Option<PathBuf>, 99 proc_macro_dylib_path: Option<PathBuf>,
100 is_workspace_member: Option<bool>,
101 source: Option<CrateSource>,
91} 102}
92 103
93#[derive(Deserialize)] 104#[derive(Deserialize)]
@@ -113,5 +124,20 @@ struct DepData {
113 /// Identifies a crate by position in the crates array. 124 /// Identifies a crate by position in the crates array.
114 #[serde(rename = "crate")] 125 #[serde(rename = "crate")]
115 krate: usize, 126 krate: usize,
116 name: String, 127 #[serde(deserialize_with = "deserialize_crate_name")]
128 name: CrateName,
129}
130
131#[derive(Deserialize)]
132struct CrateSource {
133 include_dirs: Vec<PathBuf>,
134 exclude_dirs: Vec<PathBuf>,
135}
136
137fn deserialize_crate_name<'de, D>(de: D) -> Result<CrateName, D::Error>
138where
139 D: de::Deserializer<'de>,
140{
141 let name = String::deserialize(de)?;
142 CrateName::new(&name).map_err(|err| de::Error::custom(format!("invalid crate name: {:?}", err)))
117} 143}
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs
index 943ff92df..a10ade375 100644
--- a/crates/ra_project_model/src/sysroot.rs
+++ b/crates/ra_project_model/src/sysroot.rs
@@ -3,19 +3,19 @@
3use std::{convert::TryFrom, env, ops, path::Path, process::Command}; 3use std::{convert::TryFrom, env, ops, path::Path, process::Command};
4 4
5use anyhow::{bail, format_err, Result}; 5use anyhow::{bail, format_err, Result};
6use paths::{AbsPath, AbsPathBuf};
6use ra_arena::{Arena, Idx}; 7use ra_arena::{Arena, Idx};
7 8
8use crate::output; 9use crate::utf8_stdout;
9use paths::{AbsPath, AbsPathBuf};
10 10
11#[derive(Default, Debug, Clone)] 11#[derive(Default, Debug, Clone, Eq, PartialEq)]
12pub struct Sysroot { 12pub struct Sysroot {
13 crates: Arena<SysrootCrateData>, 13 crates: Arena<SysrootCrateData>,
14} 14}
15 15
16pub type SysrootCrate = Idx<SysrootCrateData>; 16pub type SysrootCrate = Idx<SysrootCrateData>;
17 17
18#[derive(Debug, Clone)] 18#[derive(Debug, Clone, Eq, PartialEq)]
19pub struct SysrootCrateData { 19pub struct SysrootCrateData {
20 pub name: String, 20 pub name: String,
21 pub root: AbsPathBuf, 21 pub root: AbsPathBuf,
@@ -54,6 +54,8 @@ impl Sysroot {
54 let src = get_or_install_rust_src(cargo_toml)?; 54 let src = get_or_install_rust_src(cargo_toml)?;
55 let mut sysroot = Sysroot { crates: Arena::default() }; 55 let mut sysroot = Sysroot { crates: Arena::default() };
56 for name in SYSROOT_CRATES.trim().lines() { 56 for name in SYSROOT_CRATES.trim().lines() {
57 // FIXME: remove this path when 1.47 comes out
58 // https://github.com/rust-lang/rust/pull/73265
57 let root = src.join(format!("lib{}", name)).join("lib.rs"); 59 let root = src.join(format!("lib{}", name)).join("lib.rs");
58 if root.exists() { 60 if root.exists() {
59 sysroot.crates.alloc(SysrootCrateData { 61 sysroot.crates.alloc(SysrootCrateData {
@@ -61,6 +63,15 @@ impl Sysroot {
61 root, 63 root,
62 deps: Vec::new(), 64 deps: Vec::new(),
63 }); 65 });
66 } else {
67 let root = src.join(name).join("src/lib.rs");
68 if root.exists() {
69 sysroot.crates.alloc(SysrootCrateData {
70 name: name.into(),
71 root,
72 deps: Vec::new(),
73 });
74 }
64 } 75 }
65 } 76 }
66 if let Some(std) = sysroot.std() { 77 if let Some(std) = sysroot.std() {
@@ -92,26 +103,40 @@ fn get_or_install_rust_src(cargo_toml: &AbsPath) -> Result<AbsPathBuf> {
92 let current_dir = cargo_toml.parent().unwrap(); 103 let current_dir = cargo_toml.parent().unwrap();
93 let mut rustc = Command::new(ra_toolchain::rustc()); 104 let mut rustc = Command::new(ra_toolchain::rustc());
94 rustc.current_dir(current_dir).args(&["--print", "sysroot"]); 105 rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
95 let rustc_output = output(rustc)?; 106 let stdout = utf8_stdout(rustc)?;
96 let stdout = String::from_utf8(rustc_output.stdout)?;
97 let sysroot_path = AbsPath::assert(Path::new(stdout.trim())); 107 let sysroot_path = AbsPath::assert(Path::new(stdout.trim()));
98 let src_path = sysroot_path.join("lib/rustlib/src/rust/src"); 108 let mut src = get_rust_src(sysroot_path);
99 109 if src.is_none() {
100 if !src_path.exists() {
101 let mut rustup = Command::new(ra_toolchain::rustup()); 110 let mut rustup = Command::new(ra_toolchain::rustup());
102 rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]); 111 rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]);
103 let _output = output(rustup)?; 112 utf8_stdout(rustup)?;
113 src = get_rust_src(sysroot_path);
104 } 114 }
105 if !src_path.exists() { 115 match src {
106 bail!( 116 Some(r) => Ok(r),
117 None => bail!(
107 "can't load standard library from sysroot\n\ 118 "can't load standard library from sysroot\n\
108 {}\n\ 119 {}\n\
109 (discovered via `rustc --print sysroot`)\n\ 120 (discovered via `rustc --print sysroot`)\n\
110 try running `rustup component add rust-src` or set `RUST_SRC_PATH`", 121 try running `rustup component add rust-src` or set `RUST_SRC_PATH`",
111 src_path.display(), 122 sysroot_path.display(),
112 ) 123 ),
124 }
125}
126
127fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
128 // try the new path first since the old one still exists
129 let mut src_path = sysroot_path.join("lib/rustlib/src/rust/library");
130 if !src_path.exists() {
131 // FIXME: remove this path when 1.47 comes out
132 // https://github.com/rust-lang/rust/pull/73265
133 src_path = sysroot_path.join("lib/rustlib/src/rust/src");
134 }
135 if src_path.exists() {
136 Some(src_path)
137 } else {
138 None
113 } 139 }
114 Ok(src_path)
115} 140}
116 141
117impl SysrootCrateData { 142impl SysrootCrateData {
@@ -121,43 +146,28 @@ impl SysrootCrateData {
121} 146}
122 147
123const SYSROOT_CRATES: &str = " 148const SYSROOT_CRATES: &str = "
124std
125core
126alloc 149alloc
127collections 150core
128libc 151panic_abort
129panic_unwind 152panic_unwind
130proc_macro 153proc_macro
131rustc_unicode 154profiler_builtins
132std_unicode 155rtstartup
133test 156std
134alloc_jemalloc 157stdarch
135alloc_system
136compiler_builtins
137getopts
138panic_unwind
139panic_abort
140rand
141term 158term
142unwind 159test
143build_helper 160unwind";
144rustc_asan
145rustc_lsan
146rustc_msan
147rustc_tsan
148syntax";
149 161
150const STD_DEPS: &str = " 162const STD_DEPS: &str = "
151alloc 163alloc
152alloc_jemalloc
153alloc_system
154core 164core
155panic_abort 165panic_abort
156rand 166panic_unwind
157compiler_builtins 167profiler_builtins
158unwind 168rtstartup
159rustc_asan 169proc_macro
160rustc_lsan 170stdarch
161rustc_msan 171term
162rustc_tsan 172test
163build_helper"; 173unwind";