aboutsummaryrefslogtreecommitdiff
path: root/cbt
blob: 1ec2d0f04fc3421dd8764e1e1ed629e4a8dad625 (plain) (blame)
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
#!/usr/bin/env bash
# Launcher bash script that bootstraps CBT from source.
# (Some of the code for reporting missing dependencies and waiting for nailgun to come up is a bit weird.)
# This is intentionally kept as small as possible.
# Welcome improvements to this file:
# - reduce code size through better ideas
# - reduce code size by moving more of this into type-checked Java/Scala code (if possible without performance loss).
# - reduction of dependencies
# - performance improvements
shopt -qs extglob

seconds() {
	date +"%s"
}

nanos() {
	n=$(date +"%N")
	if [ "$n" = "N" ]; then
		n=$(gdate +"%N" 2>/dev/null)
	fi
	if [ "$n" = "" ]; then
		n="0"
	fi
	echo 1$n
}

start_seconds=$(seconds)
start_nanos=$(nanos)

time_taken() {
	i=$(( $(seconds) - start_seconds ))
	n=$(( $(( $(nanos) - start_nanos )) / 1000000 ))
	if [[ ( $n -lt 0 ) ]]; then
		i=$(( i-1 ))
		n=$(( n+1000 ))
	fi
	echo "$i.$n"
}

# utility function to log message to stderr with stating the time
log () {
	msg=$1
	enabled=1
	while test $# -gt 0; do
		if [[ "$1" == "-Dlog="* ]]; then
			if [[ "$1" == *"time"* ]] || [[ "$1" == *"bash"* ]] || [[ "$1" == *"all"* ]]; then
				enabled=0
			fi
		fi
		shift
	done
	if [ $enabled -eq 0 ]; then
		delta=$(time_taken)
		echo "[$delta] $msg" 1>&2
	fi
}

log "Checking for dependencies" "$@"

which javac >/dev/null 2>/dev/null
javac_installed=$?
if [ ! $javac_installed -eq 0 ]; then
	echo "You need to install javac 1.7 or later! CBT needs it to bootstrap from Java sources into Scala." 1>&2
	exit 1
fi

# log "cutting javac version" "$@"
# javac_version=$(javac -version 2>&1) # e.g. "javac 1.8.0_u60"
# javac_version_update=${javac_version/javac 1./} # e.g. "8.0_u60"
# javac_version_minor_pointed=${javac_version_update%_*} # e.g. "8.0"
# javac_version_minor=${javac_version_minor_pointed%.*} # e.g. "8"
# log "cutting javac version done" "$@"
# if [ ! "$javac_version_minor" -ge "7" ]; then
# 	echo "You need to install javac version 1.7 or greater!" 2>&1
# 	echo "Current javac version is $javac_version" 2>&1
# 	exit 1
# fi

which realpath >/dev/null 2>/dev/null
realpath_installed=$?
which gcc >/dev/null 2>/dev/null
gcc_installed=$?
if [ ! $realpath_installed -eq 0 ] && [ ! $gcc_installed -eq 0 ]; then
	echo "You need realpath or gcc installed! CBT needs it to locate itself reliably." 1>&2
	exit 252
fi

which gpg >/dev/null 2>/dev/null
gpg_installed=$?
if [ ! $gpg_installed -eq 0 ]; then
	echo "(Note: gpg not found. In order to use publishSigned you'll need it.)" 1>&2
fi

CWD=$(pwd)

CBT_SCRIPT="$(readlink "$0")"
if [ "$CBT_SCRIPT" = "" ]; then
	CBT_SCRIPT="$0"
fi
if [ "$CBT_SCRIPT" = "" ]; then
	echo "cannot locate cbt launcher" 1>&2
	exit 252
fi
_DIR=$(dirname "$CBT_SCRIPT" 2>/dev/null || dirname "$0" 2>/dev/null )

log "Find out real path. Build realpath if needed." "$@"

export CBT_HOME
CBT_HOME="$(dirname "$("$_DIR"/realpath/realpath.sh "$0")")"

export NAILGUN="$CBT_HOME"/nailgun_launcher/
export TARGET=target/scala-2.11/classes/
mkdir -p "$NAILGUN$TARGET"

nailgun_out="$NAILGUN/target/nailgun.stdout.log"
nailgun_err="$NAILGUN/target/nailgun.strerr.log"

DEBUG=""
foo(){
	while test $# -gt 0; do
		case "$1" in
			"-debug")
				DEBUG="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005"
			;;
			"-Dlog=nailgun")
				nailgun_out=/dev/stderr
				nailgun_err=/dev/stderr
			;;
			"-Dlog=all")
				nailgun_out=/dev/stderr
				nailgun_err=/dev/stderr
			;;
		esac
		shift
	done
}

foo "$@"

if [ ! "$DEBUG" == "" ]; then
	shift
fi

