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

Implement selective optimization levels on a per-dependency basis. #1826

Closed
wants to merge 2 commits into from
Closed
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
10 changes: 10 additions & 0 deletions src/cargo/core/dependency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub struct Dependency {
optional: bool,
default_features: bool,
features: Vec<String>,
opt_level: Option<u32>,

// This dependency should be used only for this platform.
// `None` means *all platforms*.
Expand Down Expand Up @@ -59,6 +60,7 @@ impl Dependency {
default_features: true,
specified_req: None,
only_for_platform: None,
opt_level: None,
}
}

Expand Down Expand Up @@ -111,6 +113,12 @@ impl Dependency {
self
}

/// Set the minimum optimization level for this dependency
pub fn set_opt_level(mut self, opt_level: u32) -> Dependency {
self.opt_level = Some(opt_level);
self
}

pub fn set_only_for_platform(mut self, platform: Option<String>)
-> Dependency {
self.only_for_platform = platform;
Expand Down Expand Up @@ -140,6 +148,8 @@ impl Dependency {
pub fn uses_default_features(&self) -> bool { self.default_features }
/// Returns the list of features that are requested by the dependency.
pub fn features(&self) -> &[String] { &self.features }
/// Returns the optimization level requested for this dependency.
pub fn opt_level(&self) -> Option<u32> { self.opt_level }

/// Returns true if the package (`sum`) can fulfill this dependency request.
pub fn matches(&self, sum: &Summary) -> bool {
Expand Down
1 change: 1 addition & 0 deletions src/cargo/core/resolver/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ impl EncodableResolve {
graph: g,
root: try!(self.root.to_package_id(default)),
features: HashMap::new(),
opt_levels: HashMap::new(),
metadata: self.metadata.clone(),
})
}
Expand Down
31 changes: 29 additions & 2 deletions src/cargo/core/resolver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,9 @@
//! for a bit longer as well!

use std::cell::RefCell;
use std::cmp;
use std::collections::HashSet;
use std::collections::hash_map::HashMap;
use std::collections::hash_map::{Entry, HashMap};
use std::fmt;
use std::rc::Rc;
use std::slice;
Expand All @@ -120,6 +121,7 @@ mod encode;
pub struct Resolve {
graph: Graph<PackageId>,
features: HashMap<PackageId, HashSet<String>>,
opt_levels: HashMap<PackageId, u32>,
root: PackageId,
metadata: Option<Metadata>,
}
Expand All @@ -132,6 +134,7 @@ pub enum Method<'a> {
features: &'a [String],
uses_default_features: bool,
target_platform: Option<&'a str>,
opt_level: Option<u32>,
},
}

