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

r104:ad31dc743745 - - 10 files changed: 113 inserted, 80 deleted

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