forked from dodona-edu/judge-java
-
Notifications
You must be signed in to change notification settings - Fork 0
/
run
executable file
·264 lines (215 loc) · 9.4 KB
/
run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
#!/bin/sh
set -e
# Temp files
config="$(mktemp)" # configuration
compilation="$(mktemp)" # output of compilation
importclass="$(mktemp -d)" # to check imports
mkdir "/tmp/build" # compilation directory
trap "rm -rf '$config' '$compilation' '$importclass' '/tmp/build'" EXIT
# Saving the configuration from stdin
cat > "$config"
# Directories containing the test files and the judge repository
resources="$(jq -r '.resources' "$config")"
judge="$(jq -r '.judge' "$config")"
workdir="$(jq -r '.workdir' "$config")"
filename="$(jq -r '.filename' "$config")"
# Natural language of the user
natural_language="$(jq -r '.natural_language' "$config")"
. "$judge"/i18n/"$natural_language"
# memory limit with some margin
memory_limit="$(jq -r '.memory_limit' "$config")"
memory_limit="$(( memory_limit * 9 / 10000 ))"
# Other configuration parameters.
allow_compilation_warnings="$(jq -r '.allow_compilation_warnings == true' "$config")"
generated_output_cutoff="$(jq -r '.generated_output_cutoff // ""' "$config")"
############################## [ Helper functions ] ############################
# Import json helper functions.
dodona() { "$judge/dodona" "$@"; }
compilation_error_count() {
tail -n2 | sed -n 's/^\([0-9]\+\) errors\?$/\1/p'
}
compilation_warning_count() {
tail -n2 | sed -n 's/^\([0-9]\+\) warnings\?$/\1/p'
}
explain_compilation_error() {
case "$1" in
*"should be declared in a file named"*)
# Wrong class name.
printf "$i18n_wrong_class_name\n" "${filename%.java}"
;;
*"cannot find symbol"*)
# Cannot find symbol - forgotten import.
class_name="$(echo "$1" | sed -n '/symbol: *class/s/.*class \(\S\+\)\s*.*/\1/p')"
[ -z "$class_name" ] || printf "$i18n_forgot_import\n" "$class_name"
;;
*"assign a value to final variable"*)
# Assignment to final variable.
printf "$i18n_assign_to_final\n" "$(echo "$1" | grep -o "final variable \S\+" | sed "s/final variable //" | sed "s/\\\n//")"
;;
esac
}
explain_compilation_warning() {
case "$1" in
*"found raw type"*)
echo "$i18n_raw_type"
;;
esac
}
parse_compilation_error_staff() {
# arg1: 1 compiler log
dodona start-context
dodona start-testcase -f plain -d "$i18n_compilation_error"
dodona append-message -f code -p staff -d "$1"
dodona close-testcase -A
dodona close-context
}
parse_compilation_error_student() {
# arg1: 1 compiler log
# arg2: 1 to annotate, 0 to not annotate
annotate="$2"
dodona start-context
# Determine the kind of compilation message.
if echo "$1" | grep -q "^[^:]*[.]java:[0-9]\+: warning:"; then
# warning
type='warning'
explanation="$(explain_compilation_warning "$1")"
testcase_msg="$i18n_compilation_warning"
else
# error
type='error'
explanation="$(explain_compilation_error "$1")"
testcase_msg="$i18n_compilation_error"
fi
# Start the case, add the explanation message if it exists, add compilation output as message
[ -z "$explanation" ] || testcase_msg="$testcase_msg: $explanation"
dodona start-testcase -f plain -d "$testcase_msg"
dodona append-message -f code -d "$1"
# Annotate the code if required.
if [ "$annotate" -eq 1 ]; then
# Get the code annotation information.
annotation_column="$(echo "$1" | sed -n '/^ *^/p' | wc -c)"
annotation_column="$((annotation_column - 2))"
[ "$annotation_column" -lt 0 ] && annotation_column=0
annotation_row="$(echo "$1" | sed -n "s/^[^:]*[.]java:\([0-9]\+\):.*/\1/p")"
annotation_row="$((annotation_row - 1))"
[ "$annotation_row" -lt 0 ] && annotation_row=0
if [ -z "$explanation" ]; then
# Use the first line of the compilation log.
explanation="$(echo "$1" | sed 's/.*: \(error\|warning\): //' | head -n 1)"
fi
# Annotate the code accordingly, if required.
dodona annotate-code -r "$annotation_row" -c "$annotation_column" -t "$type" -m "$explanation"
fi
dodona close-testcase -A
dodona close-context
}
parse_compilation_errors() {
# arg1: permission
# arg2: 1 to annotate, 0 to not annotate
# reads log from stdin
annotate="$2"
# Loop over the compile log and process each message individually.
compile_err=""
while IFS= read -r line; do
if echo "$line" | egrep -q "^[^:]*[.]java:[0-9]+:"; then
if [ -n "$compile_err" ]; then
if [ "$1" = 'student' ]; then
parse_compilation_error_student "$compile_err" "$annotate" </dev/zero
else
parse_compilation_error_staff "$compile_err" </dev/zero
fi
fi
compile_err="$line"
else
compile_err="$(printf '%s\n%s' "$compile_err" "$line")"
fi
done
# Last error/warning will end with "x errors/y warnings; remove this part."
compile_err="$(echo "$compile_err" | sed "s/[0-9]\+ warnings\?//g")"
compile_err="$(echo "$compile_err" | sed "s/[0-9]\+ errors\?//g")"
# Process the last error/warning.
if [ "$1" = 'student' ]; then
parse_compilation_error_student "$compile_err" "$annotate" </dev/zero
else
parse_compilation_error_staff "$compile_err" </dev/zero
fi
}
compilation_failed() {
compilation="$1"
callout="$2"
human="$3"
target="$4"
annotate="$5"
# Counting compilation errors and warning.
compile_error_count="$(compilation_error_count < "$compilation")"
compile_error_count="${compile_error_count:-0}"
compile_warning_count="$(compilation_warning_count < "$compilation")"
compile_warning_count="${compile_warning_count:-0}"
# Build the compilation counts message.
case "$compile_error_count" in
0) described_error_count="" ;;
1) described_error_count="1 $i18n_error" ;;
*) described_error_count="$compile_error_count $i18n_errors" ;;
esac
case "$compile_warning_count" in
0) described_warning_count="" ;;
1) described_warning_count="1 $i18n_warning" ;;
*) described_warning_count="$compile_error_count $i18n_warnings" ;;
esac
[ "$compile_error_count" -ne 0 -a "$compile_warning_count" -ne 0 ] \
&& described_both_count="$described_error_count $i18n_and $described_warning_count" \
|| described_both_count="$described_error_count$described_warning_count"
dodona append-message -f callout -d "$(printf "$callout" "$described_both_count")"
# Append a (failed) testcase per compilation error
sed 's_.*/\([^/]*.java\)_\1_' "$compilation" | parse_compilation_errors "$target" "$annotate"
dodona close-tab # -b "$compile_errwarn_sum" TODO https://github.ugent.be/dodona/dodona/pull/1051
dodona close-judgement -A -e 'compilation error' -h "$(printf "$human" "$described_error_count")"
exit 0
}
################################# [ Start run ] ################################
dodona start-judgement
dodona start-tab -h -t 'Compiler'
testlibs="$(find "$judge/lib" "$resources" -name '*.jar' | xargs echo | tr ' ' ':')"
worklibs="$([ -d "$workdir" ] && find "$workdir" -name '*.jar' | xargs echo | tr ' ' ':')"
# Compiling the workdir given code
if ! find . -name '*.java' | xargs --no-run-if-empty javac -cp ".:${worklibs}:${testlibs}" -d . -sourcepath . > "$compilation" 2>&1; then
compilation_failed "$compilation" "$i18n_workdir_compilation_message" "$i18n_workdir_compilation_summary" 'staff' 0
fi
# Create the Input.java class, containing the submitted code
cat "$(jq -r '.source' "$config")" > "$filename"
# Very conservative removal of package statements
sed -i '1,2{/^package [a-z0-9_.]*;/d}' "$filename"
# Compiling the user code
[ "$allow_compilation_warnings" = 'true' ] || compile_opt='-Werror'
if ! javac -cp ".:${worklibs}" -Xlint:all $compile_opt "$filename" > "$compilation" 2>&1; then
compilation_failed "$compilation" "$i18n_user_compilation_message" "%s" 'student' 1
fi
# Verify the student submitted the requested class
cat > "$importclass/Import.java" <<HERE
public class Import {
Class<?> userclass = ${filename%.java}.class;
}
HERE
if ! javac -cp . -d "$importclass" "$importclass/Import.java" >/dev/null 2>&1; then
dodona start-context -f plain -d "$(printf "$i18n_class_not_submitted" "${filename%.java}")"
dodona close-context -A
if grep -q '^package' "$filename"; then
dodona append-message -f callout -d "$i18n_default_package"
fi
dodona close-tab
dodona close-judgement -A -e 'compilation error' -h "$i18n_compilation_error"
exit 0
fi
# Compiling judge into "build" and getting a jar in the workdir
find "$judge/src" -name '*.java' \
| xargs javac -classpath "$judge/lib/*:/tmp/build" -d /tmp/build -sourcepath "$judge/src"
jar -cf "judge.jar" -C /tmp/build .
# Compiling the tests
if ! find "$resources" -name '*.java' | xargs javac -Xdiags:verbose -cp ".:${resources}:${worklibs}:${testlibs}:judge.jar" -d . -sourcepath "$resources" > "$compilation" 2>&1; then
compilation_failed "$compilation" "$i18n_test_compilation_message" "$i18n_test_compilation_summary" 'student' 0
fi
# Everything is compiled
dodona close-tab
# Running the tests
java -Xmx"${memory_limit}k" -cp ".:${worklibs}:${testlibs}:judge.jar:${resources}/properties" -Ddodona.language="${natural_language}" -Ddodona.output_cutoff="${generated_output_cutoff}" dodona.junit.JUnitJSON
dodona close-judgement