Expand All @@ -149,7 +152,13 @@ impl Resolve {
fn new(root: PackageId) -> Resolve {
let mut g = Graph::new();
g.add(root.clone(), &[]);
Resolve { graph: g, root: root, features: HashMap::new(), metadata: None }
Resolve {
graph: g,
root: root,
features: HashMap::new(),
opt_levels: HashMap::new(),
metadata: None,
}
}

pub fn copy_metadata(&mut self, other: &Resolve) {
Expand Down Expand Up @@ -215,6 +224,10 @@ impl Resolve {
pub fn features(&self, pkg: &PackageId) -> Option<&HashSet<String>> {
self.features.get(pkg)
}

pub fn opt_level(&self, pkg: &PackageId) -> Option<u32> {
self.opt_levels.get(pkg).map(|opt_level| *opt_level)
}
}

impl fmt::Debug for Resolve {
Expand Down Expand Up @@ -329,6 +342,7 @@ fn activate_deps<'a>(cx: Box<Context>,
features: features,
uses_default_features: dep.uses_default_features(),
target_platform: platform,
opt_level: dep.opt_level(),
};

let prev_active = cx.prev_active(dep);
Expand Down Expand Up @@ -630,6 +644,19 @@ impl Context {
// for our own dependencies.
let deps = try!(self.resolve_features(parent, method));

// Record the optimization level for this package.
if let Method::Required{opt_level: Some(opt_level), ..} = method {
match self.resolve.opt_levels.entry(parent.package_id().clone()) {
Entry::Occupied(mut entry) => {
let max_opt_level = cmp::max(*entry.get(), opt_level);
entry.insert(max_opt_level);
}
Entry::Vacant(entry) => {
entry.insert(opt_level);
}
}
}

// Next, transform all dependencies into a list of possible candidates
// which can satisfy that dependency.
let mut deps = try!(deps.into_iter().map(|(dep, features)| {
Expand Down
5 changes: 4 additions & 1 deletion src/cargo/ops/cargo_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,14 @@ pub fn compile_pkg<'a>(package: &Package,

let platform = target.as_ref().map(|e| &e[..]).or(Some(&rustc_host[..]));

// We specify no optimization level here because at the top level
// the user can already effectively specify that via the profile.
let method = Method::Required{
dev_deps: true, // TODO: remove this option?
features: &features,
uses_default_features: !no_default_features,
target_platform: platform};
target_platform: platform,
opt_level: None};

let resolved_with_overrides =
try!(ops::resolve_with_previous(&mut registry, package, method,
Expand Down
1 change: 1 addition & 0 deletions src/cargo/ops/cargo_rustc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ fn build_base_args(cx: &Context,
cmd.arg("-C").arg("prefer-dynamic");
}

let opt_level = cx.resolve.opt_level(pkg.package_id()).unwrap_or(opt_level);
if opt_level != 0 {
cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
}
Expand Down
6 changes: 5 additions & 1 deletion src/cargo/util/toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ pub struct DetailedTomlDependency {
features: Option<Vec<String>>,
optional: Option<bool>,
default_features: Option<bool>,
opt_level: Option<u32>,
}

#[derive(RustcDecodable)]
Expand Down Expand Up @@ -665,10 +666,13 @@ fn process_dependencies<F>(cx: &mut Context,
details.version.as_ref()
.map(|v| &v[..]),
&new_source_id));
let dep = f(dep)
let mut dep = f(dep)
.set_features(details.features.unwrap_or(Vec::new()))
.set_default_features(details.default_features.unwrap_or(true))
.set_optional(details.optional.unwrap_or(false));
if let Some(opt_level) = details.opt_level {
dep = dep.set_opt_level(opt_level)
}
cx.deps.push(dep);
}

Expand Down
15 changes: 15 additions & 0 deletions src/doc/manifest.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,20 @@ openssl = "1.0.1"
native = { path = "native/x86_64" }
```

Sometimes, you may wish a dependency to be compiled at a minimum optimization
level. This is useful if you strongly suspect you will not need to debug the
dependency itself. To use this feature, specify an `opt_level` key in the
dependency table as follows:

```toml
[dependencies.hammer]
version = "0.5.0"
opt_level = 2
```

If multiple crates specify different optimization levels for the same
dependency, Cargo chooses the highest level of optimization.

# The `[profile.*]` Sections

Cargo supports custom configuration of how rustc is invoked through **profiles**
Expand Down Expand Up @@ -504,3 +518,4 @@ crate-type = ["dylib"]
The available options are `dylib`, `rlib`, and `staticlib`. You should only use
this option in a project. Cargo will always compile **packages** (dependencies)
based on the requirements of the project that includes them.

101 changes: 101 additions & 0 deletions tests/test_cargo_opt_levels.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use std::path::MAIN_SEPARATOR as SEP;

use support::{project, execs};
use support::{COMPILING, RUNNING};
use hamcrest::assert_that;

fn setup() {
}

test!(basic_per_dependency_opt_levels {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []

[dependencies.bar]
path = "bar"
opt_level = 2
"#)
.file("src/main.rs", r#"
extern crate bar;
fn main() {}
"#)
.file("bar/Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
"#)
.file("bar/src/lib.rs", "pub fn bar() {}");

assert_that(p.cargo_process("build").arg("-v"),
execs().with_status(0).with_stdout(format!("\
{compiling} bar v0.0.1 ({url})
{running} `rustc bar{sep}src{sep}lib.rs --crate-name bar --crate-type lib \
-C opt-level=2 -g [..]
{compiling} foo v0.0.1 ({url})
{running} `rustc src{sep}main.rs --crate-name foo --crate-type bin -g \
--out-dir {dir}{sep}target{sep}debug [..]",
running = RUNNING, compiling = COMPILING, sep = SEP,
dir = p.root().display(), url = p.url())));
});

test!(highest_opt_level_wins_in_per_dependency_opt_levels {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []

[dependencies.bar]
path = "bar"

[dependencies.baz]
path = "baz"
opt_level = 1
"#)
.file("src/main.rs", r#"
extern crate bar;
fn main() {}
"#)
.file("bar/Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []

[dependencies.baz]
path = "../baz"
opt_level = 2
"#)
.file("bar/src/lib.rs", r#"
extern crate baz;
pub fn bar() {}
"#)
.file("baz/Cargo.toml", r#"
[package]
name = "baz"
version = "0.0.1"
authors = []
"#)
.file("baz/src/lib.rs", "pub fn baz() {}");

assert_that(p.cargo_process("build").arg("-v"),
execs().with_status(0).with_stdout(format!("\
{compiling} baz v0.0.1 ({url})
{running} `rustc baz{sep}src{sep}lib.rs --crate-name baz --crate-type lib \
-C opt-level=2 -g [..]
{compiling} bar v0.0.1 ({url})
{running} `rustc bar{sep}src{sep}lib.rs --crate-name bar --crate-type lib \
-g [..]
{compiling} foo v0.0.1 ({url})
{running} `rustc src{sep}main.rs --crate-name foo --crate-type bin -g \
--out-dir {dir}{sep}target{sep}debug [..]",
running = RUNNING, compiling = COMPILING, sep = SEP,
dir = p.root().display(), url = p.url())));
});

1 change: 1 addition & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ mod test_cargo_fetch;
mod test_cargo_freshness;
mod test_cargo_generate_lockfile;
mod test_cargo_new;
mod test_cargo_opt_levels;
mod test_cargo_package;
mod test_cargo_profiles;
mod test_cargo_publish;
Expand Down