JAVA_OPTS_CBT=($DEBUG -Xmx1536m -Xss10M "-XX:MaxJavaStackTraceDepth=-1" -XX:+TieredCompilation "-XX:TieredStopAtLevel=1" -Xverify:none)

# ng on osx, install via brew install nailgun
# ng-nailgun on unbuntu, install via apt-get install nailgun
NG_EXECUTABLE=$(which ng 2>/dev/null|| env which ng-nailgun 2>/dev/null)

# on osx: install via brew install nailgun. /usr/local/Cellar/nailgun/*/libexec/nailgun-server-*.jar
# on unbuntu: install via apt-get install nailgun. /usr/share/java/nailgun.jar
# on debian: install via apt-get install nailgun. /usr/share/java/nailgun-server.jar
# on fedora: install via dnf install nailgun. /usr/share/java/nailgun/nailgun-server.jar
NG_SERVER_JAR=$( \
	find /usr/local/Cellar/nailgun/*/libexec/nailgun-server-*.jar 2>/dev/null \
	|| find /usr/share/java/nailgun.jar 2>/dev/null \
	|| find /usr/share/java/nailgun-server.jar 2>/dev/null \
	|| find /usr/share/java/nailgun/nailgun-server.jar 2>/dev/null \
)

nailgun_installed=0
if ([ "$NG_EXECUTABLE" == "" ] || [ "$NG_SERVER_JAR" == "" ]) && [ "$1" != "direct" ]; then
	nailgun_installed=1
	echo "(Note: nailgun not found. It makes CBT faster! Try 'brew install nailgun' or 'apt-get install nailgun'.)" 1>&2
fi

NAILGUN_PORT=4444
NG="$NG_EXECUTABLE --nailgun-port $NAILGUN_PORT"

if [ "$1" = "kill" ]; then
	echo "Stopping background process (nailgun)" 1>&2
	$NG ng-stop >> "$nailgun_out" 2>> "$nailgun_err" &
	exit 1
fi

which nc >/dev/null 2>/dev/null
nc_installed=$?

log "Check for running nailgun with nc." "$@"

server_up=1
if [ $nc_installed -eq 0 ]; then
	nc -z -n -w 1 127.0.0.1 $NAILGUN_PORT > /dev/null 2>&1
	server_up=$?
else
	echo "(Note: nc not found. It will make slightly startup faster.)" 1>&2
fi

use_nailgun=0
if [  "$1" = "direct" ]; then
	use_nailgun=1
	shift
fi
loop=1
if [ "$1" == "loop" ]; then
	loop=0
	shift
fi
clearScreen=1
if [ "$1" == "clear" ]; then
	clearScreen=0
	shift
fi

if [ $nailgun_installed -eq 1 ] || [ "$1" = "publishSigned" ]; then
	use_nailgun=1
fi

if [ $use_nailgun -eq 0 ] && [ $server_up -eq 0 ] && [ ! "$DEBUG" == "" ]; then
	echo "Can't use \`-debug\` (without \`direct\`) when nailgun is already running. If you started it up with \`-debug\` you can still connect to it. Otherwise use \`cbt kill\` to kill it."
	exit 1
fi

if [ $use_nailgun -eq 0 ] && [ ! $server_up -eq 0 ]; then
	log "Starting background process (nailgun)" "$@"
	# try to start nailgun-server, just in case it's not up
	java "${options[@]}" "${JAVA_OPTS_CBT[@]}" -jar "$NG_SERVER_JAR" 127.0.0.1:$NAILGUN_PORT >> "$nailgun_out" 2>> "$nailgun_err" &
	if [ ! "$DEBUG" == "" ]; then
		echo "Started nailgun server in debug mode"
		exit 1
	fi
fi

stage1 () {
	log "Checking for changes in cbt/nailgun_launcher" "$@"
	NAILGUN_INDICATOR=$NAILGUN$TARGET../classes.last-success
	changed=1
	NAILGUN_SOURCES=("$NAILGUN"*.java "$CBT_HOME"/libraries/common-0/*.java)
	for file in "${NAILGUN_SOURCES[@]}"; do
		if [ "$file" -nt "$NAILGUN_INDICATOR" ]; then changed=0; fi
	done
	exitCode=0
	if [ $changed -eq 0 ]; then
		echo "Stopping background process (nailgun) if running" 1>&2
		$NG ng-stop >> "$nailgun_out" 2>> "$nailgun_err" &
		#rm $NAILGUN$TARGET/cbt/*.class 2>/dev/null # defensive delete of potentially broken class files
		echo "Compiling cbt/nailgun_launcher" 1>&2
		COMPILE_TIME=$(date +%YY%mm%dd%HH%MM.%SS|sed "s/[YmdHMS]//g")
		#echo javac -Xlint:deprecation -Xlint:unchecked -d "$NAILGUN$TARGET" "${NAILGUN_SOURCES[@]}"
		javac -Xlint:deprecation -Xlint:unchecked -d "$NAILGUN$TARGET" "${NAILGUN_SOURCES[@]}"
		exitCode=$?
		if [ $exitCode -eq 0 ]; then
			touch -t "$COMPILE_TIME" "$NAILGUN_INDICATOR"
			if [ $use_nailgun -eq 0 ]; then
				echo "Starting background process (nailgun)" 1>&2
				java "${options[@]}" "${JAVA_OPTS_CBT[@]}" -jar "$NG_SERVER_JAR" 127.0.0.1:$NAILGUN_PORT >> "$nailgun_out" 2>> "$nailgun_err" &
				sleep 1
			fi
		fi
	fi

	log "run CBT and loop if desired. This allows recompiling CBT itself as part of compile looping." "$@"

	if [ $exitCode -eq 0 ]; then
		if [ ! $use_nailgun -eq 0 ]
		then
			log "Running JVM directly" "$@"
			options=($JAVA_OPTS)
			# JVM options to improve startup time. See https://github.com/cvogt/cbt/pull/262
			java "${options[@]}" "${JAVA_OPTS_CBT[@]}" -cp "$NAILGUN$TARGET" cbt.NailgunLauncher "$(time_taken)" "$CWD" "$loop" "$@"
			exitCode=$?
		else
			log "Running via background process (nailgun)" "$@"
			for i in 0 1 2 3 4 5 6 7 8 9; do
				log "Adding classpath." "$@"
				$NG ng-cp "$NAILGUN$TARGET" >> "$nailgun_out" 2>> "$nailgun_err"
				log "Checking if nailgun is up yet." "$@"
				$NG cbt.NailgunLauncher check-alive >> "$nailgun_out" 2>> "$nailgun_err"
				alive=$?
				if [ $alive -eq 131 ] || [ $alive -eq 33 ]; then
				# the 33 is not working right now
				#	echo "Nailgun call failed. Try 'cbt kill' and check the error log cbt/nailgun_launcher/target/nailgun.stderr.log" 1>&2
				#elif [ $alive -eq 33 ]; then
					break
				else
					log "Nope. Sleeping for 0.5 seconds" "$@"
					#if [ "$i" -gt 1 ]; then
					#	echo "Waiting for nailgun to start... (In case of problems try -Dlog=nailgun or check logs in cbt/nailgun_launcher/target/*.log)" 1>&2
					#fi
				fi
				sleep 0.3
			done
			log "Running CBT via Nailgun." "$@"
			$NG cbt.NailgunLauncher "$(time_taken)" "$CWD" "$loop" "$@"
			exitCode=$?
		fi
		log "Done running CBT." "$@"
	fi
}


USER_PRESSED_CTRL_C=130

CBT_LOOP_FILE="$CWD/target/.cbt-loop.tmp"
CBT_KILL_FILE="$CWD/target/.cbt-kill.tmp"
if [ $loop -eq 0 ]; then
	which fswatch >/dev/null 2>/dev/null
	export fswatch_installed=$?
	if [ ! $fswatch_installed -eq 0 ]; then
		echo "please install fswatch to use cbt loop, e.g. via brew install fswatch"
		exit 1
	fi
fi
while true; do
	if [ $clearScreen -eq 0 ]; then
		clear
	fi
	if [ -f "$CBT_LOOP_FILE" ]; then
		rm "$CBT_LOOP_FILE"
	fi
	if [ -f "$CBT_KILL_FILE" ]; then
		rm "$CBT_KILL_FILE"
	fi
	stage1 "$@"
	if [ ! $loop -eq 0 ] || [ $exitCode -eq $USER_PRESSED_CTRL_C ]; then
		log "not looping, exiting" "$@"
		break
	else
		for file in "${NAILGUN_SOURCES[@]}"; do
			echo "$file" >> "$CBT_LOOP_FILE"
		done
		files=
		if [ -f "$CBT_LOOP_FILE" ]; then
			files=($(sort "$CBT_LOOP_FILE"))
		fi
		pids=
		if [ -f "$CBT_KILL_FILE" ]; then
			pids=($(cat "$CBT_KILL_FILE")) # FIXME: should we uniq here?
			#rm "$CBT_LOOP_FILE"
		fi
		echo ""
		echo "Watching for file changes... (ctrl+c short press for loop, long press for abort)"
		for file in "${files[@]}"; do
			if [ $file == "" ]; then
				echo "warning: empty file found in loop file list" 1>&2
			fi
		done
		fswatch --one-event "${files[@]}"
		for pid in "${pids[@]}"; do
			if [ $pid == "" ]; then
				echo "warning: empty pid found in pid kill list" 1>&2
			else
				log "killing process $pid"
				kill -KILL $pid
			fi
		done
	fi
done

log "Exiting CBT" "$@"
exit $exitCode