Skip to content

Commit 509c5b4

Browse files
committed
Extract new build_context module out of context module
1 parent c32e395 commit 509c5b4

File tree

5 files changed

+332
-323
lines changed

5 files changed

+332
-323
lines changed
Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
use std::env;
2+
use std::path::Path;
3+
use std::str::{self, FromStr};
4+
5+
use core::profiles::Profiles;
6+
use core::{Dependency, Workspace};
7+
use core::{Package, PackageId, PackageSet, Resolve};
8+
use util::errors::CargoResult;
9+
use util::{profile, Cfg, CfgExpr, Config};
10+
11+
use super::{BuildConfig, Kind, TargetConfig, Unit};
12+
13+
mod target_info;
14+
pub use self::target_info::{FileFlavor, TargetInfo};
15+
16+
/// The build context, containing all information about a build task
17+
pub struct BuildContext<'a, 'cfg: 'a> {
18+
/// The workspace the build is for
19+
pub ws: &'a Workspace<'cfg>,
20+
/// The cargo configuration
21+
pub config: &'cfg Config,
22+
/// The dependency graph for our build
23+
pub resolve: &'a Resolve,
24+
pub profiles: &'a Profiles,
25+
pub build_config: &'a BuildConfig,
26+
/// This is a workaround to carry the extra compiler args for either
27+
/// `rustc` or `rustdoc` given on the command-line for the commands `cargo
28+
/// rustc` and `cargo rustdoc`. These commands only support one target,
29+
/// but we don't want the args passed to any dependencies, so we include
30+
/// the `Unit` corresponding to the top-level target.
31+
pub extra_compiler_args: Option<(Unit<'a>, Vec<String>)>,
32+
pub packages: &'a PackageSet<'cfg>,
33+
34+
pub target_info: TargetInfo,
35+
pub host_info: TargetInfo,
36+
pub incremental_env: Option<bool>,
37+
}
38+
39+
impl<'a, 'cfg> BuildContext<'a, 'cfg> {
40+
pub fn new(
41+
ws: &'a Workspace<'cfg>,
42+
resolve: &'a Resolve,
43+
packages: &'a PackageSet<'cfg>,
44+
config: &'cfg Config,
45+
build_config: &'a BuildConfig,
46+
profiles: &'a Profiles,
47+
extra_compiler_args: Option<(Unit<'a>, Vec<String>)>,
48+
) -> CargoResult<BuildContext<'a, 'cfg>> {
49+
let incremental_env = match env::var("CARGO_INCREMENTAL") {
50+
Ok(v) => Some(v == "1"),
51+
Err(_) => None,
52+
};
53+
54+
let (host_info, target_info) = {
55+
let _p = profile::start("BuildContext::probe_target_info");
56+
debug!("probe_target_info");
57+
let host_info = TargetInfo::new(config, &build_config, Kind::Host)?;
58+
let target_info = TargetInfo::new(config, &build_config, Kind::Target)?;
59+
(host_info, target_info)
60+
};
61+
62+
Ok(BuildContext {
63+
ws,
64+
resolve,
65+
packages,
66+
config,
67+
target_info,
68+
host_info,
69+
build_config,
70+
profiles,
71+
incremental_env,
72+
extra_compiler_args,
73+
})
74+
}
75+
76+
pub fn extern_crate_name(&self, unit: &Unit<'a>, dep: &Unit<'a>) -> CargoResult<String> {
77+
let deps = {
78+
let a = unit.pkg.package_id();
79+
let b = dep.pkg.package_id();
80+
if a == b {
81+
&[]
82+
} else {
83+
self.resolve.dependencies_listed(a, b)
84+
}
85+
};
86+
87+
let crate_name = dep.target.crate_name();
88+
let mut names = deps.iter()
89+
.map(|d| d.rename().unwrap_or(&crate_name));
90+
let name = names.next().unwrap_or(&crate_name);
91+
for n in names {
92+
if n == name {
93+
continue
94+
}
95+
bail!("multiple dependencies listed for the same crate must \
96+
all have the same name, but the dependency on `{}` \
97+
is listed as having different names", dep.pkg.package_id());
98+
}
99+
Ok(name.to_string())
100+
}
101+
102+
/// Whether a dependency should be compiled for the host or target platform,
103+
/// specified by `Kind`.
104+
pub fn dep_platform_activated(&self, dep: &Dependency, kind: Kind) -> bool {
105+
// If this dependency is only available for certain platforms,
106+
// make sure we're only enabling it for that platform.
107+
let platform = match dep.platform() {
108+
Some(p) => p,
109+
None => return true,
110+
};
111+
let (name, info) = match kind {
112+
Kind::Host => (self.build_config.host_triple(), &self.host_info),
113+
Kind::Target => (self.build_config.target_triple(), &self.target_info),
114+
};
115+
platform.matches(name, info.cfg())
116+
}
117+
118+
/// Gets a package for the given package id.
119+
pub fn get_package(&self, id: &PackageId) -> CargoResult<&'a Package> {
120+
self.packages.get(id)
121+
}
122+
123+
/// Get the user-specified linker for a particular host or target
124+
pub fn linker(&self, kind: Kind) -> Option<&Path> {
125+
self.target_config(kind).linker.as_ref().map(|s| s.as_ref())
126+
}
127+
128+
/// Get the user-specified `ar` program for a particular host or target
129+
pub fn ar(&self, kind: Kind) -> Option<&Path> {
130+
self.target_config(kind).ar.as_ref().map(|s| s.as_ref())
131+
}
132+
133+
/// Get the list of cfg printed out from the compiler for the specified kind
134+
pub fn cfg(&self, kind: Kind) -> &[Cfg] {
135+
let info = match kind {
136+
Kind::Host => &self.host_info,
137+
Kind::Target => &self.target_info,
138+
};
139+
info.cfg().unwrap_or(&[])
140+
}
141+
142+
/// Get the target configuration for a particular host or target
143+
fn target_config(&self, kind: Kind) -> &TargetConfig {
144+
match kind {
145+
Kind::Host => &self.build_config.host,
146+
Kind::Target => &self.build_config.target,
147+
}
148+
}
149+
150+
/// Number of jobs specified for this build
151+
pub fn jobs(&self) -> u32 {
152+
self.build_config.jobs
153+
}
154+
155+
pub fn rustflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> {
156+
env_args(
157+
self.config,
158+
&self.build_config,
159+
self.info(&unit.kind).cfg(),
160+
unit.kind,
161+
"RUSTFLAGS",
162+
)
163+
}
164+
165+
pub fn rustdocflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> {
166+
env_args(
167+
self.config,
168+
&self.build_config,
169+
self.info(&unit.kind).cfg(),
170+
unit.kind,
171+
"RUSTDOCFLAGS",
172+
)
173+
}
174+
175+
pub fn show_warnings(&self, pkg: &PackageId) -> bool {
176+
pkg.source_id().is_path() || self.config.extra_verbose()
177+
}
178+
179+
fn info(&self, kind: &Kind) -> &TargetInfo {
180+
match *kind {
181+
Kind::Host => &self.host_info,
182+
Kind::Target => &self.target_info,
183+
}
184+
}
185+
186+
pub fn extra_args_for(&self, unit: &Unit<'a>) -> Option<&Vec<String>> {
187+
if let Some((ref args_unit, ref args)) = self.extra_compiler_args {
188+
if args_unit == unit {
189+
return Some(args);
190+
}
191+
}
192+
None
193+
}
194+
}
195+
196+
/// Acquire extra flags to pass to the compiler from various locations.
197+
///
198+
/// The locations are:
199+
///
200+
/// - the `RUSTFLAGS` environment variable
201+
///
202+
/// then if this was not found
203+
///
204+
/// - `target.*.rustflags` from the manifest (Cargo.toml)
205+
/// - `target.cfg(..).rustflags` from the manifest
206+
///
207+
/// then if neither of these were found
208+
///
209+
/// - `build.rustflags` from the manifest
210+
///
211+
/// Note that if a `target` is specified, no args will be passed to host code (plugins, build
212+
/// scripts, ...), even if it is the same as the target.
213+
fn env_args(
214+
config: &Config,
215+
build_config: &BuildConfig,
216+
target_cfg: Option<&[Cfg]>,
217+
kind: Kind,
218+
name: &str,
219+
) -> CargoResult<Vec<String>> {
220+
// We *want* to apply RUSTFLAGS only to builds for the
221+
// requested target architecture, and not to things like build
222+
// scripts and plugins, which may be for an entirely different
223+
// architecture. Cargo's present architecture makes it quite
224+
// hard to only apply flags to things that are not build
225+
// scripts and plugins though, so we do something more hacky
226+
// instead to avoid applying the same RUSTFLAGS to multiple targets
227+
// arches:
228+
//
229+
// 1) If --target is not specified we just apply RUSTFLAGS to
230+
// all builds; they are all going to have the same target.
231+
//
232+
// 2) If --target *is* specified then we only apply RUSTFLAGS
233+
// to compilation units with the Target kind, which indicates
234+
// it was chosen by the --target flag.
235+
//
236+
// This means that, e.g. even if the specified --target is the
237+
// same as the host, build scripts in plugins won't get
238+
// RUSTFLAGS.
239+
let compiling_with_target = build_config.requested_target.is_some();
240+
let is_target_kind = kind == Kind::Target;
241+
242+
if compiling_with_target && !is_target_kind {
243+
// This is probably a build script or plugin and we're
244+
// compiling with --target. In this scenario there are
245+
// no rustflags we can apply.
246+
return Ok(Vec::new());
247+
}
248+
249+
// First try RUSTFLAGS from the environment
250+
if let Ok(a) = env::var(name) {
251+
let args = a.split(' ')
252+
.map(str::trim)
253+
.filter(|s| !s.is_empty())
254+
.map(str::to_string);
255+
return Ok(args.collect());
256+
}
257+
258+
let mut rustflags = Vec::new();
259+
260+
let name = name.chars()
261+
.flat_map(|c| c.to_lowercase())
262+
.collect::<String>();
263+
// Then the target.*.rustflags value...
264+
let target = build_config
265+
.requested_target
266+
.as_ref()
267+
.map(|s| s.as_str())
268+
.unwrap_or(build_config.host_triple());
269+
let key = format!("target.{}.{}", target, name);
270+
if let Some(args) = config.get_list_or_split_string(&key)? {
271+
let args = args.val.into_iter();
272+
rustflags.extend(args);
273+
}
274+
// ...including target.'cfg(...)'.rustflags
275+
if let Some(target_cfg) = target_cfg {
276+
if let Some(table) = config.get_table("target")? {
277+
let cfgs = table.val.keys().filter_map(|t| {
278+
if t.starts_with("cfg(") && t.ends_with(')') {
279+
let cfg = &t[4..t.len() - 1];
280+
CfgExpr::from_str(cfg).ok().and_then(|c| {
281+
if c.matches(target_cfg) {
282+
Some(t)
283+
} else {
284+
None
285+
}
286+
})
287+
} else {
288+
None
289+
}
290+
});
291+
292+
// Note that we may have multiple matching `[target]` sections and
293+
// because we're passing flags to the compiler this can affect
294+
// cargo's caching and whether it rebuilds. Ensure a deterministic
295+
// ordering through sorting for now. We may perhaps one day wish to
296+
// ensure a deterministic ordering via the order keys were defined
297+
// in files perhaps.
298+
let mut cfgs = cfgs.collect::<Vec<_>>();
299+
cfgs.sort();
300+
301+
for n in cfgs {
302+
let key = format!("target.{}.{}", n, name);
303+
if let Some(args) = config.get_list_or_split_string(&key)? {
304+
let args = args.val.into_iter();
305+
rustflags.extend(args);
306+
}
307+
}
308+
}
309+
}
310+
311+
if !rustflags.is_empty() {
312+
return Ok(rustflags);
313+
}
314+
315+
// Then the build.rustflags value
316+
let key = format!("build.{}", name);
317+
if let Some(args) = config.get_list_or_split_string(&key)? {
318+
let args = args.val.into_iter();
319+
return Ok(args.collect());
320+
}
321+
322+
Ok(Vec::new())
323+
}

0 commit comments

Comments
 (0)