Description:
more commenting on script
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r257:98303143aafa - - 3 files changed: 24 inserted, 2 deleted

@@ -1,203 +1,204
1 require 'fileutils'
1 require 'fileutils'
2 require File.join(File.dirname(__FILE__),'dir_init')
2 require File.join(File.dirname(__FILE__),'dir_init')
3
3
4 module Grader
4 module Grader
5
5
6 #
6 #
7 # A grader engine grades a submission, against anything: a test
7 # A grader engine grades a submission, against anything: a test
8 # data, or a user submitted test data. It uses two helpers objects:
8 # data, or a user submitted test data. It uses two helpers objects:
9 # room_maker and reporter.
9 # room_maker and reporter.
10 #
10 #
11 class Engine
11 class Engine
12
12
13 attr_writer :room_maker
13 attr_writer :room_maker
14 attr_writer :reporter
14 attr_writer :reporter
15
15
16 def initialize(options={})
16 def initialize(options={})
17 # default options
17 # default options
18 if not options.include? :room_maker
18 if not options.include? :room_maker
19 options[:room_maker] = Grader::SubmissionRoomMaker.new
19 options[:room_maker] = Grader::SubmissionRoomMaker.new
20 end
20 end
21 if not options.include? :reporter
21 if not options.include? :reporter
22 options[:reporter] = Grader::SubmissionReporter.new
22 options[:reporter] = Grader::SubmissionReporter.new
23 end
23 end
24
24
25 @config = Grader::Configuration.get_instance
25 @config = Grader::Configuration.get_instance
26
26
27 @room_maker = options[:room_maker]
27 @room_maker = options[:room_maker]
28 @reporter = options[:reporter]
28 @reporter = options[:reporter]
29 end
29 end
30
30
31 # takes a submission, asks room_maker to produce grading directories,
31 # takes a submission, asks room_maker to produce grading directories,
32 # calls grader scripts, and asks reporter to save the result
32 # calls grader scripts, and asks reporter to save the result
33 def grade(submission)
33 def grade(submission)
34 current_dir = FileUtils.pwd
34 current_dir = FileUtils.pwd
35
35
36 user = submission.user
36 user = submission.user
37 problem = submission.problem
37 problem = submission.problem
38
38
39 begin
39 begin
40 # TODO: will have to create real exception for this
40 # TODO: will have to create real exception for this
41 if user==nil or problem == nil
41 if user==nil or problem == nil
42 @reporter.report_error(submission,"Grading error: problem with submission")
42 @reporter.report_error(submission,"Grading error: problem with submission")
43 raise "engine: user or problem is nil"
43 raise "engine: user or problem is nil"
44 end
44 end
45
45
46 # TODO: this is another hack so that output only task can be judged
46 # TODO: this is another hack so that output only task can be judged
47 if submission.language!=nil
47 if submission.language!=nil
48 language = submission.language.name
48 language = submission.language.name
49 lang_ext = submission.language.ext
49 lang_ext = submission.language.ext
50 else
50 else
51 language = 'c'
51 language = 'c'
52 lang_ext = 'c'
52 lang_ext = 'c'
53 end
53 end
54
54
55 # This is needed because older version of std-scripts/compile
55 # This is needed because older version of std-scripts/compile
56 # only look for c++.
56 # only look for c++.
57 if language == 'cpp'
57 if language == 'cpp'
58 language = 'c++'
58 language = 'c++'
59 end
59 end
60
60
61 # COMMENT: should it be only source.ext?
61 # COMMENT: should it be only source.ext?
62 if problem!=nil
62 if problem!=nil
63 source_name = "#{problem.name}.#{lang_ext}"
63 source_name = "#{problem.name}.#{lang_ext}"
64 else
64 else
65 source_name = "source.#{lang_ext}"
65 source_name = "source.#{lang_ext}"
66 end
66 end
67
67
68 grading_dir = @room_maker.produce_grading_room(submission)
68 grading_dir = @room_maker.produce_grading_room(submission)
69 @room_maker.save_source(submission,source_name)
69 @room_maker.save_source(submission,source_name)
70 problem_home = @room_maker.find_problem_home(submission)
70 problem_home = @room_maker.find_problem_home(submission)
71
71
72 # puts "GRADING DIR: #{grading_dir}"
72 # puts "GRADING DIR: #{grading_dir}"
73 # puts "PROBLEM DIR: #{problem_home}"
73 # puts "PROBLEM DIR: #{problem_home}"
74
74
75 if !FileTest.exist?(problem_home)
75 if !FileTest.exist?(problem_home)
76 puts "PROBLEM DIR: #{problem_home}"
76 puts "PROBLEM DIR: #{problem_home}"
77 raise "engine: No test data."
77 raise "engine: No test data."
78 end
78 end
79
79
80 talk "ENGINE: grading dir at #{grading_dir} is created"
80 talk "ENGINE: grading dir at #{grading_dir} is created"
81 + talk "ENGINE: located problem home at #{problem_home} is created"
81
82
82 # copy the source script, using lock
83 # copy the source script, using lock
83 dinit = DirInit::Manager.new(problem_home)
84 dinit = DirInit::Manager.new(problem_home)
84
85
85 # lock the directory and copy the scripts
86 # lock the directory and copy the scripts
86 dinit.setup do
87 dinit.setup do
87 copy_log = copy_script(problem_home)
88 copy_log = copy_script(problem_home)
88 save_copy_log(problem_home,copy_log)
89 save_copy_log(problem_home,copy_log)
89 talk "ENGINE: following std script is copied: #{copy_log.join ' '}"
90 talk "ENGINE: following std script is copied: #{copy_log.join ' '}"
90 end
91 end
91
92
92
93
93 call_judge(problem_home,language,grading_dir,source_name)
94 call_judge(problem_home,language,grading_dir,source_name)
94
95
95 @reporter.report(submission,"#{grading_dir}/test-result")
96 @reporter.report(submission,"#{grading_dir}/test-result")
96
97
97 # unlock the directory
98 # unlock the directory
98 dinit.teardown do
99 dinit.teardown do
99 copy_log = load_copy_log(problem_home)
100 copy_log = load_copy_log(problem_home)
100 clear_copy_log(problem_home)
101 clear_copy_log(problem_home)
101 clear_script(copy_log,problem_home)
102 clear_script(copy_log,problem_home)
102 end
103 end
103
104
104 rescue RuntimeError => msg
105 rescue RuntimeError => msg
105 @reporter.report_error(submission, msg)
106 @reporter.report_error(submission, msg)
106 puts "ERROR: #{msg}"
107 puts "ERROR: #{msg}"
107
108
108 ensure
109 ensure
109 @room_maker.clean_up(submission)
110 @room_maker.clean_up(submission)
110 Dir.chdir(current_dir) # this is really important
111 Dir.chdir(current_dir) # this is really important
111 end
112 end
112 end
113 end
113
114
114 protected
115 protected
115
116
116 def talk(str)
117 def talk(str)
117 if @config.talkative
118 if @config.talkative
118 puts str
119 puts str
119 end
120 end
120 end
121 end
121
122
122 #change directory to problem_home
123 #change directory to problem_home
123 #call the "judge" script
124 #call the "judge" script
124 def call_judge(problem_home,language,grading_dir,fname)
125 def call_judge(problem_home,language,grading_dir,fname)
125 ENV['PROBLEM_HOME'] = problem_home
126 ENV['PROBLEM_HOME'] = problem_home
126 ENV['RUBYOPT'] = ''
127 ENV['RUBYOPT'] = ''
127
128
128 Dir.chdir grading_dir
129 Dir.chdir grading_dir
129 script_name = "#{problem_home}/script/judge"
130 script_name = "#{problem_home}/script/judge"
130 cmd = "#{script_name} #{language} #{fname}"
131 cmd = "#{script_name} #{language} #{fname}"
131 talk "ENGINE: Calling Judge at #{cmd}"
132 talk "ENGINE: Calling Judge at #{cmd}"
132 warn "ERROR: file does not exists #{script_name}" unless File.exists? script_name
133 warn "ERROR: file does not exists #{script_name}" unless File.exists? script_name
133 system(cmd)
134 system(cmd)
134 end
135 end
135
136
136 def get_std_script_dir
137 def get_std_script_dir
137 GRADER_ROOT + '/std-script'
138 GRADER_ROOT + '/std-script'
138 end
139 end
139
140
140 #copy any script presented in std-script directory that is not in the problem_home
141 #copy any script presented in std-script directory that is not in the problem_home
141 #this allow a problem setter to provide their own version for each script
142 #this allow a problem setter to provide their own version for each script
142 #in case that they want to hack something
143 #in case that they want to hack something
143 def copy_script(problem_home)
144 def copy_script(problem_home)
144 script_dir = "#{problem_home}/script"
145 script_dir = "#{problem_home}/script"
145 std_script_dir = get_std_script_dir
146 std_script_dir = get_std_script_dir
146
147
147 raise "engine: std-script directory not found" if !FileTest.exist?(std_script_dir)
148 raise "engine: std-script directory not found" if !FileTest.exist?(std_script_dir)
148
149
149 scripts = Dir[std_script_dir + '/*']
150 scripts = Dir[std_script_dir + '/*']
150
151
151 copied = []
152 copied = []
152
153
153 scripts.each do |s|
154 scripts.each do |s|
154 fname = File.basename(s)
155 fname = File.basename(s)
155 next if FileTest.directory?(s)
156 next if FileTest.directory?(s)
156 if !FileTest.exist?("#{script_dir}/#{fname}")
157 if !FileTest.exist?("#{script_dir}/#{fname}")
157 copied << fname
158 copied << fname
158 FileUtils.cp(s, "#{script_dir}", :preserve => true)
159 FileUtils.cp(s, "#{script_dir}", :preserve => true)
159 end
160 end
160 end
161 end
161
162
162 return copied
163 return copied
163 end
164 end
164
165
165 def copy_log_filename(problem_home)
166 def copy_log_filename(problem_home)
166 return File.join(problem_home, '.scripts_copied')
167 return File.join(problem_home, '.scripts_copied')
167 end
168 end
168
169
169 def save_copy_log(problem_home, log)
170 def save_copy_log(problem_home, log)
170 f = File.new(copy_log_filename(problem_home),"w")
171 f = File.new(copy_log_filename(problem_home),"w")
171 log.each do |fname|
172 log.each do |fname|
172 f.write("#{fname}\n")
173 f.write("#{fname}\n")
173 end
174 end
174 f.close
175 f.close
175 end
176 end
176
177
177 def load_copy_log(problem_home)
178 def load_copy_log(problem_home)
178 f = File.new(copy_log_filename(problem_home),"r")
179 f = File.new(copy_log_filename(problem_home),"r")
179 log = []
180 log = []
180 f.readlines.each do |line|
181 f.readlines.each do |line|
181 log << line.strip
182 log << line.strip
182 end
183 end
183 f.close
184 f.close
184 log
185 log
185 end
186 end
186
187
187 def clear_copy_log(problem_home)
188 def clear_copy_log(problem_home)
188 File.delete(copy_log_filename(problem_home))
189 File.delete(copy_log_filename(problem_home))
189 end
190 end
190
191
191 def clear_script(log,problem_home)
192 def clear_script(log,problem_home)
192 log.each do |s|
193 log.each do |s|
193 FileUtils.rm("#{problem_home}/script/#{s}")
194 FileUtils.rm("#{problem_home}/script/#{s}")
194 end
195 end
195 end
196 end
196
197
197 def mkdir_if_does_not_exist(dirname)
198 def mkdir_if_does_not_exist(dirname)
198 Dir.mkdir(dirname) if !FileTest.exist?(dirname)
199 Dir.mkdir(dirname) if !FileTest.exist?(dirname)
199 end
200 end
200
201
201 end
202 end
202
203
203 end
204 end
@@ -1,192 +1,194
1 #!/usr/bin/env ruby
1 #!/usr/bin/env ruby
2
2
3 require 'fileutils'
3 require 'fileutils'
4
4
5 def log(str='')
5 def log(str='')
6 if ENV['TALKATIVE']!=nil
6 if ENV['TALKATIVE']!=nil
7 puts str
7 puts str
8 end
8 end
9 if ENV['GRADER_LOGGING']!=nil
9 if ENV['GRADER_LOGGING']!=nil
10 log_fname = ENV['GRADER_LOGGING']
10 log_fname = ENV['GRADER_LOGGING']
11 fp = File.open(log_fname,"a")
11 fp = File.open(log_fname,"a")
12 fp.puts("judge: #{Time.new.strftime("%H:%M")} #{str}")
12 fp.puts("judge: #{Time.new.strftime("%H:%M")} #{str}")
13 fp.close
13 fp.close
14 end
14 end
15 end
15 end
16
16
17 problem_home = ENV['PROBLEM_HOME']
17 problem_home = ENV['PROBLEM_HOME']
18
18
19 def execute(command, error_message="")
19 def execute(command, error_message="")
20 if not system(command)
20 if not system(command)
21 msg = "ERROR: #{error_message}"
21 msg = "ERROR: #{error_message}"
22 log msg
22 log msg
23 raise(msg)
23 raise(msg)
24 end
24 end
25 end
25 end
26
26
27 def call_and_log(error_message)
27 def call_and_log(error_message)
28 begin
28 begin
29 yield
29 yield
30 rescue
30 rescue
31 msg = "JUDGE: ERROR: #{error_message}"
31 msg = "JUDGE: ERROR: #{error_message}"
32 log msg
32 log msg
33 raise msg
33 raise msg
34 end
34 end
35 end
35 end
36
36
37 def clear_and_create_empty_dir(dir)
37 def clear_and_create_empty_dir(dir)
38 FileUtils.rm_rf(dir, :secure => true)
38 FileUtils.rm_rf(dir, :secure => true)
39 call_and_log("Cannot make directory #{dir}.") { FileUtils.mkdir(dir) }
39 call_and_log("Cannot make directory #{dir}.") { FileUtils.mkdir(dir) }
40 end
40 end
41
41
42 # ARGV[0] --- language
42 # ARGV[0] --- language
43 # ARGV[1] --- program source file
43 # ARGV[1] --- program source file
44 # ARGV[2] --- test result directory
44 # ARGV[2] --- test result directory
45 # ARGV[3] --- sandbox directory
45 # ARGV[3] --- sandbox directory
46
46
47 if ARGV.length < 2 || ARGV.length > 4
47 if ARGV.length < 2 || ARGV.length > 4
48 puts "Usage: judge <language> <program-source> [<test-result-directory>] [<sandbox-directory>]"
48 puts "Usage: judge <language> <program-source> [<test-result-directory>] [<sandbox-directory>]"
49 puts " <sandbox-directory> is defaulted to ./sandbox"
49 puts " <sandbox-directory> is defaulted to ./sandbox"
50 puts " <test-result-directory> is defaulted to ./test-result"
50 puts " <test-result-directory> is defaulted to ./test-result"
51 puts "WARNING: The judge script will forcefully create the (implicitly and explicitly) specified directories and remove anything inside it."
51 puts "WARNING: The judge script will forcefully create the (implicitly and explicitly) specified directories and remove anything inside it."
52 exit(127)
52 exit(127)
53 end
53 end
54
54
55 language = ARGV[0]
55 language = ARGV[0]
56 if language != "c" && language != "c++" && language != "pas" && language != "java" && language != "ruby" && language != "python" && language != "php" && language != "haskell"
56 if language != "c" && language != "c++" && language != "pas" && language != "java" && language != "ruby" && language != "python" && language != "php" && language != "haskell"
57 log "JUDGE: You specified a language that is not supported: #{language}."
57 log "JUDGE: You specified a language that is not supported: #{language}."
58 exit(127)
58 exit(127)
59 end
59 end
60
60
61 source_file = ARGV[1]
61 source_file = ARGV[1]
62 ENV['SOURCE_NAME'] = source_file
62 ENV['SOURCE_NAME'] = source_file
63 if File.exist?(source_file) == false
63 if File.exist?(source_file) == false
64 log "JUDGE: The source file does not exist."
64 log "JUDGE: The source file does not exist."
65 exit(127)
65 exit(127)
66 end
66 end
67
67
68 log "JUDGE: Making test result and sandbox directories..."
68 log "JUDGE: Making test result and sandbox directories..."
69
69
70 current_dir = FileUtils.pwd
70 current_dir = FileUtils.pwd
71 current_dir.strip!
71 current_dir.strip!
72
72
73 if ARGV.length >= 3
73 if ARGV.length >= 3
74 test_result_dir = ARGV[2]
74 test_result_dir = ARGV[2]
75 else
75 else
76 test_result_dir = "#{current_dir}/test-result"
76 test_result_dir = "#{current_dir}/test-result"
77 end
77 end
78
78
79 log "JUDGE: Test result directory: #{test_result_dir}"
79 log "JUDGE: Test result directory: #{test_result_dir}"
80 clear_and_create_empty_dir(test_result_dir)
80 clear_and_create_empty_dir(test_result_dir)
81
81
82 if ARGV.length >= 4
82 if ARGV.length >= 4
83 sandbox_dir = ARGV[3]
83 sandbox_dir = ARGV[3]
84 else
84 else
85 sandbox_dir = "#{current_dir}/sandbox"
85 sandbox_dir = "#{current_dir}/sandbox"
86 end
86 end
87 log "JUDGE: Sandbox directory: #{sandbox_dir}"
87 log "JUDGE: Sandbox directory: #{sandbox_dir}"
88 clear_and_create_empty_dir(sandbox_dir)
88 clear_and_create_empty_dir(sandbox_dir)
89
89
90 # ------------------------------
90 # ------------------------------
91 # Compile
91 # Compile
92 # ------------------------------
92 # ------------------------------
93 log "JUDGE: Compiling..."
93 log "JUDGE: Compiling..."
94 log
94 log
95 call_and_log("Cannot copy the source file to #{sandbox_dir}") {
95 call_and_log("Cannot copy the source file to #{sandbox_dir}") {
96 FileUtils.cp(source_file, sandbox_dir)
96 FileUtils.cp(source_file, sandbox_dir)
97 }
97 }
98 begin
98 begin
99 Dir.chdir sandbox_dir
99 Dir.chdir sandbox_dir
100 rescue
100 rescue
101 log "JUDGE: ERROR: Cannot change directory to #{sandbox_dir}."
101 log "JUDGE: ERROR: Cannot change directory to #{sandbox_dir}."
102 exit(127)
102 exit(127)
103 end
103 end
104 execute("#{problem_home}/script/compile #{language} #{source_file}", "Compilation error!")
104 execute("#{problem_home}/script/compile #{language} #{source_file}", "Compilation error!")
105 compile_message = open("compiler_message").read
105 compile_message = open("compiler_message").read
106 compile_message.strip!
106 compile_message.strip!
107 call_and_log("Cannot move the compiler message to #{test_result_dir}.") {
107 call_and_log("Cannot move the compiler message to #{test_result_dir}.") {
108 FileUtils.mv("compiler_message", test_result_dir)
108 FileUtils.mv("compiler_message", test_result_dir)
109 }
109 }
110 if !FileTest.exist?("a.out")
110 if !FileTest.exist?("a.out")
111 log "JUDGE: EROOR: Cannot compile the source code. See message in #{test_result_dir}/compile_message"
111 log "JUDGE: EROOR: Cannot compile the source code. See message in #{test_result_dir}/compile_message"
112 exit(127)
112 exit(127)
113 else
113 else
114 call_and_log("Cannot move the compiled program to #{test_result_dir}") {
114 call_and_log("Cannot move the compiled program to #{test_result_dir}") {
115 FileUtils.mv("a.out",test_result_dir)
115 FileUtils.mv("a.out",test_result_dir)
116 if language == "java" then Dir["*.class"].each { |file| FileUtils.mv(file,test_result_dir)} end
116 if language == "java" then Dir["*.class"].each { |file| FileUtils.mv(file,test_result_dir)} end
117 if language == "python" then Dir["*.pyc"].each { |file| FileUtils.mv(file,test_result_dir)} end
117 if language == "python" then Dir["*.pyc"].each { |file| FileUtils.mv(file,test_result_dir)} end
118 }
118 }
119 FileUtils.rm_rf("#{sandbox_dir}/.")
119 FileUtils.rm_rf("#{sandbox_dir}/.")
120 end
120 end
121
121
122
122
123 #-----------------------------------------------
123 #-----------------------------------------------
124 # run
124 # run
125 #-----------------------------------------------
125 #-----------------------------------------------
126 require "#{problem_home}/script/test_dsl.rb"
126 require "#{problem_home}/script/test_dsl.rb"
127 load "#{problem_home}/test_cases/all_tests.cfg"
127 load "#{problem_home}/test_cases/all_tests.cfg"
128 problem = Problem.get_instance
128 problem = Problem.get_instance
129
129
130 if problem.well_formed? == false
130 if problem.well_formed? == false
131 log "The problem specification is not well formed."
131 log "The problem specification is not well formed."
132 exit(127)
132 exit(127)
133 end
133 end
134
134
135 # Doing the testing.
135 # Doing the testing.
136 log
136 log
137 log "JUDGE: Running each test case..."
137 log "JUDGE: Running each test case..."
138 (1..(problem.num_tests)).each do |test_num|
138 (1..(problem.num_tests)).each do |test_num|
139
139
140 $stdout.print "[#{test_num}]"
140 $stdout.print "[#{test_num}]"
141 $stdout.flush
141 $stdout.flush
142
142
143 call_and_log("Cannot copy the compiled program into #{sandbox_dir}") {
143 call_and_log("Cannot copy the compiled program into #{sandbox_dir}") {
144 FileUtils.cp("#{test_result_dir}/a.out", sandbox_dir, :preserve => true)
144 FileUtils.cp("#{test_result_dir}/a.out", sandbox_dir, :preserve => true)
145 if language == "java" then Dir["#{test_result_dir}/*.class"].each { |file| FileUtils.cp(file,sandbox_dir)} end
145 if language == "java" then Dir["#{test_result_dir}/*.class"].each { |file| FileUtils.cp(file,sandbox_dir)} end
146 if language == "python" then Dir["#{test_result_dir}/*.pyc"].each { |file| FileUtils.cp(file,sandbox_dir)} end
146 if language == "python" then Dir["#{test_result_dir}/*.pyc"].each { |file| FileUtils.cp(file,sandbox_dir)} end
147 }
147 }
148
148
149 #additionally copy any extra .txt file
149 #additionally copy any extra .txt file
150 data_files = Dir[problem_home + '/*.txt']
150 data_files = Dir[problem_home + '/*.txt']
151 data_files.each do |file|
151 data_files.each do |file|
152 FileUtils.cp(file,sandbox_dir)
152 FileUtils.cp(file,sandbox_dir)
153 end
153 end
154
154
155 begin
155 begin
156 execute("#{problem_home}/script/run #{language} #{test_num} ", "Error occured during execution of the run script")
156 execute("#{problem_home}/script/run #{language} #{test_num} ", "Error occured during execution of the run script")
157 rescue
157 rescue
158 # do nothing
158 # do nothing
159 end
159 end
160
160
161 +
162 + #copy the output of run script to each test-result folder
161 call_and_log("Cannot create directory #{test_result_dir}/#{test_num}") {
163 call_and_log("Cannot create directory #{test_result_dir}/#{test_num}") {
162 FileUtils.mkdir "#{test_result_dir}/#{test_num}"
164 FileUtils.mkdir "#{test_result_dir}/#{test_num}"
163 }
165 }
164 call_and_log("Cannot copy the result file into #{test_result_dir}/#{test_num}") {
166 call_and_log("Cannot copy the result file into #{test_result_dir}/#{test_num}") {
165 FileUtils.mv "#{sandbox_dir}/result", "#{test_result_dir}/#{test_num}"
167 FileUtils.mv "#{sandbox_dir}/result", "#{test_result_dir}/#{test_num}"
166 }
168 }
167 call_and_log("Cannot copy the comment file into #{test_result_dir}/#{test_num}") {
169 call_and_log("Cannot copy the comment file into #{test_result_dir}/#{test_num}") {
168 FileUtils.mv "#{sandbox_dir}/comment", "#{test_result_dir}/#{test_num}"
170 FileUtils.mv "#{sandbox_dir}/comment", "#{test_result_dir}/#{test_num}"
169 }
171 }
170 call_and_log("Cannot copy the output file into #{test_result_dir}/#{test_num}") {
172 call_and_log("Cannot copy the output file into #{test_result_dir}/#{test_num}") {
171 FileUtils.mv "#{sandbox_dir}/output.txt", "#{test_result_dir}/#{test_num}"
173 FileUtils.mv "#{sandbox_dir}/output.txt", "#{test_result_dir}/#{test_num}"
172 }
174 }
173 call_and_log("Cannot clear #{sandbox_dir}") {
175 call_and_log("Cannot clear #{sandbox_dir}") {
174 FileUtils.rm_rf(Dir.glob("#{sandbox_dir}/*"), :secure => true)
176 FileUtils.rm_rf(Dir.glob("#{sandbox_dir}/*"), :secure => true)
175 }
177 }
176 end
178 end
177
179
178 $stdout.print "[done]\n"
180 $stdout.print "[done]\n"
179
181
180 # Grade
182 # Grade
181 log
183 log
182 log "JUDGE: Grading..."
184 log "JUDGE: Grading..."
183 begin
185 begin
184 Dir.chdir test_result_dir
186 Dir.chdir test_result_dir
185 rescue
187 rescue
186 log "ERROR: Cannot change directory to #{test_result_dir}."
188 log "ERROR: Cannot change directory to #{test_result_dir}."
187 exit(127)
189 exit(127)
188 end
190 end
189 execute("#{problem_home}/script/grade", "An error occured during grading!")
191 execute("#{problem_home}/script/grade", "An error occured during grading!")
190
192
191 log
193 log
192 log "All done!"
194 log "All done!"
@@ -1,192 +1,211
1 #!/usr/bin/env ruby
1 #!/usr/bin/env ruby
2
2
3 + ##
4 + # This program should be run in the sandbox dir containing the compiled file
5 + # (or source file for script language). It will call the sandbox program with
6 + # the given input and process the output of the sandbox
7 + #
8 + # If sandbox exit normally, this program will call the "check" script to do
9 + # scoring. Otherwise, it would record the error accordingly
10 + #
11 + # This program produces several file
12 + # * result - the result from check script
13 + # * comment - comment from sandbox
14 + # * output - output of the program
15 + #
16 +
3 require 'fileutils'
17 require 'fileutils'
4
18
5 def log(str='')
19 def log(str='')
6 if ENV['TALKATIVE']!=nil
20 if ENV['TALKATIVE']!=nil
7 puts str
21 puts str
8 end
22 end
9 if ENV['GRADER_LOGGING']!=nil
23 if ENV['GRADER_LOGGING']!=nil
10 log_fname = ENV['GRADER_LOGGING']
24 log_fname = ENV['GRADER_LOGGING']
11 fp = File.open(log_fname,"a")
25 fp = File.open(log_fname,"a")
12 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
26 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
13 fp.close
27 fp.close
14 end
28 end
15 end
29 end
16
30
17 def extract_time(t)
31 def extract_time(t)
18 # puts "TIME: #{t}"
32 # puts "TIME: #{t}"
19 if (result=/^(.*)r(.*)u(.*)s/.match(t))
33 if (result=/^(.*)r(.*)u(.*)s/.match(t))
20 {:real => result[1], :user => result[2], :sys => result[3]}
34 {:real => result[1], :user => result[2], :sys => result[3]}
21 else
35 else
22 #{:real => 0, :user => 0, :sys => 0}
36 #{:real => 0, :user => 0, :sys => 0}
23 #puts "ERROR READING RUNNING TIME: #{t}"
37 #puts "ERROR READING RUNNING TIME: #{t}"
24 raise "Error reading running time: #{t}"
38 raise "Error reading running time: #{t}"
25 end
39 end
26 end
40 end
27
41
28 def compile_box(source,bin)
42 def compile_box(source,bin)
29 system("g++ #{source} -o #{bin}")
43 system("g++ #{source} -o #{bin}")
30 end
44 end
31
45
46 + #------------------------------------------
47 + # MAIN
48 + #------------------------------------------
49 +
50 + #parse parameter
32 if ARGV.length < 2 || ARGV.length > 3
51 if ARGV.length < 2 || ARGV.length > 3
33 puts "Usage: run <language> <test-num> [<program-name>]"
52 puts "Usage: run <language> <test-num> [<program-name>]"
34 exit(127)
53 exit(127)
35 end
54 end
36
55
37 language = ARGV[0]
56 language = ARGV[0]
38 test_num = ARGV[1].to_i
57 test_num = ARGV[1].to_i
39 if ARGV.length > 2
58 if ARGV.length > 2
40 program_name = ARGV[2]
59 program_name = ARGV[2]
41 else
60 else
42 program_name = "a.out"
61 program_name = "a.out"
43 end
62 end
44
63
45 problem_home = ENV['PROBLEM_HOME']
64 problem_home = ENV['PROBLEM_HOME']
46 source_name = ENV['SOURCE_NAME']
65 source_name = ENV['SOURCE_NAME']
47 require "#{problem_home}/script/test_dsl.rb"
66 require "#{problem_home}/script/test_dsl.rb"
48 load "#{problem_home}/test_cases/all_tests.cfg"
67 load "#{problem_home}/test_cases/all_tests.cfg"
49 problem = Problem.get_instance
68 problem = Problem.get_instance
50
69
51 sandbox_dir = Dir.getwd
70 sandbox_dir = Dir.getwd
52
71
53 if problem.well_formed? == false
72 if problem.well_formed? == false
54 - log "RUN: The problem specification is not well formed."
73 + log "RUN: ERROR: The problem specification is not well formed."
55 exit(127)
74 exit(127)
56 end
75 end
57
76
58 # Check if the test number is okay.
77 # Check if the test number is okay.
59 if test_num <= 0 || test_num > problem.num_tests
78 if test_num <= 0 || test_num > problem.num_tests
60 - log "RUN: You have specified a wrong test number."
79 + log "RUN: ERROR: You have specified a wrong test number."
61 exit(127)
80 exit(127)
62 end
81 end
63
82
64 #####################################
83 #####################################
65 # Set the relavant file names here. #
84 # Set the relavant file names here. #
66 #####################################
85 #####################################
67
86
68 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
87 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
69
88
70 #####################################
89 #####################################
71
90
72 time_limit = problem.get_time_limit test_num
91 time_limit = problem.get_time_limit test_num
73 mem_limit = problem.get_mem_limit(test_num) * 1024
92 mem_limit = problem.get_mem_limit(test_num) * 1024
74
93
75 # Copy the input file.
94 # Copy the input file.
76 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
95 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
77
96
78 # check if box is there, if not, compile it!
97 # check if box is there, if not, compile it!
79 if !File.exists?("#{problem_home}/script/box")
98 if !File.exists?("#{problem_home}/script/box")
80 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
99 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
81 compile_box("#{problem_home}/script/box.cc",
100 compile_box("#{problem_home}/script/box.cc",
82 "#{problem_home}/script/box")
101 "#{problem_home}/script/box")
83 end
102 end
84
103
85 # Hide PROBLEM_HOME
104 # Hide PROBLEM_HOME
86 ENV['PROBLEM_HOME'] = nil
105 ENV['PROBLEM_HOME'] = nil
87 ENV['SOURCE_NAME'] = nil
106 ENV['SOURCE_NAME'] = nil
88
107
89 # Run the program.
108 # Run the program.
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}"
109 #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 #
110 #
92
111
93 JAVA_OPTION = "-s set_robust_list -s futex -s clone -s getppid -s clone -s wait4 -p /usr/bin/ -p ./"
112 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 -s getrandom"
113 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 -s getrandom"
95 PYTHON_OPTION = "-p /usr/lib64/ -p /usr/local/lib64/ -p /usr/local/lib/ -p /usr/bin/ -p /lib64/ -p /dev/urandom -p /usr/ -p #{sandbox_dir}/#{program_name} -p ./#{program_name} -p #{sandbox_dir}/#{source_name} -p /proc/sys/crypto/fips_enabled -p /proc/self/status -p /proc/mounts -p /var/lib/dpkg/status -s statfs -s set_robust_list -s openat -s sysinfo -s recvmsg -s connect -s socket -s sendto -s futex -s sigaltstack -s getrandom -E PYTHONNOUSERSITE=yes"
114 PYTHON_OPTION = "-p /usr/lib64/ -p /usr/local/lib64/ -p /usr/local/lib/ -p /usr/bin/ -p /lib64/ -p /dev/urandom -p /usr/ -p #{sandbox_dir}/#{program_name} -p ./#{program_name} -p #{sandbox_dir}/#{source_name} -p /proc/sys/crypto/fips_enabled -p /proc/self/status -p /proc/mounts -p /var/lib/dpkg/status -s statfs -s set_robust_list -s openat -s sysinfo -s recvmsg -s connect -s socket -s sendto -s futex -s sigaltstack -s getrandom -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"
115 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 HASKELL_OPTION = "-s set_robust_list -s clock_gettime -s sysinfo -s timer_create -s timer_settime -s futex -s timer_delete"
116 HASKELL_OPTION = "-s set_robust_list -s clock_gettime -s sysinfo -s timer_create -s timer_settime -s futex -s timer_delete"
98
117
99 case language
118 case language
100 when "java"
119 when "java"
101 # for java, extract the classname
120 # for java, extract the classname
102 # wne have to add additional systemcall and we don't check the mem limit (dunno how to fix...)
121 # wne have to add additional systemcall and we don't check the mem limit (dunno how to fix...)
103 classname = 'DUMMY'
122 classname = 'DUMMY'
104 File.open(program_name,"r").each do |line|
123 File.open(program_name,"r").each do |line|
105 classname = line
124 classname = line
106 end
125 end
107 #for java, we cannot really check the memory limit...
126 #for java, we cannot really check the memory limit...
108 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} "
127 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} "
109 when "ruby"
128 when "ruby"
110 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} "
129 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} "
111 when "python"
130 when "python"
112 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{[512 * 1024,mem_limit].max} #{PYTHON_OPTION} -i #{input_file_name} -o output.txt /usr/bin/python3 #{program_name} "
131 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{[512 * 1024,mem_limit].max} #{PYTHON_OPTION} -i #{input_file_name} -o output.txt /usr/bin/python3 #{program_name} "
113 when "haskell"
132 when "haskell"
114 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit} -m #{[512 * 1024,mem_limit].max} #{HASKELL_OPTION} -i #{input_file_name} -o output.txt #{program_name} "
133 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit} -m #{[512 * 1024,mem_limit].max} #{HASKELL_OPTION} -i #{input_file_name} -o output.txt #{program_name} "
115 when "php"
134 when "php"
116 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{[512 * 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} "
135 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{[512 * 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} "
117 else # for c++, pascal, we do the normal checking
136 else # for c++, pascal, we do the normal checking
118 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} "
137 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} "
119 end
138 end
120
139
121
140
122 log "RUN: Running test #{test_num}..."
141 log "RUN: Running test #{test_num}..."
123 log "RUN: Run command = [#{run_command}]"
142 log "RUN: Run command = [#{run_command}]"
124 log
143 log
125 system(run_command,err: 'run_result')
144 system(run_command,err: 'run_result')
126
145
127 # Restore PROBLEM_HOME
146 # Restore PROBLEM_HOME
128 ENV['PROBLEM_HOME'] = problem_home
147 ENV['PROBLEM_HOME'] = problem_home
129
148
130 # Create the result file.
149 # Create the result file.
131 result_file = File.new("result", "w")
150 result_file = File.new("result", "w")
132 comment_file = File.new("comment", "w")
151 comment_file = File.new("comment", "w")
133
152
134 # Check if the program actually produced any output.
153 # Check if the program actually produced any output.
135 run_result_file = File.new("run_result", "r")
154 run_result_file = File.new("run_result", "r")
136 run_result = run_result_file.readlines
155 run_result = run_result_file.readlines
137 run_result_file.close
156 run_result_file.close
138
157
139 run_stat = run_result[run_result.length-1]
158 run_stat = run_result[run_result.length-1]
140 running_time = extract_time(run_stat)
159 running_time = extract_time(run_stat)
141
160
142 report = lambda{ |status, points, comment|
161 report = lambda{ |status, points, comment|
143 result_file.write status.strip
162 result_file.write status.strip
144 result_file.write "\n"
163 result_file.write "\n"
145 result_file.write points.to_s.strip
164 result_file.write points.to_s.strip
146 result_file.write "\n"
165 result_file.write "\n"
147 result_file.write run_stat.strip
166 result_file.write run_stat.strip
148 result_file.write "\n"
167 result_file.write "\n"
149 result_file.close
168 result_file.close
150 FileUtils.rm "run_result"
169 FileUtils.rm "run_result"
151 # `rm output.txt` --- keep the output
170 # `rm output.txt` --- keep the output
152
171
153 comment_file.write comment
172 comment_file.write comment
154
173
155 # added for debuggin --- jittat
174 # added for debuggin --- jittat
156 comment_file.write "--run-result--\n"
175 comment_file.write "--run-result--\n"
157 run_result.each do |l|
176 run_result.each do |l|
158 comment_file.write l
177 comment_file.write l
159 end
178 end
160
179
161 comment_file.close
180 comment_file.close
162
181
163 log "Done!"
182 log "Done!"
164 exit(0)
183 exit(0)
165 }
184 }
166
185
167
186
168 if run_result[0][0,2] != "OK"
187 if run_result[0][0,2] != "OK"
169 log "There was a runtime error."
188 log "There was a runtime error."
170 report.call(run_result[0], 0, "No comment.\n")
189 report.call(run_result[0], 0, "No comment.\n")
171 end
190 end
172
191
173 if running_time[:user].to_f > time_limit
192 if running_time[:user].to_f > time_limit
174 log "Time limit exceeded."
193 log "Time limit exceeded."
175 report.call("Time limit exceeded", 0, "No comment.\n")
194 report.call("Time limit exceeded", 0, "No comment.\n")
176 end
195 end
177
196
178 # Run 'check' to evaluate the output.
197 # Run 'check' to evaluate the output.
179 #puts "There was no runtime error. Proceed to checking the output."
198 #puts "There was no runtime error. Proceed to checking the output."
180 check_command = "#{problem_home}/script/check #{language} #{test_num}"
199 check_command = "#{problem_home}/script/check #{language} #{test_num}"
181 log "Checking the output..."
200 log "Checking the output..."
182 log check_command
201 log check_command
183 if not system(check_command)
202 if not system(check_command)
184 log "Problem with check script"
203 log "Problem with check script"
185 report.call("Incorrect",0,"Check script error.\n")
204 report.call("Incorrect",0,"Check script error.\n")
186 exit(127)
205 exit(127)
187 end
206 end
188
207
189 check_file = File.new("check_result", "r")
208 check_file = File.new("check_result", "r")
190 check_file_lines = check_file.readlines
209 check_file_lines = check_file.readlines
191
210
192 report.call(check_file_lines[0], check_file_lines[1], "No comment.\n")
211 report.call(check_file_lines[0], check_file_lines[1], "No comment.\n")
You need to be logged in to leave comments. Login now