Description:
big merge with dae
Commit status:
[Not Reviewed]
References:
merge default
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r189:45a8ea0792f3 - - 12 files changed: 267 inserted, 31 deleted

@@ -0,0 +1,66
1 + #!/usr/bin/env ruby
2 +
3 + problem_home = ENV['PROBLEM_HOME']
4 + require "#{problem_home}/script/test_dsl.rb"
5 +
6 + if ARGV.length < 2
7 + puts "Usage: check <language> <test-number> [<output-file>]"
8 + exit(0)
9 + end
10 +
11 + language = ARGV[0]
12 + test_num = ARGV[1].to_i
13 + if ARGV.length >= 3
14 + output_file_name = ARGV[2]
15 + else
16 + output_file_name = "output.txt"
17 + end
18 +
19 + load "#{problem_home}/test_cases/all_tests.cfg"
20 + problem = Problem.get_instance
21 +
22 + output_file = File.new(output_file_name, "r")
23 + answer_file = File.new("#{problem_home}/test_cases/#{test_num}/answer-#{test_num}.txt")
24 + result_file = File.new("check_result", "w")
25 +
26 + output_file_content = output_file.read
27 + answer_file_content = answer_file.read
28 +
29 + report_correct = lambda {
30 + result_file.write "Correct\n"
31 + result_file.write problem.get_score(test_num)
32 + result_file.write "\n"
33 + result_file.close
34 + exit(0)
35 + }
36 +
37 + report_wrong = lambda {
38 + result_file.write "Incorrect\n"
39 + result_file.write "0\n"
40 + result_file.close
41 + exit(0)
42 + }
43 +
44 + ##################
45 + # Your code here #
46 + ##################
47 +
48 + ########### THIS IS FOR CHECKING FLOAT with EPSILON error ##########
49 +
50 + EPSILON = 0.000001
51 +
52 + out_items = output_file_content.split
53 + ans_items = answer_file_content.split
54 +
55 + if out_items.length != ans_items.length
56 + report_wrong.call
57 + else
58 + out_items.length.times do |i|
59 + out_value = out_items[i].to_f
60 + ans_value = ans_items[i].to_f
61 + if (out_value - ans_value).abs > EPSILON * [out_value.abs,ans_value.abs].max
62 + report_wrong.call
63 + end
64 + end
65 + report_correct.call
66 + end
@@ -36,53 +36,56
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] [options]
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 queue: repeatedly check the task queue and grade any available tasks
55 55
56 56 prob: re-grade every user latest submission of the specific problem.
57 57 the problem name must be specified by the next argument.
58 58
59 59 additional options:
60 + --all-sub re-grade every submissions instead of just the latest submission of each user.
60 61
61 - --all-sub re-grade every submissions instead of just the latest submission of each user.
62 62 sub: re-grader the specified submission.
63 63 The submission ID to be re-graded must be specified by the next argument.
64 64
65 + options:
66 + --err-log log error to a file in the log dir
67 +
65 68 (3) create stop-file to stop running grader in queue mode
66 69 (4) You are here.
67 70 USAGE
68 71 end
69 72
70 73 def process_options_and_stop_file
71 74 # The list of options are:
72 75 # - stop [all|process ids]
73 76 # -
74 77
75 78 # Process 'help' option
76 79 if (ARGV.length==1) and (/help/.match(ARGV[0]))
77 80 display_manual
78 81 exit(0)
79 82 end
80 83
81 84 # Process 'stop' option.
82 85 if (ARGV.length >= 1) and (ARGV[0]=='stop')
83 86 if ARGV.length==1
84 87 puts "you should specify pid-list or 'all'"
85 88 display_manual
86 89 elsif (ARGV.length==2) and (ARGV[1]=='all')
87 90 stop_grader(:all)
88 91 puts "A global stop file ('stop.all') created."
@@ -113,48 +116,50
113 116 # Process mode and environment option
114 117 if ARGV.length >= 1
115 118 options[:environment] = ARGV.shift
116 119 if ARGV.length >=1
117 120 options[:mode] = ARGV.shift
118 121 end
119 122 else
120 123 puts 'no argument specified, using default mode and environment.'
121 124 end
122 125
123 126 options[:dry_run] = (ARGV.delete('--dry') != nil)
124 127 if options[:dry_run] and (not ['prob','contest','autonew'].include? options[:mode])
125 128 puts "Dry run currently works only for 'prob' or 'contest' modes."
126 129 exit(0)
127 130 end
128 131
129 132 options[:report] = (ARGV.delete('--report') != nil)
130 133 if options[:report] and (not ['prob','contest','autonew'].include? options[:mode])
131 134 puts "Report currently works only for 'prob' or 'contest' modes."
132 135 exit(0)
133 136 end
134 137
135 138 options[:all_sub] = (ARGV.delete('--all-sub') != nil)
136 139
140 + options[:err_log] = (ARGV.delete('--err-log') != nil)
141 +
137 142 return options
138 143 end
139 144
140 145 class ResultCollector
141 146 def initialize
142 147 @results = {}
143 148 @problems = {}
144 149 @users = {}
145 150 end
146 151
147 152 def after_save_hook(submission, grading_result)
148 153 end
149 154
150 155 def save(submission, grading_result)
151 156 user = submission.user
152 157 problem = submission.problem
153 158 if not @problems.has_key? problem.id
154 159 @problems[problem.id] = problem
155 160 end
156 161 if not @users.has_key? user.id
157 162 @users[user.id] = user
158 163 end
159 164 @results[[user.id, problem.id]] = grading_result
160 165
@@ -399,48 +404,55
399 404 # add grader_mode to config
400 405 # this is needed because method log needs it. TODO: clean this up
401 406 class << config
402 407 attr_accessor :grader_mode
403 408 end
404 409 config.grader_mode = grader_mode
405 410
406 411 # reading rails environment
407 412 log 'Reading rails environment'
408 413
409 414 RAILS_ENV = config.rails_env
410 415 require RAILS_ROOT + '/config/environment'
411 416
412 417 # register grader process
413 418 if config.report_grader
414 419 grader_proc = GraderProcess.register(config.grader_hostname,
415 420 Process.pid,
416 421 grader_mode)
417 422 else
418 423 grader_proc = nil
419 424 end
420 425
421 426 #set loggin environment
422 427 ENV['GRADER_LOGGING'] = log_file_name
428 + if options[:err_log]
429 + err_file_name = log_file_name + '.err'
430 + $stderr.reopen(err_file_name,"a")
431 + log "STDERR log to file [#{err_file_name}]"
432 + warn "start logging for grader PID #{Process.pid} on #{Time.now.in_time_zone}"
433 + end
434 +
423 435
424 436 # register exit handler to report inactive, and terminated
425 437 at_exit do
426 438 if grader_proc!=nil
427 439 grader_proc.report_inactive
428 440 grader_proc.terminate
429 441 end
430 442 end
431 443
432 444 #
433 445 # MAIN LOOP
434 446 #
435 447
436 448 case grader_mode
437 449 when "queue"
438 450 grader_queue_loop(grader_proc, options)
439 451
440 452 when "test_request"
441 453 grader_test_request_loop(grader_proc, options)
442 454
443 455 when "prob"
444 456 grader_grade_problems(grader_proc, options)
445 457
446 458 when "contest"
@@ -97,50 +97,52
97 97 rescue RuntimeError => msg
98 98 @reporter.report_error(submission, msg)
99 99 puts "ERROR: #{msg}"
100 100
101 101 ensure
102 102 @room_maker.clean_up(submission)
103 103 Dir.chdir(current_dir) # this is really important
104 104 end
105 105 end
106 106
107 107 protected
108 108
109 109 def talk(str)
110 110 if @config.talkative
111 111 puts str
112 112 end
113 113 end
114 114
115 115 def call_judge(problem_home,language,grading_dir,fname)
116 116 ENV['PROBLEM_HOME'] = problem_home
117 117 ENV['RUBYOPT'] = ''
118 118
119 119 talk grading_dir
120 120 Dir.chdir grading_dir
121 - cmd = "#{problem_home}/script/judge #{language} #{fname}"
121 + script_name = "#{problem_home}/script/judge"
122 + cmd = "#{script_name} #{language} #{fname}"
122 123 talk "CMD: #{cmd}"
124 + warn "ERROR: file does not exists #{script_name}" unless File.exists? script_name
123 125 system(cmd)
124 126 end
125 127
126 128 def get_std_script_dir
127 129 GRADER_ROOT + '/std-script'
128 130 end
129 131
130 132 def copy_script(problem_home)
131 133 script_dir = "#{problem_home}/script"
132 134 std_script_dir = get_std_script_dir
133 135
134 136 raise "engine: std-script directory not found" if !FileTest.exist?(std_script_dir)
135 137
136 138 scripts = Dir[std_script_dir + '/*']
137 139
138 140 copied = []
139 141
140 142 scripts.each do |s|
141 143 fname = File.basename(s)
142 144 next if FileTest.directory?(s)
143 145 if !FileTest.exist?("#{script_dir}/#{fname}")
144 146 copied << fname
145 147 FileUtils.cp(s, "#{script_dir}", :preserve => true)
146 148 end
@@ -24,44 +24,44
24 24 return task
25 25 end
26 26
27 27 def grade_problem(problem, options={})
28 28 User.find_each do |u|
29 29 puts "user: #{u.login}"
30 30 if options[:user_conditions]!=nil
31 31 con_proc = options[:user_conditions]
32 32 next if not con_proc.call(u)
33 33 end
34 34 if options[:all_sub]
35 35 Submission.where(user_id: u.id,problem_id: problem.id).find_each do |sub|
36 36 @engine.grade(sub)
37 37 end
38 38 else
39 39 last_sub = Submission.find_last_by_user_and_problem(u.id,problem.id)
40 40 if last_sub!=nil
41 41 @engine.grade(last_sub)
42 42 end
43 43 end
44 44 end
45 45 end
46 46
47 47 def grade_submission(submission)
48 - puts "Submission: #{submission.id} by #{submission.user.full_name}"
48 + puts "Submission: #{submission.id} by #{submission.try(:user).try(:full_name)}"
49 49 @engine.grade(submission)
50 50 end
51 51
52 52 def grade_oldest_test_request
53 53 test_request = TestRequest.get_inqueue_and_change_status(Task::STATUS_GRADING)
54 54 if test_request!=nil
55 55 @grader_process.report_active(test_request) if @grader_process!=nil
56 56
57 57 @engine.grade(test_request)
58 58 test_request.status_complete!
59 59 @grader_process.report_inactive(test_request) if @grader_process!=nil
60 60 end
61 61 return test_request
62 62 end
63 63
64 64 end
65 65
66 66 end
67 67
@@ -44,91 +44,109
44 44 if @result_collector
45 45 @result_collector.save(sub,
46 46 result)
47 47 end
48 48 save_result(sub,result)
49 49 end
50 50
51 51 def report_error(sub,msg)
52 52 save_result(sub,{:points => 0,
53 53 :comment => "Grading error: #{msg}" })
54 54 end
55 55
56 56 protected
57 57 def read_result(test_result_dir)
58 58 cmp_msg_fname = "#{test_result_dir}/compiler_message"
59 59 if FileTest.exist?(cmp_msg_fname)
60 60 cmp_file = File.open(cmp_msg_fname)
61 61 cmp_msg = cmp_file.read
62 62 cmp_file.close
63 63 else
64 64 cmp_msg = ""
65 65 end
66 66
67 67 result_fname = "#{test_result_dir}/result"
68 - comment_fname = "#{test_result_dir}/comment"
68 + comment_fname = "#{test_result_dir}/comment"
69 + runstat_fname = "#{test_result_dir}/run_stat"
69 70 if FileTest.exist?(result_fname)
70 71 comment = ""
71 72 begin
72 73 result_file = File.open(result_fname)
73 74 result = result_file.readline.to_i
74 75 result_file.close
75 76 rescue
76 77 result = 0
77 78 comment = "error reading result file."
78 79 end
79 80
80 81 begin
81 82 comment_file = File.open(comment_fname)
82 83 comment += comment_file.readline.chomp
83 84 comment_file.close
84 85 rescue
85 86 comment += ""
86 87 end
87 88
88 - return {:points => result,
89 - :comment => comment,
90 - :cmp_msg => cmp_msg}
89 + begin
90 + runstat_file = File.open(runstat_fname)
91 + max_runtime = runstat_file.readline.to_f
92 + peak_memory = runstat_file.readline.to_i
93 + rescue
94 + max_runtime = -1
95 + peak_memory = -1
96 + end
97 +
98 +
99 + return {points: result,
100 + comment: comment,
101 + cmp_msg: cmp_msg,
102 + max_runtime: max_runtime,
103 + peak_memory: peak_memory
104 + }
91 105 else
92 106 if FileTest.exist?("#{test_result_dir}/a.out")
93 107 return {:points => 0,
94 108 :comment => 'error during grading',
95 109 :cmp_msg => cmp_msg}
96 110 else
97 111 return {:points => 0,
98 112 :comment => 'compilation error',
99 113 :cmp_msg => cmp_msg}
100 114 end
101 115 end
102 116 end
103 117
104 118 def save_result(submission,result)
105 119 problem = submission.problem
106 120 submission.graded_at = Time.now.gmtime
107 121 points = result[:points]
108 122 submission.points = points
109 123 comment = @config.report_comment(result[:comment])
110 124
125 + submission.peak_memory = result[:peak_memory]
126 + submission.max_runtime = result[:max_runtime]
127 + submission.effective_code_length =submission.source.length
128 +
111 129 #
112 130 # TODO: FIX THIS MESSAGE
113 131 #
114 132 if problem == nil
115 133 submission.grader_comment = 'PASSED: ' + comment + '(problem is nil)'
116 134 elsif points == problem.full_score
117 135 #submission.grader_comment = 'PASSED: ' + comment
118 136 submission.grader_comment = comment
119 137 elsif result[:comment].chomp =~ /^[\[\]P]+$/
120 138 submission.grader_comment = 'PASSED: ' + comment + '(inconsistent score)'
121 139 else
122 140 #submission.grader_comment = 'FAILED: ' + comment
123 141 submission.grader_comment = comment
124 142 end
125 143 submission.compiler_message = result[:cmp_msg] or ''
126 144
127 145 if not @dry_run
128 146 submission.save
129 147 end
130 148 end
131 149
132 150 end
133 151
134 152 end
@@ -177,49 +177,49
177 177 end
178 178 end
179 179
180 180 def extract_running_stat(results)
181 181 running_stat_line = results[-1]
182 182
183 183 # extract exit status line
184 184 run_stat = ""
185 185 if !(/[Cc]orrect/.match(results[0]))
186 186 run_stat = results[0].chomp
187 187 else
188 188 run_stat = 'Program exited normally'
189 189 end
190 190
191 191 # extract running time
192 192 if res = /r(.*)u(.*)s/.match(running_stat_line)
193 193 seconds = (res[1].to_f + res[2].to_f)
194 194 time_stat = "Time used: #{seconds} sec."
195 195 else
196 196 seconds = nil
197 197 time_stat = "Time used: n/a sec."
198 198 end
199 199
200 200 # extract memory usage
201 - if res = /s(.*)m/.match(running_stat_line)
201 + if res = /s(.*)kbytes/.match(running_stat_line)
202 202 memory_used = res[1].to_i
203 203 else
204 204 memory_used = -1
205 205 end
206 206
207 207 return {
208 208 :msg => "#{run_stat}\n#{time_stat}",
209 209 :running_time => seconds,
210 210 :exit_status => run_stat,
211 211 :memory_usage => memory_used
212 212 }
213 213 end
214 214
215 215 def save_result(test_request,result)
216 216 if result[:output_file_name]!=nil
217 217 test_request.output_file_name = link_output_file(test_request,
218 218 result[:output_file_name])
219 219 end
220 220 test_request.graded_at = Time.now
221 221 test_request.compiler_message = (result[:cmp_msg] or '')
222 222 test_request.grader_comment = (result[:comment] or '')
223 223 if result[:running_stat]!=nil
224 224 test_request.running_stat = (result[:running_stat][:msg] or '')
225 225 test_request.running_time = (result[:running_stat][:running_time] or nil)
@@ -5,52 +5,60
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(str='')
15 15 if ENV['TALKATIVE']!=nil
16 16 puts str
17 17 end
18 18 if ENV['GRADER_LOGGING']!=nil
19 19 log_fname = ENV['GRADER_LOGGING']
20 20 fp = File.open(log_fname,"a")
21 21 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
22 22 fp.close
23 23 end
24 24 end
25 25
26 26 C_COMPILER = "/usr/bin/gcc"
27 27 CPLUSPLUS_COMPILER = "/usr/bin/g++"
28 28 PASCAL_COMPILER = "/usr/bin/fpc"
29 + JAVA_COMPILER = "/usr/bin/javac"
30 + RUBY_INTERPRETER = "/usr/bin/ruby"
31 + PYTHON_INTERPRETER = "/usr/bin/python"
32 + PYTHON_CHECKER = "/usr/bin/pyflakes"
33 + PHP_INTERPRETER = "/usr/bin/php"
29 34
30 35 C_OPTIONS = "-O2 -s -static -std=c99 -DCONTEST -lm -Wall"
31 - CPLUSPLUS_OPTIONS = "-O2 -s -static -DCONTEST -lm -Wall"
36 + CPLUSPLUS_OPTIONS = "-O2 -s -std=c++11 -static -DCONTEST -lm -Wall"
32 37 PASCAL_OPTIONS = "-O1 -XS -dCONTEST"
38 + JAVA_OPTIONS = ""
39 + PYTHON_OPTIONS = ""
40 + PHP_OPTIONS = "-l"
33 41
34 42 # Check for the correct number of arguments. Otherwise, print usage.
35 43 if ARGV.length == 0 or ARGV.length > 4
36 44 puts "Usage: compile <language> [<source-file>] [<output-file>] [<message-file>]"
37 45 puts
38 46 puts "<source-file> is defaulted to \"source\"."
39 47 puts "<output-file> is defaulted to \"a.out\"."
40 48 puts "<message-file> is defaulted to \"compiler_message\"."
41 49 puts
42 50 exit(127)
43 51 end
44 52
45 53 PARAMS = {
46 54 :source_file => [1,'source'],
47 55 :output_file => [2,'a.out'],
48 56 :message_file => [3,'compiler_message']
49 57 }
50 58
51 59 params = {}
52 60 params[:prog_lang] = ARGV[0]
53 61 PARAMS.each_key do |param_name|
54 62 index, default = PARAMS[param_name]
55 63 if ARGV.length > index
56 64 params[param_name] = ARGV[index]
@@ -63,51 +71,118
63 71 # Remove any remaining output files or message files.
64 72 if FileTest.exists? params[:output_file]
65 73 FileUtils.rm(params[:output_file])
66 74 end
67 75 if FileTest.exists? params[:message_file]
68 76 FileUtils.rm(params[:message_file])
69 77 end
70 78
71 79 # Check if the source file exists before attempt compiling.
72 80 if !FileTest.exists? params[:source_file]
73 81 talk("ERROR: The source file does not exist!")
74 82 open(params[:message_file],"w") do |f|
75 83 f.puts "ERROR: The source file did not exist."
76 84 end
77 85 exit(127)
78 86 end
79 87
80 88 if params[:prog_lang]=='cpp'
81 89 params[:prog_lang] = 'c++'
82 90 end
83 91
84 92 # Compile.
85 93 case params[:prog_lang]
86 94
87 - when "c"
88 - command = "#{C_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{C_OPTIONS} 2> #{params[:message_file]}"
89 - system(command)
95 + when "c"
96 + command = "#{C_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{C_OPTIONS}"
97 + system(command, err: params[:message_file])
98 +
99 + when "c++"
100 + command = "#{CPLUSPLUS_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{CPLUSPLUS_OPTIONS}"
101 + system(command, err: params[:message_file])
102 +
103 + when "pas"
104 + command = "#{PASCAL_COMPILER} #{params[:source_file]} -ooutpas #{PASCAL_OPTIONS}"
105 + system(command,out: params[:message_file])
106 + FileUtils.mv("output", params[:output_file])
107 +
108 + when "java"
109 + #rename the file to the public class name
110 +
111 + #get the class name
112 + classname = 'DUMMY'
113 + source = Array.new
114 + File.foreach(params[:source_file],'r:UTF-8') do |line|
115 + line.encode!('UTF-8','UTF-8',invalid: :replace, replace: '')
116 + md = /\s*public\s*class\s*(\w*)/.match(line)
117 + classname=md[1] if md
118 + source << line unless line =~ /\s*package\s*\w+\s*\;/
119 + end
120 + File.open("#{classname}.java","w") do |file|
121 + source.each do |s|
122 + file.puts s
123 + end
124 + end
125 + #system("cp #{params[:source_file]} #{classname}.java")
126 + command = "#{JAVA_COMPILER} -encoding utf8 #{classname}.java"
127 + system(command, err: params[:message_file])
128 + if File.exists?(classname + ".class")
129 + File.open(params[:output_file],"w") {|file| file.write("#{classname}")}
130 + end
131 + if classname == 'DUMMY'
132 + File.open(params[:message_file],"w") {|file| file.write("Cannot find any public class in the source code\n")}
133 + end
90 134
91 - when "c++"
92 - command = "#{CPLUSPLUS_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{CPLUSPLUS_OPTIONS} 2> #{params[:message_file]}"
93 - system(command)
94 -
95 - when "pas"
96 - command = "#{PASCAL_COMPILER} #{params[:source_file]} -ooutpas #{PASCAL_OPTIONS} > #{params[:message_file]}"
97 - system(command)
98 - FileUtils.mv("output", params[:output_file])
99 -
100 - else
135 + when "ruby"
136 + command = "#{RUBY_INTERPRETER} -c #{params[:source_file]}"
137 + if system(command, err: params[:message_file])
138 + File.open(params[:output_file],"w") do |out_file|
139 + out_file.puts "#!#{RUBY_INTERPRETER}"
140 + File.open(params[:source_file],"r").each do |line|
141 + out_file.print line
142 + end
143 + end
144 + File.chmod(0755, params[:output_file])
145 + end
146 +
147 + when "python"
148 + command = "#{PYTHON_CHECKER} #{params[:source_file]}"
149 + if system(command, out: params[:message_file])
150 + #compile to python bytecode
151 + command = "#{PYTHON_INTERPRETER} -m py_compile #{params[:source_file]}"
152 + puts "compile: #{command}"
153 + system(command)
154 + puts "pwd: " + Dir.pwd
155 + Dir.new('.').each {|file| puts file}
156 + File.open(params[:output_file],"w") do |out_file|
157 + out_file.puts "#!#{PYTHON_INTERPRETER} #{params[:source_file]}c"
158 + end
159 + File.chmod(0755, params[:output_file])
160 + FileUtils.cp("#{params[:source_file]}c",params[:output_file])
161 + end
162 +
163 + when "php"
164 + command = "#{PHP_INTERPRETER} #{PHP_OPTIONS} #{params[:source_file]}"
165 + if system(command, err: params[:message_file])
166 + File.open(params[:output_file],"w") do |out_file|
167 + out_file.puts "#!#{PHP_INTERPRETER}"
168 + File.open(params[:source_file],"r").each do |line|
169 + out_file.print line
170 + end
171 + end
172 + File.chmod(0755, params[:output_file])
173 + end
174 +
175 + else
101 176 talk("ERROR: Invalid language specified!")
102 177 open(params[:message_file],"w") do |f|
103 178 f.puts "ERROR: Invalid language specified!"
104 179 end
105 180 exit(127)
106 181 end
107 182
108 183 # Report success or failure.
109 184 if FileTest.exists? params[:output_file]
110 185 talk "Compilation was successful!"
111 186 else
112 187 talk "ERROR: Something was wrong during the compilation!"
113 188 end
@@ -10,97 +10,125
10 10 puts str
11 11 end
12 12 if ENV['GRADER_LOGGING']!=nil
13 13 log_fname = ENV['GRADER_LOGGING']
14 14 fp = File.open(log_fname,"a")
15 15 fp.puts("grade: #{Time.new.strftime("%H:%M")} #{str}")
16 16 fp.close
17 17 end
18 18 end
19 19
20 20 def char_comment(comment)
21 21 if comment =~ /[Ii]ncorrect/
22 22 INCORRECT_MARK
23 23 elsif comment =~ /[Cc]orrect/
24 24 CORRECT_MARK
25 25 elsif comment =~ /[Tt]ime/
26 26 TIMEOUT_MARK
27 27 elsif res = /^[Cc]omment:(.*)$/.match(comment)
28 28 res[1]
29 29 else
30 30 RUN_ERROR_MARK # these are run time errors
31 31 end
32 32 end
33 33
34 + def extract_time(t)
35 + #puts "TIME: #{t}"
36 + if (result=/^(.*)r(.*)u(.*)s(.*)kbytes/.match(t))
37 + {:real => result[1], :user => result[2], :sys => result[3], :mem => result[4]}
38 + else
39 + #{:real => 0, :user => 0, :sys => 0}
40 + #puts "ERROR READING RUNNING TIME: #{t}"
41 + raise "Error reading running time: #{t}"
42 + end
43 + end
44 +
34 45 problem_home = ENV['PROBLEM_HOME']
35 46 require "#{problem_home}/script/test_dsl.rb"
36 47 load "#{problem_home}/test_cases/all_tests.cfg"
37 48 problem = Problem.get_instance
38 49
39 50 if problem.well_formed? == false
40 51 log "The problem specification is not well formed."
41 52 exit(127)
42 53 end
43 54
44 55 all_score = 0
45 56 all_comment = ''
57 + peak_memory = -1
58 + max_runtime = -1
46 59 (1..(problem.runs.length-1)).each do |k|
47 60 log "grade run #{k}"
48 61 run = problem.runs[k]
49 62 run_score = nil
50 63 run_comment = ''
51 64 run_comment_short = ''
52 65 run.tests.each do |test_num|
53 66 result_file_name = "#{test_num}/result"
54 67 if not File.exists?(result_file_name)
55 68 run_comment += "result file for test #{test_num} not found\n"
56 69 run_comment_short += RUN_ERROR_MARK
57 70 log "Cannot find the file #{test_num}/result!"
58 71 else
59 72 result_file = File.new(result_file_name, "r")
60 73 result_file_lines = result_file.readlines
61 - if result_file_lines.length>=2
74 + if result_file_lines.length>=3
62 75 current_run_score = result_file_lines[1].to_i
63 76 run_comment += result_file_lines[0]
64 77 run_comment_short += char_comment(result_file_lines[0].chomp)
78 +
79 + #update max runtime & memory
80 + run_stat = extract_time result_file_lines[2]
81 + peak_memory = [peak_memory,run_stat[:mem].to_i].max
82 + max_runtime = [max_runtime,run_stat[:user].to_f + run_stat[:sys].to_f].max
65 83 else
66 84 current_run_score = 0
67 85 run_comment += "result file for test #{test_num} error\n"
68 86 run_comment_short += RUN_ERROR_MARK
69 87 log "Error in #{test_num}/result!"
70 88 end
71 89
72 90 # the score of this run should be the minimum of the score for
73 91 # each test case
74 92 if (run_score==nil) or (run_score>current_run_score)
75 93 run_score = current_run_score
76 94 end
77 95 result_file.close
78 96 end
79 97 end
80 98
81 99 run_result_file = File.new("result-#{k}", "w")
82 100 run_result_file.write run_score
83 101 run_result_file.write "\n"
84 102 run_result_file.close
85 103
86 104 run_comment_file = File.new("comment-#{k}", "w")
87 105 run_comment_file.write "#{run_comment}\n"
88 106 run_comment_file.close
89 107
90 108 all_score = all_score + run_score
91 109
92 110 # append comment for test run with many test cases
93 111 if run.tests.length > 1
94 112 run_comment_short = '[' + run_comment_short + ']'
95 113 end
96 114 all_comment += run_comment_short
97 115 end
98 116
99 117 result_file = File.new("result", "w")
100 118 result_file.write all_score
101 119 result_file.write "\n"
102 120 result_file.close
103 121
104 122 comment_file = File.new("comment", "w")
105 123 comment_file.write "#{all_comment}\n"
106 124 comment_file.close
125 +
126 +
127 + File.open("run_stat","w") do |file|
128 + file.puts max_runtime
129 + file.puts peak_memory
130 + end
131 +
132 + puts "#{all_score} #{all_comment}"
133 + log "score = #{all_score}\ncomment = #{all_comment}"
134 + log "max_runtime = #{max_runtime}\npeak_memory = #{peak_memory}"
@@ -32,54 +32,55
32 32 log msg
33 33 raise msg
34 34 end
35 35 end
36 36
37 37 def clear_and_create_empty_dir(dir)
38 38 FileUtils.rm_rf(dir, :secure => true)
39 39 call_and_log("Cannot make directory #{dir}.") { FileUtils.mkdir(dir) }
40 40 end
41 41
42 42 # ARGV[0] --- language
43 43 # ARGV[1] --- program source file
44 44 # ARGV[2] --- test result directory
45 45 # ARGV[3] --- sandbox directory
46 46
47 47 if ARGV.length < 2 || ARGV.length > 4
48 48 puts "Usage: judge <language> <program-source> [<test-result-directory>] [<sandbox-directory>]"
49 49 puts " <sandbox-directory> is defaulted to ./sandbox"
50 50 puts " <test-result-directory> is defaulted to ./test-result"
51 51 puts "WARNING: The judge script will forcefully create the (implicitly and explicitly) specified directories and remove anything inside it."
52 52 exit(127)
53 53 end
54 54
55 55 language = ARGV[0]
56 - if language != "c" && language != "c++" && language != "pas"
56 + if language != "c" && language != "c++" && language != "pas" && language != "java" && language != "ruby" && language != "python" && language != "php"
57 57 log "You specified a language that is not supported: #{language}."
58 58 exit(127)
59 59 end
60 60
61 61 source_file = ARGV[1]
62 + ENV['SOURCE_NAME'] = source_file
62 63 if File.exist?(source_file) == false
63 64 log "The source file does not exist."
64 65 exit(127)
65 66 end
66 67
67 68 log "Making test result and sandbox directories..."
68 69
69 70 current_dir = FileUtils.pwd
70 71 current_dir.strip!
71 72
72 73 if ARGV.length >= 3
73 74 test_result_dir = ARGV[2]
74 75 else
75 76 test_result_dir = "#{current_dir}/test-result"
76 77 end
77 78
78 79 log "Test result directory: #{test_result_dir}"
79 80 clear_and_create_empty_dir(test_result_dir)
80 81
81 82 if ARGV.length >= 4
82 83 sandbox_dir = ARGV[3]
83 84 else
84 85 sandbox_dir = "#{current_dir}/sandbox"
85 86 end
@@ -89,75 +90,79
89 90 # Compile
90 91 log
91 92 log "Compiling..."
92 93 call_and_log("Cannot copy the source file to #{sandbox_dir}") {
93 94 FileUtils.cp(source_file, sandbox_dir)
94 95 }
95 96 begin
96 97 Dir.chdir sandbox_dir
97 98 rescue
98 99 log "ERROR: Cannot change directory to #{sandbox_dir}."
99 100 exit(127)
100 101 end
101 102 execute("#{problem_home}/script/compile #{language} #{source_file}", "Compilation error!")
102 103 compile_message = open("compiler_message").read
103 104 compile_message.strip!
104 105 call_and_log("Cannot move the compiler message to #{test_result_dir}.") {
105 106 FileUtils.mv("compiler_message", test_result_dir)
106 107 }
107 108 if !FileTest.exist?("a.out")
108 109 log "Cannot compile the source code. See message in #{test_result_dir}/compile_message"
109 110 exit(127)
110 111 else
111 112 call_and_log("Cannot move the compiled program to #{test_result_dir}") {
112 113 FileUtils.mv("a.out",test_result_dir)
114 + if language == "java" then Dir["*.class"].each { |file| FileUtils.mv(file,test_result_dir)} end
115 + if language == "python" then Dir["*.pyc"].each { |file| FileUtils.mv(file,test_result_dir)} end
113 116 }
114 117 FileUtils.rm_rf("#{sandbox_dir}/.")
115 118 end
116 119
117 120 require "#{problem_home}/script/test_dsl.rb"
118 121 load "#{problem_home}/test_cases/all_tests.cfg"
119 122 problem = Problem.get_instance
120 123
121 124 if problem.well_formed? == false
122 125 log "The problem specification is not well formed."
123 126 exit(127)
124 127 end
125 128
126 129 # Doing the testing.
127 130 (1..(problem.num_tests)).each do |test_num|
128 131
129 132 $stdout.print "[#{test_num}]"
130 133 $stdout.flush
131 134
132 135 log "Test number: #{test_num}"
133 136
134 137 call_and_log("Cannot copy the compiled program into #{sandbox_dir}") {
135 138 FileUtils.cp("#{test_result_dir}/a.out", sandbox_dir, :preserve => true)
139 + if language == "java" then Dir["#{test_result_dir}/*.class"].each { |file| FileUtils.cp(file,sandbox_dir)} end
140 + if language == "python" then Dir["#{test_result_dir}/*.pyc"].each { |file| FileUtils.cp(file,sandbox_dir)} end
136 141 }
137 142
138 143 begin
139 - execute("#{problem_home}/script/run #{language} #{test_num}", "Error occured during execution of the run script")
144 + execute("#{problem_home}/script/run #{language} #{test_num} ", "Error occured during execution of the run script")
140 145 rescue
141 146 # do nothing
142 147 end
143 148
144 149 call_and_log("Cannot create directory #{test_result_dir}/#{test_num}") {
145 150 FileUtils.mkdir "#{test_result_dir}/#{test_num}"
146 151 }
147 152 call_and_log("Cannot copy the result file into #{test_result_dir}/#{test_num}") {
148 153 FileUtils.mv "#{sandbox_dir}/result", "#{test_result_dir}/#{test_num}"
149 154 }
150 155 call_and_log("Cannot copy the comment file into #{test_result_dir}/#{test_num}") {
151 156 FileUtils.mv "#{sandbox_dir}/comment", "#{test_result_dir}/#{test_num}"
152 157 }
153 158 call_and_log("Cannot copy the output file into #{test_result_dir}/#{test_num}") {
154 159 FileUtils.mv "#{sandbox_dir}/output.txt", "#{test_result_dir}/#{test_num}"
155 160 }
156 161 call_and_log("Cannot clear #{sandbox_dir}") {
157 162 FileUtils.rm_rf(Dir.glob("#{sandbox_dir}/*"), :secure => true)
158 163 }
159 164 end
160 165
161 166 $stdout.print "[done]\n"
162 167
163 168 # Grade
@@ -22,138 +22,168
22 22 #{:real => 0, :user => 0, :sys => 0}
23 23 #puts "ERROR READING RUNNING TIME: #{t}"
24 24 raise "Error reading running time: #{t}"
25 25 end
26 26 end
27 27
28 28 def compile_box(source,bin)
29 29 system("g++ #{source} -o #{bin}")
30 30 end
31 31
32 32 if ARGV.length < 2 || ARGV.length > 3
33 33 puts "Usage: run <language> <test-num> [<program-name>]"
34 34 exit(127)
35 35 end
36 36
37 37 language = ARGV[0]
38 38 test_num = ARGV[1].to_i
39 39 if ARGV.length > 2
40 40 program_name = ARGV[2]
41 41 else
42 42 program_name = "a.out"
43 43 end
44 44
45 45 problem_home = ENV['PROBLEM_HOME']
46 + source_name = ENV['SOURCE_NAME']
46 47 require "#{problem_home}/script/test_dsl.rb"
47 48 load "#{problem_home}/test_cases/all_tests.cfg"
48 49 problem = Problem.get_instance
49 50
51 + sandbox_dir = Dir.getwd
52 +
50 53 if problem.well_formed? == false
51 54 log "The problem specification is not well formed."
52 55 exit(127)
53 56 end
54 57
55 58 # Check if the test number is okay.
56 59 if test_num <= 0 || test_num > problem.num_tests
57 60 log "You have specified a wrong test number."
58 61 exit(127)
59 62 end
60 63
61 64 #####################################
62 65 # Set the relavant file names here. #
63 66 #####################################
64 67
65 68 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
66 69
67 70 #####################################
68 71
69 72 time_limit = problem.get_time_limit test_num
70 73 mem_limit = problem.get_mem_limit(test_num) * 1024
71 74
72 75 # Copy the input file.
73 76 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
74 77
75 78 # check if box is there, if not, compile it!
76 79 if !File.exists?("#{problem_home}/script/box")
77 80 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
78 81 compile_box("#{problem_home}/script/box.cc",
79 82 "#{problem_home}/script/box")
80 83 end
81 84
82 85 # Hide PROBLEM_HOME
83 86 ENV['PROBLEM_HOME'] = nil
87 + ENV['SOURCE_NAME'] = nil
84 88
85 89 # Run the program.
86 90 #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}"
91 + #
87 92
88 - 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"
93 + JAVA_OPTION = "-s set_robust_list -s futex -s clone -s getppid -s clone -s wait4 -p /usr/bin/ -p ./"
94 + RUBY_OPTION = "-p /usr/lib64/ -p /usr/local/lib64/ -p /usr/local/lib/ -p /lib64/ -p /dev/urandom -p #{sandbox_dir}/#{program_name} -p #{sandbox_dir}/ -s set_robust_list -s sched_getaffinity -s clock_gettime -s sigaltstack -s pipe2 -s clone -s futex -s openat -s pipe"
95 + PYTHON_OPTION = "-p /usr/lib64/ -p /usr/local/lib64/ -p /usr/local/lib/ -p /usr/bin/ -p /lib64/ -p #{sandbox_dir}/#{program_name} -p ./#{program_name} -p #{sandbox_dir}/#{source_name} -s set_robust_list -s openat -s recvmsg -s connect -s socket -s sendto -s futex -E PYTHONNOUSERSITE=yes"
96 + PHP_OPTION = "-p /usr/lib64/ -p/lib64/ -p /usr/bin/ -p #{sandbox_dir}/#{program_name} -p ./#{program_name} -p /usr/share/ -s setfsuid -s setfsgid -s openat -s set_robust_list -s futex -s clone -s socket -s connect"
97 +
98 + case language
99 + when "java"
100 + # for java, extract the classname
101 + # wne have to add additional systemcall and we don't check the mem limit (dunno how to fix...)
102 + classname = 'DUMMY'
103 + File.open(program_name,"r").each do |line|
104 + classname = line
105 + end
106 + #for java, we cannot really check the memory limit...
107 + run_command = "#{problem_home}/script/box -a 3 -f -T -t #{time_limit} #{JAVA_OPTION} -i #{input_file_name} -o output.txt /usr/bin/java -A -Xmx#{mem_limit}k -A #{classname} "
108 + when "ruby"
109 + run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{mem_limit} #{RUBY_OPTION} -i #{input_file_name} -o output.txt /usr/bin/ruby #{program_name} "
110 + when "python"
111 + run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{mem_limit} #{PYTHON_OPTION} -i #{input_file_name} -o output.txt /usr/bin/python #{program_name} "
112 + when "php"
113 + run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{[128*1024,mem_limit].max} #{PHP_OPTION} -i #{input_file_name} -o output.txt /usr/bin/php -A -d -A memory_limit=#{mem_limit}k -A #{program_name} "
114 + else # for c++, pascal, we do the normal checking
115 + 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} "
116 + end
117 +
89 118
90 119 log "Running test #{test_num}..."
91 120 log run_command
92 - log
93 - system(run_command)
121 + log
122 + system(run_command,err: 'run_result')
94 123
95 124 # Restore PROBLEM_HOME
96 125 ENV['PROBLEM_HOME'] = problem_home
97 126
98 127 # Create the result file.
99 128 result_file = File.new("result", "w")
100 129 comment_file = File.new("comment", "w")
101 130
102 131 # Check if the program actually produced any output.
103 132 run_result_file = File.new("run_result", "r")
104 133 run_result = run_result_file.readlines
105 134 run_result_file.close
106 135
107 136 run_stat = run_result[run_result.length-1]
108 137 running_time = extract_time(run_stat)
109 138
110 139 report = lambda{ |status, points, comment|
111 140 result_file.write status.strip
112 141 result_file.write "\n"
113 142 result_file.write points.to_s.strip
114 143 result_file.write "\n"
115 144 result_file.write run_stat.strip
116 145 result_file.write "\n"
117 146 result_file.close
118 147 FileUtils.rm "run_result"
119 148 # `rm output.txt` --- keep the output
120 149
121 150 comment_file.write comment
122 151
123 152 # added for debuggin --- jittat
124 153 comment_file.write "--run-result--\n"
125 154 run_result.each do |l|
126 155 comment_file.write l
127 156 end
128 157
129 158 comment_file.close
130 159
131 160 log "Done!"
132 161 exit(0)
133 162 }
134 163
164 +
135 165 if run_result[0][0,2] != "OK"
136 166 log "There was a runtime error."
137 167 report.call(run_result[0], 0, "No comment.\n")
138 168 end
139 169
140 - if running_time[:user].to_f + running_time[:sys].to_f > time_limit
170 + if running_time[:user].to_f > time_limit
141 171 log "Time limit exceeded."
142 172 report.call("Time limit exceeded", 0, "No comment.\n")
143 173 end
144 174
145 175 # Run 'check' to evaluate the output.
146 176 #puts "There was no runtime error. Proceed to checking the output."
147 177 check_command = "#{problem_home}/script/check #{language} #{test_num}"
148 178 log "Checking the output..."
149 179 log check_command
150 180 if not system(check_command)
151 181 log "Problem with check script"
152 182 report.call("Incorrect",0,"Check script error.\n")
153 183 exit(127)
154 184 end
155 185
156 186 check_file = File.new("check_result", "r")
157 187 check_file_lines = check_file.readlines
158 188
159 189 report.call(check_file_lines[0], check_file_lines[1], "No comment.\n")
@@ -1,25 +1,25
1 - #!/usr/bin/ruby
1 + #!/usr/bin/env ruby
2 2
3 3 problem_home = ENV['PROBLEM_HOME']
4 4 require "#{problem_home}/script/test_dsl.rb"
5 5
6 6 if ARGV.length < 2
7 7 puts "Usage: check <language> <test-number> [<output-file>]"
8 8 exit(0)
9 9 end
10 10
11 11 language = ARGV[0]
12 12 test_num = ARGV[1].to_i
13 13 if ARGV.length >= 3
14 14 output_file_name = ARGV[2]
15 15 else
16 16 output_file_name = "output.txt"
17 17 end
18 18
19 19 load "#{problem_home}/test_cases/all_tests.cfg"
20 20 problem = Problem.get_instance
21 21
22 22 output_file = File.new(output_file_name, "r")
23 23 answer_file = File.new("#{problem_home}/test_cases/#{test_num}/answer-#{test_num}.txt")
24 24 result_file = File.new("check_result", "w")
25 25
@@ -1,25 +1,25
1 - #!/usr/bin/ruby
1 + #!/usr/bin/env ruby
2 2
3 3 problem_home = ENV['PROBLEM_HOME']
4 4 require "#{problem_home}/script/test_dsl.rb"
5 5
6 6 if ARGV.length < 2
7 7 puts "Usage: check <language> <test-number> [<output-file>]"
8 8 exit(0)
9 9 end
10 10
11 11 language = ARGV[0]
12 12 test_num = ARGV[1].to_i
13 13 if ARGV.length >= 3
14 14 output_file_name = ARGV[2]
15 15 else
16 16 output_file_name = "output.txt"
17 17 end
18 18
19 19 load "#{problem_home}/test_cases/all_tests.cfg"
20 20 problem = Problem.get_instance
21 21
22 22 output_file = File.new(output_file_name, "r")
23 23 answer_file = File.new("#{problem_home}/test_cases/#{test_num}/answer-#{test_num}.txt")
24 24 result_file = File.new("check_result", "w")
25 25
You need to be logged in to leave comments. Login now