]> git.proxmox.com Git - cargo.git/blob - src/cargo/core/compiler/compilation.rs
47c64c49775335c29b6807904d7be552127f0c13
[cargo.git] / src / cargo / core / compiler / compilation.rs
1 use std::collections::{BTreeSet, HashMap, HashSet};
2 use std::env;
3 use std::ffi::OsStr;
4 use std::path::PathBuf;
5
6 use semver::Version;
7
8 use super::BuildContext;
9 use crate::core::{Edition, Package, PackageId, Target};
10 use crate::util::{self, join_paths, process, CargoResult, CfgExpr, Config, ProcessBuilder};
11
12 pub struct Doctest {
13 /// The package being doc-tested.
14 pub package: Package,
15 /// The target being tested (currently always the package's lib).
16 pub target: Target,
17 /// Extern dependencies needed by `rustdoc`. The path is the location of
18 /// the compiled lib.
19 pub deps: Vec<(String, PathBuf)>,
20 }
21
22 /// A structure returning the result of a compilation.
23 pub struct Compilation<'cfg> {
24 /// An array of all tests created during this compilation.
25 /// `(package, target, path_to_test_exe)`
26 pub tests: Vec<(Package, Target, PathBuf)>,
27
28 /// An array of all binaries created.
29 pub binaries: Vec<PathBuf>,
30
31 /// All directories for the output of native build commands.
32 ///
33 /// This is currently used to drive some entries which are added to the
34 /// LD_LIBRARY_PATH as appropriate.
35 ///
36 /// The order should be deterministic.
37 pub native_dirs: BTreeSet<PathBuf>,
38
39 /// Root output directory (for the local package's artifacts)
40 pub root_output: PathBuf,
41
42 /// Output directory for rust dependencies.
43 /// May be for the host or for a specific target.
44 pub deps_output: PathBuf,
45
46 /// Output directory for the rust host dependencies.
47 pub host_deps_output: PathBuf,
48
49 /// The path to rustc's own libstd
50 pub host_dylib_path: PathBuf,
51
52 /// The path to libstd for the target
53 pub target_dylib_path: PathBuf,
54
55 /// Extra environment variables that were passed to compilations and should
56 /// be passed to future invocations of programs.
57 pub extra_env: HashMap<PackageId, Vec<(String, String)>>,
58
59 /// Libraries to test with rustdoc.
60 pub to_doc_test: Vec<Doctest>,
61
62 /// Features per package enabled during this compilation.
63 pub cfgs: HashMap<PackageId, HashSet<String>>,
64
65 /// Flags to pass to rustdoc when invoked from cargo test, per package.
66 pub rustdocflags: HashMap<PackageId, Vec<String>>,
67
68 pub host: String,
69 pub target: String,
70
71 config: &'cfg Config,
72 rustc_process: ProcessBuilder,
73 primary_unit_rustc_process: Option<ProcessBuilder>,
74
75 target_runner: Option<(PathBuf, Vec<String>)>,
76 }
77
78 impl<'cfg> Compilation<'cfg> {
79 pub fn new<'a>(bcx: &BuildContext<'a, 'cfg>) -> CargoResult<Compilation<'cfg>> {
80 let mut rustc = bcx.rustc.process();
81
82 let mut primary_unit_rustc_process = bcx.build_config.primary_unit_rustc.clone();
83
84 if bcx.config.extra_verbose() {
85 rustc.display_env_vars();
86
87 if let Some(rustc) = primary_unit_rustc_process.as_mut() {
88 rustc.display_env_vars();
89 }
90 }
91
92 Ok(Compilation {
93 // TODO: deprecated; remove.
94 native_dirs: BTreeSet::new(),
95 root_output: PathBuf::from("/"),
96 deps_output: PathBuf::from("/"),
97 host_deps_output: PathBuf::from("/"),
98 host_dylib_path: bcx.host_info.sysroot_libdir.clone(),
99 target_dylib_path: bcx.target_info.sysroot_libdir.clone(),
100 tests: Vec::new(),
101 binaries: Vec::new(),
102 extra_env: HashMap::new(),
103 to_doc_test: Vec::new(),
104 cfgs: HashMap::new(),
105 rustdocflags: HashMap::new(),
106 config: bcx.config,
107 rustc_process: rustc,
108 primary_unit_rustc_process,
109 host: bcx.host_triple().to_string(),
110 target: bcx.target_triple().to_string(),
111 target_runner: target_runner(bcx)?,
112 })
113 }
114
115 /// See `process`.
116 pub fn rustc_process(
117 &self,
118 pkg: &Package,
119 target: &Target,
120 is_primary: bool,
121 ) -> CargoResult<ProcessBuilder> {
122 let rustc = if is_primary {
123 self.primary_unit_rustc_process
124 .clone()
125 .unwrap_or_else(|| self.rustc_process.clone())
126 } else {
127 self.rustc_process.clone()
128 };
129
130 let mut p = self.fill_env(rustc, pkg, true)?;
131 if target.edition() != Edition::Edition2015 {
132 p.arg(format!("--edition={}", target.edition()));
133 }
134 Ok(p)
135 }
136
137 /// See `process`.
138 pub fn rustdoc_process(&self, pkg: &Package, target: &Target) -> CargoResult<ProcessBuilder> {
139 let mut p = self.fill_env(process(&*self.config.rustdoc()?), pkg, false)?;
140 if target.edition() != Edition::Edition2015 {
141 p.arg(format!("--edition={}", target.edition()));
142 }
143 Ok(p)
144 }
145
146 /// See `process`.
147 pub fn host_process<T: AsRef<OsStr>>(
148 &self,
149 cmd: T,
150 pkg: &Package,
151 ) -> CargoResult<ProcessBuilder> {
152 self.fill_env(process(cmd), pkg, true)
153 }
154
155 fn target_runner(&self) -> &Option<(PathBuf, Vec<String>)> {
156 &self.target_runner
157 }
158
159 /// See `process`.
160 pub fn target_process<T: AsRef<OsStr>>(
161 &self,
162 cmd: T,
163 pkg: &Package,
164 ) -> CargoResult<ProcessBuilder> {
165 let builder = if let Some((ref runner, ref args)) = *self.target_runner() {
166 let mut builder = process(runner);
167 builder.args(args);
168 builder.arg(cmd);
169 builder
170 } else {
171 process(cmd)
172 };
173 self.fill_env(builder, pkg, false)
174 }
175
176 /// Prepares a new process with an appropriate environment to run against
177 /// the artifacts produced by the build process.
178 ///
179 /// The package argument is also used to configure environment variables as
180 /// well as the working directory of the child process.
181 fn fill_env(
182 &self,
183 mut cmd: ProcessBuilder,
184 pkg: &Package,
185 is_host: bool,
186 ) -> CargoResult<ProcessBuilder> {
187 let mut search_path = if is_host {
188 let mut search_path = vec![self.host_deps_output.clone()];
189 search_path.push(self.host_dylib_path.clone());
190 search_path
191 } else {
192 let mut search_path =
193 super::filter_dynamic_search_path(self.native_dirs.iter(), &self.root_output);
194 search_path.push(self.deps_output.clone());
195 search_path.push(self.root_output.clone());
196 search_path.push(self.target_dylib_path.clone());
197 search_path
198 };
199
200 let dylib_path = util::dylib_path();
201 let dylib_path_is_empty = dylib_path.is_empty();
202 search_path.extend(dylib_path.into_iter());
203 if cfg!(target_os = "macos") && dylib_path_is_empty {
204 // These are the defaults when DYLD_FALLBACK_LIBRARY_PATH isn't
205 // set or set to an empty string. Since Cargo is explicitly setting
206 // the value, make sure the defaults still work.
207 if let Some(home) = env::var_os("HOME") {
208 search_path.push(PathBuf::from(home).join("lib"));
209 }
210 search_path.push(PathBuf::from("/usr/local/lib"));
211 search_path.push(PathBuf::from("/usr/lib"));
212 }
213 let search_path = join_paths(&search_path, util::dylib_path_envvar())?;
214
215 cmd.env(util::dylib_path_envvar(), &search_path);
216 if let Some(env) = self.extra_env.get(&pkg.package_id()) {
217 for &(ref k, ref v) in env {
218 cmd.env(k, v);
219 }
220 }
221
222 let metadata = pkg.manifest().metadata();
223
224 let cargo_exe = self.config.cargo_exe()?;
225 cmd.env(crate::CARGO_ENV, cargo_exe);
226
227 // When adding new environment variables depending on
228 // crate properties which might require rebuild upon change
229 // consider adding the corresponding properties to the hash
230 // in BuildContext::target_metadata()
231 cmd.env("CARGO_MANIFEST_DIR", pkg.root())
232 .env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string())
233 .env("CARGO_PKG_VERSION_MINOR", &pkg.version().minor.to_string())
234 .env("CARGO_PKG_VERSION_PATCH", &pkg.version().patch.to_string())
235 .env(
236 "CARGO_PKG_VERSION_PRE",
237 &pre_version_component(pkg.version()),
238 )
239 .env("CARGO_PKG_VERSION", &pkg.version().to_string())
240 .env("CARGO_PKG_NAME", &*pkg.name())
241 .env(
242 "CARGO_PKG_DESCRIPTION",
243 metadata.description.as_ref().unwrap_or(&String::new()),
244 )
245 .env(
246 "CARGO_PKG_HOMEPAGE",
247 metadata.homepage.as_ref().unwrap_or(&String::new()),
248 )
249 .env(
250 "CARGO_PKG_REPOSITORY",
251 metadata.repository.as_ref().unwrap_or(&String::new()),
252 )
253 .env("CARGO_PKG_AUTHORS", &pkg.authors().join(":"))
254 .cwd(pkg.root());
255 Ok(cmd)
256 }
257 }
258
259 fn pre_version_component(v: &Version) -> String {
260 if v.pre.is_empty() {
261 return String::new();
262 }
263
264 let mut ret = String::new();
265
266 for (i, x) in v.pre.iter().enumerate() {
267 if i != 0 {
268 ret.push('.')
269 };
270 ret.push_str(&x.to_string());
271 }
272
273 ret
274 }
275
276 fn target_runner(bcx: &BuildContext<'_, '_>) -> CargoResult<Option<(PathBuf, Vec<String>)>> {
277 let target = bcx.target_triple();
278
279 // try target.{}.runner
280 let key = format!("target.{}.runner", target);
281 if let Some(v) = bcx.config.get_path_and_args(&key)? {
282 return Ok(Some(v.val));
283 }
284
285 // try target.'cfg(...)'.runner
286 if let Some(table) = bcx.config.get_table("target")? {
287 let mut matching_runner = None;
288
289 for key in table.val.keys() {
290 if CfgExpr::matches_key(key, bcx.target_info.cfg()) {
291 let key = format!("target.{}.runner", key);
292 if let Some(runner) = bcx.config.get_path_and_args(&key)? {
293 // more than one match, error out
294 if matching_runner.is_some() {
295 failure::bail!(
296 "several matching instances of `target.'cfg(..)'.runner` \
297 in `.cargo/config`"
298 )
299 }
300
301 matching_runner = Some(runner.val);
302 }
303 }
304 }
305
306 return Ok(matching_runner);
307 }
308
309 Ok(None)
310 }