Skip to content

Commit 1bf8b61

Browse files
committed
rustpkg: Make rustpkg tests stop comparing dates
Instead of scrutinizing modification times in rustpkg tests, change output files to be read-only and detect attempts to write to them (hack suggested by Jack). This avoids time granularity problems. As part of this change, I discovered that some dependencies weren't getting written correctly (involving built executables and library files), so this patch fixes that too. This partly addresses #9441, but one test (test_rebuild_when_needed) is still ignored on Linux.
1 parent 3f32898 commit 1bf8b61

File tree

6 files changed

+232
-114
lines changed

6 files changed

+232
-114
lines changed

src/libextra/workcache.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ impl Logger {
219219
}
220220

221221
pub fn info(&self, i: &str) {
222-
io::println(~"workcache: " + i);
222+
info2!("workcache: {}", i);
223223
}
224224
}
225225

src/librustpkg/conditions.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ condition! {
2323
pub bad_stat: (Path, ~str) -> stat;
2424
}
2525

26+
condition! {
27+
pub bad_kind: (~str) -> ();
28+
}
29+
2630
condition! {
2731
pub nonexistent_package: (PkgId, ~str) -> Path;
2832
}

src/librustpkg/package_source.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ use crate::Crate;
1919
use messages::*;
2020
use source_control::{git_clone, git_clone_general};
2121
use path_util::{find_dir_using_rust_path_hack, default_workspace, make_dir_rwx_recursive};
22-
use util::compile_crate;
22+
use util::{compile_crate, DepMap};
2323
use workspace::is_workspace;
2424
use workcache_support;
2525
use workcache_support::crate_tag;
2626
use extra::workcache;
27+
use extra::treemap::TreeMap;
2728

