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

r208:82f46028829e - - 11 files changed: 277 inserted, 32 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 puts str
36 puts str
37 end
37 end
38 if config.logging
38 if config.logging
39 fp = File.open(log_file_name,"a")
39 fp = File.open(log_file_name,"a")
40 fp.puts("GRADER: #{Time.new.strftime("%H:%M")} #{str}")
40 fp.puts("GRADER: #{Time.new.strftime("%H:%M")} #{str}")
41 fp.close
41 fp.close
42 end
42 end
43 end
43 end
44
44
45 def display_manual
45 def display_manual
46 puts <<USAGE
46 puts <<USAGE
47 Grader.
47 Grader.
48 using: (1) grader
48 using: (1) grader
49 (2) grader environment [mode] [options]
49 (2) grader environment [mode] [options]
50 (3) grader stop [all|pids-list]
50 (3) grader stop [all|pids-list]
51 (4) grader --help
51 (4) grader --help
52 (1) call grader with environment = 'exam', mode = 'queue'
52 (1) call grader with environment = 'exam', mode = 'queue'
53 (2) possible modes are: 'queue', 'test_request', 'prob', 'sub', 'contest', and 'autonew'
53 (2) possible modes are: 'queue', 'test_request', 'prob', 'sub', 'contest', and 'autonew'
54 queue: repeatedly check the task queue and grade any available tasks
54 queue: repeatedly check the task queue and grade any available tasks
55
55
56 prob: re-grade every user latest submission of the specific problem.
56 prob: re-grade every user latest submission of the specific problem.
57 the problem name must be specified by the next argument.
57 the problem name must be specified by the next argument.
58
58
59 additional options:
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 sub: re-grader the specified submission.
62 sub: re-grader the specified submission.
63 The submission ID to be re-graded must be specified by the next argument.
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 (3) create stop-file to stop running grader in queue mode
68 (3) create stop-file to stop running grader in queue mode
66 (4) You are here.
69 (4) You are here.
67 USAGE
70 USAGE
68 end
71 end
69
72
70 def process_options_and_stop_file
73 def process_options_and_stop_file
71 # The list of options are:
74 # The list of options are:
72 # - stop [all|process ids]
75 # - stop [all|process ids]
73 # -
76 # -
74
77
75 # Process 'help' option
78 # Process 'help' option
76 if (ARGV.length==1) and (/help/.match(ARGV[0]))
79 if (ARGV.length==1) and (/help/.match(ARGV[0]))
77 display_manual
80 display_manual
78 exit(0)
81 exit(0)
79 end
82 end
80
83
81 # Process 'stop' option.
84 # Process 'stop' option.
82 if (ARGV.length >= 1) and (ARGV[0]=='stop')
85 if (ARGV.length >= 1) and (ARGV[0]=='stop')
83 if ARGV.length==1
86 if ARGV.length==1
84 puts "you should specify pid-list or 'all'"
87 puts "you should specify pid-list or 'all'"
85 display_manual
88 display_manual
86 elsif (ARGV.length==2) and (ARGV[1]=='all')
89 elsif (ARGV.length==2) and (ARGV[1]=='all')
87 stop_grader(:all)
90 stop_grader(:all)
88 puts "A global stop file ('stop.all') created."
91 puts "A global stop file ('stop.all') created."
@@ -113,48 +116,50
113 # Process mode and environment option
116 # Process mode and environment option
114 if ARGV.length >= 1
117 if ARGV.length >= 1
115 options[:environment] = ARGV.shift
118 options[:environment] = ARGV.shift
116 if ARGV.length >=1
119 if ARGV.length >=1
117 options[:mode] = ARGV.shift
120 options[:mode] = ARGV.shift
118 end
121 end
119 else
122 else
120 puts 'no argument specified, using default mode and environment.'
123 puts 'no argument specified, using default mode and environment.'
121 end
124 end
122
125
123 options[:dry_run] = (ARGV.delete('--dry') != nil)
126 options[:dry_run] = (ARGV.delete('--dry') != nil)
124 if options[:dry_run] and (not ['prob','contest','autonew'].include? options[:mode])
127 if options[:dry_run] and (not ['prob','contest','autonew'].include? options[:mode])
125 puts "Dry run currently works only for 'prob' or 'contest' modes."
128 puts "Dry run currently works only for 'prob' or 'contest' modes."
126 exit(0)
129 exit(0)
127 end
130 end
128
131
129 options[:report] = (ARGV.delete('--report') != nil)
132 options[:report] = (ARGV.delete('--report') != nil)
130 if options[:report] and (not ['prob','contest','autonew'].include? options[:mode])
133 if options[:report] and (not ['prob','contest','autonew'].include? options[:mode])
131 puts "Report currently works only for 'prob' or 'contest' modes."
134 puts "Report currently works only for 'prob' or 'contest' modes."
132 exit(0)
135 exit(0)
133 end
136 end
134
137
135 options[:all_sub] = (ARGV.delete('--all-sub') != nil)
138 options[:all_sub] = (ARGV.delete('--all-sub') != nil)
136
139
140 + options[:err_log] = (ARGV.delete('--err-log') != nil)
141 +
137 return options
142 return options
138 end
143 end
139
144
140 class ResultCollector
145 class ResultCollector
141 def initialize
146 def initialize
142 @results = {}
147 @results = {}
143 @problems = {}
148 @problems = {}
144 @users = {}
149 @users = {}
145 end
150 end
146
151
147 def after_save_hook(submission, grading_result)
152 def after_save_hook(submission, grading_result)
148 end
153 end
149
154
150 def save(submission, grading_result)
155 def save(submission, grading_result)
151 user = submission.user
156 user = submission.user
152 problem = submission.problem
157 problem = submission.problem
153 if not @problems.has_key? problem.id
158 if not @problems.has_key? problem.id
154 @problems[problem.id] = problem
159 @problems[problem.id] = problem
155 end
160 end
156 if not @users.has_key? user.id
161 if not @users.has_key? user.id
157 @users[user.id] = user
162 @users[user.id] = user
158 end
163 end
159 @results[[user.id, problem.id]] = grading_result
164 @results[[user.id, problem.id]] = grading_result
160
165
@@ -399,48 +404,55
399 # add grader_mode to config
404 # add grader_mode to config
400 # this is needed because method log needs it. TODO: clean this up
405 # this is needed because method log needs it. TODO: clean this up
401 class << config
406 class << config
402 attr_accessor :grader_mode
407 attr_accessor :grader_mode
403 end
408 end
404 config.grader_mode = grader_mode
409 config.grader_mode = grader_mode
405
410
406 # reading rails environment
411 # reading rails environment
407 log 'Reading rails environment'
412 log 'Reading rails environment'
408
413
409 RAILS_ENV = config.rails_env
414 RAILS_ENV = config.rails_env
410 require RAILS_ROOT + '/config/environment'
415 require RAILS_ROOT + '/config/environment'
411
416
412 # register grader process
417 # register grader process
413 if config.report_grader
418 if config.report_grader
414 grader_proc = GraderProcess.register(config.grader_hostname,
419 grader_proc = GraderProcess.register(config.grader_hostname,
415 Process.pid,
420 Process.pid,
416 grader_mode)
421 grader_mode)
417 else
422 else
418 grader_proc = nil
423 grader_proc = nil
419 end
424 end
420
425
421 #set loggin environment
426 #set loggin environment
422 ENV['GRADER_LOGGING'] = log_file_name
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 # register exit handler to report inactive, and terminated
436 # register exit handler to report inactive, and terminated
425 at_exit do
437 at_exit do
426 if grader_proc!=nil
438 if grader_proc!=nil
427 grader_proc.report_inactive
439 grader_proc.report_inactive
428 grader_proc.terminate
440 grader_proc.terminate
429 end
441 end
430 end
442 end
431
443
432 #
444 #
433 # MAIN LOOP
445 # MAIN LOOP
434 #
446 #
435
447
436 case grader_mode
448 case grader_mode
437 when "queue"
449 when "queue"
438 grader_queue_loop(grader_proc, options)
450 grader_queue_loop(grader_proc, options)
439
451
440 when "test_request"
452 when "test_request"
441 grader_test_request_loop(grader_proc, options)
453 grader_test_request_loop(grader_proc, options)
442
454
443 when "prob"
455 when "prob"
444 grader_grade_problems(grader_proc, options)
456 grader_grade_problems(grader_proc, options)
445
457
446 when "contest"
458 when "contest"
@@ -1,45 +1,45
1 #!/bin/sh
1 #!/bin/sh
2
2
3 echo "This script will install and configure Cafe grader."
3 echo "This script will install and configure Cafe grader."
4
4
5 - $RUBY_VERSION="2.2.0"
5 + $RUBY_VERSION="2.1.2"
6 echo "This will install Ruby $RUBY_VERSION under RVM"
6 echo "This will install Ruby $RUBY_VERSION under RVM"
7
7
8 echo "Installing required apts"
8 echo "Installing required apts"
9
9
10 sudo apt-get update
10 sudo apt-get update
11 sudo apt-get install mysql-server mysql-client \
11 sudo apt-get install mysql-server mysql-client \
12 g++ gcc apache2 libmysqlclient15-dev build-essential \
12 g++ gcc apache2 libmysqlclient15-dev build-essential \
13 git-core openssl libreadline6 libreadline6-dev \
13 git-core openssl libreadline6 libreadline6-dev \
14 zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev \
14 zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev \
15 sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev \
15 sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev \
16 ncurses-dev automake libtool bison subversion \
16 ncurses-dev automake libtool bison subversion \
17 - pkg-config curl nodejs unzip
17 + pkg-config curl nodejs unzip pyflakes ruby default-jdk
18
18
19 echo "Installing RVM"
19 echo "Installing RVM"
20 curl -k -L https://get.rvm.io | bash -s stable
20 curl -k -L https://get.rvm.io | bash -s stable
21 - ~/.rvm/scripts/rvm
21 + source ~/.rvm/scripts/rvm
22
22
23 echo "Installing Ruby $RUBY_VERSION in RVM"
23 echo "Installing Ruby $RUBY_VERSION in RVM"
24
24
25 rvm install $RUBY_VERSION
25 rvm install $RUBY_VERSION
26 rvm use $RUBY_VERSION
26 rvm use $RUBY_VERSION
27
27
28 echo "Fetching Cafe Grader from Git repositories"
28 echo "Fetching Cafe Grader from Git repositories"
29
29
30 echo "Fetching web interface"
30 echo "Fetching web interface"
31
31
32 mkdir cafe_grader
32 mkdir cafe_grader
33 cd cafe_grader
33 cd cafe_grader
34 git clone -q git://github.com/jittat/cafe-grader-web.git web
34 git clone -q git://github.com/jittat/cafe-grader-web.git web
35
35
36 echo "Configuring rails app"
36 echo "Configuring rails app"
37
37
38 cp web/config/application.rb.SAMPLE web/config/application.rb
38 cp web/config/application.rb.SAMPLE web/config/application.rb
39 cp web/config/initializers/cafe_grader_config.rb.SAMPLE web/config/initializers/cafe_grader_config.rb
39 cp web/config/initializers/cafe_grader_config.rb.SAMPLE web/config/initializers/cafe_grader_config.rb
40
40
41 #replace UTC in application.rb with the system timezone
41 #replace UTC in application.rb with the system timezone
42 timezone='UTC'
42 timezone='UTC'
43 if [ -f '/etc/timezone' ]; then
43 if [ -f '/etc/timezone' ]; then
44 timezone=\"`cat /etc/timezone`\"
44 timezone=\"`cat /etc/timezone`\"
45 else
45 else
@@ -136,36 +136,45
136 echo "Fetching grader"
136 echo "Fetching grader"
137
137
138 cd ..
138 cd ..
139
139
140 mkdir judge
140 mkdir judge
141 cd judge
141 cd judge
142 git clone -q git://github.com/jittat/cafe-grader-judge-scripts.git scripts
142 git clone -q git://github.com/jittat/cafe-grader-judge-scripts.git scripts
143 mkdir raw
143 mkdir raw
144 mkdir ev-exam
144 mkdir ev-exam
145 mkdir ev
145 mkdir ev
146 mkdir result
146 mkdir result
147 mkdir log
147 mkdir log
148
148
149 echo "Configuring grader"
149 echo "Configuring grader"
150
150
151 cp scripts/config/env_exam.rb.SAMPLE scripts/config/env_exam.rb
151 cp scripts/config/env_exam.rb.SAMPLE scripts/config/env_exam.rb
152 cp scripts/config/env_grading.rb.SAMPLE scripts/config/env_grading.rb
152 cp scripts/config/env_grading.rb.SAMPLE scripts/config/env_grading.rb
153
153
154 # create new environment.rb file
154 # create new environment.rb file
155 echo "RAILS_ROOT = '$CAFE_PATH/web'" > scripts/config/environment.rb
155 echo "RAILS_ROOT = '$CAFE_PATH/web'" > scripts/config/environment.rb
156 echo "GRADER_ROOT = '$CAFE_PATH/judge/scripts'" >> scripts/config/environment.rb
156 echo "GRADER_ROOT = '$CAFE_PATH/judge/scripts'" >> scripts/config/environment.rb
157 echo "require File.join(File.dirname(__FILE__),'../lib/boot')" >> scripts/config/environment.rb
157 echo "require File.join(File.dirname(__FILE__),'../lib/boot')" >> scripts/config/environment.rb
158 echo "require File.dirname(__FILE__) + \"/env_#{GRADER_ENV}.rb\"" >> scripts/config/environment.rb
158 echo "require File.dirname(__FILE__) + \"/env_#{GRADER_ENV}.rb\"" >> scripts/config/environment.rb
159
159
160 + # compiling box
161 + MACHINE_TYPE=`uname -m`
162 + if [ ${MACHINE_TYPE} == 'x86_64' ]; then
163 + gcc -std=c99 -o scripts/std-script/box scripts/std-script/box64-new.c
164 + else
165 + g++ -o scripts/std-script/box scripts/std-script/box.cc
166 + fi
167 +
168 +
160 cd ..
169 cd ..
161
170
162 echo "Now you are ready to run cafe grader...."
171 echo "Now you are ready to run cafe grader...."
163 echo
172 echo
164 echo "Try:"
173 echo "Try:"
165 echo
174 echo
166 echo " cd web"
175 echo " cd web"
167 echo " rails s"
176 echo " rails s"
168 echo
177 echo
169 echo "and access web at http://localhost:3000/"
178 echo "and access web at http://localhost:3000/"
170 echo "The root username is 'root', its password is 'ioionrails'."
179 echo "The root username is 'root', its password is 'ioionrails'."
171
180
@@ -97,50 +97,52
97 rescue RuntimeError => msg
97 rescue RuntimeError => msg
98 @reporter.report_error(submission, msg)
98 @reporter.report_error(submission, msg)
99 puts "ERROR: #{msg}"
99 puts "ERROR: #{msg}"
100
100
101 ensure
101 ensure
102 @room_maker.clean_up(submission)
102 @room_maker.clean_up(submission)
103 Dir.chdir(current_dir) # this is really important
103 Dir.chdir(current_dir) # this is really important
104 end
104 end
105 end
105 end
106
106
107 protected
107 protected
108
108
109 def talk(str)
109 def talk(str)
110 if @config.talkative
110 if @config.talkative
111 puts str
111 puts str
112 end
112 end
113 end
113 end
114
114
115 def call_judge(problem_home,language,grading_dir,fname)
115 def call_judge(problem_home,language,grading_dir,fname)
116 ENV['PROBLEM_HOME'] = problem_home
116 ENV['PROBLEM_HOME'] = problem_home
117 ENV['RUBYOPT'] = ''
117 ENV['RUBYOPT'] = ''
118
118
119 talk grading_dir
119 talk grading_dir
120 Dir.chdir grading_dir
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 talk "CMD: #{cmd}"
123 talk "CMD: #{cmd}"
124 + warn "ERROR: file does not exists #{script_name}" unless File.exists? script_name
123 system(cmd)
125 system(cmd)
124 end
126 end
125
127
126 def get_std_script_dir
128 def get_std_script_dir
127 GRADER_ROOT + '/std-script'
129 GRADER_ROOT + '/std-script'
128 end
130 end
129
131
130 def copy_script(problem_home)
132 def copy_script(problem_home)
131 script_dir = "#{problem_home}/script"
133 script_dir = "#{problem_home}/script"
132 std_script_dir = get_std_script_dir
134 std_script_dir = get_std_script_dir
133
135
134 raise "engine: std-script directory not found" if !FileTest.exist?(std_script_dir)
136 raise "engine: std-script directory not found" if !FileTest.exist?(std_script_dir)
135
137
136 scripts = Dir[std_script_dir + '/*']
138 scripts = Dir[std_script_dir + '/*']
137
139
138 copied = []
140 copied = []
139
141
140 scripts.each do |s|
142 scripts.each do |s|
141 fname = File.basename(s)
143 fname = File.basename(s)
142 next if FileTest.directory?(s)
144 next if FileTest.directory?(s)
143 if !FileTest.exist?("#{script_dir}/#{fname}")
145 if !FileTest.exist?("#{script_dir}/#{fname}")
144 copied << fname
146 copied << fname
145 FileUtils.cp(s, "#{script_dir}", :preserve => true)
147 FileUtils.cp(s, "#{script_dir}", :preserve => true)
146 end
148 end
@@ -24,44 +24,44
24 return task
24 return task
25 end
25 end
26
26
27 def grade_problem(problem, options={})
27 def grade_problem(problem, options={})
28 User.find_each do |u|
28 User.find_each do |u|
29 puts "user: #{u.login}"
29 puts "user: #{u.login}"
30 if options[:user_conditions]!=nil
30 if options[:user_conditions]!=nil
31 con_proc = options[:user_conditions]
31 con_proc = options[:user_conditions]
32 next if not con_proc.call(u)
32 next if not con_proc.call(u)
33 end
33 end
34 if options[:all_sub]
34 if options[:all_sub]
35 Submission.where(user_id: u.id,problem_id: problem.id).find_each do |sub|
35 Submission.where(user_id: u.id,problem_id: problem.id).find_each do |sub|
36 @engine.grade(sub)
36 @engine.grade(sub)
37 end
37 end
38 else
38 else
39 last_sub = Submission.find_last_by_user_and_problem(u.id,problem.id)
39 last_sub = Submission.find_last_by_user_and_problem(u.id,problem.id)
40 if last_sub!=nil
40 if last_sub!=nil
41 @engine.grade(last_sub)
41 @engine.grade(last_sub)
42 end
42 end
43 end
43 end
44 end
44 end
45 end
45 end
46
46
47 def grade_submission(submission)
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 @engine.grade(submission)
49 @engine.grade(submission)
50 end
50 end
51
51
52 def grade_oldest_test_request
52 def grade_oldest_test_request
53 test_request = TestRequest.get_inqueue_and_change_status(Task::STATUS_GRADING)
53 test_request = TestRequest.get_inqueue_and_change_status(Task::STATUS_GRADING)
54 if test_request!=nil
54 if test_request!=nil
55 @grader_process.report_active(test_request) if @grader_process!=nil
55 @grader_process.report_active(test_request) if @grader_process!=nil
56
56
57 @engine.grade(test_request)
57 @engine.grade(test_request)
58 test_request.status_complete!
58 test_request.status_complete!
59 @grader_process.report_inactive(test_request) if @grader_process!=nil
59 @grader_process.report_inactive(test_request) if @grader_process!=nil
60 end
60 end
61 return test_request
61 return test_request
62 end
62 end
63
63
64 end
64 end
65
65
66 end
66 end
67
67
@@ -44,91 +44,109
44 if @result_collector
44 if @result_collector
45 @result_collector.save(sub,
45 @result_collector.save(sub,
46 result)
46 result)
47 end
47 end
48 save_result(sub,result)
48 save_result(sub,result)
49 end
49 end
50
50
51 def report_error(sub,msg)
51 def report_error(sub,msg)
52 save_result(sub,{:points => 0,
52 save_result(sub,{:points => 0,
53 :comment => "Grading error: #{msg}" })
53 :comment => "Grading error: #{msg}" })
54 end
54 end
55
55
56 protected
56 protected
57 def read_result(test_result_dir)
57 def read_result(test_result_dir)
58 cmp_msg_fname = "#{test_result_dir}/compiler_message"
58 cmp_msg_fname = "#{test_result_dir}/compiler_message"
59 if FileTest.exist?(cmp_msg_fname)
59 if FileTest.exist?(cmp_msg_fname)
60 cmp_file = File.open(cmp_msg_fname)
60 cmp_file = File.open(cmp_msg_fname)
61 cmp_msg = cmp_file.read
61 cmp_msg = cmp_file.read
62 cmp_file.close
62 cmp_file.close
63 else
63 else
64 cmp_msg = ""
64 cmp_msg = ""
65 end
65 end
66
66
67 result_fname = "#{test_result_dir}/result"
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 if FileTest.exist?(result_fname)
70 if FileTest.exist?(result_fname)
70 comment = ""
71 comment = ""
71 begin
72 begin
72 result_file = File.open(result_fname)
73 result_file = File.open(result_fname)
73 result = result_file.readline.to_i
74 result = result_file.readline.to_i
74 result_file.close
75 result_file.close
75 rescue
76 rescue
76 result = 0
77 result = 0
77 comment = "error reading result file."
78 comment = "error reading result file."
78 end
79 end
79
80
80 begin
81 begin
81 comment_file = File.open(comment_fname)
82 comment_file = File.open(comment_fname)
82 comment += comment_file.readline.chomp
83 comment += comment_file.readline.chomp
83 comment_file.close
84 comment_file.close
84 rescue
85 rescue
85 comment += ""
86 comment += ""
86 end
87 end
87
88
88 - return {:points => result,
89 + begin
89 - :comment => comment,
90 + runstat_file = File.open(runstat_fname)
90 - :cmp_msg => cmp_msg}
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 else
105 else
92 if FileTest.exist?("#{test_result_dir}/a.out")
106 if FileTest.exist?("#{test_result_dir}/a.out")
93 return {:points => 0,
107 return {:points => 0,
94 :comment => 'error during grading',
108 :comment => 'error during grading',
95 :cmp_msg => cmp_msg}
109 :cmp_msg => cmp_msg}
96 else
110 else
97 return {:points => 0,
111 return {:points => 0,
98 :comment => 'compilation error',
112 :comment => 'compilation error',
99 :cmp_msg => cmp_msg}
113 :cmp_msg => cmp_msg}
100 end
114 end
101 end
115 end
102 end
116 end
103
117
104 def save_result(submission,result)
118 def save_result(submission,result)
105 problem = submission.problem
119 problem = submission.problem
106 submission.graded_at = Time.now.gmtime
120 submission.graded_at = Time.now.gmtime
107 points = result[:points]
121 points = result[:points]
108 submission.points = points
122 submission.points = points
109 comment = @config.report_comment(result[:comment])
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 # TODO: FIX THIS MESSAGE
130 # TODO: FIX THIS MESSAGE
113 #
131 #
114 if problem == nil
132 if problem == nil
115 submission.grader_comment = 'PASSED: ' + comment + '(problem is nil)'
133 submission.grader_comment = 'PASSED: ' + comment + '(problem is nil)'
116 elsif points == problem.full_score
134 elsif points == problem.full_score
117 #submission.grader_comment = 'PASSED: ' + comment
135 #submission.grader_comment = 'PASSED: ' + comment
118 submission.grader_comment = comment
136 submission.grader_comment = comment
119 elsif result[:comment].chomp =~ /^[\[\]P]+$/
137 elsif result[:comment].chomp =~ /^[\[\]P]+$/
120 submission.grader_comment = 'PASSED: ' + comment + '(inconsistent score)'
138 submission.grader_comment = 'PASSED: ' + comment + '(inconsistent score)'
121 else
139 else
122 #submission.grader_comment = 'FAILED: ' + comment
140 #submission.grader_comment = 'FAILED: ' + comment
123 submission.grader_comment = comment
141 submission.grader_comment = comment
124 end
142 end
125 submission.compiler_message = result[:cmp_msg] or ''
143 submission.compiler_message = result[:cmp_msg] or ''
126
144
127 if not @dry_run
145 if not @dry_run
128 submission.save
146 submission.save
129 end
147 end
130 end
148 end
131
149
132 end
150 end
133
151
134 end
152 end
@@ -177,49 +177,49
177 end
177 end
178 end
178 end
179
179
180 def extract_running_stat(results)
180 def extract_running_stat(results)
181 running_stat_line = results[-1]
181 running_stat_line = results[-1]
182
182
183 # extract exit status line
183 # extract exit status line
184 run_stat = ""
184 run_stat = ""
185 if !(/[Cc]orrect/.match(results[0]))
185 if !(/[Cc]orrect/.match(results[0]))
186 run_stat = results[0].chomp
186 run_stat = results[0].chomp
187 else
187 else
188 run_stat = 'Program exited normally'
188 run_stat = 'Program exited normally'
189 end
189 end
190
190
191 # extract running time
191 # extract running time
192 if res = /r(.*)u(.*)s/.match(running_stat_line)
192 if res = /r(.*)u(.*)s/.match(running_stat_line)
193 seconds = (res[1].to_f + res[2].to_f)
193 seconds = (res[1].to_f + res[2].to_f)
194 time_stat = "Time used: #{seconds} sec."
194 time_stat = "Time used: #{seconds} sec."
195 else
195 else
196 seconds = nil
196 seconds = nil
197 time_stat = "Time used: n/a sec."
197 time_stat = "Time used: n/a sec."
198 end
198 end
199
199
200 # extract memory usage
200 # extract memory usage
201 - if res = /s(.*)m/.match(running_stat_line)
201 + if res = /s(.*)kbytes/.match(running_stat_line)
202 memory_used = res[1].to_i
202 memory_used = res[1].to_i
203 else
203 else
204 memory_used = -1
204 memory_used = -1
205 end
205 end
206
206
207 return {
207 return {
208 :msg => "#{run_stat}\n#{time_stat}",
208 :msg => "#{run_stat}\n#{time_stat}",
209 :running_time => seconds,
209 :running_time => seconds,
210 :exit_status => run_stat,
210 :exit_status => run_stat,
211 :memory_usage => memory_used
211 :memory_usage => memory_used
212 }
212 }
213 end
213 end
214
214
215 def save_result(test_request,result)
215 def save_result(test_request,result)
216 if result[:output_file_name]!=nil
216 if result[:output_file_name]!=nil
217 test_request.output_file_name = link_output_file(test_request,
217 test_request.output_file_name = link_output_file(test_request,
218 result[:output_file_name])
218 result[:output_file_name])
219 end
219 end
220 test_request.graded_at = Time.now
220 test_request.graded_at = Time.now
221 test_request.compiler_message = (result[:cmp_msg] or '')
221 test_request.compiler_message = (result[:cmp_msg] or '')
222 test_request.grader_comment = (result[:comment] or '')
222 test_request.grader_comment = (result[:comment] or '')
223 if result[:running_stat]!=nil
223 if result[:running_stat]!=nil
224 test_request.running_stat = (result[:running_stat][:msg] or '')
224 test_request.running_stat = (result[:running_stat][:msg] or '')
225 test_request.running_time = (result[:running_stat][:running_time] or nil)
225 test_request.running_time = (result[:running_stat][:running_time] or nil)
@@ -5,52 +5,60
5 ##############################
5 ##############################
6 #
6 #
7 # Standard Compile Script
7 # Standard Compile Script
8 #
8 #
9 # Supported compilers:
9 # Supported compilers:
10 # gcc, g++, and fpc.
10 # gcc, g++, and fpc.
11 #
11 #
12 ##############################
12 ##############################
13
13
14 def talk(str='')
14 def talk(str='')
15 if ENV['TALKATIVE']!=nil
15 if ENV['TALKATIVE']!=nil
16 puts str
16 puts str
17 end
17 end
18 if ENV['GRADER_LOGGING']!=nil
18 if ENV['GRADER_LOGGING']!=nil
19 log_fname = ENV['GRADER_LOGGING']
19 log_fname = ENV['GRADER_LOGGING']
20 fp = File.open(log_fname,"a")
20 fp = File.open(log_fname,"a")
21 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
21 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
22 fp.close
22 fp.close
23 end
23 end
24 end
24 end
25
25
26 C_COMPILER = "/usr/bin/gcc"
26 C_COMPILER = "/usr/bin/gcc"
27 CPLUSPLUS_COMPILER = "/usr/bin/g++"
27 CPLUSPLUS_COMPILER = "/usr/bin/g++"
28 PASCAL_COMPILER = "/usr/bin/fpc"
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 C_OPTIONS = "-O2 -s -static -std=c99 -DCONTEST -lm -Wall"
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 PASCAL_OPTIONS = "-O1 -XS -dCONTEST"
37 PASCAL_OPTIONS = "-O1 -XS -dCONTEST"
38 + JAVA_OPTIONS = ""
39 + PYTHON_OPTIONS = ""
40 + PHP_OPTIONS = "-l"
33
41
34 # Check for the correct number of arguments. Otherwise, print usage.
42 # Check for the correct number of arguments. Otherwise, print usage.
35 if ARGV.length == 0 or ARGV.length > 4
43 if ARGV.length == 0 or ARGV.length > 4
36 puts "Usage: compile <language> [<source-file>] [<output-file>] [<message-file>]"
44 puts "Usage: compile <language> [<source-file>] [<output-file>] [<message-file>]"
37 puts
45 puts
38 puts "<source-file> is defaulted to \"source\"."
46 puts "<source-file> is defaulted to \"source\"."
39 puts "<output-file> is defaulted to \"a.out\"."
47 puts "<output-file> is defaulted to \"a.out\"."
40 puts "<message-file> is defaulted to \"compiler_message\"."
48 puts "<message-file> is defaulted to \"compiler_message\"."
41 puts
49 puts
42 exit(127)
50 exit(127)
43 end
51 end
44
52
45 PARAMS = {
53 PARAMS = {
46 :source_file => [1,'source'],
54 :source_file => [1,'source'],
47 :output_file => [2,'a.out'],
55 :output_file => [2,'a.out'],
48 :message_file => [3,'compiler_message']
56 :message_file => [3,'compiler_message']
49 }
57 }
50
58
51 params = {}
59 params = {}
52 params[:prog_lang] = ARGV[0]
60 params[:prog_lang] = ARGV[0]
53 PARAMS.each_key do |param_name|
61 PARAMS.each_key do |param_name|
54 index, default = PARAMS[param_name]
62 index, default = PARAMS[param_name]
55 if ARGV.length > index
63 if ARGV.length > index
56 params[param_name] = ARGV[index]
64 params[param_name] = ARGV[index]
@@ -63,51 +71,118
63 # Remove any remaining output files or message files.
71 # Remove any remaining output files or message files.
64 if FileTest.exists? params[:output_file]
72 if FileTest.exists? params[:output_file]
65 FileUtils.rm(params[:output_file])
73 FileUtils.rm(params[:output_file])
66 end
74 end
67 if FileTest.exists? params[:message_file]
75 if FileTest.exists? params[:message_file]
68 FileUtils.rm(params[:message_file])
76 FileUtils.rm(params[:message_file])
69 end
77 end
70
78
71 # Check if the source file exists before attempt compiling.
79 # Check if the source file exists before attempt compiling.
72 if !FileTest.exists? params[:source_file]
80 if !FileTest.exists? params[:source_file]
73 talk("ERROR: The source file does not exist!")
81 talk("ERROR: The source file does not exist!")
74 open(params[:message_file],"w") do |f|
82 open(params[:message_file],"w") do |f|
75 f.puts "ERROR: The source file did not exist."
83 f.puts "ERROR: The source file did not exist."
76 end
84 end
77 exit(127)
85 exit(127)
78 end
86 end
79
87
80 if params[:prog_lang]=='cpp'
88 if params[:prog_lang]=='cpp'
81 params[:prog_lang] = 'c++'
89 params[:prog_lang] = 'c++'
82 end
90 end
83
91
84 # Compile.
92 # Compile.
85 case params[:prog_lang]
93 case params[:prog_lang]
86
94
87 - when "c"
95 + when "c"
88 - command = "#{C_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{C_OPTIONS} 2> #{params[:message_file]}"
96 + command = "#{C_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{C_OPTIONS}"
89 - system(command)
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++"
135 + when "ruby"
92 - command = "#{CPLUSPLUS_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{CPLUSPLUS_OPTIONS} 2> #{params[:message_file]}"
136 + command = "#{RUBY_INTERPRETER} -c #{params[:source_file]}"
93 - system(command)
137 + if system(command, err: params[:message_file])
94 -
138 + File.open(params[:output_file],"w") do |out_file|
95 - when "pas"
139 + out_file.puts "#!#{RUBY_INTERPRETER}"
96 - command = "#{PASCAL_COMPILER} #{params[:source_file]} -ooutpas #{PASCAL_OPTIONS} > #{params[:message_file]}"
140 + File.open(params[:source_file],"r").each do |line|
97 - system(command)
141 + out_file.print line
98 - FileUtils.mv("output", params[:output_file])
142 + end
99 -
143 + end
100 - else
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 talk("ERROR: Invalid language specified!")
176 talk("ERROR: Invalid language specified!")
102 open(params[:message_file],"w") do |f|
177 open(params[:message_file],"w") do |f|
103 f.puts "ERROR: Invalid language specified!"
178 f.puts "ERROR: Invalid language specified!"
104 end
179 end
105 exit(127)
180 exit(127)
106 end
181 end
107
182
108 # Report success or failure.
183 # Report success or failure.
109 if FileTest.exists? params[:output_file]
184 if FileTest.exists? params[:output_file]
110 talk "Compilation was successful!"
185 talk "Compilation was successful!"
111 else
186 else
112 talk "ERROR: Something was wrong during the compilation!"
187 talk "ERROR: Something was wrong during the compilation!"
113 end
188 end
@@ -10,97 +10,125
10 puts str
10 puts str
11 end
11 end
12 if ENV['GRADER_LOGGING']!=nil
12 if ENV['GRADER_LOGGING']!=nil
13 log_fname = ENV['GRADER_LOGGING']
13 log_fname = ENV['GRADER_LOGGING']
14 fp = File.open(log_fname,"a")
14 fp = File.open(log_fname,"a")
15 fp.puts("grade: #{Time.new.strftime("%H:%M")} #{str}")
15 fp.puts("grade: #{Time.new.strftime("%H:%M")} #{str}")
16 fp.close
16 fp.close
17 end
17 end
18 end
18 end
19
19
20 def char_comment(comment)
20 def char_comment(comment)
21 if comment =~ /[Ii]ncorrect/
21 if comment =~ /[Ii]ncorrect/
22 INCORRECT_MARK
22 INCORRECT_MARK
23 elsif comment =~ /[Cc]orrect/
23 elsif comment =~ /[Cc]orrect/
24 CORRECT_MARK
24 CORRECT_MARK
25 elsif comment =~ /[Tt]ime/
25 elsif comment =~ /[Tt]ime/
26 TIMEOUT_MARK
26 TIMEOUT_MARK
27 elsif res = /^[Cc]omment:(.*)$/.match(comment)
27 elsif res = /^[Cc]omment:(.*)$/.match(comment)
28 res[1]
28 res[1]
29 else
29 else
30 RUN_ERROR_MARK # these are run time errors
30 RUN_ERROR_MARK # these are run time errors
31 end
31 end
32 end
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 problem_home = ENV['PROBLEM_HOME']
45 problem_home = ENV['PROBLEM_HOME']
35 require "#{problem_home}/script/test_dsl.rb"
46 require "#{problem_home}/script/test_dsl.rb"
36 load "#{problem_home}/test_cases/all_tests.cfg"
47 load "#{problem_home}/test_cases/all_tests.cfg"
37 problem = Problem.get_instance
48 problem = Problem.get_instance
38
49
39 if problem.well_formed? == false
50 if problem.well_formed? == false
40 log "The problem specification is not well formed."
51 log "The problem specification is not well formed."
41 exit(127)
52 exit(127)
42 end
53 end
43
54
44 all_score = 0
55 all_score = 0
45 all_comment = ''
56 all_comment = ''
57 + peak_memory = -1
58 + max_runtime = -1
46 (1..(problem.runs.length-1)).each do |k|
59 (1..(problem.runs.length-1)).each do |k|
47 log "grade run #{k}"
60 log "grade run #{k}"
48 run = problem.runs[k]
61 run = problem.runs[k]
49 run_score = nil
62 run_score = nil
50 run_comment = ''
63 run_comment = ''
51 run_comment_short = ''
64 run_comment_short = ''
52 run.tests.each do |test_num|
65 run.tests.each do |test_num|
53 result_file_name = "#{test_num}/result"
66 result_file_name = "#{test_num}/result"
54 if not File.exists?(result_file_name)
67 if not File.exists?(result_file_name)
55 run_comment += "result file for test #{test_num} not found\n"
68 run_comment += "result file for test #{test_num} not found\n"
56 run_comment_short += RUN_ERROR_MARK
69 run_comment_short += RUN_ERROR_MARK
57 log "Cannot find the file #{test_num}/result!"
70 log "Cannot find the file #{test_num}/result!"
58 else
71 else
59 result_file = File.new(result_file_name, "r")
72 result_file = File.new(result_file_name, "r")
60 result_file_lines = result_file.readlines
73 result_file_lines = result_file.readlines
61 - if result_file_lines.length>=2
74 + if result_file_lines.length>=3
62 current_run_score = result_file_lines[1].to_i
75 current_run_score = result_file_lines[1].to_i
63 run_comment += result_file_lines[0]
76 run_comment += result_file_lines[0]
64 run_comment_short += char_comment(result_file_lines[0].chomp)
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 else
83 else
66 current_run_score = 0
84 current_run_score = 0
67 run_comment += "result file for test #{test_num} error\n"
85 run_comment += "result file for test #{test_num} error\n"
68 run_comment_short += RUN_ERROR_MARK
86 run_comment_short += RUN_ERROR_MARK
69 log "Error in #{test_num}/result!"
87 log "Error in #{test_num}/result!"
70 end
88 end
71
89
72 # the score of this run should be the minimum of the score for
90 # the score of this run should be the minimum of the score for
73 # each test case
91 # each test case
74 if (run_score==nil) or (run_score>current_run_score)
92 if (run_score==nil) or (run_score>current_run_score)
75 run_score = current_run_score
93 run_score = current_run_score
76 end
94 end
77 result_file.close
95 result_file.close
78 end
96 end
79 end
97 end
80
98
81 run_result_file = File.new("result-#{k}", "w")
99 run_result_file = File.new("result-#{k}", "w")
82 run_result_file.write run_score
100 run_result_file.write run_score
83 run_result_file.write "\n"
101 run_result_file.write "\n"
84 run_result_file.close
102 run_result_file.close
85
103
86 run_comment_file = File.new("comment-#{k}", "w")
104 run_comment_file = File.new("comment-#{k}", "w")
87 run_comment_file.write "#{run_comment}\n"
105 run_comment_file.write "#{run_comment}\n"
88 run_comment_file.close
106 run_comment_file.close
89
107
90 all_score = all_score + run_score
108 all_score = all_score + run_score
91
109
92 # append comment for test run with many test cases
110 # append comment for test run with many test cases
93 if run.tests.length > 1
111 if run.tests.length > 1
94 run_comment_short = '[' + run_comment_short + ']'
112 run_comment_short = '[' + run_comment_short + ']'
95 end
113 end
96 all_comment += run_comment_short
114 all_comment += run_comment_short
97 end
115 end
98
116
99 result_file = File.new("result", "w")
117 result_file = File.new("result", "w")
100 result_file.write all_score
118 result_file.write all_score
101 result_file.write "\n"
119 result_file.write "\n"
102 result_file.close
120 result_file.close
103
121
104 comment_file = File.new("comment", "w")
122 comment_file = File.new("comment", "w")
105 comment_file.write "#{all_comment}\n"
123 comment_file.write "#{all_comment}\n"
106 comment_file.close
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 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"
56 + if language != "c" && language != "c++" && language != "pas" && language != "java" && language != "ruby" && language != "python" && language != "php"
57 log "You specified a language that is not supported: #{language}."
57 log "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 if File.exist?(source_file) == false
63 if File.exist?(source_file) == false
63 log "The source file does not exist."
64 log "The source file does not exist."
64 exit(127)
65 exit(127)
65 end
66 end
66
67
67 log "Making test result and sandbox directories..."
68 log "Making test result and sandbox directories..."
68
69
69 current_dir = FileUtils.pwd
70 current_dir = FileUtils.pwd
70 current_dir.strip!
71 current_dir.strip!
71
72
72 if ARGV.length >= 3
73 if ARGV.length >= 3
73 test_result_dir = ARGV[2]
74 test_result_dir = ARGV[2]
74 else
75 else
75 test_result_dir = "#{current_dir}/test-result"
76 test_result_dir = "#{current_dir}/test-result"
76 end
77 end
77
78
78 log "Test result directory: #{test_result_dir}"
79 log "Test result directory: #{test_result_dir}"
79 clear_and_create_empty_dir(test_result_dir)
80 clear_and_create_empty_dir(test_result_dir)
80
81
81 if ARGV.length >= 4
82 if ARGV.length >= 4
82 sandbox_dir = ARGV[3]
83 sandbox_dir = ARGV[3]
83 else
84 else
84 sandbox_dir = "#{current_dir}/sandbox"
85 sandbox_dir = "#{current_dir}/sandbox"
85 end
86 end
@@ -89,75 +90,79
89 # Compile
90 # Compile
90 log
91 log
91 log "Compiling..."
92 log "Compiling..."
92 call_and_log("Cannot copy the source file to #{sandbox_dir}") {
93 call_and_log("Cannot copy the source file to #{sandbox_dir}") {
93 FileUtils.cp(source_file, sandbox_dir)
94 FileUtils.cp(source_file, sandbox_dir)
94 }
95 }
95 begin
96 begin
96 Dir.chdir sandbox_dir
97 Dir.chdir sandbox_dir
97 rescue
98 rescue
98 log "ERROR: Cannot change directory to #{sandbox_dir}."
99 log "ERROR: Cannot change directory to #{sandbox_dir}."
99 exit(127)
100 exit(127)
100 end
101 end
101 execute("#{problem_home}/script/compile #{language} #{source_file}", "Compilation error!")
102 execute("#{problem_home}/script/compile #{language} #{source_file}", "Compilation error!")
102 compile_message = open("compiler_message").read
103 compile_message = open("compiler_message").read
103 compile_message.strip!
104 compile_message.strip!
104 call_and_log("Cannot move the compiler message to #{test_result_dir}.") {
105 call_and_log("Cannot move the compiler message to #{test_result_dir}.") {
105 FileUtils.mv("compiler_message", test_result_dir)
106 FileUtils.mv("compiler_message", test_result_dir)
106 }
107 }
107 if !FileTest.exist?("a.out")
108 if !FileTest.exist?("a.out")
108 log "Cannot compile the source code. See message in #{test_result_dir}/compile_message"
109 log "Cannot compile the source code. See message in #{test_result_dir}/compile_message"
109 exit(127)
110 exit(127)
110 else
111 else
111 call_and_log("Cannot move the compiled program to #{test_result_dir}") {
112 call_and_log("Cannot move the compiled program to #{test_result_dir}") {
112 FileUtils.mv("a.out",test_result_dir)
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 FileUtils.rm_rf("#{sandbox_dir}/.")
117 FileUtils.rm_rf("#{sandbox_dir}/.")
115 end
118 end
116
119
117 require "#{problem_home}/script/test_dsl.rb"
120 require "#{problem_home}/script/test_dsl.rb"
118 load "#{problem_home}/test_cases/all_tests.cfg"
121 load "#{problem_home}/test_cases/all_tests.cfg"
119 problem = Problem.get_instance
122 problem = Problem.get_instance
120
123
121 if problem.well_formed? == false
124 if problem.well_formed? == false
122 log "The problem specification is not well formed."
125 log "The problem specification is not well formed."
123 exit(127)
126 exit(127)
124 end
127 end
125
128
126 # Doing the testing.
129 # Doing the testing.
127 (1..(problem.num_tests)).each do |test_num|
130 (1..(problem.num_tests)).each do |test_num|
128
131
129 $stdout.print "[#{test_num}]"
132 $stdout.print "[#{test_num}]"
130 $stdout.flush
133 $stdout.flush
131
134
132 log "Test number: #{test_num}"
135 log "Test number: #{test_num}"
133
136
134 call_and_log("Cannot copy the compiled program into #{sandbox_dir}") {
137 call_and_log("Cannot copy the compiled program into #{sandbox_dir}") {
135 FileUtils.cp("#{test_result_dir}/a.out", sandbox_dir, :preserve => true)
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 begin
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 rescue
145 rescue
141 # do nothing
146 # do nothing
142 end
147 end
143
148
144 call_and_log("Cannot create directory #{test_result_dir}/#{test_num}") {
149 call_and_log("Cannot create directory #{test_result_dir}/#{test_num}") {
145 FileUtils.mkdir "#{test_result_dir}/#{test_num}"
150 FileUtils.mkdir "#{test_result_dir}/#{test_num}"
146 }
151 }
147 call_and_log("Cannot copy the result file into #{test_result_dir}/#{test_num}") {
152 call_and_log("Cannot copy the result file into #{test_result_dir}/#{test_num}") {
148 FileUtils.mv "#{sandbox_dir}/result", "#{test_result_dir}/#{test_num}"
153 FileUtils.mv "#{sandbox_dir}/result", "#{test_result_dir}/#{test_num}"
149 }
154 }
150 call_and_log("Cannot copy the comment file into #{test_result_dir}/#{test_num}") {
155 call_and_log("Cannot copy the comment file into #{test_result_dir}/#{test_num}") {
151 FileUtils.mv "#{sandbox_dir}/comment", "#{test_result_dir}/#{test_num}"
156 FileUtils.mv "#{sandbox_dir}/comment", "#{test_result_dir}/#{test_num}"
152 }
157 }
153 call_and_log("Cannot copy the output file into #{test_result_dir}/#{test_num}") {
158 call_and_log("Cannot copy the output file into #{test_result_dir}/#{test_num}") {
154 FileUtils.mv "#{sandbox_dir}/output.txt", "#{test_result_dir}/#{test_num}"
159 FileUtils.mv "#{sandbox_dir}/output.txt", "#{test_result_dir}/#{test_num}"
155 }
160 }
156 call_and_log("Cannot clear #{sandbox_dir}") {
161 call_and_log("Cannot clear #{sandbox_dir}") {
157 FileUtils.rm_rf(Dir.glob("#{sandbox_dir}/*"), :secure => true)
162 FileUtils.rm_rf(Dir.glob("#{sandbox_dir}/*"), :secure => true)
158 }
163 }
159 end
164 end
160
165
161 $stdout.print "[done]\n"
166 $stdout.print "[done]\n"
162
167
163 # Grade
168 # Grade
@@ -22,138 +22,168
22 #{:real => 0, :user => 0, :sys => 0}
22 #{:real => 0, :user => 0, :sys => 0}
23 #puts "ERROR READING RUNNING TIME: #{t}"
23 #puts "ERROR READING RUNNING TIME: #{t}"
24 raise "Error reading running time: #{t}"
24 raise "Error reading running time: #{t}"
25 end
25 end
26 end
26 end
27
27
28 def compile_box(source,bin)
28 def compile_box(source,bin)
29 system("g++ #{source} -o #{bin}")
29 system("g++ #{source} -o #{bin}")
30 end
30 end
31
31
32 if ARGV.length < 2 || ARGV.length > 3
32 if ARGV.length < 2 || ARGV.length > 3
33 puts "Usage: run <language> <test-num> [<program-name>]"
33 puts "Usage: run <language> <test-num> [<program-name>]"
34 exit(127)
34 exit(127)
35 end
35 end
36
36
37 language = ARGV[0]
37 language = ARGV[0]
38 test_num = ARGV[1].to_i
38 test_num = ARGV[1].to_i
39 if ARGV.length > 2
39 if ARGV.length > 2
40 program_name = ARGV[2]
40 program_name = ARGV[2]
41 else
41 else
42 program_name = "a.out"
42 program_name = "a.out"
43 end
43 end
44
44
45 problem_home = ENV['PROBLEM_HOME']
45 problem_home = ENV['PROBLEM_HOME']
46 + source_name = ENV['SOURCE_NAME']
46 require "#{problem_home}/script/test_dsl.rb"
47 require "#{problem_home}/script/test_dsl.rb"
47 load "#{problem_home}/test_cases/all_tests.cfg"
48 load "#{problem_home}/test_cases/all_tests.cfg"
48 problem = Problem.get_instance
49 problem = Problem.get_instance
49
50
51 + sandbox_dir = Dir.getwd
52 +
50 if problem.well_formed? == false
53 if problem.well_formed? == false
51 log "The problem specification is not well formed."
54 log "The problem specification is not well formed."
52 exit(127)
55 exit(127)
53 end
56 end
54
57
55 # Check if the test number is okay.
58 # Check if the test number is okay.
56 if test_num <= 0 || test_num > problem.num_tests
59 if test_num <= 0 || test_num > problem.num_tests
57 log "You have specified a wrong test number."
60 log "You have specified a wrong test number."
58 exit(127)
61 exit(127)
59 end
62 end
60
63
61 #####################################
64 #####################################
62 # Set the relavant file names here. #
65 # Set the relavant file names here. #
63 #####################################
66 #####################################
64
67
65 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
68 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
66
69
67 #####################################
70 #####################################
68
71
69 time_limit = problem.get_time_limit test_num
72 time_limit = problem.get_time_limit test_num
70 mem_limit = problem.get_mem_limit(test_num) * 1024
73 mem_limit = problem.get_mem_limit(test_num) * 1024
71
74
72 # Copy the input file.
75 # Copy the input file.
73 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
76 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
74
77
75 # check if box is there, if not, compile it!
78 # check if box is there, if not, compile it!
76 if !File.exists?("#{problem_home}/script/box")
79 if !File.exists?("#{problem_home}/script/box")
77 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
80 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
78 compile_box("#{problem_home}/script/box.cc",
81 compile_box("#{problem_home}/script/box.cc",
79 "#{problem_home}/script/box")
82 "#{problem_home}/script/box")
80 end
83 end
81
84
82 # Hide PROBLEM_HOME
85 # Hide PROBLEM_HOME
83 ENV['PROBLEM_HOME'] = nil
86 ENV['PROBLEM_HOME'] = nil
87 + ENV['SOURCE_NAME'] = nil
84
88
85 # Run the program.
89 # Run the program.
86 #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}"
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 log "Running test #{test_num}..."
119 log "Running test #{test_num}..."
91 log run_command
120 log run_command
92 - log
121 + log
93 - system(run_command)
122 + system(run_command,err: 'run_result')
94
123
95 # Restore PROBLEM_HOME
124 # Restore PROBLEM_HOME
96 ENV['PROBLEM_HOME'] = problem_home
125 ENV['PROBLEM_HOME'] = problem_home
97
126
98 # Create the result file.
127 # Create the result file.
99 result_file = File.new("result", "w")
128 result_file = File.new("result", "w")
100 comment_file = File.new("comment", "w")
129 comment_file = File.new("comment", "w")
101
130
102 # Check if the program actually produced any output.
131 # Check if the program actually produced any output.
103 run_result_file = File.new("run_result", "r")
132 run_result_file = File.new("run_result", "r")
104 run_result = run_result_file.readlines
133 run_result = run_result_file.readlines
105 run_result_file.close
134 run_result_file.close
106
135
107 run_stat = run_result[run_result.length-1]
136 run_stat = run_result[run_result.length-1]
108 running_time = extract_time(run_stat)
137 running_time = extract_time(run_stat)
109
138
110 report = lambda{ |status, points, comment|
139 report = lambda{ |status, points, comment|
111 result_file.write status.strip
140 result_file.write status.strip
112 result_file.write "\n"
141 result_file.write "\n"
113 result_file.write points.to_s.strip
142 result_file.write points.to_s.strip
114 result_file.write "\n"
143 result_file.write "\n"
115 result_file.write run_stat.strip
144 result_file.write run_stat.strip
116 result_file.write "\n"
145 result_file.write "\n"
117 result_file.close
146 result_file.close
118 FileUtils.rm "run_result"
147 FileUtils.rm "run_result"
119 # `rm output.txt` --- keep the output
148 # `rm output.txt` --- keep the output
120
149
121 comment_file.write comment
150 comment_file.write comment
122
151
123 # added for debuggin --- jittat
152 # added for debuggin --- jittat
124 comment_file.write "--run-result--\n"
153 comment_file.write "--run-result--\n"
125 run_result.each do |l|
154 run_result.each do |l|
126 comment_file.write l
155 comment_file.write l
127 end
156 end
128
157
129 comment_file.close
158 comment_file.close
130
159
131 log "Done!"
160 log "Done!"
132 exit(0)
161 exit(0)
133 }
162 }
134
163
164 +
135 if run_result[0][0,2] != "OK"
165 if run_result[0][0,2] != "OK"
136 log "There was a runtime error."
166 log "There was a runtime error."
137 report.call(run_result[0], 0, "No comment.\n")
167 report.call(run_result[0], 0, "No comment.\n")
138 end
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 log "Time limit exceeded."
171 log "Time limit exceeded."
142 report.call("Time limit exceeded", 0, "No comment.\n")
172 report.call("Time limit exceeded", 0, "No comment.\n")
143 end
173 end
144
174
145 # Run 'check' to evaluate the output.
175 # Run 'check' to evaluate the output.
146 #puts "There was no runtime error. Proceed to checking the output."
176 #puts "There was no runtime error. Proceed to checking the output."
147 check_command = "#{problem_home}/script/check #{language} #{test_num}"
177 check_command = "#{problem_home}/script/check #{language} #{test_num}"
148 log "Checking the output..."
178 log "Checking the output..."
149 log check_command
179 log check_command
150 if not system(check_command)
180 if not system(check_command)
151 log "Problem with check script"
181 log "Problem with check script"
152 report.call("Incorrect",0,"Check script error.\n")
182 report.call("Incorrect",0,"Check script error.\n")
153 exit(127)
183 exit(127)
154 end
184 end
155
185
156 check_file = File.new("check_result", "r")
186 check_file = File.new("check_result", "r")
157 check_file_lines = check_file.readlines
187 check_file_lines = check_file.readlines
158
188
159 report.call(check_file_lines[0], check_file_lines[1], "No comment.\n")
189 report.call(check_file_lines[0], check_file_lines[1], "No comment.\n")
You need to be logged in to leave comments. Login now