Description:
Merge branch 'master' into win-local
Commit status:
[Not Reviewed]
References:
merge default
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r104:ad31dc743745 - - 10 files changed: 111 inserted, 78 deleted

@@ -1,344 +1,344
1 1 #!/usr/bin/ruby
2 2
3 3 def stop_grader(id)
4 4 if id==:all
5 5 File.open(File.dirname(__FILE__) + "/stop.all",'w').close
6 6 else
7 7 File.open(File.dirname(__FILE__) + "/stop.#{id}",'w').close
8 8 end
9 9 end
10 10
11 11 def check_stopfile
12 12 FileTest.exist?(File.dirname(__FILE__) + "/stop.all") or
13 13 FileTest.exist?(File.dirname(__FILE__) + "/stop.#{Process.pid}")
14 14 end
15 15
16 16 def clear_stopfile
17 17 if FileTest.exist?(File.dirname(__FILE__) + "/stop.#{Process.pid}")
18 - system("rm " + File.dirname(__FILE__) + "/stop.#{Process.pid}")
18 + File.delete(File.dirname(__FILE__) + "/stop.#{Process.pid}")
19 19 end
20 20 end
21 21
22 22 def config
23 23 Grader::Configuration.get_instance
24 24 end
25 25
26 26 def log_file_name
27 27 if !File.exists?(config.log_dir)
28 28 raise "Log directory does not exist: #{config.log_dir}"
29 29 end
30 30 config.log_dir +
31 31 "/#{GRADER_ENV}_#{config.grader_mode}.#{Process.pid}"
32 32 end
33 33
34 34 def log(str)
35 35 if config.talkative
36 36 puts str
37 37 end
38 38 if config.logging
39 39 fp = File.open(log_file_name,"a")
40 40 fp.puts("GRADER: #{Time.new.strftime("%H:%M")} #{str}")
41 41 fp.close
42 42 end
43 43 end
44 44
45 45 def display_manual
46 46 puts <<USAGE
47 47 Grader.
48 48 using: (1) grader
49 49 (2) grader environment [mode]
50 50 (3) grader stop [all|pids-list]
51 51 (4) grader --help
52 52 (1) call grader with environment = 'exam', mode = 'queue'
53 53 (2) possible modes are: 'queue', 'prob', 'test_request'
54 54 (3) create stop-file to stop running grader in queue mode
55 55 (4) You are here.
56 56 USAGE
57 57 end
58 58
59 59 def process_options_and_stop_file
60 60 # The list of options are:
61 61 # - stop [all|process ids]
62 62 # -
63 63
64 64 # Process 'help' option
65 65 if (ARGV.length==1) and (/help/.match(ARGV[0]))
66 66 display_manual
67 67 exit(0)
68 68 end
69 69
70 70 # Process 'stop' option.
71 71 if (ARGV.length >= 1) and (ARGV[0]=='stop')
72 72 if ARGV.length==1
73 73 puts "you should specify pid-list or 'all'"
74 74 display_manual
75 75 elsif (ARGV.length==2) and (ARGV[1]=='all')
76 76 stop_grader(:all)
77 77 puts "A global stop file ('stop.all') created."
78 78 puts "You should remove it manually later."
79 79 else
80 80 (1..ARGV.length-1).each do |i|
81 81 stop_grader(ARGV[i])
82 82 end
83 83 puts "stop file(s) created"
84 84 end
85 85 exit(0)
86 86 end
87 87
88 88 # Check stop file.
89 89 if check_stopfile
90 90 puts "Stop file exists. Terminated."
91 91 clear_stopfile
92 92 exit(0)
93 93 end
94 94
95 95 #default options
96 96 options = {
97 97 :mode => 'queue',
98 98 :environment => 'exam',
99 99 :dry_run => false,
100 100 }
101 101
102 102 # Process mode and environment option
103 103 if ARGV.length >= 1
104 104 options[:environment] = ARGV.shift
105 105 if ARGV.length >=1
106 106 options[:mode] = ARGV.shift
107 107 end
108 108 end
109 109
110 110 options[:dry_run] = (ARGV.delete('--dry') != nil)
111 111 if options[:dry_run] and (not ['prob','contest'].include? options[:mode])
112 112 puts "Dry run currently works only for 'prob' or 'contest' modes."
113 113 exit(0)
114 114 end
115 115
116 116 options[:report] = (ARGV.delete('--report') != nil)
117 117 if options[:report] and (not ['prob','contest'].include? options[:mode])
118 118 puts "Report currently works only for 'prob' or 'contest' modes."
119 119 exit(0)
120 120 end
121 121
122 122 return options
123 123 end
124 124
125 125 class ResultCollector
126 126 def initialize
127 127 @results = {}
128 128 @problems = {}
129 129 @users = {}
130 130 end
131 131
132 132 def save(user, problem, grading_result)
133 133 if not @problems.has_key? problem.id
134 134 @problems[problem.id] = problem
135 135 end
136 136 if not @users.has_key? user.id
137 137 @users[user.id] = user
138 138 end
139 139 @results[[user.id, problem.id]] = grading_result
140 140 end
141 141
142 142 def print_report_by_user
143 143 puts "---------------------"
144 144 puts " REPORT"
145 145 puts "---------------------"
146 146
147 147 print "login,email"
148 148 @problems.each_value do |problem|
149 149 print ",#{problem.name}"
150 150 end
151 151 print "\n"
152 152
153 153 @users.each_value do |user|
154 154 print "#{user.login},#{user.email}"
155 155 @problems.each_value do |problem|
156 156 if @results.has_key? [user.id, problem.id]
157 157 print ",#{@results[[user.id,problem.id]][:points]}"
158 158 else
159 159 print ","
160 160 end
161 161 end
162 162 print "\n"
163 163 end
164 164 end
165 165 end
166 166
167 167 #########################################
168 168 # main program
169 169 #########################################
170 170
171 171 options = process_options_and_stop_file
172 172 GRADER_ENV = options[:environment]
173 173 grader_mode = options[:mode]
174 174 dry_run = options[:dry_run]
175 175
176 176 puts "environment: #{GRADER_ENV}"
177 177 require File.join(File.dirname(__FILE__),'config/environment')
178 178
179 179 # add grader_mode to config
180 180 # this is needed because method log needs it. TODO: clean this up
181 181 class << config
182 182 attr_accessor :grader_mode
183 183 end
184 184 config.grader_mode = grader_mode
185 185
186 186 # reading rails environment
187 187 log 'Reading rails environment'
188 188
189 189 RAILS_ENV = config.rails_env
190 190 require RAILS_ROOT + '/config/environment'
191 191
192 192 # register grader process
193 193 if config.report_grader
194 194 grader_proc = GraderProcess.register(config.grader_hostname,
195 195 Process.pid,
196 196 grader_mode)
197 197 else
198 198 grader_proc = nil
199 199 end
200 200
201 201 #set loggin environment
202 202 ENV['GRADER_LOGGING'] = log_file_name
203 203
204 204 # register exit handler to report inactive, and terminated
205 205 at_exit do
206 206 if grader_proc!=nil
207 207 grader_proc.report_inactive
208 208 grader_proc.terminate
209 209 end
210 210 end
211 211
212 212 #
213 213 # MAIN LOOP
214 214 #
215 215
216 216 case grader_mode
217 217 when "queue", "test_request"
218 218 log "Grader: #{grader_mode}"
219 219 if grader_mode=="queue"
220 220 engine = Grader::Engine.new
221 221 else
222 222 engine = Grader::Engine.new(:room_maker => Grader::TestRequestRoomMaker.new,
223 223 :reporter => Grader::TestRequestReporter.new)
224 224 end
225 225
226 226 runner = Grader::Runner.new(engine, grader_proc)
227 227 while true
228 228
229 229 if check_stopfile # created by calling grader stop
230 230 clear_stopfile
231 231 log "stopped (with stop file)"
232 232 break
233 233 end
234 234
235 235 if grader_mode=="queue"
236 236 task = runner.grade_oldest_task
237 237 else
238 238 task = runner.grade_oldest_test_request
239 239 end
240 240 if task==nil
241 241 sleep(1)
242 242 end
243 243 end
244 244
245 245 when "prob"
246 246 if options[:report]
247 247 result_collector = ResultCollector.new
248 248 else
249 249 result_collector = nil
250 250 end
251 251
252 252 if options[:dry_run]
253 253 puts "Running in dry mode"
254 254 end
255 255
256 256 prob_reporter = Grader::SubmissionReporter.new(:dry_run => dry_run,
257 257 :result_collector => result_collector)
258 258 engine = Grader::Engine.new(:reporter => prob_reporter)
259 259 runner = Grader::Runner.new(engine, grader_proc)
260 260
261 261 grader_proc.report_active if grader_proc!=nil
262 262
263 263 ARGV.each do |prob_name|
264 264 prob = Problem.find_by_name(prob_name)
265 265 if prob==nil
266 266 puts "cannot find problem: #{prob_name}"
267 267 else
268 268 runner.grade_problem(prob)
269 269 end
270 270 end
271 271
272 272 if options[:report]
273 273 result_collector.print_report_by_user
274 274 end
275 275
276 276 when "contest"
277 277 # always use dry run when grading during contest
278 278 contest_name = ARGV.shift
279 279
280 280 dry_run = options[:dry_run] = true
281 281
282 282 contest = Contest.find_by_name(contest_name)
283 283 if contest==nil
284 284 puts "cannot find contest: #{contest_name}"
285 285 exit(0)
286 286 end
287 287
288 288 if options[:report]
289 289 result_collector = ResultCollector.new
290 290 else
291 291 result_collector = nil
292 292 end
293 293
294 294 if options[:dry_run]
295 295 puts "Running in dry mode"
296 296 end
297 297
298 298 prob_reporter = Grader::SubmissionReporter.new(:dry_run => dry_run,
299 299 :result_collector => result_collector)
300 300 engine = Grader::Engine.new(:reporter => prob_reporter)
301 301 runner = Grader::Runner.new(engine, grader_proc)
302 302
303 303 grader_proc.report_active if grader_proc!=nil
304 304
305 305 contest.problems.each do |problem|
306 306 puts "Grading: #{problem.name}"
307 307 runner.grade_problem(problem,
308 308 :user_conditions => lambda do |u|
309 309 u.contest_finished? and
310 310 u.contest_ids.include?(contest.id)
311 311 end)
312 312 end
313 313
314 314 if options[:report]
315 315 result_collector.print_report_by_user
316 316 end
317 317
318 318 when "sub"
319 319 engine = Grader::Engine.new
320 320 runner = Grader::Runner.new(engine, grader_proc)
321 321
322 322 grader_proc.report_active if grader_proc!=nil
323 323
324 324 ARGV.each do |sub_id|
325 325 puts "Grading #{sub_id}"
326 326 begin
327 327 submission = Submission.find(sub_id.to_i)
328 328 rescue ActiveRecord::RecordNotFound
329 329 puts "Record not found"
330 330 submission = nil
331 331 end
332 332
333 333 if submission!=nil
334 334 runner.grade_submission(submission)
335 335 end
336 336 end
337 337
338 338
339 339
340 340 else
341 341 display_manual
342 342 exit(0)
343 343 end
344 344
@@ -1,110 +1,91
1 1 #!/usr/bin/ruby
2 2
3 + require 'fileutils'
4 +
3 5 def talk(str)
4 6 if TALKATIVE
5 7 puts str
6 8 end
7 9 end
8 10
9 - def execute(command, error_message="")
10 - if not system(command)
11 - puts "ERROR: #{error_message}"
12 - exit(127)
13 - end
14 - end
15 -
16 11 def save_source(submission,dir,fname)
17 12 f = File.open("#{dir}/#{fname}","w")
18 13 f.write(submission.source)
19 14 f.close
20 15 end
21 16
22 17 def call_judge(problem_home,language,problem_out_dir,fname)
23 18 ENV['PROBLEM_HOME'] = problem_home
24 19 Dir.chdir problem_out_dir
25 20 cmd = "#{problem_home}/script/judge #{language} #{fname}"
26 21 # puts "CMD: #{cmd}"
27 22 system(cmd)
28 23 end
29 24
30 25 def read_result(test_result_dir)
31 26 cmp_msg_fname = "#{test_result_dir}/compiler_message"
32 27 cmp_msg = File.open(cmp_msg_fname).read
33 28
34 29 result_fname = "#{test_result_dir}/result"
35 30 comment_fname = "#{test_result_dir}/comment"
36 31 if FileTest.exist?(result_fname)
37 32 result = File.open(result_fname).readline.to_i
38 33 comment = File.open(comment_fname).readline.chomp
39 34 return {:points => result,
40 35 :comment => comment,
41 36 :cmp_msg => cmp_msg}
42 37 else
43 38 return {:points => 0,
44 39 :comment => 'compile error',
45 40 :cmp_msg => cmp_msg}
46 41 end
47 42 end
48 43
49 44 def save_result(submission,result)
50 45 submission.graded_at = Time.now
51 46 submission.points = result[:points]
52 47 submission.grader_comment = report_comment(result[:comment])
53 48 submission.compiler_message = result[:cmp_msg]
54 49 submission.save
55 50 end
56 51
57 52 def grade(submission_id)
58 53 sub = Submission.find(submission_id)
59 54 user = sub.user
60 55 problem = sub.problem
61 56
62 57 language = sub.language.name
63 58 lang_ext = sub.language.ext
64 59 # FIX THIS
65 60 talk 'some hack on language'
66 61 if language == 'cpp'
67 62 language = 'c++'
68 63 end
69 64
70 65 user_dir = "#{USER_RESULT_DIR}/#{user.login}"
71 66 Dir.mkdir(user_dir) if !FileTest.exist?(user_dir)
72 67
73 68 problem_out_dir = "#{user_dir}/#{problem.name}"
74 69 Dir.mkdir(problem_out_dir) if !FileTest.exist?(problem_out_dir)
75 70
76 71 problem_home = "#{PROBLEMS_DIR}/#{problem.name}"
77 72 source_name = "#{problem.name}.#{lang_ext}"
78 73
79 74 save_source(sub,problem_out_dir,source_name)
80 75 call_judge(problem_home,language,problem_out_dir,source_name)
81 76 save_result(sub,read_result("#{problem_out_dir}/test-result"))
82 77 end
83 78
84 - def stop_grader
85 - File.open(File.dirname(__FILE__) + '/stop','w')
86 - end
87 -
88 - def check_stopfile
89 - FileTest.exist?(File.dirname(__FILE__) + '/stop')
90 - end
91 -
92 - def clear_stopfile
93 - system("rm " + File.dirname(__FILE__) + '/stop')
94 - end
95 -
96 79 # reading environment and options
97 80 GRADER_ENV = 'exam'
98 81 puts "environment: #{GRADER_ENV}"
99 82 require File.dirname(__FILE__) + "/environment.rb"
100 83
101 84 #main program
102 85 talk 'Reading rails environment'
103 86
104 87 RAILS_ENV = 'development'
105 88 require RAILS_APP_DIR + '/config/environment'
106 89
107 - current_dir = `pwd`
90 + current_dir = FileUtils.pwd
108 91 grade(ARGV[0].to_i)
109 -
110 -
@@ -1,228 +1,236
1 1 #!/usr/bin/ruby
2 2
3 3 require 'erb'
4 4 require 'fileutils'
5 5 require File.join(File.dirname(__FILE__),'lib/import_helper')
6 6
7 7 JUDGE_ENVIRONMENTS = [:grading, :exam]
8 8 ENV_INFO = {
9 9 :grading => {
10 10 :ev_dir => 'ev',
11 11 :raw_prefix => '',
12 12 },
13 13 :exam => {
14 14 :ev_dir => 'ev-exam',
15 15 :raw_prefix => 'ex.',
16 16 }
17 17 }
18 18
19 19 def input_filename(dir,i)
20 20 "#{dir}/input-#{i}.txt"
21 21 end
22 22
23 23 def answer_filename(dir,i)
24 24 "#{dir}/answer-#{i}.txt"
25 25 end
26 26
27 27 def build_testrun_info_from_dir(num_testruns, importing_test_dir, raw_prefix='')
28 28 filenames = Dir["#{importing_test_dir}/#{raw_prefix}*.in"].collect do |filename|
29 29 File.basename((/(.*)\.in/.match(filename))[1])
30 30 end
31 31 build_testrun_info(num_testruns,filenames,raw_prefix)
32 32 end
33 33
34 34 def copy_testcase(importing_test_dir,fname,dir,i)
35 - system("cp #{importing_test_dir}/#{fname}.in #{input_filename(dir,i)}")
36 - system("cp #{importing_test_dir}/#{fname}.sol #{answer_filename(dir,i)}")
35 + FileUtils.cp("#{importing_test_dir}/#{fname}.in", "#{input_filename(dir,i)}")
36 + FileUtils.cp("#{importing_test_dir}/#{fname}.sol", "#{answer_filename(dir,i)}")
37 37 end
38 38
39 39 def process_options(options)
40 40 i = 3
41 41 while i<ARGV.length
42 42 if ARGV[i]=='-t'
43 43 options[:time_limit] = ARGV[i+1].to_f if ARGV.length>i+1
44 44 i += 1
45 45 end
46 46 if ARGV[i]=='-m'
47 47 options[:mem_limit] = ARGV[i+1].to_i if ARGV.length>i+1
48 48 i += 1
49 49 end
50 50 i += 1
51 51 end
52 52 end
53 53
54 54 def print_usage
55 55 puts "using: import_problem_new name dir check [options]
56 56
57 57 where: name = problem_name (put '-' (dash) to use dir name)
58 58 dir = importing testcase directory
59 59 check = check script, which can be
60 60 'integer', 'text' (for standard script),
61 61 path_to_your_script, or
62 62 'wrapper:(path_to_your_wrapped_script)'
63 63 options: -t time-limit (in seconds)
64 64 -m memory-limit (in megabytes)
65 65
66 66 The script looks at test data files in the dir of the forms: *.in and
67 67 *.sol and import them to the evaluation dir for their environment,
68 68 based on their prefixes.
69 69
70 70 Currently supporting environments are:"
71 71
72 72 JUDGE_ENVIRONMENTS.each do |env|
73 73 prefix = ENV_INFO[env][:raw_prefix]
74 74 prefix = 'no prefix' if prefix==''
75 75 puts " * #{env}"
76 76 puts " import to: #{ENV_INFO[env][:ev_dir]}"
77 77 puts " prefix with: #{prefix} (e.g., #{ENV_INFO[env][:raw_prefix]}1.in, #{ENV_INFO[env][:raw_prefix]}5a.sol)"
78 78 end
79 79
80 80 puts"
81 81 For each environment, the script
82 82 * creates a directory for a problem in ev dir of that environment,
83 83 * copies testdata in the old format and create standard testcase config file
84 84 * copies a check script for grading
85 85 * creates a test_request template in the ev dir + '/test_request'
86 86
87 87 For wrapped checked script see comment in templates/check_wrapper for
88 88 information."
89 89 end
90 90
91 91 def count_testruns(testcase_dir, raw_prefix)
92 92 n = 0
93 93 begin
94 94 # check for test case n+1
95 95 if ((Dir["#{testcase_dir}/#{raw_prefix}#{n+1}.in"].length==0) and
96 96 (Dir["#{testcase_dir}/#{raw_prefix}#{n+1}[a-z].in"].length==0))
97 97 return n
98 98 end
99 99 n += 1
100 100 end while true
101 101 end
102 102
103 + def create_dir_if_not_exists(dir)
104 + if ! FileTest.exists? dir
105 + FileUtils.mkdir(dir)
106 + end
107 + end
108 +
103 109 def import_problem(ev_dir, problem, testcase_dir, num_testruns, raw_prefix, check_script, options)
104 110 testrun_info = build_testrun_info_from_dir(num_testruns, testcase_dir, raw_prefix)
105 111
106 112 if !(FileTest.exists? ev_dir)
107 113 puts "Testdata dir (#{ev_dir}) not found."
108 114 return
109 115 end
110 116
111 117 problem_dir = "#{ev_dir}/#{problem}"
112 118
113 119 # start working
114 120 puts "creating directories"
115 121
116 - system("mkdir #{problem_dir}")
117 - system("mkdir #{problem_dir}/script")
118 - system("mkdir #{problem_dir}/test_cases")
122 + create_dir_if_not_exists("#{problem_dir}")
123 + create_dir_if_not_exists("#{problem_dir}/script")
124 + create_dir_if_not_exists("#{problem_dir}/test_cases")
119 125
120 126 puts "copying testcases"
121 127
122 128 tr_num = 0
123 129
124 130 num_testcases = 0
125 131
126 132 testrun_info.each do |testrun|
127 133 tr_num += 1
128 134 puts "testrun: #{tr_num}"
129 135
130 136 testrun.each do |testcase_info|
131 137 testcase_num, testcase_fname = testcase_info
132 138
133 139 puts "copy #{testcase_fname} to #{testcase_num}"
134 140
135 - system("mkdir #{problem_dir}/test_cases/#{testcase_num}")
141 + create_dir_if_not_exists("#{problem_dir}/test_cases/#{testcase_num}")
136 142 copy_testcase("#{testcase_dir}",testcase_fname,"#{problem_dir}/test_cases/#{testcase_num}",testcase_num)
137 143
138 144 num_testcases += 1
139 145 end
140 146 end
141 147
142 148 # generating all_tests.cfg
143 149 puts "generating testcase config file"
144 150
145 151 template = File.open(SCRIPT_DIR + "/templates/all_tests.cfg.erb").read
146 152 all_test_cfg = ERB.new(template)
147 153
148 154 cfg_file = File.open("#{problem_dir}/test_cases/all_tests.cfg","w")
149 155 cfg_file.puts all_test_cfg.result binding
150 156 cfg_file.close
151 157
152 158 # copy check script
153 159 if res = /^wrapper:(.*)$/.match(check_script)
154 160 # wrapper script
155 161 check_script_fname = res[1]
156 162 script_name = File.basename(check_script_fname)
157 163 check_wrapper_template = File.open(SCRIPT_DIR + "/templates/check_wrapper").read
158 164 check_wrapper = ERB.new(check_wrapper_template)
159 165
160 166 check_file = File.open("#{problem_dir}/script/check","w")
161 167 check_file.puts check_wrapper.result binding
162 168 check_file.close
163 169
164 170 File.chmod(0755,"#{problem_dir}/script/check")
165 171
166 - system("cp #{check_script_fname} #{problem_dir}/script/#{script_name}")
172 + FileUtils.cp("#{check_script_fname}", "#{problem_dir}/script/#{script_name}")
167 173 else
168 174 if File.exists?(SCRIPT_DIR + "/templates/check.#{check_script}")
169 175 check_script_fname = SCRIPT_DIR + "/templates/check.#{check_script}"
170 176 else
171 177 check_script_fname = check_script
172 178 end
173 - system("cp #{check_script_fname} #{problem_dir}/script/check")
179 + FileUtils.cp("#{check_script_fname}", "#{problem_dir}/script/check")
174 180 end
175 181
176 182 # generating test_request directory
177 183 puts "generating test_request template"
178 184 FileUtils.mkdir_p("#{ev_dir}/test_request/#{problem}/script")
179 185 FileUtils.mkdir_p("#{ev_dir}/test_request/#{problem}/test_cases/1")
180 186
181 187 template = File.open(SCRIPT_DIR + "/templates/test_request_all_tests.cfg.erb").read
182 188 test_request_all_test_cfg = ERB.new(template)
183 189
184 190 cfg_file = File.open("#{ev_dir}/test_request/#{problem}/test_cases/all_tests.cfg","w")
185 191 cfg_file.puts test_request_all_test_cfg.result
186 192 cfg_file.close
187 193
188 - system("cp #{SCRIPT_DIR}/templates/check_empty #{ev_dir}/test_request/#{problem}/script/check")
189 - system("cp #{SCRIPT_DIR}/templates/answer-1.txt #{ev_dir}/test_request/#{problem}/test_cases/1")
194 + FileUtils.cp("#{SCRIPT_DIR}/templates/check_empty",
195 + "#{ev_dir}/test_request/#{problem}/script/check")
196 + FileUtils.cp("#{SCRIPT_DIR}/templates/answer-1.txt",
197 + "#{ev_dir}/test_request/#{problem}/test_cases/1")
190 198
191 199 puts "done"
192 200 end
193 201
194 202
195 203 SCRIPT_DIR = File.dirname(__FILE__)
196 204
197 205 # print usage
198 206 if (ARGV.length < 3) or (ARGV[2][0,1]=="-")
199 207 print_usage
200 208 exit(127)
201 209 end
202 210
203 211 # processing arguments
204 212 problem = ARGV[0]
205 213 testcase_dir = ARGV[1]
206 214 problem = File.basename(testcase_dir) if problem=="-"
207 215 check_script = ARGV[2]
208 216 options = {:time_limit => 1, :mem_limit => 16}
209 217 process_options(options)
210 218
211 219 JUDGE_ENVIRONMENTS.each do |env|
212 220 ev_dir = ENV_INFO[env][:ev_dir]
213 221 raw_prefix = ENV_INFO[env][:raw_prefix]
214 222
215 223 num_testruns = count_testruns(testcase_dir,raw_prefix)
216 224
217 225 puts ""
218 226 puts "*** Environment: #{env} (#{num_testruns} test runs) ***"
219 227 puts ""
220 228
221 229 import_problem(ev_dir,
222 230 problem,
223 231 testcase_dir,
224 232 num_testruns,
225 233 raw_prefix,
226 234 check_script,
227 235 options)
228 236 end
@@ -1,109 +1,109
1 1 require 'ftools'
2 2
3 3 # DirInit::Manager handles directory initialization and clean-up when
4 4 # there are many concurrent processes that wants to modify the
5 5 # directory in the same way.
6 6 #
7 7 # An example usage is when each process wants to copy some temporary
8 8 # files to the directory and delete these files after finishing its
9 9 # job. Problems may occur when the first process delete the files
10 10 # while the second process is still using the files.
11 11 #
12 12 # This library maintain a reference counter on the processes using the
13 13 # directory. It locks the dir to manage critical section when
14 14 # updating the reference counter.
15 15
16 16 module DirInit
17 17
18 18 class Manager
19 19
20 20 def initialize(dir_name, usage_filename='.usage_counter')
21 21 @dir_name = dir_name
22 22 @usage_filename = usage_filename
23 23 end
24 24
25 25 # Check if someone has initialized the dir. If not, call block.
26 26
27 27 def setup # :yields: block
28 - dir = File.new(@dir_name)
28 + dir = File.new(@dir_name + '/lockfile',"w+")
29 29 dir.flock(File::LOCK_EX)
30 30 begin
31 31 counter_filename = get_counter_filename
32 32 if File.exist? counter_filename
33 33 # someone is here
34 34 f = File.new(counter_filename,"r+")
35 35 counter = f.read.to_i
36 36 f.seek(0)
37 37 f.write("#{counter+1}\n")
38 38 f.close
39 39 else
40 40 # i'm the first, create the counter file
41 41 counter = 0
42 42 f = File.new(counter_filename,"w")
43 43 f.write("1\n")
44 44 f.close
45 45 end
46 46
47 47 # if no one is here
48 48 if counter == 0
49 49 if block_given?
50 50 yield
51 51 end
52 52 end
53 53
54 54 rescue
55 55 raise
56 56
57 57 ensure
58 58 # make sure it unlock the directory
59 59 dir.flock(File::LOCK_UN)
60 60 dir.close
61 61 end
62 62 end
63 63
64 64 # Check if I am the last one using the dir. If true, call block.
65 65
66 66 def teardown
67 67 dir = File.new(@dir_name)
68 68 dir.flock(File::LOCK_EX)
69 69 begin
70 70 counter_filename = get_counter_filename
71 71 if File.exist? counter_filename
72 72 # someone is here
73 73 f = File.new(counter_filename,"r+")
74 74 counter = f.read.to_i
75 75 f.seek(0)
76 76 f.write("#{counter-1}\n")
77 77 f.close
78 78
79 79 if counter == 1
80 80 # i'm the last one
81 81
82 82 File.delete(counter_filename)
83 83 if block_given?
84 84 yield
85 85 end
86 86 end
87 87 else
88 88 # This is BAD
89 89 raise "Error: reference count missing"
90 90 end
91 91
92 92 rescue
93 93 raise
94 94
95 95 ensure
96 96 # make sure it unlock the directory
97 97 dir.flock(File::LOCK_UN)
98 98 dir.close
99 99 end
100 100 end
101 101
102 102 protected
103 103
104 104 def get_counter_filename
105 105 return File.join(@dir_name,@usage_filename)
106 106 end
107 107
108 108 end
109 109 end
@@ -1,187 +1,187
1 1 require 'fileutils'
2 - require 'ftools'
3 2 require File.join(File.dirname(__FILE__),'dir_init')
4 3
5 4 module Grader
6 5
7 6 #
8 7 # A grader engine grades a submission, against anything: a test
9 8 # data, or a user submitted test data. It uses two helpers objects:
10 9 # room_maker and reporter.
11 10 #
12 11 class Engine
13 12
14 13 attr_writer :room_maker
15 14 attr_writer :reporter
16 15
17 16 def initialize(options={})
18 17 # default options
19 18 if not options.include? :room_maker
20 19 options[:room_maker] = Grader::SubmissionRoomMaker.new
21 20 end
22 21 if not options.include? :reporter
23 22 options[:reporter] = Grader::SubmissionReporter.new
24 23 end
25 24
26 25 @config = Grader::Configuration.get_instance
27 26
28 27 @room_maker = options[:room_maker]
29 28 @reporter = options[:reporter]
30 29 end
31 30
32 31 # takes a submission, asks room_maker to produce grading directories,
33 32 # calls grader scripts, and asks reporter to save the result
34 33 def grade(submission)
35 - current_dir = `pwd`.chomp
34 + current_dir = FileUtils.pwd
36 35
37 36 user = submission.user
38 37 problem = submission.problem
39 38
40 39 # TODO: will have to create real exception for this
41 40 if user==nil or problem == nil
42 41 @reporter.report_error(submission,"Grading error: problem with submission")
43 42 #raise "engine: user or problem is nil"
44 43 end
45 44
46 45 # TODO: this is another hack so that output only task can be judged
47 46 if submission.language!=nil
48 47 language = submission.language.name
49 48 lang_ext = submission.language.ext
50 49 else
51 50 language = 'c'
52 51 lang_ext = 'c'
53 52 end
54 53
55 54 # FIX THIS
56 55 talk 'some hack on language'
57 56 if language == 'cpp'
58 57 language = 'c++'
59 58 end
60 59
61 60 # COMMENT: should it be only source.ext?
62 61 if problem!=nil
63 62 source_name = "#{problem.name}.#{lang_ext}"
64 63 else
65 64 source_name = "source.#{lang_ext}"
66 65 end
67 66
68 67 begin
69 68 grading_dir = @room_maker.produce_grading_room(submission)
70 69 @room_maker.save_source(submission,source_name)
71 70 problem_home = @room_maker.find_problem_home(submission)
72 71
73 72 # puts "GRADING DIR: #{grading_dir}"
74 73 # puts "PROBLEM DIR: #{problem_home}"
75 74
76 75 if !FileTest.exist?(problem_home)
77 76 raise "No test data."
78 77 end
79 78
80 79 dinit = DirInit::Manager.new(problem_home)
81 80
82 81 dinit.setup do
83 82 copy_log = copy_script(problem_home)
84 83 save_copy_log(problem_home,copy_log)
85 84 end
86 85
87 86 call_judge(problem_home,language,grading_dir,source_name)
88 87
89 88 @reporter.report(submission,"#{grading_dir}/test-result")
90 89
91 90 dinit.teardown do
92 91 copy_log = load_copy_log(problem_home)
93 92 clear_copy_log(problem_home)
94 93 clear_script(copy_log,problem_home)
95 94 end
96 95
97 96 rescue RuntimeError => msg
98 97 @reporter.report_error(submission, msg)
99 98
100 99 ensure
101 100 @room_maker.clean_up(submission)
102 101 Dir.chdir(current_dir) # this is really important
103 102 end
104 103 end
105 104
106 105 protected
107 106
108 107 def talk(str)
109 108 if @config.talkative
110 109 puts str
111 110 end
112 111 end
113 112
114 113 def call_judge(problem_home,language,grading_dir,fname)
115 114 ENV['PROBLEM_HOME'] = problem_home
116 115
117 116 talk grading_dir
118 117 Dir.chdir grading_dir
119 118 cmd = "#{problem_home}/script/judge #{language} #{fname}"
120 119 talk "CMD: #{cmd}"
121 120 system(cmd)
122 121 end
123 122
124 123 def get_std_script_dir
125 124 GRADER_ROOT + '/std-script'
126 125 end
127 126
128 127 def copy_script(problem_home)
129 128 script_dir = "#{problem_home}/script"
130 129 std_script_dir = get_std_script_dir
131 130
132 131 raise "std-script directory not found" if !FileTest.exist?(std_script_dir)
133 132
134 133 scripts = Dir[std_script_dir + '/*']
135 134
136 135 copied = []
137 136
138 137 scripts.each do |s|
139 138 fname = File.basename(s)
139 + next if FileTest.directory?(s)
140 140 if !FileTest.exist?("#{script_dir}/#{fname}")
141 141 copied << fname
142 - system("cp #{s} #{script_dir}")
142 + FileUtils.cp(s, "#{script_dir}")
143 143 end
144 144 end
145 145
146 146 return copied
147 147 end
148 148
149 149 def copy_log_filename(problem_home)
150 150 return File.join(problem_home, '.scripts_copied')
151 151 end
152 152
153 153 def save_copy_log(problem_home, log)
154 154 f = File.new(copy_log_filename(problem_home),"w")
155 155 log.each do |fname|
156 156 f.write("#{fname}\n")
157 157 end
158 158 f.close
159 159 end
160 160
161 161 def load_copy_log(problem_home)
162 162 f = File.new(copy_log_filename(problem_home),"r")
163 163 log = []
164 164 f.readlines.each do |line|
165 165 log << line.strip
166 166 end
167 167 f.close
168 168 log
169 169 end
170 170
171 171 def clear_copy_log(problem_home)
172 172 File.delete(copy_log_filename(problem_home))
173 173 end
174 174
175 175 def clear_script(log,problem_home)
176 176 log.each do |s|
177 - system("rm #{problem_home}/script/#{s}")
177 + FileUtils.rm("#{problem_home}/script/#{s}")
178 178 end
179 179 end
180 180
181 181 def mkdir_if_does_not_exist(dirname)
182 182 Dir.mkdir(dirname) if !FileTest.exist?(dirname)
183 183 end
184 184
185 185 end
186 186
187 187 end
@@ -1,242 +1,251
1 1 #
2 2 # This part contains various test_request helpers for interfacing
3 3 # with Grader::Engine. There are TestRequestRoomMaker and
4 4 # TestRequestReporter.
5 5
6 6 module Grader
7 7
8 + def self.link_or_copy(src, des)
9 + begin
10 + FileUtils.ln_s(src, des)
11 + rescue NotImplementedError
12 + FileUtils.cp(src,des)
13 + end
14 + end
15 +
16 + def self.call_and_log(error_message)
17 + begin
18 + yield
19 + rescue
20 + msg = "ERROR: #{error_message}"
21 + raise msg
22 + end
23 + end
24 +
8 25 #
9 26 # A TestRequestRoomMaker is a helper object for Engine
10 27 # - finds grading room: in user_result_dir/(user)/test_request/ ...
11 28 # - prepare problem configuration for grading --- basically it copy
12 29 # all config files, and copy user's input into the testcase
13 30 # directory. First, it finds the template from problem template
14 31 # directory; if it can't find a template, it'll use the template
15 32 # from default template.
16 33 class TestRequestRoomMaker
17 34 def initialize
18 35 @config = Grader::Configuration.get_instance
19 36 end
20 37
21 38 def produce_grading_room(test_request)
22 39 grading_room = grading_room_dir(test_request)
23 40 FileUtils.mkdir_p(grading_room)
24 41
25 42 #
26 43 # Also copy additional submitted file to this directory as well.
27 44 # The program would see this file only if it is copied
28 45 # to the sandbox directory later. The run script should do it.
29 46 #
30 47 if FileTest.exists?("#{test_request.input_file_name}.files")
31 - cmd = "cp #{test_request.input_file_name}.files/* #{grading_room}"
32 - system(cmd)
48 + FileUtils.cp_r("#{test_request.input_file_name}.files/.",
49 + "#{grading_room}")
33 50 end
34 51
35 52 grading_room
36 53 end
37 54
38 55 def find_problem_home(test_request)
39 56 problem_name = test_request.problem_name
40 57
41 58 template_dir = "#{@config.test_request_problem_templates_dir}/" + problem_name
42 59
43 60 raise "Test Request: error template not found" if !File.exists?(template_dir)
44 61
45 62 problem_home = problem_home_dir(test_request)
46 63 FileUtils.mkdir_p(problem_home)
47 64
48 65 copy_problem_template(template_dir,problem_home)
49 66 link_input_file(test_request,problem_home)
50 67
51 68 problem_home
52 69 end
53 70
54 71 def save_source(test_request,source_name)
55 72 dir = self.produce_grading_room(test_request)
56 73 submission = test_request.submission
57 74 f = File.open("#{dir}/#{source_name}","w")
58 75 f.write(submission.source)
59 76 f.close
60 77 end
61 78
62 79 def clean_up(test_request)
63 80 problem_home = problem_home_dir(test_request)
64 81 remove_data_files(problem_home)
65 82 end
66 83
67 84 protected
68 85 def grading_room_dir(test_request)
69 86 problem_name = test_request.problem_name
70 87 user = test_request.user
71 88 grading_room = "#{@config.user_result_dir}" +
72 89 "/#{user.login}/test_request" +
73 90 "/#{problem_name}/#{test_request.id}"
74 91 grading_room
75 92 end
76 93
77 94 def problem_home_dir(test_request)
78 95 problem_name = test_request.problem_name
79 96 user = test_request.user
80 97 "#{@config.user_result_dir}" +
81 98 "/#{user.login}/test_request/#{problem_name}"
82 99 end
83 100
84 101 def copy_problem_template(template_dir,problem_home)
85 - cmd = "cp -R #{template_dir}/* #{problem_home}"
86 - system_and_raise_when_fail(cmd,"Test Request: cannot copy problem template")
102 + Grader::call_and_log("Test Request: cannot copy problem template") {
103 + FileUtils.cp_r("#{template_dir}/.","#{problem_home}")
104 + }
87 105 end
88 106
89 107 def link_input_file(test_request,problem_home)
90 108 input_fname = "#{test_request.input_file_name}"
91 109 if !File.exists?(input_fname)
92 110 raise "Test Request: input file not found."
93 111 end
94 112
95 113 input_fname_problem_home = "#{problem_home}/test_cases/1/input-1.txt"
96 114 if File.exists?(input_fname_problem_home)
97 115 FileUtils.rm([input_fname_problem_home], :force => true)
98 116 end
99 117
100 - cmd = "ln -s #{input_fname} #{input_fname_problem_home}"
101 - system_and_raise_when_fail(cmd,"Test Request: cannot link input file")
118 + Grader::link_or_copy("#{input_fname}", "#{input_fname_problem_home}")
102 119 end
103 120
104 121 def remove_data_files(problem_home)
105 122 if File.exists?("#{problem_home}/test_cases/1/input-1.txt")
106 - cmd = "rm #{problem_home}/test_cases/1/*"
107 - system_and_raise_when_fail(cmd,"Test Request: cannot remove data files")
108 - end
109 - end
110 -
111 - def system_and_raise_when_fail(cmd,msg)
112 - if !system(cmd)
113 - raise msg
123 + Grader::call_and_log("Test Request: cannot remove data files") {
124 + FileUtils.rm Dir.glob("#{problem_home}/test_cases/1/*")
125 + }
114 126 end
115 127 end
116 128
117 129 end
118 130
119 131 class TestRequestReporter
120 132 def initialize
121 133 @config = Grader::Configuration.get_instance
122 134 end
123 135
124 136 def report(test_request,test_result_dir)
125 137 save_result(test_request,read_result(test_result_dir))
126 138 end
127 139
128 140 def report_error(test_request, msg)
129 141 save_result(test_request, {:running_stat => {
130 142 :msg => "#{msg}",
131 143 :running_time => nil,
132 144 :exit_status => "Some error occured. Program did not run",
133 145 :memory_usage => nil
134 146 }})
135 147 end
136 148
137 149 protected
138 150 def read_result(test_result_dir)
139 151 # TODO:
140 152 cmp_msg_fname = "#{test_result_dir}/compiler_message"
141 153 cmp_file = File.open(cmp_msg_fname)
142 154 cmp_msg = cmp_file.read
143 155 cmp_file.close
144 156
145 157 result_file_name = "#{test_result_dir}/1/result"
146 158
147 159 if File.exists?(result_file_name)
148 160 output_file_name = "#{test_result_dir}/1/output.txt"
149 161 results = File.open("#{test_result_dir}/1/result").readlines
150 162 stat = extract_running_stat(results)
151 163
152 164 return {
153 165 :output_file_name => output_file_name,
154 166 :running_stat => stat,
155 167 :comment => "",
156 168 :cmp_msg => cmp_msg}
157 169 else
158 170 return {
159 171 :running_stat => nil,
160 172 :comment => "Compilation error",
161 173 :cmp_msg => cmp_msg}
162 174 end
163 175 end
164 176
165 177 def extract_running_stat(results)
166 178 running_stat_line = results[-1]
167 179
168 180 # extract exit status line
169 181 run_stat = ""
170 182 if !(/[Cc]orrect/.match(results[0]))
171 183 run_stat = results[0].chomp
172 184 else
173 185 run_stat = 'Program exited normally'
174 186 end
175 187
176 188 # extract running time
177 189 if res = /r(.*)u(.*)s/.match(running_stat_line)
178 190 seconds = (res[1].to_f + res[2].to_f)
179 191 time_stat = "Time used: #{seconds} sec."
180 192 else
181 193 seconds = nil
182 194 time_stat = "Time used: n/a sec."
183 195 end
184 196
185 197 # extract memory usage
186 198 if res = /s(.*)m/.match(running_stat_line)
187 199 memory_used = res[1].to_i
188 200 else
189 201 memory_used = -1
190 202 end
191 203
192 204 return {
193 205 :msg => "#{run_stat}\n#{time_stat}",
194 206 :running_time => seconds,
195 207 :exit_status => run_stat,
196 208 :memory_usage => memory_used
197 209 }
198 210 end
199 211
200 212 def save_result(test_request,result)
201 213 if result[:output_file_name]!=nil
202 214 test_request.output_file_name = link_output_file(test_request,
203 215 result[:output_file_name])
204 216 end
205 217 test_request.graded_at = Time.now
206 218 test_request.compiler_message = (result[:cmp_msg] or '')
207 219 test_request.grader_comment = (result[:comment] or '')
208 220 if result[:running_stat]!=nil
209 221 test_request.running_stat = (result[:running_stat][:msg] or '')
210 222 test_request.running_time = (result[:running_stat][:running_time] or nil)
211 223 test_request.exit_status = result[:running_stat][:exit_status]
212 224 test_request.memory_usage = result[:running_stat][:memory_usage]
213 225 else
214 226 test_request.running_stat = ''
215 227 end
216 228 test_request.save
217 229 end
218 230
219 231 protected
220 232 def link_output_file(test_request, fname)
221 233 target_file_name = random_output_file_name(test_request.user,
222 234 test_request.problem)
223 235 FileUtils.mkdir_p(File.dirname(target_file_name))
224 - cmd = "ln -s #{fname} #{target_file_name}"
225 - if !system(cmd)
226 - raise "TestRequestReporter: cannot move output file"
227 - end
236 + Grader::link_or_copy("#{fname}", "#{target_file_name}")
228 237 return target_file_name
229 238 end
230 239
231 240 def random_output_file_name(user,problem)
232 241 problem_name = TestRequest.name_of(problem)
233 242 begin
234 243 tmpname = "#{@config.test_request_output_base_dir}" +
235 244 "/#{user.login}/#{problem_name}/#{rand(10000)}"
236 245 end while File.exists?(tmpname)
237 246 tmpname
238 247 end
239 248
240 249 end
241 250
242 251 end
@@ -1,138 +1,172
1 1 #!/usr/bin/ruby
2 2
3 + require 'fileutils'
4 +
3 5 def log(str='')
4 6 if ENV['TALKATIVE']!=nil
5 7 puts str
6 8 end
7 9 if ENV['GRADER_LOGGING']!=nil
8 10 log_fname = ENV['GRADER_LOGGING']
9 11 fp = File.open(log_fname,"a")
10 12 fp.puts("judge: #{Time.new.strftime("%H:%M")} #{str}")
11 13 fp.close
12 14 end
13 15 end
14 16
15 17 problem_home = ENV['PROBLEM_HOME']
16 18
17 19 def execute(command, error_message="")
18 20 if not system(command)
19 21 msg = "ERROR: #{error_message}"
20 22 log msg
21 23 raise msg
22 24 end
23 25 end
24 26
27 + def call_and_log(error_message)
28 + begin
29 + yield
30 + rescue
31 + msg = "ERROR: #{error_message}"
32 + log msg
33 + raise msg
34 + end
35 + end
36 +
37 + def clear_and_create_empty_dir(dir)
38 + FileUtils.rm_rf(dir, :secure => true)
39 + call_and_log("Cannot make directory #{dir}.") { FileUtils.mkdir(dir) }
40 + end
41 +
25 42 # ARGV[0] --- language
26 43 # ARGV[1] --- program source file
27 44 # ARGV[2] --- test result directory
28 45 # ARGV[3] --- sandbox directory
29 46
30 47 if ARGV.length < 2 || ARGV.length > 4
31 48 puts "Usage: judge <language> <program-source> [<test-result-directory>] [<sandbox-directory>]"
32 49 puts " <sandbox-directory> is defaulted to ./sandbox"
33 50 puts " <test-result-directory> is defaulted to ./test-result"
34 51 puts "WARNING: The judge script will forcefully create the (implicitly and explicitly) specified directories and remove anything inside it."
35 52 exit(127)
36 53 end
37 54
38 55 language = ARGV[0]
39 56 if language != "c" && language != "c++" && language != "pas"
40 57 log "You specified a language that is not supported: #{language}."
41 58 exit(127)
42 59 end
43 60
44 61 source_file = ARGV[1]
45 62 if File.exist?(source_file) == false
46 63 log "The source file does not exist."
47 64 exit(127)
48 65 end
49 66
50 67 log "Making test result and sandbox directories..."
51 68
52 - current_dir = `pwd`
69 + current_dir = FileUtils.pwd
53 70 current_dir.strip!
54 71
55 72 if ARGV.length >= 3
56 73 test_result_dir = ARGV[2]
57 74 else
58 75 test_result_dir = "#{current_dir}/test-result"
59 76 end
77 +
60 78 log "Test result directory: #{test_result_dir}"
61 - system("rm -Rf #{test_result_dir}")
62 - execute("mkdir #{test_result_dir}", "Cannot make directory #{test_result_dir}.")
79 + clear_and_create_empty_dir(test_result_dir)
63 80
64 81 if ARGV.length >= 4
65 82 sandbox_dir = ARGV[3]
66 83 else
67 84 sandbox_dir = "#{current_dir}/sandbox"
68 85 end
69 86 log "Sandbox directory: #{sandbox_dir}"
70 - system("rm -Rf #{sandbox_dir}")
71 - execute("mkdir #{sandbox_dir}", "Cannot make directory #{sandbox_dir}")
87 + clear_and_create_empty_dir(sandbox_dir)
72 88
73 89 # Compile
74 90 log
75 91 log "Compiling..."
76 - execute("cp #{source_file} #{sandbox_dir}", "Cannot copy the source file to #{sandbox_dir}")
92 + call_and_log("Cannot copy the source file to #{sandbox_dir}") {
93 + FileUtils.cp(source_file, sandbox_dir)
94 + }
77 95 begin
78 96 Dir.chdir sandbox_dir
79 97 rescue
80 98 log "ERROR: Cannot change directory to #{sandbox_dir}."
81 99 exit(127)
82 100 end
83 101 execute("#{problem_home}/script/compile #{language} #{source_file}", "Compilation error!")
84 - compile_message = `cat compiler_message`
102 + compile_message = open("compiler_message").read
85 103 compile_message.strip!
86 - execute("mv compiler_message #{test_result_dir}", "Cannot move the compiler message to #{test_result_dir}.")
104 + call_and_log("Cannot move the compiler message to #{test_result_dir}.") {
105 + FileUtils.mv("compiler_message", test_result_dir)
106 + }
87 107 if !FileTest.exist?("a.out")
88 108 log "Cannot compile the source code. See message in #{test_result_dir}/compile_message"
89 109 exit(127)
90 110 else
91 - execute("mv a.out #{test_result_dir}", "Cannot move the compiled program to #{test_result_dir}")
92 - system("rm -Rf #{sandbox_dir}/*")
111 + call_and_log("Cannot move the compiled program to #{test_result_dir}") {
112 + FileUtils.mv("a.out",test_result_dir)
113 + }
114 + FileUtils.rm_rf("#{sandbox_dir}/.")
93 115 end
94 116
95 117 require "#{problem_home}/script/test_dsl.rb"
96 118 load "#{problem_home}/test_cases/all_tests.cfg"
97 119 problem = Problem.get_instance
98 120
99 121 if problem.well_formed? == false
100 122 log "The problem specification is not well formed."
101 123 exit(127)
102 124 end
103 125
104 126 # Doing the testing.
105 127 (1..(problem.num_tests)).each do |test_num|
106 128
107 129 $stdout.print "[#{test_num}]"
108 130 $stdout.flush
109 131
110 132 log "Test number: #{test_num}"
111 - execute("cp #{test_result_dir}/a.out #{sandbox_dir}", "Cannot copy the compiled program into #{sandbox_dir}")
133 + call_and_log("Cannot copy the compiled program into #{sandbox_dir}") {
134 + FileUtils.cp("#{test_result_dir}/a.out", sandbox_dir)
135 + }
112 136 begin
113 137 execute("#{problem_home}/script/run #{language} #{test_num}", "Error occured during execution of the run script")
114 138 rescue
115 139 # do nothing
116 140 end
117 - execute("mkdir #{test_result_dir}/#{test_num}", "Cannot create directory #{test_result_dir}/#{test_num}")
118 - execute("mv #{sandbox_dir}/result #{test_result_dir}/#{test_num}", "Cannot copy the result file into #{test_result_dir}/#{test_num}")
119 - execute("mv #{sandbox_dir}/comment #{test_result_dir}/#{test_num}", "Cannot copy the comment file into #{test_result_dir}/#{test_num}")
120 - execute("mv #{sandbox_dir}/output.txt #{test_result_dir}/#{test_num}", "Cannot copy the output file into #{test_result_dir}/#{test_num}")
121 - execute("rm -Rf #{sandbox_dir}/*", "Cannot clear #{sandbox_dir}")
141 + call_and_log("Cannot create directory #{test_result_dir}/#{test_num}") {
142 + FileUtils.mkdir "#{test_result_dir}/#{test_num}"
143 + }
144 + call_and_log("Cannot copy the result file into #{test_result_dir}/#{test_num}") {
145 + FileUtils.mv "#{sandbox_dir}/result", "#{test_result_dir}/#{test_num}"
146 + }
147 + call_and_log("Cannot copy the comment file into #{test_result_dir}/#{test_num}") {
148 + FileUtils.mv "#{sandbox_dir}/comment", "#{test_result_dir}/#{test_num}"
149 + }
150 + call_and_log("Cannot copy the output file into #{test_result_dir}/#{test_num}") {
151 + FileUtils.mv "#{sandbox_dir}/output.txt", "#{test_result_dir}/#{test_num}"
152 + }
153 + call_and_log("Cannot clear #{sandbox_dir}") {
154 + FileUtils.rm_rf(Dir.glob("#{sandbox_dir}/*"), :secure => true)
155 + }
122 156 end
123 157
124 158 $stdout.print "[done]\n"
125 159
126 160 # Grade
127 161 log
128 162 log "Grading..."
129 163 begin
130 164 Dir.chdir test_result_dir
131 165 rescue
132 166 log "ERROR: Cannot change directory to #{test_result_dir}."
133 167 exit(127)
134 168 end
135 169 execute("#{problem_home}/script/grade", "An error occured during grading!")
136 170
137 171 log
138 172 log "All done!"
@@ -1,155 +1,155
1 1 #!/usr/bin/ruby
2 2
3 3 def log(str='')
4 4 if ENV['TALKATIVE']!=nil
5 5 puts str
6 6 end
7 7 if ENV['GRADER_LOGGING']!=nil
8 8 log_fname = ENV['GRADER_LOGGING']
9 9 fp = File.open(log_fname,"a")
10 10 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
11 11 fp.close
12 12 end
13 13 end
14 14
15 15 def extract_time(t)
16 16 # puts "TIME: #{t}"
17 17 if (result=/^(.*)r(.*)u(.*)s/.match(t))
18 18 {:real => result[1], :user => result[2], :sys => result[3]}
19 19 else
20 20 #{:real => 0, :user => 0, :sys => 0}
21 21 #puts "ERROR READING RUNNING TIME: #{t}"
22 22 raise "Error reading running time: #{t}"
23 23 end
24 24 end
25 25
26 26 def compile_box(source,bin)
27 27 system("g++ #{source} -o #{bin}")
28 28 end
29 29
30 30 if ARGV.length < 2 || ARGV.length > 3
31 31 puts "Usage: run <language> <test-num> [<program-name>]"
32 32 exit(127)
33 33 end
34 34
35 35 language = ARGV[0]
36 36 test_num = ARGV[1].to_i
37 37 if ARGV.length > 2
38 38 program_name = ARGV[2]
39 39 else
40 40 program_name = "a.out"
41 41 end
42 42
43 43 problem_home = ENV['PROBLEM_HOME']
44 44 require "#{problem_home}/script/test_dsl.rb"
45 45 load "#{problem_home}/test_cases/all_tests.cfg"
46 46 problem = Problem.get_instance
47 47
48 48 if problem.well_formed? == false
49 49 log "The problem specification is not well formed."
50 50 exit(127)
51 51 end
52 52
53 53 # Check if the test number is okay.
54 54 if test_num <= 0 || test_num > problem.num_tests
55 55 log "You have specified a wrong test number."
56 56 exit(127)
57 57 end
58 58
59 59 #####################################
60 60 # Set the relavant file names here. #
61 61 #####################################
62 62
63 63 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
64 64
65 65 #####################################
66 66
67 67 time_limit = problem.get_time_limit test_num
68 68 mem_limit = problem.get_mem_limit(test_num) * 1024
69 69
70 70 # Copy the input file.
71 71 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
72 72
73 73 # check if box is there, if not, compile it!
74 74 if !File.exists?("#{problem_home}/script/box")
75 75 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
76 76 compile_box("#{problem_home}/script/box.cc",
77 77 "#{problem_home}/script/box")
78 78 end
79 79
80 80 # Hide PROBLEM_HOME
81 81 ENV['PROBLEM_HOME'] = nil
82 82
83 83 # Run the program.
84 84 #run_command = "/usr/bin/time -f \"#{time_output_format}\" 2>run_result #{problem_home}/script/box_new -a 2 -f -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name}"
85 85 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name} 2>run_result"
86 86 log "Running test #{test_num}..."
87 87 log run_command
88 88 log
89 89 system(run_command)
90 90
91 91 # Restore PROBLEM_HOME
92 92 ENV['PROBLEM_HOME'] = problem_home
93 93
94 94 # Create the result file.
95 95 result_file = File.new("result", "w")
96 96 comment_file = File.new("comment", "w")
97 97
98 98 # Check if the program actually produced any output.
99 99 run_result_file = File.new("run_result", "r")
100 100 run_result = run_result_file.readlines
101 101 run_result_file.close
102 102
103 103 run_stat = run_result[run_result.length-1]
104 104 running_time = extract_time(run_stat)
105 105
106 106 report = lambda{ |status, points, comment|
107 107 result_file.write status.strip
108 108 result_file.write "\n"
109 109 result_file.write points.to_s.strip
110 110 result_file.write "\n"
111 111 result_file.write run_stat.strip
112 112 result_file.write "\n"
113 113 result_file.close
114 - `rm run_result`
114 + FileUtils.rm "run_result"
115 115 # `rm output.txt` --- keep the output
116 116
117 117 comment_file.write comment
118 118
119 119 # added for debuggin --- jittat
120 120 comment_file.write "--run-result--\n"
121 121 run_result.each do |l|
122 122 comment_file.write l
123 123 end
124 124
125 125 comment_file.close
126 126
127 127 log "Done!"
128 128 exit(0)
129 129 }
130 130
131 131 if run_result[0][0,2] != "OK"
132 132 log "There was a runtime error."
133 133 report.call(run_result[0], 0, "No comment.\n")
134 134 end
135 135
136 136 if running_time[:user].to_f + running_time[:sys].to_f > time_limit
137 137 log "Time limit exceeded."
138 138 report.call("Time limit exceeded", 0, "No comment.\n")
139 139 end
140 140
141 141 # Run 'check' to evaluate the output.
142 142 #puts "There was no runtime error. Proceed to checking the output."
143 143 check_command = "#{problem_home}/script/check #{language} #{test_num}"
144 144 log "Checking the output..."
145 145 log check_command
146 146 if not system(check_command)
147 147 log "Problem with check script"
148 148 report.call("Incorrect",0,"Check script error.\n")
149 149 exit(127)
150 150 end
151 151
152 152 check_file = File.new("check_result", "r")
153 153 check_file_lines = check_file.readlines
154 154
155 155 report.call(check_file_lines[0], check_file_lines[1], "No comment.\n")
@@ -1,30 +1,31
1 + require 'fileutils'
2 +
1 3 module GraderEngineHelperMethods
2 4
3 5 def clear_sandbox
4 6 config = Grader::Configuration.get_instance
5 - clear_cmd = "rm -rf #{config.test_sandbox_dir}/*"
6 - system(clear_cmd)
7 + FileUtils.rm_rf(Dir.glob("#{config.test_sandbox_dir}/*"),
8 + :secure => true)
7 9 end
8 10
9 11 def init_sandbox
10 12 config = Grader::Configuration.get_instance
11 13 clear_sandbox
12 14 FileUtils.mkdir_p config.user_result_dir
13 - cp_cmd = "cp -R #{config.test_data_dir}/ev #{config.test_sandbox_dir}"
14 - system(cp_cmd)
15 + FileUtils.cp_r("#{config.test_data_dir}/ev", "#{config.test_sandbox_dir}")
15 16 end
16 17
17 18 def create_submission_from_file(id, user, problem,
18 19 source_fname, language=nil)
19 20
20 21 language = stub(Language, :name => 'c', :ext => 'c') if language==nil
21 22
22 23 config = Grader::Configuration.get_instance
23 24 source = File.open(config.test_data_dir + "/" + source_fname).read
24 25 stub(Submission,
25 26 :id => id, :user => user, :problem => problem,
26 27 :source => source, :language => language)
27 28 end
28 29
29 30 end
30 31
You need to be logged in to leave comments. Login now