Skip to content

Commit 90fc9f6

Browse files
committed
Auto merge of #5628 - ehuss:metabuild, r=alexcrichton
Metabuild (RFC 2196)
2 parents 3ce6527 + ecc87b1 commit 90fc9f6

File tree

13 files changed

+974
-48
lines changed

13 files changed

+974
-48
lines changed

src/cargo/core/compiler/custom_build.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
134134
let build_plan = bcx.build_config.build_plan;
135135
let invocation_name = unit.buildkey();
136136

137+
if let Some(deps) = unit.pkg.manifest().metabuild() {
138+
prepare_metabuild(cx, build_script_unit, deps)?;
139+
}
140+
137141
// Building the command to execute
138142
let to_exec = script_output.join(unit.target.name());
139143

@@ -532,6 +536,38 @@ impl BuildOutput {
532536
}
533537
}
534538

539+
fn prepare_metabuild<'a, 'cfg>(
540+
cx: &Context<'a, 'cfg>,
541+
unit: &Unit<'a>,
542+
deps: &[String],
543+
) -> CargoResult<()> {
544+
let mut output = Vec::new();
545+
let available_deps = cx.dep_targets(unit);
546+
// Filter out optional dependencies, and look up the actual lib name.
547+
let meta_deps: Vec<_> = deps
548+
.iter()
549+
.filter_map(|name| {
550+
available_deps
551+
.iter()
552+
.find(|u| u.pkg.name().as_str() == name.as_str())
553+
.map(|dep| dep.target.crate_name())
554+
})
555+
.collect();
556+
for dep in &meta_deps {
557+
output.push(format!("extern crate {};\n", dep));
558+
}
559+
output.push("fn main() {\n".to_string());
560+
for dep in &meta_deps {
561+
output.push(format!(" {}::metabuild();\n", dep));
562+
}
563+
output.push("}\n".to_string());
564+
let output = output.join("");
565+
let path = unit.pkg.manifest().metabuild_path(cx.bcx.ws.target_dir());
566+
fs::create_dir_all(path.parent().unwrap())?;
567+
paths::write_if_changed(path, &output)?;
568+
Ok(())
569+
}
570+
535571
impl BuildDeps {
536572
pub fn new(output_file: &Path, output: Option<&BuildOutput>) -> BuildDeps {
537573
BuildDeps {

src/cargo/core/compiler/mod.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::sync::Arc;
88
use same_file::is_same_file;
99
use serde_json;
1010

11+
use core::manifest::TargetSourcePath;
1112
use core::profiles::{Lto, Profile};
1213
use core::shell::ColorChoice;
1314
use core::{PackageId, Target};
@@ -390,7 +391,6 @@ fn link_targets<'a, 'cfg>(
390391
let outputs = cx.outputs(unit)?;
391392
let export_dir = cx.files().export_dir();
392393
let package_id = unit.pkg.package_id().clone();
393-
let target = unit.target.clone();
394394
let profile = unit.profile;
395395
let unit_mode = unit.mode;
396396
let features = bcx.resolve
@@ -399,6 +399,12 @@ fn link_targets<'a, 'cfg>(
399399
.map(|s| s.to_owned())
400400
.collect();
401401
let json_messages = bcx.build_config.json_messages();
402+
let mut target = unit.target.clone();
403+
if let TargetSourcePath::Metabuild = target.src_path() {
404+
// Give it something to serialize.
405+
let path = unit.pkg.manifest().metabuild_path(cx.bcx.ws.target_dir());
406+
target.set_src_path(TargetSourcePath::Path(path));
407+
}
402408

403409
Ok(Work::new(move |_| {
404410
// If we're a "root crate", e.g. the target of this compilation, then we
@@ -668,12 +674,16 @@ fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult
668674
// second is the cwd that rustc should operate in.
669675
fn path_args(bcx: &BuildContext, unit: &Unit) -> (PathBuf, PathBuf) {
670676
let ws_root = bcx.ws.root();
671-
let src = unit.target.src_path();
677+
let src = if unit.target.is_custom_build() && unit.pkg.manifest().metabuild().is_some() {
678+
unit.pkg.manifest().metabuild_path(bcx.ws.target_dir())
679+
} else {
680+
unit.target.src_path().path().to_path_buf()
681+
};
672682
assert!(src.is_absolute());
673-
match src.strip_prefix(ws_root) {
674-
Ok(path) => (path.to_path_buf(), ws_root.to_path_buf()),
675-
Err(_) => (src.to_path_buf(), unit.pkg.root().to_path_buf()),
683+
if let Ok(path) = src.strip_prefix(ws_root) {
684+
return (path.to_path_buf(), ws_root.to_path_buf());
676685
}
686+
(src, unit.pkg.root().to_path_buf())
677687
}
678688

679689
fn add_path_args(bcx: &BuildContext, unit: &Unit, cmd: &mut ProcessBuilder) {

src/cargo/core/features.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ features! {
189189

190190
// "default-run" manifest option,
191191
[unstable] default_run: bool,
192+
193+
// Declarative build scripts.
194+
[unstable] metabuild: bool,
192195
}
193196
}
194197

src/cargo/core/manifest.rs

Lines changed: 106 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
#![allow(deprecated)] // for SipHasher
2+
13
use std::collections::{BTreeMap, HashMap};
24
use std::fmt;
3-
use std::hash::{Hash, Hasher};
5+
use std::hash::{Hash, Hasher, SipHasher};
46
use std::path::{Path, PathBuf};
57
use std::rc::Rc;
68

@@ -15,7 +17,7 @@ use core::{Dependency, PackageId, PackageIdSpec, SourceId, Summary};
1517
use core::{Edition, Feature, Features, WorkspaceConfig};
1618
use util::errors::*;
1719
use util::toml::TomlManifest;
18-
use util::Config;
20+
use util::{Config, Filesystem};
1921

2022
pub enum EitherManifest {
2123
Real(Manifest),
@@ -44,6 +46,7 @@ pub struct Manifest {
4446
edition: Edition,
4547
im_a_teapot: Option<bool>,
4648
default_run: Option<String>,
49+
metabuild: Option<Vec<String>>,
4750
}
4851

4952
/// When parsing `Cargo.toml`, some warnings should silenced
@@ -190,7 +193,7 @@ pub struct Target {
190193
// as it's absolute currently and is otherwise a little too brittle for
191194
// causing rebuilds. Instead the hash for the path that we send to the
192195
// compiler is handled elsewhere.
193-
src_path: NonHashedPathBuf,
196+
src_path: TargetSourcePath,
194197
required_features: Option<Vec<String>>,
195198
tested: bool,
196199
benched: bool,
@@ -202,19 +205,50 @@ pub struct Target {
202205
}
203206

204207
#[derive(Clone, PartialEq, Eq)]
205-
struct NonHashedPathBuf {
206-
path: PathBuf,
208+
pub enum TargetSourcePath {
209+
Path(PathBuf),
210+
Metabuild,
207211
}
208212

209-
impl Hash for NonHashedPathBuf {
213+
impl TargetSourcePath {
214+
pub fn path(&self) -> &Path {
215+
match self {
216+
TargetSourcePath::Path(path) => path.as_ref(),
217+
TargetSourcePath::Metabuild => panic!("metabuild not expected"),
218+
}
219+
}
220+
221+
pub fn is_path(&self) -> bool {
222+
match self {
223+
TargetSourcePath::Path(_) => true,
224+
_ => false,
225+
}
226+
}
227+
}
228+
229+
impl Hash for TargetSourcePath {
210230
fn hash<H: Hasher>(&self, _: &mut H) {
211231
// ...
212232
}
213233
}
214234

215-
impl fmt::Debug for NonHashedPathBuf {
235+
impl fmt::Debug for TargetSourcePath {
216236
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
217-
self.path.fmt(f)
237+
match self {
238+
TargetSourcePath::Path(path) => path.fmt(f),
239+
TargetSourcePath::Metabuild => "metabuild".fmt(f),
240+
}
241+
}
242+
}
243+
244+
impl From<PathBuf> for TargetSourcePath {
245+
fn from(path: PathBuf) -> Self {
246+
assert!(
247+
path.is_absolute(),
248+
"`{}` is not absolute",
249+
path.display()
250+
);
251+
TargetSourcePath::Path(path)
218252
}
219253
}
220254

@@ -239,7 +273,7 @@ impl ser::Serialize for Target {
239273
kind: &self.kind,
240274
crate_types: self.rustc_crate_types(),
241275
name: &self.name,
242-
src_path: &self.src_path.path,
276+
src_path: &self.src_path.path().to_path_buf(),
243277
edition: &self.edition.to_string(),
244278
required_features: self
245279
.required_features
@@ -253,34 +287,43 @@ compact_debug! {
253287
impl fmt::Debug for Target {
254288
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
255289
let (default, default_name) = {
256-
let src = self.src_path().to_path_buf();
257290
match &self.kind {
258291
TargetKind::Lib(kinds) => {
259292
(
260293
Target::lib_target(
261294
&self.name,
262295
kinds.clone(),
263-
src.clone(),
264-
Edition::Edition2015,
296+
self.src_path().path().to_path_buf(),
297+
self.edition,
265298
),
266-
format!("lib_target({:?}, {:?}, {:?})",
267-
self.name, kinds, src),
299+
format!("lib_target({:?}, {:?}, {:?}, {:?})",
300+
self.name, kinds, self.src_path, self.edition),
268301
)
269302
}
270303
TargetKind::CustomBuild => {
271-
(
272-
Target::custom_build_target(
273-
&self.name,
274-
src.clone(),
275-
Edition::Edition2015,
276-
),
277-
format!("custom_build_target({:?}, {:?})",
278-
self.name, src),
279-
)
304+
match self.src_path {
305+
TargetSourcePath::Path(ref path) => {
306+
(
307+
Target::custom_build_target(
308+
&self.name,
309+
path.to_path_buf(),
310+
self.edition,
311+
),
312+
format!("custom_build_target({:?}, {:?}, {:?})",
313+
self.name, path, self.edition),
314+
)
315+
}
316+
TargetSourcePath::Metabuild => {
317+
(
318+
Target::metabuild_target(&self.name),
319+
format!("metabuild_target({:?})", self.name),
320+
)
321+
}
322+
}
280323
}
281324
_ => (
282-
Target::with_path(src.clone(), Edition::Edition2015),
283-
format!("with_path({:?})", src),
325+
Target::new(self.src_path.clone(), self.edition),
326+
format!("with_path({:?}, {:?})", self.src_path, self.edition),
284327
),
285328
}
286329
};
@@ -321,6 +364,7 @@ impl Manifest {
321364
im_a_teapot: Option<bool>,
322365
default_run: Option<String>,
323366
original: Rc<TomlManifest>,
367+
metabuild: Option<Vec<String>>,
324368
) -> Manifest {
325369
Manifest {
326370
summary,
@@ -342,6 +386,7 @@ impl Manifest {
342386
im_a_teapot,
343387
default_run,
344388
publish_lockfile,
389+
metabuild,
345390
}
346391
}
347392

@@ -464,6 +509,20 @@ impl Manifest {
464509
pub fn default_run(&self) -> Option<&str> {
465510
self.default_run.as_ref().map(|s| &s[..])
466511
}
512+
513+
pub fn metabuild(&self) -> Option<&Vec<String>> {
514+
self.metabuild.as_ref()
515+
}
516+
517+
pub fn metabuild_path(&self, target_dir: Filesystem) -> PathBuf {
518+
let mut hasher = SipHasher::new_with_keys(0, 0);
519+
self.package_id().hash(&mut hasher);
520+
let hash = hasher.finish();
521+
target_dir
522+
.into_path_unlocked()
523+
.join(".metabuild")
524+
.join(format!("metabuild-{}-{:016x}.rs", self.name(), hash))
525+
}
467526
}
468527

469528
impl VirtualManifest {
@@ -508,16 +567,11 @@ impl VirtualManifest {
508567
}
509568

510569
impl Target {
511-
fn with_path(src_path: PathBuf, edition: Edition) -> Target {
512-
assert!(
513-
src_path.is_absolute(),
514-
"`{}` is not absolute",
515-
src_path.display()
516-
);
570+
fn new(src_path: TargetSourcePath, edition: Edition) -> Target {
517571
Target {
518572
kind: TargetKind::Bin,
519573
name: String::new(),
520-
src_path: NonHashedPathBuf { path: src_path },
574+
src_path,
521575
required_features: None,
522576
doc: false,
523577
doctest: false,
@@ -529,6 +583,10 @@ impl Target {
529583
}
530584
}
531585

586+
fn with_path(src_path: PathBuf, edition: Edition) -> Target {
587+
Target::new(TargetSourcePath::from(src_path), edition)
588+
}
589+
532590
pub fn lib_target(
533591
name: &str,
534592
crate_targets: Vec<LibKind>,
@@ -575,6 +633,17 @@ impl Target {
575633
}
576634
}
577635

636+
pub fn metabuild_target(name: &str) -> Target {
637+
Target {
638+
kind: TargetKind::CustomBuild,
639+
name: name.to_string(),
640+
for_host: true,
641+
benched: false,
642+
tested: false,
643+
..Target::new(TargetSourcePath::Metabuild, Edition::Edition2015)
644+
}
645+
}
646+
578647
pub fn example_target(
579648
name: &str,
580649
crate_targets: Vec<LibKind>,
@@ -634,8 +703,11 @@ impl Target {
634703
pub fn crate_name(&self) -> String {
635704
self.name.replace("-", "_")
636705
}
637-
pub fn src_path(&self) -> &Path {
638-
&self.src_path.path
706+
pub fn src_path(&self) -> &TargetSourcePath {
707+
&self.src_path
708+
}
709+
pub fn set_src_path(&mut self, src_path: TargetSourcePath) {
710+
self.src_path = src_path;
639711
}
640712
pub fn required_features(&self) -> Option<&Vec<String>> {
641713
self.required_features.as_ref()

0 commit comments

Comments
 (0)