Description:
autonew skips submissions with bad problems, forbids getch, better compiler msg when preprocessing fails, reports compile error when the executable is too large (100MB)
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r132:ca6ce454fda9 - - 2 files changed: 18 inserted, 4 deleted

@@ -1,440 +1,443
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 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', 'test_request', 'prob', 'sub', 'contest', and 'autonew'
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','autonew'].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','autonew'].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 after_save_hook(submission, grading_result)
133 133 end
134 134
135 135 def save(submission, grading_result)
136 136 user = submission.user
137 137 problem = submission.problem
138 138 if not @problems.has_key? problem.id
139 139 @problems[problem.id] = problem
140 140 end
141 141 if not @users.has_key? user.id
142 142 @users[user.id] = user
143 143 end
144 144 @results[[user.id, problem.id]] = grading_result
145 145
146 146 after_save_hook(submission, grading_result)
147 147 end
148 148
149 149 def print_report_by_user
150 150 puts "---------------------"
151 151 puts " REPORT"
152 152 puts "---------------------"
153 153
154 154 print "login,email"
155 155 @problems.each_value do |problem|
156 156 print ",#{problem.name}"
157 157 end
158 158 print "\n"
159 159
160 160 @users.each_value do |user|
161 161 print "#{user.login},#{user.email}"
162 162 @problems.each_value do |problem|
163 163 if @results.has_key? [user.id, problem.id]
164 164 print ",#{@results[[user.id,problem.id]][:points]}"
165 165 else
166 166 print ","
167 167 end
168 168 end
169 169 print "\n"
170 170 end
171 171 end
172 172 end
173 173
174 174 def grader_general_loop(engine, grader_proc, options)
175 175 runner = Grader::Runner.new(engine, grader_proc)
176 176 while true
177 177
178 178 if check_stopfile # created by calling grader stop
179 179 clear_stopfile
180 180 log "stopped (with stop file)"
181 181 break
182 182 end
183 183
184 184 task = yield(runner)
185 185
186 186 if task==nil
187 187 sleep(1)
188 188 end
189 189 end
190 190 end
191 191
192 192 def grader_queue_loop(grader_proc, options)
193 193 log "Grader: queue"
194 194 engine = Grader::Engine.new
195 195 grader_general_loop(engine, grader_proc, options) do |runner|
196 196 runner.grade_oldest_task
197 197 end
198 198 end
199 199
200 200 def grader_test_request_loop(grader_proc, options)
201 201 log "Grader: test_request"
202 202 engine = Grader::Engine.new(:room_maker => Grader::TestRequestRoomMaker.new,
203 203 :reporter => Grader::TestRequestReporter.new)
204 204 grader_general_loop(engine, grader_proc, options) do |runner|
205 205 runner.grade_oldest_test_request
206 206 end
207 207 end
208 208
209 209 def grader_autonew_loop(grader_proc, options)
210 210 log "Grader: autonew"
211 211
212 212 if options[:report]
213 213 result_collector = ResultCollector.new
214 214 else
215 215 result_collector = nil
216 216 end
217 217
218 218 if options[:dry_run]
219 219 puts "Running in dry mode"
220 220 end
221 221
222 222 prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run],
223 223 :result_collector => result_collector)
224 224
225 225 engine = Grader::Engine.new(:reporter => prob_reporter)
226 226 runner = Grader::Runner.new(engine, grader_proc)
227 227
228 228 grader_proc.report_active if grader_proc!=nil
229 229
230 230 latest_submitted_at = nil
231 231 graded_submission_ids = {}
232 232
233 233 while true
234 234
235 235 if check_stopfile # created by calling grader stop
236 236 clear_stopfile
237 237 log "stopped (with stop file)"
238 238 break
239 239 end
240 240
241 241 if latest_submitted_at==nil
242 242 submissions = Submission.all
243 243 else
244 244 submissions = Submission.all(:conditions => ["submitted_at >= :latest",
245 245 {:latest => latest_submitted_at}])
246 246 end
247 247
248 248 graded_any = false
249 249
250 250 if submissions.length != 0
251 251 submissions.each do |submission|
252 + if (submission.problem == nil) or (!submission.problem.available)
253 + next
254 + end
252 255 if ! graded_submission_ids[submission.id]
253 256 runner.grade_submission(submission)
254 257 graded_submission_ids[submission.id] = true
255 258 if (!latest_submitted_at or
256 259 latest_submitted_at < submission.submitted_at)
257 260 latest_submitted_at = submission.submitted_at
258 261 end
259 262 puts "graded: #{submission.id}"
260 263 puts "latest: #{latest_submitted_at}"
261 264 graded_any = true
262 265 end
263 266 end
264 267 end
265 268
266 269 if ! graded_any
267 270 sleep(1)
268 271 end
269 272 end
270 273 end
271 274
272 275 def grader_grade_problems(grader_proc, options)
273 276 if options[:report]
274 277 result_collector = ResultCollector.new
275 278 else
276 279 result_collector = nil
277 280 end
278 281
279 282 if options[:dry_run]
280 283 puts "Running in dry mode"
281 284 end
282 285
283 286 prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run],
284 287 :result_collector => result_collector)
285 288 engine = Grader::Engine.new(:reporter => prob_reporter)
286 289 runner = Grader::Runner.new(engine, grader_proc)
287 290
288 291 grader_proc.report_active if grader_proc!=nil
289 292
290 293 ARGV.each do |prob_name|
291 294 prob = Problem.find_by_name(prob_name)
292 295 if prob==nil
293 296 puts "cannot find problem: #{prob_name}"
294 297 else
295 298 runner.grade_problem(prob)
296 299 end
297 300 end
298 301
299 302 if options[:report]
300 303 result_collector.print_report_by_user
301 304 end
302 305 end
303 306
304 307 def grader_grade_contests(grader_proc, options)
305 308 # always use dry run when grading during contest
306 309 dry_run = options[:dry_run] = true
307 310
308 311 contest_name = ARGV.shift
309 312
310 313 contest = Contest.find_by_name(contest_name)
311 314 if contest==nil
312 315 puts "cannot find contest: #{contest_name}"
313 316 exit(0)
314 317 end
315 318
316 319 if options[:report]
317 320 result_collector = ResultCollector.new
318 321 else
319 322 result_collector = nil
320 323 end
321 324
322 325 if options[:dry_run]
323 326 puts "Running in dry mode"
324 327 end
325 328
326 329 prob_reporter = Grader::SubmissionReporter.new(:dry_run => dry_run,
327 330 :result_collector => result_collector)
328 331 engine = Grader::Engine.new(:reporter => prob_reporter)
329 332 runner = Grader::Runner.new(engine, grader_proc)
330 333
331 334 grader_proc.report_active if grader_proc!=nil
332 335
333 336 contest.problems.each do |problem|
334 337 puts "Grading: #{problem.name}"
335 338 runner.grade_problem(problem,
336 339 :user_conditions => lambda do |u|
337 340 u.contest_finished? and
338 341 u.contest_ids.include?(contest.id)
339 342 end)
340 343 end
341 344
342 345 if options[:report]
343 346 result_collector.print_report_by_user
344 347 end
345 348 end
346 349
347 350 def grader_grade_submissions(grader_proc, options)
348 351 engine = Grader::Engine.new
349 352 runner = Grader::Runner.new(engine, grader_proc)
350 353
351 354 grader_proc.report_active if grader_proc!=nil
352 355
353 356 ARGV.each do |sub_id|
354 357 puts "Grading #{sub_id}"
355 358 begin
356 359 submission = Submission.find(sub_id.to_i)
357 360 rescue ActiveRecord::RecordNotFound
358 361 puts "Record not found"
359 362 submission = nil
360 363 end
361 364
362 365 if submission!=nil
363 366 runner.grade_submission(submission)
364 367 end
365 368 end
366 369 end
367 370
368 371 #########################################
369 372 # main program
370 373 #########################################
371 374
372 375 options = process_options_and_stop_file
373 376 GRADER_ENV = options[:environment]
374 377 grader_mode = options[:mode]
375 378 dry_run = options[:dry_run]
376 379
377 380 puts "environment: #{GRADER_ENV}"
378 381 require File.join(File.dirname(__FILE__),'config/environment')
379 382
380 383 # add grader_mode to config
381 384 # this is needed because method log needs it. TODO: clean this up
382 385 class << config
383 386 attr_accessor :grader_mode
384 387 end
385 388 config.grader_mode = grader_mode
386 389
387 390 # reading rails environment
388 391 log 'Reading rails environment'
389 392
390 393 RAILS_ENV = config.rails_env
391 394 require RAILS_ROOT + '/config/environment'
392 395
393 396 # register grader process
394 397 if config.report_grader
395 398 grader_proc = GraderProcess.register(config.grader_hostname,
396 399 Process.pid,
397 400 grader_mode)
398 401 else
399 402 grader_proc = nil
400 403 end
401 404
402 405 #set loggin environment
403 406 ENV['GRADER_LOGGING'] = log_file_name
404 407
405 408 # register exit handler to report inactive, and terminated
406 409 at_exit do
407 410 if grader_proc!=nil
408 411 grader_proc.report_inactive
409 412 grader_proc.terminate
410 413 end
411 414 end
412 415
413 416 #
414 417 # MAIN LOOP
415 418 #
416 419
417 420 case grader_mode
418 421 when "queue"
419 422 grader_queue_loop(grader_proc, options)
420 423
421 424 when "test_request"
422 425 grader_test_request_loop(grader_proc, options)
423 426
424 427 when "prob"
425 428 grader_grade_problems(grader_proc, options)
426 429
427 430 when "contest"
428 431 grader_grade_contests(grader_proc, options)
429 432
430 433 when "sub"
431 434 grader_grade_submissions(grader_proc, options)
432 435
433 436 when "autonew"
434 437 grader_autonew_loop(grader_proc, options)
435 438
436 439 else
437 440 display_manual
438 441 exit(0)
439 442 end
440 443
@@ -1,130 +1,141
1 1 #!/usr/bin/ruby
2 2
3 3 require 'fileutils'
4 4
5 5 ##############################
6 6 #
7 7 # Standard Compile Script
8 8 #
9 9 # Supported compilers:
10 10 # gcc, g++, and fpc.
11 11 #
12 12 ##############################
13 13
14 14 def talk(msg)
15 15 if ENV['TALKATIVE']!=nil
16 16 puts str
17 17 end
18 18
19 19 if ENV['GRADER_LOGGING']!=nil
20 20 log_fname = ENV['GRADER_LOGGING']
21 21 fp = File.open(log_fname,"a")
22 22 fp.puts("run: #{Time.new.strftime("%H:%M")} #{msg}")
23 23 fp.close
24 24 end
25 25 end
26 26
27 27 C_COMPILER = "gcc"
28 28 CPLUSPLUS_COMPILER = "g++"
29 29 PASCAL_COMPILER = "fpc"
30 30
31 31 C_OPTIONS = "-O2 -s -static -std=c99 -DCONTEST -lm -Wall"
32 32 CPLUSPLUS_OPTIONS = "-O2 -s -static -DCONTEST -lm -Wall"
33 33 PASCAL_OPTIONS = "-O1 -XS -dCONTEST"
34 34
35 35 # Check for the correct number of arguments. Otherwise, print usage.
36 36 if ARGV.length == 0 or ARGV.length > 4
37 37 puts "Usage: compile <language> [<source-file>] [<output-file>] [<message-file>]"
38 38 puts
39 39 puts "<source-file> is defaulted to \"source\"."
40 40 puts "<output-file> is defaulted to \"a.out\"."
41 41 puts "<message-file> is defaulted to \"compiler_message\"."
42 42 puts
43 43 exit(127)
44 44 end
45 45
46 46 PARAMS = {
47 47 :source_file => [1,'source'],
48 48 :output_file => [2,'a.out'],
49 49 :message_file => [3,'compiler_message']
50 50 }
51 51
52 52 params = {}
53 53 params[:prog_lang] = ARGV[0]
54 54 PARAMS.each_key do |param_name|
55 55 index, default = PARAMS[param_name]
56 56 if ARGV.length > index
57 57 params[param_name] = ARGV[index]
58 58 else
59 59 params[param_name] = default
60 60 end
61 61 talk "#{param_name}: #{params[param_name]}"
62 62 end
63 63
64 64 # Remove any remaining output files or message files.
65 65 if FileTest.exists? params[:output_file]
66 66 FileUtils.rm(params[:output_file])
67 67 end
68 68 if FileTest.exists? params[:message_file]
69 69 FileUtils.rm(params[:message_file])
70 70 end
71 71
72 72 # Check if the source file exists before attempt compiling.
73 73 if !FileTest.exists? params[:source_file]
74 74 talk("ERROR: The source file does not exist!")
75 75 open(params[:message_file],"w") do |f|
76 76 f.puts "ERROR: The source file did not exist."
77 77 end
78 78 exit(127)
79 79 end
80 80
81 81 if params[:prog_lang]=='cpp':
82 82 params[:prog_lang] = 'c++'
83 83 end
84 84
85 - forbidden_functions = ['system', 'fopen', 'freopen', 'open', 'remove', 'rename']
85 + forbidden_functions = ['system', 'fopen', 'freopen', 'open', 'remove', 'rename', 'getch']
86 86 redefine_list = []
87 87 forbidden_count = 0
88 88 forbidden_key = (rand(8999) + 1000).to_s
89 89 forbidden_functions.each do |f|
90 90 redefine_list << "-D#{f}=forbidden#{forbidden_key}#{forbidden_count}"
91 91 forbidden_count += 1
92 92 end
93 93 redefine_str = redefine_list.join ' '
94 94 puts redefine_str
95 95
96 96 # Compile.
97 97 case params[:prog_lang]
98 98
99 99 when "c"
100 - command = "#{C_COMPILER} -E #{params[:source_file]} -o source_prep.c"
100 + command = "#{C_COMPILER} -E #{params[:source_file]} -o source_prep.c 2> #{params[:message_file]}"
101 101 system(command)
102 + if FileTest.exist? "source_prep.c"
102 103 command = "#{C_COMPILER} source_prep.c #{redefine_str} -o #{params[:output_file]} #{C_OPTIONS} 2> #{params[:message_file]}"
103 - puts command
104 104 system(command)
105 + end
105 106
106 107 when "c++"
107 - command = "#{CPLUSPLUS_COMPILER} -E #{params[:source_file]} -o source_prep.cpp"
108 + command = "#{CPLUSPLUS_COMPILER} -E #{params[:source_file]} -o source_prep.cpp 2> #{params[:message_file]}"
108 109 system(command)
110 + if FileTest.exist? "source_prep.cpp"
109 111 command = "#{CPLUSPLUS_COMPILER} #{redefine_str} source_prep.cpp -o #{params[:output_file]} #{CPLUSPLUS_OPTIONS} 2> #{params[:message_file]}"
110 112 system(command)
113 + end
111 114
112 115 when "pas"
113 116 command = "#{PASCAL_COMPILER} #{params[:source_file]} -ooutpas #{PASCAL_OPTIONS} > #{params[:message_file]}"
114 117 system(command)
115 118 FileUtils.mv("output", params[:output_file])
116 119
117 120 else
118 121 talk("ERROR: Invalid language specified!")
119 122 open(params[:message_file],"w") do |f|
120 123 f.puts "ERROR: Invalid language specified!"
121 124 end
122 125 exit(127)
123 126 end
124 127
125 128 # Report success or failure.
126 129 if FileTest.exists? params[:output_file]
127 130 talk "Compilation was successful!"
131 +
132 + if File.size(params[:output_file]) > 100000000
133 + talk "But the executable is too big."
134 + open(params[:message_file],"w+") do |f|
135 + f.puts "Executable is too large."
136 + end
137 + File.delete(params[:output_file])
138 + end
128 139 else
129 140 talk "ERROR: Something was wrong during the compilation!"
130 141 end
You need to be logged in to leave comments. Login now