-
Notifications
You must be signed in to change notification settings - Fork 91
/
Copy pathgriddle
executable file
·677 lines (599 loc) · 20.6 KB
/
griddle
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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
#!/bin/bash
#
# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
#
# SPDX-License-Identifier: GPL-2.0-only
#
# Note: This script uses bash for its execution, but not because it uses any
# Bashisms; instead, it is to work around a POSIX-violating bug in signal
# handling in dash, which is the /bin/sh on most Debian-based systems. See
# <https://bugs.debian.org/779416>, filed in 2015 and still not fixed (posh has
# the same problem; ksh, mksh, yash, and zsh do not).
# Note: This is _not_ a legitimate parser for CMakeLists! It is quite crude.
# TODO: Rewrite this in Python!
set -eu
SOURCE_ROOT=${0%/*}
PROGNAME=${0##*/}
REPO_DIR=.repo
CMAKE_COMPILER_DEFAULT=gcc
CMAKE_COMPILER=$CMAKE_COMPILER_DEFAULT
CMAKELISTS="$SOURCE_ROOT"/CMakeLists.txt
CMAKECACHE=CMakeCache.txt
CMAKETOOLCHAIN="$SOURCE_ROOT"/kernel/"$CMAKE_COMPILER".cmake
DO_CMAKE_INITIALIZE=
EASY_KNOBS="$SOURCE_ROOT"/easy-settings.cmake
CMAKE_ARGS=
# Set to a non-null string (like "yes") to enable debugging output.
DEBUG_MATCHER=
MODE=invoke
# We use the following exit status conventions:
# 0: normal operation, successful, "true"
# 1: expected failure, "false"
# 2: usage error
# 3: other error
EXIT_STATUS=3
# Set up terminal capabilities (for displaying in bold and colors).
#
# See terminfo(5) for a list of terminal capability strings.
#
# tput returns an empty string (and exits with a nonzero status) for
# unsupported string capabilities, and -1 for unsupported integer
# capablilities.
BOLD=$(tput bold) || BOLD=
NORMAL=$(tput sgr0) || NORMAL=
NCOLORS=$(tput colors)
# If the terminal doesn't support color at all, these will remain null.
RED=
GREEN=
YELLOW=
CYAN=
# We want different foreground color numbers if we have a terminal capable of
# more than 8, because generally the contrast is bad if we use the low-numbered
# colors (bold helps, but only so much). On terminals truly capable of only 8
# colors, we have to rely on the implementation to provide good contrast.
if [ -n "$NCOLORS" ]
then
if [ $NCOLORS -gt 8 ]
then
RED=$(tput setaf 9)
GREEN=$(tput setaf 10)
YELLOW=$(tput setaf 11)
CYAN=$(tput setaf 14)
# This is an exact equality match on purpose. tput will report -1 for a
# truly monochrome terminal and in that case we don't want to mess with
# the setaf capability at all.
elif [ $NCOLORS -eq 8 ]
then
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
YELLOW=$(tput setaf 3)
CYAN=$(tput setaf 6)
fi
fi
# Emit diagnostic message.
# @params: a set of strings comprising a human-intelligible message
#
# Display the diagnostic message itself in bold.
_print () {
echo "${PROGNAME:-(unknown program)}: $BOLD$*$NORMAL"
}
# Emit debugging message to standard error.
# @params: a set of strings comprising a human-intelligible message
debug () {
_print "${CYAN}debug: $*" >&2
}
# Emit informational message to standard error.
notice () {
_print "${GREEN}notice: $*" >&2
}
# Emit warning message to standard error.
warn () {
_print "${YELLOW}warning: $*" >&2
}
# Emit error message to standard error.
fail () {
_print "${RED}error: $*" >&2
}
# Report unrecoverable error and terminate script.
# @params: a set of strings comprising a human-intelligible message
#
# Note: $EXIT_STATUS, if set in the invoking scope, determines the exit status
# used by this function.
die () {
_print "${RED}fatal error: $*" >&2
exit ${EXIT_STATUS:-3}
}
# [debugging] Report how the input line was classified.
# @params: a string describing the classification
describe () {
test -n "$DEBUG_MATCHER" && debug "$CMAKE_LINENO: $*" || :
}
# Back up the CMake cache file, re-run CMake, and see if the new cache file
# differs from the backup. If it does, the configuration is not stable and we
# will warn about it (see end of script).
#
# Returns 0 (success) if the files are the same; 1 if they differ; other if
# trouble.
is_configuration_stable () {
CMAKECACHE_BACKUP=$CMAKECACHE.griddle.bak
if ! [ -e $CMAKECACHE ]
then
die "CMake cache file \"$CMAKECACHE\" unexpectedly does not exist!"
fi
cp $CMAKECACHE $CMAKECACHE_BACKUP
# $CMAKE_ARGS is unquoted because because cmake needs shell word-splitting
# to be done on its parameters. Furthermore, there should be no
# configuration variables with whitespace embedded in their flag names or
# values. (Well, certainly not the _names_...)
cmake $CMAKE_ARGS . || die "cmake failed"
cmp -s $CMAKECACHE $CMAKECACHE_BACKUP
# `return` with no arguments returns the exit status of the last "simple
# command" executed, so don't insert anything between `cmp` and `return`.
return
}
# Break up Set directive and save interesting parts.
# @params: one or more strings comprising a line from a CMake input file
unpack_set () {
# TODO: Handle a last parameter of "FORCE".
MYLINE=$*
# Chop off directive.
MYLINE=${MYLINE#set(}
# Chop off trailing parenthesis.
MYLINE=${MYLINE%)}
# By turning off globbing and leaving $MYLINE unquoted, we largely get the
# word-splitting we want.
set -o noglob
set -- $MYLINE
set +o noglob
CONFIG_VAR=$1
shift
DEFAULT_VALUE=$1
shift
if [ "$1" = "CACHE" ]
then
CACHED="(cached)"
else
CACHED="(not cached)"
fi
shift
TYPE=$1
shift
DESCRIPTION=$*
# Chop off leading and trailing double quotes.
DESCRIPTION=${DESCRIPTION#\"}
DESCRIPTION=${DESCRIPTION%\"}
}
# Set the value of the variable named in $1 to the maximum of $2 and its current
# value.
# @params: $1: a variable name; $2: the potential new value
update_field_width () {
VAR=$1
# We use eval so we can get the value of the indirectly-referenced variable
# in VAR. E.g., if $VAR is "CV_WIDTH", we set $OLD_WIDTH to the value of
# $CV_WIDTH below.
eval OLD_WIDTH=\$$VAR
shift
VALUE=$*
NEW_WIDTH=${#VALUE}
if [ $NEW_WIDTH -gt $OLD_WIDTH ]
then
# We use eval to assign to the variable named in $VAR.
eval $VAR=$NEW_WIDTH
fi
}
# Perform sanity checks on the environment.
# Is a repo dir present in the PWD?
if [ -d "$REPO_DIR" ]
then
die "run this tool from a build directory (e.g., \"mkdir build; cd build\")"
fi
# Guard against rookie mistake of running tool in some non-build subdirectory of
# the repo checkout.
THIS_DIR=${PWD##*/}
if [ "$THIS_DIR" = kernel ] || [ "$THIS_DIR" = projects ] \
|| [ "$THIS_DIR" = tools ]
then
die "run this tool from a build directory (e.g., \"mkdir ../build;" \
" cd ../build\")"
fi
# Is a repo dir present in the PWD?
if ! [ -d "$SOURCE_ROOT"/"$REPO_DIR" ]
then
# We are completely in the wilderness.
die "cannot find \"$REPO_DIR\" in this directory or its parent;" \
"${NORMAL}you need to (1) initialise a repo with \"repo init -u" \
"\$GIT_CLONE_URL\", (2) \"repo sync\", (3) create a build directory" \
"(e.g., \"mkdir build\"), (4) change into that directory (e.g." \
"\"cd build\"), and (5) try to run this tool again."
fi
# Is an easy config file available?
if ! [ -r "$EASY_KNOBS" ]
then
# At this point we know we're probably in a build directory and there is a
# CMake lists file, but not an easy settings file.
die "\"$EASY_KNOBS\" does not exist or is not readable;" \
"${NORMAL}this project may not yet support \"$PROGNAME\""
fi
CMAKE_LINENO=0
# Set up some variables to compute pleasant field widths.
CV_WIDTH=0 # $CONFIG_VAR field with
TY_WIDTH=0 # $TYPE field width
DV_WIDTH=0 # $DEFAULT_VALUE field width
while read -r LINE
do
CMAKE_LINENO=$((CMAKE_LINENO + 1))
# Remove syntactically unimportant leading and trailing white space.
LINE=$(echo "$LINE" | sed -e 's/^\s\+//' -e 's/\s\+$//')
case "$LINE" in
('#'*)
describe "comment line"
;;
('')
describe "blank line"
;;
(set'('*)
describe "configuration variable: \"$LINE\""
unpack_set "$LINE"
update_field_width CV_WIDTH "$CONFIG_VAR"
update_field_width TY_WIDTH "$TYPE"
update_field_width DV_WIDTH "$DEFAULT_VALUE"
# Save the configuration variable name as an acceptable long option
# for getopt.
# If the configuration variable is of boolean type, its parameter is
# optional; getopt indicates that with a trailing double colon
# instead of a single one.
if [ "$TYPE" = BOOL ]
then
GETOPT_FLAGS=${GETOPT_FLAGS:+$GETOPT_FLAGS,}$CONFIG_VAR::
else
GETOPT_FLAGS=${GETOPT_FLAGS:+$GETOPT_FLAGS,}$CONFIG_VAR:
fi
# Use eval to interpolate $CONFIG_VAR into a shell variable. For
# instance, the following line might expand to:
# VAR_SIMULATION_TYPE=BOOL
eval "VAR_${CONFIG_VAR}_TYPE"="$TYPE"
# Pack information about the configuration variable (except for
# caching information) into a string to be decoded by show_usage().
#
# The "records" are separated by "@@" and the "fields" by "@:".
OPTIONS=${OPTIONS:+$OPTIONS@@}$CONFIG_VAR@:$TYPE@:$DEFAULT_VALUE@:$DESCRIPTION
OPTION_REPORT="${OPTION_REPORT:=}
$CONFIG_VAR is type: $TYPE, default: $DEFAULT_VALUE, $CACHED; $DESCRIPTION"
;;
(mark_as_advanced'('*)
describe "exporting external setting: \"$LINE\""
;;
(*)
die "$EASY_KNOBS:$CMAKE_LINENO: I don't know how to handle \"$LINE\""
;;
esac
done < "$EASY_KNOBS"
# Now that we've parsed the CMakefile, we know what options we can accept.
#
# Append a record separator to the end of $OPTIONS for ease of processing later.
OPTIONS=${OPTIONS:-}@@
# List supported target platforms.
#
# This function relies on the current working directory being the build
# directory, but this is true by the time it is called.
show_platform_help () {
# This is uglier than it should be because CMake insists on its input being
# seekable. So we have to set up a temporary file, ensure we write to it
# only by appending, and make sure it gets cleaned up by setting up a signal
# handler. Note also that CMake's message() writes to standard error.
#
# We quote $TEMP when dereferencing it because mktemp uses $TMPDIR, and the
# user might have set that to a whitespace-containing pathname.
#
# We give `rm` the `-f` option in the trap handler in the event we end up
# racing against the ordinary cleanup scenario. Consider:
# # Clean up the temporary file and deregister the signal handler.
# rm "$TEMP"
# <CTRL-C>
# trap - HUP INT QUIT TERM
# When the user interrupts the script, the temporary file has been removed
# but the signal handler has not yet been deregistered.
#
# This function can be greatly simplified once JIRA SELFOUR-2369 is fixed.
TEMP=$(mktemp)
# In our trap handler, we have to (1) do our cleanup work; (2) clear the
# trap handler (restoring the default signal handler); and (3) commit
# suicide so that the shell knows we exited abnormally. Unfortunately POSIX
# shell offers no way of knowing which signal we are handling, short of
# writing the trap handler multiple times (once for each signal); we choose
# INT as our final disposition somewhat arbitrarily.
#
# See <https://www.cons.org/cracauer/sigint.html> for a detailed exploration
# of this issue.
trap 'rm -f "$TEMP"; trap - HUP INT QUIT TERM; kill -s INT $$' \
HUP INT QUIT TERM
cat >> "$TEMP" <<EOF
include(configs/seL4Config.cmake)
foreach(val IN LISTS kernel_platforms)
message("\${val}")
endforeach()
EOF
(cd ../kernel && cmake -DCMAKE_TOOLCHAIN_FILE=ignore -P "$TEMP" 2>&1 \
| cut -d';' -f1)
# Clean up the temporary file and deregister the signal handler.
rm "$TEMP"
trap - HUP INT QUIT TERM
notice "not all seL4 projects (e.g., \"camkes\", \"sel4bench\") support" \
"all platforms"
}
# Display a usage message.
show_usage () {
# Make sure our field widths are wide enough for our column headings.
update_field_width CV_WIDTH "Option"
update_field_width TY_WIDTH "Type"
update_field_width DV_WIDTH "Default"
# Furthermore make sure the field width for the configuration flag name
# itself is wide enough to accommodate the two option dashes we will add.
CV_WIDTH=$(( CV_WIDTH + 2 ))
cat <<EOF
$PROGNAME: easy cooking with CMake
$PROGNAME eases the setup of seL4-related builds by exposing only the most
commonly-used configuration variables in the seL4 CMake infrastructure. These
differ between projects, but you can always discover them with:
$PROGNAME --help
Usage:
$PROGNAME [--compiler={gcc|llvm}] [CMAKE-CONFIGURATION-VARIABLE] ...
$PROGNAME --help
$PROGNAME --platform-help
Options:
--compiler={gcc|llvm} Report "gcc" or "llvm" compiler suite to CMake.
(default: $CMAKE_COMPILER_DEFAULT)
--help Display this message and exit.
--platform-help List supported target platforms and exit.
EOF
if [ -z "$OPTIONS" ]
then
cat <<EOF
The file "$EASY_KNOBS" defines no basic configuration options for this project.
EOF
return
fi
if [ -n "${GETOPT_FLAGS:+flags}" ]
then
echo
FORMAT_STRING="%${CV_WIDTH}s %${TY_WIDTH}s %${DV_WIDTH}s %s\n"
printf "$FORMAT_STRING" "Option" "Type" "Default" "Description"
echo
while [ -n "$OPTIONS" ]
do
# Unpack and display the information condensed into $OPTIONS.
#
# The "records" are separated by "@@" and the "fields" by "@:".
#
# Break off one option record at a time for clarity.
RECORD=${OPTIONS%%@@*}
OPTIONS=${OPTIONS#*@@}
# We now have one record in $RECORD. Extract the fields.
CONFIG_VAR=${RECORD%%@:*}
RECORD=${RECORD#*@:}
TYPE=${RECORD%%@:*}
RECORD=${RECORD#*@:}
DEFAULT_VALUE=${RECORD%%@:*}
RECORD=${RECORD#*@:}
DESCRIPTION=$RECORD
printf "$FORMAT_STRING" \
"--$CONFIG_VAR" "$TYPE" "$DEFAULT_VALUE" "$DESCRIPTION"
done
fi
}
# Check the option given against those extracted from the CMake file.
# @params: the option name to look up
# @return: 0 (true) if option recognized; 1 (false) otherwise
validate_name () {
FLAG=$1
if echo "$GETOPT_FLAGS" | egrep -q "(^|.+:)?$FLAG(:.+|$)?"
then
return 0
else
return 1
fi
}
# Check the option parameter given against the declared type.
# @params: an option name and its parameter
# @return: 0 (true) if parameter type-checks; 1 (false) otherwise
#
# When returning 1, be certain to issue a `fail` diagnostic.
validate_parameter () {
FLAG=$1
VALUE=$2
# Use eval to interpolate $FLAG into a shell variable which should have been
# defined in the big case statement above (when the CMake file was parsed).
#
# Calling validate_name() before this function should prevent any attempt at
# expanding an undefined variable name.
eval TYPE=\$VAR_${FLAG}_TYPE
case "$TYPE" in
(BOOL)
case "$VALUE" in
(ON|OFF)
;;
(*)
fail "\"$FLAG\" only supports values of \"ON\" or \"OFF\""
return 1
;;
esac
;;
(STRING)
# No validation at present.
;;
(*)
# This is a fatal error because it indicates a limitation of the script,
# not invalid user input.
die "unsupported configuration variable type \"$TYPE\" (\"$FLAG\")"
;;
esac
return 0
}
getopt -T || GETOPT_STATUS=$?
if [ $GETOPT_STATUS -ne 4 ]
then
die "getopt from util-linux required"
fi
if ! ARGS=$(getopt -o '' \
--long "${GETOPT_FLAGS:+$GETOPT_FLAGS,}"compiler:,help,platform-help \
--name "$PROGNAME" -- "$@")
then
show_usage >&2
exit 2
fi
eval set -- "$ARGS"
unset ARGS
HAD_ARGUMENT_PROBLEMS=
while [ -n "${1:-}" ]
do
case "$1" in
(--compiler)
if [ "$2" = gcc ] || [ "$2" = llvm ]
then
CMAKE_COMPILER="$2"
# We may be changing compilers; re-init CMake.
DO_CMAKE_INITIALIZE=yes
else
die "unrecognized compiler \"$2\"; expected \"gcc\" or \"llvm\""
fi
break
;;
(--help)
MODE=help
shift
;;
(--platform-help)
MODE=platform-help
shift
;;
(--)
shift
break
;;
(--*)
# Strip off the argument's leading dashes.
OPT=${1#--}
# Reset variables set by previous iterations.
FLAG=
VALUE=
if validate_name "$OPT"
then
MODE=invoke
else
# getopt should have caught this, but just in case...
fail "unrecognized configuration option \"$1\""
HAD_ARGUMENT_PROBLEMS=yes
fi
VALUE=$2
# Handle the option argument.
case "${VALUE:-}" in
# GNU getopt synthesises a single space as an option argument if one
# was not specified.
(" ")
fail "configuration option \"$FLAG\" must be given a value"
HAD_ARGUMENT_PROBLEMS=yes
;;
(*)
if validate_parameter "$FLAG" "$VALUE"
then
CMAKE_ARGS=$CMAKE_ARGS" -D$FLAG=$VALUE"
else
# validate_parameter() should have issued an error message.
HAD_ARGUMENT_PROBLEMS=yes
fi
;;
esac
# Dispose of the option and option-argument pair.
shift 2
# XXX: temporary hack until SELFOUR-1648 is resolved -- GBR
if [ "$FLAG" = "PLATFORM" ]
then
case "$VALUE" in
(sabre)
EXTRA_ARGS="-DAARCH32=1"
;;
(tk1)
EXTRA_ARGS="-DAARCH32HF=1"
;;
(tx[12])
EXTRA_ARGS="-DAARCH64=1"
;;
esac
fi
# XXX: end of hack -- GBR
;;
(*)
die "internal error while processing options"
;;
esac
done
if [ -n "$HAD_ARGUMENT_PROBLEMS" ]
then
notice "try \"$PROGNAME --help\" for option usage"
exit 2
fi
if ! [ -e $CMAKECACHE ]
then
# If the CMake cache file does not exist, call CMake with initialization
# flags.
DO_CMAKE_INITIALIZE=yes
fi
if [ $MODE = help ]
then
show_usage
elif [ $MODE = platform-help ]
then
show_platform_help
elif [ $MODE = invoke ]
then
if [ -n "$DO_CMAKE_INITIALIZE" ]
then
if [ -e "$CMAKELISTS" ]
then
# Some of these variables are unquoted because because cmake needs shell
# word-splitting to be done on its parameters. Furthermore, there
# should be no configuration variables with whitespace embedded in their
# flag names or values. (Well, certainly not the _names_...)
# $SOURCE_ROOT, however, could be anywhere in the user's file system and
# its value may have embedded whitespace.
cmake \
-DCMAKE_TOOLCHAIN_FILE="$CMAKETOOLCHAIN" \
-G Ninja \
${EXTRA_ARGS:-} \
$CMAKE_ARGS \
-C "$SOURCE_ROOT/settings.cmake" \
"$SOURCE_ROOT"
elif [ -e "$EASY_KNOBS" ]
then
# If we don't have a CMakeLists.txt in the top level project directory then
# assume we use the project's directory tied to easy-settings.cmake and resolve
# that to use as the CMake source directory.
REAL_EASY_KNOBS=$(realpath "$EASY_KNOBS")
PROJECT_DIR=${REAL_EASY_KNOBS%/*}
# Initialize CMake.
cmake -G Ninja ${EXTRA_ARGS:-} \
$CMAKE_ARGS \
-C "$PROJECT_DIR/settings.cmake" "$PROJECT_DIR"
else
# This case shouldn't be hit as if $SOURCE_ROOT/easy-settings.cmake doesn't
# exist then the script should have failed earlier.
die "impossible: \"$CMAKELISTS\" does not exist and \"$EASY_KNOBS\" does not either"
fi
fi
# If this tool is re-run over an existing build with new parameters, the
# CMake cache file may mutate. Warn about it if it does, and re-run CMake
# until it stabilizes.
THREW_STABILITY_DIAGNOSTIC=
while ! is_configuration_stable
do
warn "configuration not stable; regenerating"
THREW_STABILITY_DIAGNOSTIC=yes
done
if [ -n "$THREW_STABILITY_DIAGNOSTIC" ]
then
notice "configuration is stable"
fi
rm $CMAKECACHE_BACKUP
else
die "internal error; unrecognized operation mode \"$MODE\""
fi