Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metabuild (RFC 2196) #5628

Merged
merged 4 commits into from
Aug 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/cargo/core/compiler/custom_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
let build_plan = bcx.build_config.build_plan;
let invocation_name = unit.buildkey();

if let Some(deps) = unit.pkg.manifest().metabuild() {
prepare_metabuild(cx, build_script_unit, deps)?;
}

// Building the command to execute
let to_exec = script_output.join(unit.target.name());

Expand Down Expand Up @@ -532,6 +536,38 @@ impl BuildOutput {
}
}

fn prepare_metabuild<'a, 'cfg>(
cx: &Context<'a, 'cfg>,
unit: &Unit<'a>,
deps: &[String],
) -> CargoResult<()> {
let mut output = Vec::new();
let available_deps = cx.dep_targets(unit);
// Filter out optional dependencies, and look up the actual lib name.
let meta_deps: Vec<_> = deps
.iter()
.filter_map(|name| {
available_deps
.iter()
.find(|u| u.pkg.name().as_str() == name.as_str())
.map(|dep| dep.target.crate_name())
})
.collect();
Copy link
Member

@joshtriplett joshtriplett Jun 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a minor nit that I'm not sure needs fixing: this is O(n^2), because it searches all of available_deps for every entry in meta_deps. If some readily available data structure provides an O(1) mapping from name to dep, I'd suggest using that. If one doesn't exist, though, I don't know that it's worth creating one just for this, given that the common case will have relatively few metabuild entries.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unaware of any other way to get the necessary information. As you mention, I would expect these lists to usually be very small.

for dep in &meta_deps {
output.push(format!("extern crate {};\n", dep));
}
output.push("fn main() {\n".to_string());
for dep in &meta_deps {
output.push(format!(" {}::metabuild();\n", dep));
}
output.push("}\n".to_string());
let output = output.join("");
let path = unit.pkg.manifest().metabuild_path(cx.bcx.ws.target_dir());
fs::create_dir_all(path.parent().unwrap())?;
paths::write_if_changed(path, &output)?;
Ok(())
}

