Skip to content

Commit 78b8d52

Browse files
committed
Implement selective optimization levels on a per-dependency basis.
Inside the `dependencies.foo` section, you may now specify a minimum optimization level to be applied to this dependency. If multiple such optimization levels are supplied, Cargo chooses the highest one. This will probably need to go through an RFC before landing. Addresses rust-lang#1359.
1 parent 84ef25d commit 78b8d52

File tree

9 files changed

+171
-4
lines changed

9 files changed

+171
-4
lines changed

src/cargo/core/dependency.rs

+10
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub struct Dependency {
1616
optional: bool,
1717
default_features: bool,
1818
features: Vec<String>,
19+
opt_level: Option<u32>,
1920

2021
// This dependency should be used only for this platform.
2122
// `None` means *all platforms*.
@@ -59,6 +60,7 @@ impl Dependency {
5960
default_features: true,
6061
specified_req: None,
6162
only_for_platform: None,
63+
opt_level: None,
6264
}
6365
}
6466

@@ -111,6 +113,12 @@ impl Dependency {
111113
self
112114
}
113115

116+
/// Set the minimum optimization level for this dependency
117+
pub fn set_opt_level(mut self, opt_level: u32) -> Dependency {
118+
self.opt_level = Some(opt_level);
119+
self
120+
}
121+
114122
pub fn set_only_for_platform(mut self, platform: Option<String>)
115123
-> Dependency {
116124
self.only_for_platform = platform;
@@ -140,6 +148,8 @@ impl Dependency {
140148
pub fn uses_default_features(&self) -> bool { self.default_features }
141149
/// Returns the list of features that are requested by the dependency.
142150
pub fn features(&self) -> &[String] { &self.features }
151+
/// Returns the optimization level requested for this dependency.
152+
pub fn opt_level(&self) -> Option<u32> { self.opt_level }
143153

144154
/// Returns true if the package (`sum`) can fulfill this dependency request.
145155
pub fn matches(&self, sum: &Summary) -> bool {

src/cargo/core/resolver/encode.rs

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ impl EncodableResolve {
7373
graph: g,
7474
root: try!(self.root.to_package_id(default)),
7575
features: HashMap::new(),
76+
opt_levels: HashMap::new(),
7677
metadata: self.metadata.clone(),
7778
})
7879
}

src/cargo/core/resolver/mod.rs

+33-2
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,9 @@
9393
//! for a bit longer as well!
9494
9595
use std::cell::RefCell;
96+
use std::cmp;
9697
use std::collections::HashSet;
97-
use std::collections::hash_map::HashMap;
98+
use std::collections::hash_map::{Entry, HashMap};
9899
use std::fmt;
99100
use std::rc::Rc;
100101
use std::slice;
@@ -120,6 +121,7 @@ mod encode;
120121
pub struct Resolve {
121122
graph: Graph<PackageId>,
122123
features: HashMap<PackageId, HashSet<String>>,
124+
opt_levels: HashMap<PackageId, u32>,
123125
root: PackageId,
124126
metadata: Option<Metadata>,
125127
}
@@ -132,6 +134,7 @@ pub enum Method<'a> {
132134
features: &'a [String],
133135
uses_default_features: bool,
134136
target_platform: Option<&'a str>,
137+
opt_level: Option<u32>,
135138
},
136139
}
137140

