Skip to content

Commit 3a7337f

Browse files
committed
auto merge of #9437 : catamorphism/rust/rustpkg-dates, r=alexcrichton
r? @alexcrichton On most platforms, the time granularity is 1 sec or more, so comparing dates in tests that check whether rebuilding did or didn't happen leads to spurious failures. Instead, test the same thing by making an output file read-only and trapping attempts to write to it.
2 parents cd623e3 + e779313 commit 3a7337f

File tree

7 files changed

+184
-48
lines changed

7 files changed

+184
-48
lines changed

src/libextra/workcache.rs

+1-1
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/librustc/back/link.rs

+25
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,16 @@ pub fn link_binary(sess: Session,
959959
}
960960
}
961961

962+
fn is_writeable(p: &Path) -> bool {
963+
use std::libc::consts::os::posix88::S_IWUSR;
964+
965+
!os::path_exists(p) ||
966+
(match p.get_mode() {
967+
None => false,
968+
Some(m) => m & S_IWUSR as uint == S_IWUSR as uint
969+
})
970+
}
971+
962972
pub fn link_args(sess: Session,
963973
obj_filename: &Path,
964974
out_filename: &Path,
@@ -982,6 +992,21 @@ pub fn link_args(sess: Session,
982992
out_filename.clone()
983993
};
984994

995+
// Make sure the output and obj_filename are both writeable.
996+
// Mac, FreeBSD, and Windows system linkers check this already --
997+
// however, the Linux linker will happily overwrite a read-only file.
998+
// We should be consistent.
999+
let obj_is_writeable = is_writeable(obj_filename);
1000+
let out_is_writeable = is_writeable(&output);
1001+
if !out_is_writeable {
1002+
sess.fatal(format!("Output file {} is not writeable -- check its permissions.",
1003+
output.display()));
1004+
}
1005+
else if !obj_is_writeable {
1006+
sess.fatal(format!("Object file {} is not writeable -- check its permissions.",
1007+
obj_filename.display()));
1008+
}
1009+
9851010
// The default library location, we need this to find the runtime.
9861011
// The location of crates will be determined as needed.
9871012
// FIXME (#9639): This needs to handle non-utf8 paths

src/librustpkg/conditions.rs

+4
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

+15-8
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ use source_control::{safe_git_clone, git_clone_url, DirToUse, CheckedOutSources}
2121
use source_control::make_read_only;
2222
use path_util::{find_dir_using_rust_path_hack, make_dir_rwx_recursive};
2323
use path_util::{target_build_dir, versionize};
24-
use util::compile_crate;
24+
use util::{compile_crate, DepMap};
2525
use workcache_support;
2626
use workcache_support::crate_tag;
2727
use extra::workcache;
28+
use extra::treemap::TreeMap;
2829

2930
// An enumeration of the unpacked source of a package workspace.
3031
// This contains a list of files found in the source workspace.
@@ -370,6 +371,7 @@ impl PkgSrc {
370371

371372
fn build_crates(&self,
372373
ctx: &BuildContext,
374+
deps: &mut DepMap,
373375
crates: &[Crate],
374376
cfgs: &[~str],
375377
what: OutputType) {
@@ -389,12 +391,14 @@ impl PkgSrc {
389391
let id = self.id.clone();
390392
let sub_dir = self.build_workspace().clone();
391393
let sub_flags = crate.flags.clone();
394+
let sub_deps = deps.clone();
392395
do prep.exec |exec| {
393396
let result = compile_crate(&subcx,
394397
exec,
395398
&id,
396399
&subpath,
397400
&sub_dir,
401+
&mut (sub_deps.clone()),
398402
sub_flags,
399403
subcfgs,
400404
false,
@@ -428,24 +432,27 @@ impl PkgSrc {
428432
}
429433
}
430434

431-
// It would be better if build returned a Path, but then Path would have to derive
432-
// Encodable.
433435
pub fn build(&self,
434436
build_context: &BuildContext,
435-
cfgs: ~[~str]) {
437+
// DepMap is a map from str (crate name) to (kind, name) --
438+
// it tracks discovered dependencies per-crate
439+
cfgs: ~[~str]) -> DepMap {
440+
let mut deps = TreeMap::new();
441+
436442
let libs = self.libs.clone();
437443
let mains = self.mains.clone();
438444
let tests = self.tests.clone();
439445
let benchs = self.benchs.clone();
440446
debug2!("Building libs in {}, destination = {}",
441447
self.source_workspace.display(), self.build_workspace().display());
442-
self.build_crates(build_context, libs, cfgs, Lib);
448+
self.build_crates(build_context, &mut deps, libs, cfgs, Lib);
443449
debug2!("Building mains");
444-
self.build_crates(build_context, mains, cfgs, Main);
450+
self.build_crates(build_context, &mut deps, mains, cfgs, Main);
445451
debug2!("Building tests");
446-
self.build_crates(build_context, tests, cfgs, Test);
452+
self.build_crates(build_context, &mut deps, tests, cfgs, Test);
447453
debug2!("Building benches");
448-
self.build_crates(build_context, benchs, cfgs, Bench);
454+
self.build_crates(build_context, &mut deps, benchs, cfgs, Bench);
455+
deps
449456
}
450457

451458
/// Return the workspace to put temporary files in. See the comment on `PkgSrc`

src/librustpkg/rustpkg.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,8 @@ pub trait CtxMethods {
197197
fn install(&self, src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str, ~str)]);
198198
/// Returns a list of installed files
199199
fn install_no_build(&self,
200-
source_workspace: &Path,
200+
build_workspace: &Path,
201+
build_inputs: &[Path],
201202
target_workspace: &Path,
202203
id: &PkgId) -> ~[~str];
203204
fn prefer(&self, _id: &str, _vers: Option<~str>);
@@ -542,6 +543,7 @@ impl CtxMethods for BuildContext {
542543

543544
let mut installed_files = ~[];
544545
let mut inputs = ~[];
546+
let mut build_inputs = ~[];
545547

546548
debug2!("Installing package source: {}", pkg_src.to_str());
547549

@@ -558,10 +560,12 @@ impl CtxMethods for BuildContext {
558560
debug2!("Recording input: {}", path.display());
559561
// FIXME (#9639): This needs to handle non-utf8 paths
560562
inputs.push((~"file", path.as_str().unwrap().to_owned()));
563+
build_inputs.push(path);
561564
}
562565
}
563566

564567
let result = self.install_no_build(pkg_src.build_workspace(),
568+
build_inputs,
565569
&pkg_src.destination_workspace,
566570
&id).map(|s| Path::new(s.as_slice()));
567571
debug2!("install: id = {}, about to call discover_outputs, {:?}",
@@ -576,6 +580,7 @@ impl CtxMethods for BuildContext {
576580
// again, working around lack of Encodable for Path
577581
fn install_no_build(&self,
578582
build_workspace: &Path,
583+
build_inputs: &[Path],
579584
target_workspace: &Path,
580585
id: &PkgId) -> ~[~str] {
581586
use conditions::copy_failed::cond;
@@ -612,9 +617,28 @@ impl CtxMethods for BuildContext {
612617
let sublib = maybe_library.clone();
613618
let sub_target_ex = target_exec.clone();
614619
let sub_target_lib = target_lib.clone();
615-
620+
let sub_build_inputs = build_inputs.to_owned();
616621
do prep.exec |exe_thing| {
617622
let mut outputs = ~[];
623+
// Declare all the *inputs* to the declared input too, as inputs
624+
for executable in subex.iter() {
625+
exe_thing.discover_input("binary",
626+
executable.as_str().unwrap().to_owned(),
627+
workcache_support::digest_only_date(executable));
628+
}
629+
for library in sublib.iter() {
630+
exe_thing.discover_input("binary",
631+
library.as_str().unwrap().to_owned(),
632+
workcache_support::digest_only_date(library));
633+
}
634+
635+
for transitive_dependency in sub_build_inputs.iter() {
636+
exe_thing.discover_input(
637+
"file",
638+
transitive_dependency.as_str().unwrap().to_owned(),
639+
workcache_support::digest_file_with_date(transitive_dependency));
640+
}
641+
618642

619643
for exec in subex.iter() {
620644
debug2!("Copying: {} -> {}", exec.display(), sub_target_ex.display());

src/librustpkg/tests.rs

+53-27
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ use target::*;
3737
use package_source::PkgSrc;
3838
use source_control::{CheckedOutSources, DirToUse, safe_git_clone};
3939
use exit_codes::{BAD_FLAG_CODE, COPY_FAILED_CODE};
40-
use util::datestamp;
4140

4241
fn fake_ctxt(sysroot: Path, workspace: &Path) -> BuildContext {
4342
let context = workcache::Context::new(
@@ -507,6 +506,7 @@ fn output_file_name(workspace: &Path, short_name: ~str) -> Path {
507506
os::EXE_SUFFIX))
508507
}
509508

509+
#[cfg(target_os = "linux")]
510510
fn touch_source_file(workspace: &Path, pkgid: &PkgId) {
511511
use conditions::bad_path::cond;
512512
let pkg_src_dir = workspace.join_many([~"src", pkgid.to_str()]);
@@ -515,7 +515,28 @@ fn touch_source_file(workspace: &Path, pkgid: &PkgId) {
515515
if p.extension_str() == Some("rs") {
516516
// should be able to do this w/o a process
517517
// FIXME (#9639): This needs to handle non-utf8 paths
518-
if run::process_output("touch", [p.as_str().unwrap().to_owned()]).status != 0 {
518+
// n.b. Bumps time up by 2 seconds to get around granularity issues
519+
if run::process_output("touch", [~"--date",
520+
~"+2 seconds",
521+
p.as_str().unwrap().to_owned()]).status != 0 {
522+
let _ = cond.raise((pkg_src_dir.clone(), ~"Bad path"));
523+
}
524+
}
525+
}
526+
}
527+
528+
#[cfg(not(target_os = "linux"))]
529+
fn touch_source_file(workspace: &Path, pkgid: &PkgId) {
530+
use conditions::bad_path::cond;
531+
let pkg_src_dir = workspace.join_many([~"src", pkgid.to_str()]);
532+
let contents = os::list_dir_path(&pkg_src_dir);
533+
for p in contents.iter() {
534+
if p.extension_str() == Some("rs") {
535+
// should be able to do this w/o a process
536+
// FIXME (#9639): This needs to handle non-utf8 paths
537+
// n.b. Bumps time up by 2 seconds to get around granularity issues
538+
if run::process_output("touch", [~"-A02",
539+
p.as_str().unwrap().to_owned()]).status != 0 {
519540
let _ = cond.raise((pkg_src_dir.clone(), ~"Bad path"));
520541
}
521542
}
@@ -1033,12 +1054,17 @@ fn no_rebuilding() {
10331054
let workspace = create_local_package(&p_id);
10341055
let workspace = workspace.path();
10351056
command_line_test([~"build", ~"foo"], workspace);
1036-
let date = datestamp(&built_library_in_workspace(&p_id,
1037-
workspace).expect("no_rebuilding"));
1057+
let foo_lib = lib_output_file_name(workspace, "foo");
1058+
// Now make `foo` read-only so that subsequent rebuilds of it will fail
1059+
assert!(chmod_read_only(&foo_lib));
1060+
10381061
command_line_test([~"build", ~"foo"], workspace);
1039-
let newdate = datestamp(&built_library_in_workspace(&p_id,
1040-
workspace).expect("no_rebuilding (2)"));
1041-
assert_eq!(date, newdate);
1062+
1063+
match command_line_test_partial([~"build", ~"foo"], workspace) {
1064+
Success(*) => (), // ok
1065+
Fail(status) if status == 65 => fail2!("no_rebuilding failed: it tried to rebuild bar"),
1066+
Fail(_) => fail2!("no_rebuilding failed for some other reason")
1067+
}
10421068
}
10431069
10441070
#[test]
@@ -1049,55 +1075,55 @@ fn no_rebuilding_dep() {
10491075
let workspace = workspace.path();
10501076
command_line_test([~"build", ~"foo"], workspace);
10511077
let bar_lib = lib_output_file_name(workspace, "bar");
1052-
let bar_date_1 = datestamp(&bar_lib);
1053-
10541078
frob_source_file(workspace, &p_id, "main.rs");
1055-
10561079
// Now make `bar` read-only so that subsequent rebuilds of it will fail
10571080
assert!(chmod_read_only(&bar_lib));
1058-
10591081
match command_line_test_partial([~"build", ~"foo"], workspace) {
10601082
Success(*) => (), // ok
10611083
Fail(status) if status == 65 => fail2!("no_rebuilding_dep failed: it tried to rebuild bar"),
10621084
Fail(_) => fail2!("no_rebuilding_dep failed for some other reason")
10631085
}
1064-
1065-
let bar_date_2 = datestamp(&bar_lib);
1066-
assert_eq!(bar_date_1, bar_date_2);
10671086
}
10681087
10691088
#[test]
1070-
#[ignore]
10711089
fn do_rebuild_dep_dates_change() {
10721090
let p_id = PkgId::new("foo");
10731091
let dep_id = PkgId::new("bar");
10741092
let workspace = create_local_package_with_dep(&p_id, &dep_id);
10751093
let workspace = workspace.path();
10761094
command_line_test([~"build", ~"foo"], workspace);
10771095
let bar_lib_name = lib_output_file_name(workspace, "bar");
1078-
let bar_date = datestamp(&bar_lib_name);
1079-
debug2!("Datestamp on {} is {:?}", bar_lib_name.display(), bar_date);
10801096
touch_source_file(workspace, &dep_id);
1081-
command_line_test([~"build", ~"foo"], workspace);
1082-
let new_bar_date = datestamp(&bar_lib_name);
1083-
debug2!("Datestamp on {} is {:?}", bar_lib_name.display(), new_bar_date);
1084-
assert!(new_bar_date > bar_date);
1097+
1098+
// Now make `bar` read-only so that subsequent rebuilds of it will fail
1099+
assert!(chmod_read_only(&bar_lib_name));
1100+
1101+
match command_line_test_partial([~"build", ~"foo"], workspace) {
1102+
Success(*) => fail2!("do_rebuild_dep_dates_change failed: it didn't rebuild bar"),
1103+
Fail(status) if status == 65 => (), // ok
1104+
Fail(_) => fail2!("do_rebuild_dep_dates_change failed for some other reason")
1105+
}
10851106
}
10861107
10871108
#[test]
1088-
#[ignore]
10891109
fn do_rebuild_dep_only_contents_change() {
10901110
let p_id = PkgId::new("foo");
10911111
let dep_id = PkgId::new("bar");
10921112
let workspace = create_local_package_with_dep(&p_id, &dep_id);
10931113
let workspace = workspace.path();
10941114
command_line_test([~"build", ~"foo"], workspace);
1095-
let bar_date = datestamp(&lib_output_file_name(workspace, "bar"));
10961115
frob_source_file(workspace, &dep_id, "lib.rs");
1116+
let bar_lib_name = lib_output_file_name(workspace, "bar");
1117+
1118+
// Now make `bar` read-only so that subsequent rebuilds of it will fail
1119+
assert!(chmod_read_only(&bar_lib_name));
1120+
10971121
// should adjust the datestamp
1098-
command_line_test([~"build", ~"foo"], workspace);
1099-
let new_bar_date = datestamp(&lib_output_file_name(workspace, "bar"));
1100-
assert!(new_bar_date > bar_date);
1122+
match command_line_test_partial([~"build", ~"foo"], workspace) {
1123+
Success(*) => fail2!("do_rebuild_dep_only_contents_change failed: it didn't rebuild bar"),
1124+
Fail(status) if status == 65 => (), // ok
1125+
Fail(_) => fail2!("do_rebuild_dep_only_contents_change failed for some other reason")
1126+
}
11011127
}
11021128
11031129
#[test]
@@ -2003,7 +2029,7 @@ fn test_rustpkg_test_output() {
20032029
}
20042030
20052031
#[test]
2006-
#[ignore(reason = "See issue #9441")]
2032+
#[ignore(reason = "Issue 9441")]
20072033
fn test_rebuild_when_needed() {
20082034
let foo_id = PkgId::new("foo");
20092035
let foo_workspace = create_local_package(&foo_id);

0 commit comments

Comments
 (0)