2829
// An enumeration of the unpacked source of a package workspace.
2930
// This contains a list of files found in the source workspace.
@@ -293,6 +294,7 @@ impl PkgSrc {
293294
fn build_crates(&self,
294295
ctx: &BuildContext,
295296
destination_dir: &Path,
297+
deps: &mut DepMap,
296298
crates: &[Crate],
297299
cfgs: &[~str],
298300
what: OutputType) {
@@ -313,12 +315,14 @@ impl PkgSrc {
313315
let id = self.id.clone();
314316
let sub_dir = destination_dir.clone();
315317
let sub_flags = crate.flags.clone();
318+
let sub_deps = deps.clone();
316319
do prep.exec |exec| {
317320
let result = compile_crate(&subcx,
318321
exec,
319322
&id,
320323
&subpath,
321324
&sub_dir,
325+
&mut (sub_deps.clone()),
322326
sub_flags,
323327
subcfgs,
324328
false,
@@ -347,13 +351,17 @@ impl PkgSrc {
347351
}
348352
}
349353

350-
// It would be better if build returned a Path, but then Path would have to derive
354+
// It would be better if build returned a (Path, DepMap), but then Path would have to derive
351355
// Encodable.
352356
pub fn build(&self,
353357
build_context: &BuildContext,
354-
cfgs: ~[~str]) -> ~str {
358+
// DepMap is a map from str (crate name) to (kind, name) --
359+
// it tracks discovered dependencies per-crate
360+
cfgs: ~[~str]) -> (~str, DepMap) {
355361
use conditions::not_a_workspace::cond;
356362

363+
let mut deps = TreeMap::new();
364+
357365
// Determine the destination workspace (which depends on whether
358366
// we're using the rust_path_hack)
359367
let destination_workspace = if is_workspace(&self.workspace) {
@@ -379,14 +387,14 @@ impl PkgSrc {
379387
let benchs = self.benchs.clone();
380388
debug2!("Building libs in {}, destination = {}",
381389
destination_workspace.to_str(), destination_workspace.to_str());
382-
self.build_crates(build_context, &destination_workspace, libs, cfgs, Lib);
390+
self.build_crates(build_context, &destination_workspace, &mut deps, libs, cfgs, Lib);
383391
debug2!("Building mains");
384-
self.build_crates(build_context, &destination_workspace, mains, cfgs, Main);
392+
self.build_crates(build_context, &destination_workspace, &mut deps, mains, cfgs, Main);
385393
debug2!("Building tests");
386-
self.build_crates(build_context, &destination_workspace, tests, cfgs, Test);
394+
self.build_crates(build_context, &destination_workspace, &mut deps, tests, cfgs, Test);
387395
debug2!("Building benches");
388-
self.build_crates(build_context, &destination_workspace, benchs, cfgs, Bench);
389-
destination_workspace.to_str()
396+
self.build_crates(build_context, &destination_workspace, &mut deps, benchs, cfgs, Bench);
397+
(destination_workspace.to_str(), deps)
390398
}
391399

392400
/// Debugging

src/librustpkg/rustpkg.rs

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ pub trait CtxMethods {
189189
fn install(&self, src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str, ~str)]);
190190
/// Returns a list of installed files
191191
fn install_no_build(&self,
192+
declared_inputs: &[Path],
192193
source_workspace: &Path,
193194
target_workspace: &Path,
194195
id: &PkgId) -> ~[~str];
@@ -385,6 +386,9 @@ impl CtxMethods for BuildContext {
385386
/// what_to_build says: "Just build the lib.rs file in one subdirectory,
386387
/// don't walk anything recursively." Or else, everything.
387388
fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) -> Path {
389+
use conditions::nonexistent_package::cond;
390+
use bad_kind = conditions::bad_kind::cond;
391+
388392
let workspace = pkg_src.workspace.clone();
389393
let pkgid = pkg_src.id.clone();
390394

@@ -472,11 +476,61 @@ impl CtxMethods for BuildContext {
472476
}
473477
}
474478
// Build it!
475-
let rs_path = pkg_src.build(self, cfgs);
479+
480+
// Returns a map: crate_name -> (declared_input x discovered_input)
481+
let (rs_path, dep_map) = pkg_src.build(self, cfgs);
482+
debug2!("rs_path = {}", rs_path.to_str());
483+
let path = Path(rs_path);
484+
for (c, deps) in dep_map.iter() {
485+
// This is wrong for multiple crates, but they're not implemented yet anyway. #7240
486+
let output_filename_opt = if is_main(&Path(*c)) {
487+
built_executable_in_workspace(&pkgid, &path)
488+
} else {
489+
built_library_in_workspace(&pkgid, &path)
490+
};
491+
let built = match output_filename_opt {
492+
Some(p) => p,
493+
None => cond.raise((pkgid.clone(),
494+
format!("Output filename for {} doesn't exist", *c)))
495+
};
496+
let built_str = built.to_str();
497+
498+
// Here, we have to add an entry into workcache for the built executable(s).
499+
// Each one has the same declared inputs as the crate that built it.
500+
do self.workcache_context.with_prep(built_str) |prep| {
501+
prep.declare_input("file",
502+
c.to_str(),
503+
workcache_support::digest_file_with_date(&Path(*c)));
504+
let sub_built = built.clone();
505+
let sub_deps = deps.clone();
506+
do prep.exec |exe| {
507+
for &(ref kind, ref val) in sub_deps.iter() {
508+
if *kind == ~"file" {
509+
exe.discover_input(*kind,
510+
*val,
511+
workcache_support::digest_file_with_date(&Path(*val)));
512+
}
513+
else if *kind == ~"binary" {
514+
exe.discover_input(*kind,
515+
*val,
516+
digest_only_date(&Path(*val)));
517+
}
518+
else {
519+
bad_kind.raise(kind.clone());
520+
}
521+
}
522+
exe.discover_output("binary",
523+
sub_built.to_str(),
524+
digest_only_date(&sub_built));
525+
}
526+
}
527+
};
476528
Path(rs_path)
477529
}
478530
else {
479531
// Just return the source workspace
532+
// Note that this won't add anything to the workcache --
533+
// that's the package script's job
480534
workspace.clone()
481535
}
482536
}
@@ -535,7 +589,8 @@ impl CtxMethods for BuildContext {
535589
};
536590
debug2!("install: destination workspace = {}, id = {}, installing to {}",
537591
destination_workspace, id.to_str(), actual_workspace.to_str());
538-
let result = self.install_no_build(&Path(destination_workspace),
592+
let result = self.install_no_build(installed_files,
593+
&Path(destination_workspace),
539594
&actual_workspace,
540595
&id).map(|s| Path(*s));
541596
debug2!("install: id = {}, about to call discover_outputs, {:?}",
@@ -547,6 +602,7 @@ impl CtxMethods for BuildContext {
547602

548603
// again, working around lack of Encodable for Path
549604
fn install_no_build(&self,
605+
build_inputs: &[Path],
550606
source_workspace: &Path,
551607
target_workspace: &Path,
552608
id: &PkgId) -> ~[~str] {
@@ -564,23 +620,32 @@ impl CtxMethods for BuildContext {
564620
maybe_executable, maybe_library);
565621

566622
do self.workcache_context.with_prep(id.install_tag()) |prep| {
567-
for ee in maybe_executable.iter() {
568-
prep.declare_input("binary",
569-
ee.to_str(),
570-
workcache_support::digest_only_date(ee));
571-
}
572-
for ll in maybe_library.iter() {
573-
prep.declare_input("binary",
574-
ll.to_str(),
575-
workcache_support::digest_only_date(ll));
576-
}
577623
let subex = maybe_executable.clone();
578624
let sublib = maybe_library.clone();
579625
let sub_target_ex = target_exec.clone();
580626
let sub_target_lib = target_lib.clone();
581-
627+
let sub_build_inputs = build_inputs.to_owned();
582628
do prep.exec |exe_thing| {
583629
let mut outputs = ~[];
630+
// Declare all the *inputs* to the declared input too, as inputs
631+
for executable in subex.iter() {
632+
exe_thing.discover_input("binary",
633+
executable.to_str(),
634+
workcache_support::digest_only_date(executable));
635+
}
636+
for library in sublib.iter() {
637+
exe_thing.discover_input("binary",
638+
library.to_str(),
639+
workcache_support::digest_only_date(library));
640+
}
641+
642+
for transitive_dependency in sub_build_inputs.iter() {
643+
exe_thing.discover_input(
644+
"file",
645+
transitive_dependency.to_str(),
646+
workcache_support::digest_file_with_date(transitive_dependency));
647+
}
648+
584649

585650
for exec in subex.iter() {
586651
debug2!("Copying: {} -> {}", exec.to_str(), sub_target_ex.to_str());
@@ -589,8 +654,8 @@ impl CtxMethods for BuildContext {
589654
cond.raise(((*exec).clone(), sub_target_ex.clone()));
590655
}
591656
exe_thing.discover_output("binary",
592-
sub_target_ex.to_str(),
593-
workcache_support::digest_only_date(&sub_target_ex));
657+
sub_target_ex.to_str(),
658+
workcache_support::digest_only_date(&sub_target_ex));
594659
outputs.push(sub_target_ex.to_str());
595660
}
596661
for lib in sublib.iter() {

src/librustpkg/tests.rs

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ use rustc::driver::driver::{build_session, build_session_options, host_triple, o
3434
use syntax::diagnostic;
3535
use target::*;
3636
use package_source::PkgSrc;
37-
use util::datestamp;
3837

3938
fn fake_ctxt(sysroot: Path, workspace: &Path) -> BuildContext {
4039
let context = workcache::Context::new(
@@ -473,7 +472,8 @@ fn touch_source_file(workspace: &Path, pkgid: &PkgId) {
473472
for p in contents.iter() {
474473
if p.filetype() == Some(".rs") {
475474
// should be able to do this w/o a process
476-
if run::process_output("touch", [p.to_str()]).status != 0 {
475+
// n.b. Bumps time up by 2 seconds to get around granularity issues
476+
if run::process_output("touch", [~"-A", ~"02", p.to_str()]).status != 0 {
477477
let _ = cond.raise((pkg_src_dir.clone(), ~"Bad path"));
478478
}
479479
}
@@ -960,12 +960,17 @@ fn no_rebuilding() {
960960
let p_id = PkgId::new("foo");
961961
let workspace = create_local_package(&p_id);
962962
command_line_test([~"build", ~"foo"], &workspace);
963-
let date = datestamp(&built_library_in_workspace(&p_id,
964-
&workspace).expect("no_rebuilding"));
963+
let foo_lib = lib_output_file_name(&workspace, "foo");
964+
// Now make `foo` read-only so that subsequent rebuilds of it will fail
965+
assert!(chmod_read_only(&foo_lib));
966+
965967
command_line_test([~"build", ~"foo"], &workspace);
966-
let newdate = datestamp(&built_library_in_workspace(&p_id,
967-
&workspace).expect("no_rebuilding (2)"));
968-
assert_eq!(date, newdate);
968+
969+
match command_line_test_partial([~"build", ~"foo"], &workspace) {
970+
Success(*) => (), // ok
971+
Fail(status) if status == 65 => fail2!("no_rebuilding failed: it tried to rebuild bar"),
972+
Fail(_) => fail2!("no_rebuilding failed for some other reason")
973+
}
969974
}
970975
971976
#[test]
@@ -975,54 +980,53 @@ fn no_rebuilding_dep() {
975980
let workspace = create_local_package_with_dep(&p_id, &dep_id);
976981
command_line_test([~"build", ~"foo"], &workspace);
977982
let bar_lib = lib_output_file_name(&workspace, "bar");
978-
let bar_date_1 = datestamp(&bar_lib);
979-
980983
frob_source_file(&workspace, &p_id, "main.rs");
981-
982984
// Now make `bar` read-only so that subsequent rebuilds of it will fail
983985
assert!(chmod_read_only(&bar_lib));
984-
985986
match command_line_test_partial([~"build", ~"foo"], &workspace) {
986987
Success(*) => (), // ok
987988
Fail(status) if status == 65 => fail2!("no_rebuilding_dep failed: it tried to rebuild bar"),
988989
Fail(_) => fail2!("no_rebuilding_dep failed for some other reason")
989990
}
990-
991-
let bar_date_2 = datestamp(&lib_output_file_name(&workspace,
992-
"bar"));
993-
assert_eq!(bar_date_1, bar_date_2);
994991
}
995992
996993
#[test]
997-
#[ignore]
998994
fn do_rebuild_dep_dates_change() {
999995
let p_id = PkgId::new("foo");
1000996
let dep_id = PkgId::new("bar");
1001997
let workspace = create_local_package_with_dep(&p_id, &dep_id);
1002998
command_line_test([~"build", ~"foo"], &workspace);
1003999
let bar_lib_name = lib_output_file_name(&workspace, "bar");
1004-
let bar_date = datestamp(&bar_lib_name);
1005-
debug2!("Datestamp on {} is {:?}", bar_lib_name.to_str(), bar_date);
10061000
touch_source_file(&workspace, &dep_id);
1007-
command_line_test([~"build", ~"foo"], &workspace);
1008-
let new_bar_date = datestamp(&bar_lib_name);
1009-
debug2!("Datestamp on {} is {:?}", bar_lib_name.to_str(), new_bar_date);
1010-
assert!(new_bar_date > bar_date);
1001+
1002+
// Now make `bar` read-only so that subsequent rebuilds of it will fail
1003+
assert!(chmod_read_only(&bar_lib_name));
1004+
1005+
match command_line_test_partial([~"build", ~"foo"], &workspace) {
1006+
Success(*) => fail2!("do_rebuild_dep_dates_change failed: it didn't rebuild bar"),
1007+
Fail(status) if status == 65 => (), // ok
1008+
Fail(_) => fail2!("do_rebuild_dep_dates_change failed for some other reason")
1009+
}
10111010
}
10121011
10131012
#[test]
1014-
#[ignore]
10151013
fn do_rebuild_dep_only_contents_change() {
10161014
let p_id = PkgId::new("foo");
10171015
let dep_id = PkgId::new("bar");
10181016
let workspace = create_local_package_with_dep(&p_id, &dep_id);
10191017
command_line_test([~"build", ~"foo"], &workspace);
1020-
let bar_date = datestamp(&lib_output_file_name(&workspace, "bar"));
10211018
frob_source_file(&workspace, &dep_id, "lib.rs");
1019+
let bar_lib_name = lib_output_file_name(&workspace, "bar");
1020+
1021+
// Now make `bar` read-only so that subsequent rebuilds of it will fail
1022+
assert!(chmod_read_only(&bar_lib_name));
1023+
10221024
// should adjust the datestamp
1023-
command_line_test([~"build", ~"foo"], &workspace);
1024-
let new_bar_date = datestamp(&lib_output_file_name(&workspace, "bar"));
1025-
assert!(new_bar_date > bar_date);
1025+
match command_line_test_partial([~"build", ~"foo"], &workspace) {
1026+
Success(*) => fail2!("do_rebuild_dep_only_contents_change failed: it didn't rebuild bar"),
1027+
Fail(status) if status == 65 => (), // ok
1028+
Fail(_) => fail2!("do_rebuild_dep_only_contents_change failed for some other reason")
1029+
}
10261030
}
10271031
10281032
#[test]
@@ -1823,7 +1827,6 @@ fn test_rustpkg_test_output() {
18231827
}
18241828
18251829
#[test]
1826-
#[ignore(reason = "See issue #9441")]
18271830
fn test_rebuild_when_needed() {
18281831
let foo_id = PkgId::new("foo");
18291832
let foo_workspace = create_local_package(&foo_id);

0 commit comments

Comments
 (0)