7 our ($PINTOSDIR) = "/usr/class/cs140/pintos/pintos";
10 our ($start) = -d 'pintos/src' ? 4 : 1;
13 GetOptions ("v|verbose+" => \$verbose,
14 "h|help" => sub { usage (0) },
16 die "Can't start from step 2: pintos/src does not exist\n"
20 "x|extract" => sub { $stop = 2 },
21 "c|clean" => sub { $stop = 3 },
22 "b|build" => sub { $stop = 4 },
23 "t|test" => sub { $stop = 6 })
24 or die "Malformed command line; use --help for help.\n";
26 die "Exactly one non-option argument required; use --help for help.\n"
29 my (@valid_projects) = ('threads', 'userprog', 'vm', 'filesys');
30 my ($project) = $ARGV[0];
31 $project = $valid_projects[$project - 1] if $project =~ /^[1234]$/;
32 die "Unknown project \"$project\"; use --help for help.\n"
33 if !grep ($_ eq $project, @valid_projects);
38 run-tests, runs tests for grading a single submitted project
40 usage: run-tests PROJECT
41 where PROJECT is a project name (threads, userprog, vm, filesys)
44 Invoke from a directory containing a student tarball named by
45 the submit script, e.g. username.MMM.DD.YY.hh.mm.ss.tar.gz,
46 or a pintos/src directory containing a student submission.
49 1. Extracts the source tree into pintos/src.
50 2. Replaces the existing pintos/src/tests directory by a pristine
51 copy, which must be available in $PINTOSDIR.
52 3. Cleans the source tree.
53 4. Builds the source tree.
54 5. Runs the tests on the source tree and grades them.
55 6. Copies the grade report to tests.out.
56 7. Cleans the source tree again.
59 If pintos/src already exists, run-tests starts from step 4.
60 To force it to start from step 1, remove the pintos directory.
61 To force it to start from step 2, use --replace.
64 -r, --replace Start at step 2.
65 -x, --extract Stop after step 2.
66 -c, --clean Stop after step 3.
67 -b, --build Stop after step 4.
68 -t, --test Stop after step 6.
69 -v, --verbose Print command lines of subcommands before executing them.
70 -h, --help Print this help message.
76 my ($tarball) = choose_tarball ();
78 print "Extracting $tarball...\n";
79 xsystem ("tar xzf $tarball", DIE => "extraction failed\n");
83 print "Replacing tests with pristine copy...\n";
84 xsystem ("rm -rf pintos/src/tests",
85 DIE => "removal of old tests failed\n");
86 xsystem ("cp -pR $PINTOSDIR/src/tests pintos/src/tests",
87 DIE => "replacement of tests failed\n");
91 print "Cleaning...\n";
92 xsystem ("cd pintos/src && make clean", DIE => "clean failed");
96 print "Building...\n";
97 xsystem ("cd pintos/src/$project && make", DIE => "build failed");
101 print "Grading...\n";
102 xsystem ("cd pintos/src/$project && make grade", DIE => "grade failed");
106 print "Saving grade report to tests.out...\n";
107 xsystem ("cp pintos/src/$project/build/grade tests.out",
108 DIE => "copy failed");
112 print "Cleaning...\n";
113 xsystem ("cd pintos/src && make clean", DIE => "clean failed");
118 # Returns true if step $N should be executed.
121 return $n >= $start && $n <= $stop;
124 # Returns the name of the tarball to extract.
127 = grep (/^[a-z0-9]+\.[A-Za-z]+\.\d+\.\d+\.\d+\.\d+.\d+\.tar\.gz$/,
129 die "no pintos dir, no files matching username.MMM.DD.YY.hh.mm.ss.tar.gz\n"
130 if scalar (@tarballs) == 0;
133 # Sort tarballs in order by time.
134 @tarballs = sort { ext_mdyHMS ($a) cmp ext_mdyHMS ($b) } @tarballs;
136 print "Multiple tarballs:\n";
137 print "\t$_ submitted ", ext_mdyHMS ($_), "\n" foreach @tarballs;
138 print "Choosing $tarballs[$#tarballs]\n";
140 return $tarballs[$#tarballs];
143 # Extract the date within a tarball name into a string that compares
144 # lexicographically in chronological order.
147 my ($ms, $d, $y, $H, $M, $S) =
148 $s =~ /.([A-Za-z]+)\.(\d+)\.(\d+)\.(\d+)\.(\d+).(\d+)\.tar\.gz$/
150 my ($m) = index ("janfebmaraprmayjunjulaugsepoctnovdec", lc $ms) / 3
152 return sprintf "%02d-%02d-%02d %02d:%02d:%02d", $y, $m, $d, $H, $M, $S;
156 my ($command, %options) = @_;
157 print "$command\n" if $verbose || $options{VERBOSE};
159 my ($log) = $options{LOG};
163 local $SIG{ALRM} = sub { die "alarm\n" };
164 alarm $options{TIMEOUT} if defined $options{TIMEOUT};
166 die "fork: $!\n" if !defined $pid;
169 open (STDOUT, ">output/$log.out");
170 open (STDERR, ">output/$log.err");
182 die unless $@ eq "alarm\n"; # propagate unexpected errors
184 for ($i = 0; $i < 10; $i++) {
185 kill ('SIGTERM', $pid);
187 my ($retval) = waitpid ($pid, WNOHANG);
188 last if $retval == $pid || $retval == -1;
189 print "Timed out - Waiting for $pid to die" if $i == 0;
195 if (WIFSIGNALED ($status)) {
196 my ($signal) = WTERMSIG ($status);
197 die "Interrupted\n" if $signal == SIGINT;
198 print "Child terminated with signal $signal\n";
201 my ($exp_status) = !defined ($options{EXPECT}) ? 0 : $options{EXPECT};
202 $result = WIFEXITED ($status) && WEXITSTATUS ($status) == $exp_status
207 if ($result eq 'error' && defined $options{DIE}) {
208 my ($msg) = $options{DIE};
209 if (defined ($log)) {
211 $msg .= "; see output/$log.err and output/$log.out for details\n";
214 } elsif ($result ne 'error' && defined ($log)) {
215 unlink ("output/$log.err");