impl BuildDeps {
pub fn new(output_file: &Path, output: Option<&BuildOutput>) -> BuildDeps {
BuildDeps {
Expand Down
20 changes: 15 additions & 5 deletions src/cargo/core/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::sync::Arc;
use same_file::is_same_file;
use serde_json;

use core::manifest::TargetSourcePath;
use core::profiles::{Lto, Profile};
use core::shell::ColorChoice;
use core::{PackageId, Target};
Expand Down Expand Up @@ -390,7 +391,6 @@ fn link_targets<'a, 'cfg>(
let outputs = cx.outputs(unit)?;
let export_dir = cx.files().export_dir();
let package_id = unit.pkg.package_id().clone();
let target = unit.target.clone();
let profile = unit.profile;
let unit_mode = unit.mode;
let features = bcx.resolve
Expand All @@ -399,6 +399,12 @@ fn link_targets<'a, 'cfg>(
.map(|s| s.to_owned())
.collect();
let json_messages = bcx.build_config.json_messages();
let mut target = unit.target.clone();
if let TargetSourcePath::Metabuild = target.src_path() {
// Give it something to serialize.
let path = unit.pkg.manifest().metabuild_path(cx.bcx.ws.target_dir());
target.set_src_path(TargetSourcePath::Path(path));
}

Ok(Work::new(move |_| {
// If we're a "root crate", e.g. the target of this compilation, then we
Expand Down Expand Up @@ -668,12 +674,16 @@ fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult
// second is the cwd that rustc should operate in.
fn path_args(bcx: &BuildContext, unit: &Unit) -> (PathBuf, PathBuf) {
let ws_root = bcx.ws.root();
let src = unit.target.src_path();
let src = if unit.target.is_custom_build() && unit.pkg.manifest().metabuild().is_some() {
unit.pkg.manifest().metabuild_path(bcx.ws.target_dir())
} else {
unit.target.src_path().path().to_path_buf()
};
assert!(src.is_absolute());
match src.strip_prefix(ws_root) {
Ok(path) => (path.to_path_buf(), ws_root.to_path_buf()),
Err(_) => (src.to_path_buf(), unit.pkg.root().to_path_buf()),
if let Ok(path) = src.strip_prefix(ws_root) {
return (path.to_path_buf(), ws_root.to_path_buf());
}
(src, unit.pkg.root().to_path_buf())
}

fn add_path_args(bcx: &BuildContext, unit: &Unit, cmd: &mut ProcessBuilder) {
Expand Down
3 changes: 3 additions & 0 deletions src/cargo/core/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ features! {

// "default-run" manifest option,
[unstable] default_run: bool,

// Declarative build scripts.
[unstable] metabuild: bool,
}
}

Expand Down
140 changes: 106 additions & 34 deletions src/cargo/core/manifest.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#![allow(deprecated)] // for SipHasher

use std::collections::{BTreeMap, HashMap};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::hash::{Hash, Hasher, SipHasher};
use std::path::{Path, PathBuf};
use std::rc::Rc;

Expand All @@ -15,7 +17,7 @@ use core::{Dependency, PackageId, PackageIdSpec, SourceId, Summary};
use core::{Edition, Feature, Features, WorkspaceConfig};
use util::errors::*;
use util::toml::TomlManifest;
use util::Config;
use util::{Config, Filesystem};

pub enum EitherManifest {
Real(Manifest),
Expand Down Expand Up @@ -44,6 +46,7 @@ pub struct Manifest {
edition: Edition,
im_a_teapot: Option<bool>,
default_run: Option<String>,
metabuild: Option<Vec<String>>,
}

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

#[derive(Clone, PartialEq, Eq)]
struct NonHashedPathBuf {
path: PathBuf,
pub enum TargetSourcePath {
Path(PathBuf),
Metabuild,
}

impl Hash for NonHashedPathBuf {
impl TargetSourcePath {
pub fn path(&self) -> &Path {
match self {
TargetSourcePath::Path(path) => path.as_ref(),
TargetSourcePath::Metabuild => panic!("metabuild not expected"),
}
}

pub fn is_path(&self) -> bool {
match self {
TargetSourcePath::Path(_) => true,
_ => false,
}
}
}

impl Hash for TargetSourcePath {
fn hash<H: Hasher>(&self, _: &mut H) {
// ...
}
}

impl fmt::Debug for NonHashedPathBuf {
impl fmt::Debug for TargetSourcePath {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.path.fmt(f)
match self {
TargetSourcePath::Path(path) => path.fmt(f),
TargetSourcePath::Metabuild => "metabuild".fmt(f),
}
}
}

impl From<PathBuf> for TargetSourcePath {
fn from(path: PathBuf) -> Self {
assert!(
path.is_absolute(),
"`{}` is not absolute",
path.display()
);
TargetSourcePath::Path(path)
}
}

Expand All @@ -239,7 +273,7 @@ impl ser::Serialize for Target {
kind: &self.kind,
crate_types: self.rustc_crate_types(),
name: &self.name,
src_path: &self.src_path.path,
src_path: &self.src_path.path().to_path_buf(),
edition: &self.edition.to_string(),
required_features: self
.required_features
Expand All @@ -253,34 +287,43 @@ compact_debug! {
impl fmt::Debug for Target {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (default, default_name) = {
let src = self.src_path().to_path_buf();
match &self.kind {
TargetKind::Lib(kinds) => {
(
Target::lib_target(
&self.name,
kinds.clone(),
src.clone(),
Edition::Edition2015,
self.src_path().path().to_path_buf(),
self.edition,
),
format!("lib_target({:?}, {:?}, {:?})",
self.name, kinds, src),
format!("lib_target({:?}, {:?}, {:?}, {:?})",
self.name, kinds, self.src_path, self.edition),
)
}
TargetKind::CustomBuild => {
(
Target::custom_build_target(
&self.name,
src.clone(),
Edition::Edition2015,
),
format!("custom_build_target({:?}, {:?})",
self.name, src),
)
match self.src_path {
TargetSourcePath::Path(ref path) => {
(
Target::custom_build_target(
&self.name,
path.to_path_buf(),
self.edition,
),
format!("custom_build_target({:?}, {:?}, {:?})",
self.name, path, self.edition),
)
}
TargetSourcePath::Metabuild => {
(
Target::metabuild_target(&self.name),
format!("metabuild_target({:?})", self.name),
)
}
}
}
_ => (
Target::with_path(src.clone(), Edition::Edition2015),
format!("with_path({:?})", src),
Target::new(self.src_path.clone(), self.edition),
format!("with_path({:?}, {:?})", self.src_path, self.edition),
),
}
};
Expand Down Expand Up @@ -321,6 +364,7 @@ impl Manifest {
im_a_teapot: Option<bool>,
default_run: Option<String>,
original: Rc<TomlManifest>,
metabuild: Option<Vec<String>>,
) -> Manifest {
Manifest {
summary,
Expand All @@ -342,6 +386,7 @@ impl Manifest {
im_a_teapot,
default_run,
publish_lockfile,
metabuild,
}
}

Expand Down Expand Up @@ -464,6 +509,20 @@ impl Manifest {
pub fn default_run(&self) -> Option<&str> {
self.default_run.as_ref().map(|s| &s[..])
}

pub fn metabuild(&self) -> Option<&Vec<String>> {
self.metabuild.as_ref()
}

pub fn metabuild_path(&self, target_dir: Filesystem) -> PathBuf {
let mut hasher = SipHasher::new_with_keys(0, 0);
self.package_id().hash(&mut hasher);
let hash = hasher.finish();
target_dir
.into_path_unlocked()
.join(".metabuild")
.join(format!("metabuild-{}-{:016x}.rs", self.name(), hash))
}
}

impl VirtualManifest {
Expand Down Expand Up @@ -508,16 +567,11 @@ impl VirtualManifest {
}

impl Target {
fn with_path(src_path: PathBuf, edition: Edition) -> Target {
assert!(
src_path.is_absolute(),
"`{}` is not absolute",
src_path.display()
);
fn new(src_path: TargetSourcePath, edition: Edition) -> Target {
Target {
kind: TargetKind::Bin,
name: String::new(),
src_path: NonHashedPathBuf { path: src_path },
src_path,
required_features: None,
doc: false,
doctest: false,
Expand All @@ -529,6 +583,10 @@ impl Target {
}
}

fn with_path(src_path: PathBuf, edition: Edition) -> Target {
Target::new(TargetSourcePath::from(src_path), edition)
}

pub fn lib_target(
name: &str,
crate_targets: Vec<LibKind>,
Expand Down Expand Up @@ -575,6 +633,17 @@ impl Target {
}
}

pub fn metabuild_target(name: &str) -> Target {
Target {
kind: TargetKind::CustomBuild,
name: name.to_string(),
for_host: true,
benched: false,
tested: false,
..Target::new(TargetSourcePath::Metabuild, Edition::Edition2015)
}
}

pub fn example_target(
name: &str,
crate_targets: Vec<LibKind>,
Expand Down Expand Up @@ -634,8 +703,11 @@ impl Target {
pub fn crate_name(&self) -> String {
self.name.replace("-", "_")
}
pub fn src_path(&self) -> &Path {
&self.src_path.path
pub fn src_path(&self) -> &TargetSourcePath {
&self.src_path
}
pub fn set_src_path(&mut self, src_path: TargetSourcePath) {
self.src_path = src_path;
}
pub fn required_features(&self) -> Option<&Vec<String>> {
self.required_features.as_ref()
Expand Down
Loading