@@ -149,7 +152,13 @@ impl Resolve {
149152
fn new(root: PackageId) -> Resolve {
150153
let mut g = Graph::new();
151154
g.add(root.clone(), &[]);
152-
Resolve { graph: g, root: root, features: HashMap::new(), metadata: None }
155+
Resolve {
156+
graph: g,
157+
root: root,
158+
features: HashMap::new(),
159+
opt_levels: HashMap::new(),
160+
metadata: None,
161+
}
153162
}
154163

155164
pub fn copy_metadata(&mut self, other: &Resolve) {
@@ -215,6 +224,10 @@ impl Resolve {
215224
pub fn features(&self, pkg: &PackageId) -> Option<&HashSet<String>> {
216225
self.features.get(pkg)
217226
}
227+
228+
pub fn opt_level(&self, pkg: &PackageId) -> Option<u32> {
229+
self.opt_levels.get(pkg).map(|opt_level| *opt_level)
230+
}
218231
}
219232

220233
impl fmt::Debug for Resolve {
@@ -329,6 +342,7 @@ fn activate_deps<'a>(cx: Box<Context>,
329342
features: features,
330343
uses_default_features: dep.uses_default_features(),
331344
target_platform: platform,
345+
opt_level: dep.opt_level(),
332346
};
333347

334348
let prev_active = cx.prev_active(dep);
@@ -630,6 +644,23 @@ impl Context {
630644
// for our own dependencies.
631645
let deps = try!(self.resolve_features(parent, method));
632646

647+
// Record the optimization level for this package.
648+
//println!("about to record optimzation level for {:?}...", parent.package_id());
649+
if let Method::Required{opt_level: Some(opt_level), ..} = method {
650+
//println!("recording optimization level {}!", opt_level);
651+
match self.resolve.opt_levels.entry(parent.package_id().clone()) {
652+
Entry::Occupied(mut entry) => {
653+
let max_opt_level = cmp::max(*entry.get(), opt_level);
654+
entry.insert(max_opt_level);
655+
}
656+
Entry::Vacant(entry) => {
657+
entry.insert(opt_level);
658+
}
659+
}
660+
} else {
661+
//println!("no optimization level present!");
662+
}
663+
633664
// Next, transform all dependencies into a list of possible candidates
634665
// which can satisfy that dependency.
635666
let mut deps = try!(deps.into_iter().map(|(dep, features)| {

src/cargo/ops/cargo_compile.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,14 @@ pub fn compile_pkg<'a>(package: &Package,
148148

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

151+
// We specify no optimization level here because at the top level
152+
// the user can already effectively specify that via the profile.
151153
let method = Method::Required{
152154
dev_deps: true, // TODO: remove this option?
153155
features: &features,
154156
uses_default_features: !no_default_features,
155-
target_platform: platform};
157+
target_platform: platform,
158+
opt_level: None};
156159

157160
let resolved_with_overrides =
158161
try!(ops::resolve_with_previous(&mut registry, package, method,

src/cargo/ops/cargo_rustc/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,7 @@ fn build_base_args(cx: &Context,
626626
cmd.arg("-C").arg("prefer-dynamic");
627627
}
628628

629+
let opt_level = cx.resolve.opt_level(pkg.package_id()).unwrap_or(opt_level);
629630
if opt_level != 0 {
630631
cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
631632
}

src/cargo/util/toml.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ pub struct DetailedTomlDependency {
206206
features: Option<Vec<String>>,
207207
optional: Option<bool>,
208208
default_features: Option<bool>,
209+
opt_level: Option<u32>,
209210
}
210211

211212
#[derive(RustcDecodable)]
@@ -665,10 +666,13 @@ fn process_dependencies<F>(cx: &mut Context,
665666
details.version.as_ref()
666667
.map(|v| &v[..]),
667668
&new_source_id));
668-
let dep = f(dep)
669+
let mut dep = f(dep)
669670
.set_features(details.features.unwrap_or(Vec::new()))
670671
.set_default_features(details.default_features.unwrap_or(true))
671672
.set_optional(details.optional.unwrap_or(false));
673+
if let Some(opt_level) = details.opt_level {
674+
dep = dep.set_opt_level(opt_level)
675+
}
672676
cx.deps.push(dep);
673677
}
674678

src/doc/manifest.md

+15
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,20 @@ openssl = "1.0.1"
191191
native = { path = "native/x86_64" }
192192
```
193193

194+
Sometimes, you may wish a dependency to be compiled at a minimum optimization
195+
level. This is useful if you strongly suspect you will not need to debug the
196+
dependency itself. To use this feature, specify an `opt_level` key in the
197+
dependency table as follows:
198+
199+
```toml
200+
[dependencies.hammer]
201+
version = "0.5.0"
202+
opt_level = 2
203+
```
204+
205+
If multiple crates specify different optimization levels for the same
206+
dependency, Cargo chooses the highest level of optimization.
207+
194208
# The `[profile.*]` Sections
195209

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

tests/test_cargo_opt_levels.rs

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use std::path::MAIN_SEPARATOR as SEP;
2+
3+
use support::{project, execs};
4+
use support::{COMPILING, RUNNING};
5+
use hamcrest::assert_that;
6+
7+
fn setup() {
8+
}
9+
10+
test!(basic_per_dependency_opt_levels {
11+
let p = project("foo")
12+
.file("Cargo.toml", r#"
13+
[project]
14+
name = "foo"
15+
version = "0.0.1"
16+
authors = []
17+
18+
[dependencies.bar]
19+
path = "bar"
20+
opt_level = 2
21+
"#)
22+
.file("src/main.rs", r#"
23+
extern crate bar;
24+
fn main() {}
25+
"#)
26+
.file("bar/Cargo.toml", r#"
27+
[package]
28+
name = "bar"
29+
version = "0.0.1"
30+
authors = []
31+
"#)
32+
.file("bar/src/lib.rs", "pub fn bar() {}");
33+
34+
assert_that(p.cargo_process("build").arg("-v"),
35+
execs().with_status(0).with_stdout(format!("\
36+
{compiling} bar v0.0.1 ({url})
37+
{running} `rustc bar{sep}src{sep}lib.rs --crate-name bar --crate-type lib \
38+
-C opt-level=2 -g [..]
39+
{compiling} foo v0.0.1 ({url})
40+
{running} `rustc src{sep}main.rs --crate-name foo --crate-type bin -g \
41+
--out-dir {dir}{sep}target{sep}debug [..]",
42+
running = RUNNING, compiling = COMPILING, sep = SEP,
43+
dir = p.root().display(), url = p.url())));
44+
});
45+
46+
test!(highest_opt_level_wins_in_per_dependency_opt_levels {
47+
let p = project("foo")
48+
.file("Cargo.toml", r#"
49+
[project]
50+
name = "foo"
51+
version = "0.0.1"
52+
authors = []
53+
54+
[dependencies.bar]
55+
path = "bar"
56+
57+
[dependencies.baz]
58+
path = "baz"
59+
opt_level = 1
60+
"#)
61+
.file("src/main.rs", r#"
62+
extern crate bar;
63+
fn main() {}
64+
"#)
65+
.file("bar/Cargo.toml", r#"
66+
[package]
67+
name = "bar"
68+
version = "0.0.1"
69+
authors = []
70+
71+
[dependencies.baz]
72+
path = "../baz"
73+
opt_level = 2
74+
"#)
75+
.file("bar/src/lib.rs", r#"
76+
extern crate baz;
77+
pub fn bar() {}
78+
"#)
79+
.file("baz/Cargo.toml", r#"
80+
[package]
81+
name = "baz"
82+
version = "0.0.1"
83+
authors = []
84+
"#)
85+
.file("baz/src/lib.rs", "pub fn baz() {}");
86+
87+
assert_that(p.cargo_process("build").arg("-v"),
88+
execs().with_status(0).with_stdout(format!("\
89+
{compiling} baz v0.0.1 ({url})
90+
{running} `rustc baz{sep}src{sep}lib.rs --crate-name baz --crate-type lib \
91+
-C opt-level=2 -g [..]
92+
{compiling} bar v0.0.1 ({url})
93+
{running} `rustc bar{sep}src{sep}lib.rs --crate-name bar --crate-type lib \
94+
-g [..]
95+
{compiling} foo v0.0.1 ({url})
96+
{running} `rustc src{sep}main.rs --crate-name foo --crate-type bin -g \
97+
--out-dir {dir}{sep}target{sep}debug [..]",
98+
running = RUNNING, compiling = COMPILING, sep = SEP,
99+
dir = p.root().display(), url = p.url())));
100+
});
101+

tests/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ mod test_cargo_fetch;
4646
mod test_cargo_freshness;
4747
mod test_cargo_generate_lockfile;
4848
mod test_cargo_new;
49+
mod test_cargo_opt_levels;
4950
mod test_cargo_package;
5051
mod test_cargo_profiles;
5152
mod test_cargo_publish;

0 commit comments

Comments
 (0)