#!/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 NG_EXECUTABLE=$(which ng || which ng-nailgun) NG_SERVER=$(which ng-server || find /usr/local/Cellar/nailgun/*/libexec/nailgun-server-*.jar 2>/dev/null | awk '{print "java -jar " $0}') nailgun_installed=0 if [ "$NG_EXECUTABLE" == "" ] || [ "$NG_SERVER" == "" ]; then nailgun_installed=1 echo "(Note: nailgun not found. It makes CBT faster! Try 'brew install nailgun' or 'apt install nailgun'.)" 1>&2 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 NAILGUN_PORT=4444 NG="$NG_EXECUTABLE --nailgun-port $NAILGUN_PORT" 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 [ "$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 if [ $nailgun_installed -eq 1 ] || [ "$1" = "publishSigned" ]; then use_nailgun=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 $NG_SERVER 127.0.0.1:$NAILGUN_PORT >> $nailgun_out 2>> $nailgun_err & fi stage1 () { log "Checking for changes in cbt/nailgun_launcher" "$@" NAILGUN_INDICATOR=$NAILGUN$TARGET../classes.last-success changed=1 for file in "$NAILGUN"/*.java; 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") javac -Xlint:deprecation -Xlint:unchecked -d "$NAILGUN$TARGET" "$NAILGUN"*.java 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 ng-server 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[@]}" $DEBUG -Xmx6072m -Xss10M -XX:MaxJavaStackTraceDepth=-1 -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xverify:none -cp "$NAILGUN$TARGET" cbt.NailgunLauncher "$(time_taken)" "$CWD" "$@" 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" "$@" exitCode=$? fi log "Done running CBT." "$@" fi } loop=1 case "$1" in "loop") loop=0 esac case "$2" in "loop") loop=0 esac CBT_SIGNALS_LOOPING=253 USER_PRESSED_CTRL_C=130 CBT_LOOP_FILE="$CWD/target/.cbt-loop.tmp" while true; do stage1 "$@" if [ ! $loop -eq 0 ] || [ $exitCode -eq $USER_PRESSED_CTRL_C ]; then log "not looping, exiting" "$@" break else if [ ! $exitCode -eq $CBT_SIGNALS_LOOPING ]; then log "exitCode $exitCode" "$@" which fswatch >/dev/null 2>/dev/null export fswatch_installed=$? if [ -f "$CBT_LOOP_FILE" ]; then if [ $fswatch_installed -eq 0 ]; then # fswatch allows looping over CBT's sources itself log "fswatch found. looping." "$@" files=($(cat "$CBT_LOOP_FILE")) fswatch --one-event "${files[@]}" else log "fswatch not installed, stopping cbt" "$@" break fi else log "no $CBT_LOOP_FILE file, stopping cbt" "$@" break fi fi fi echo "======= Restarting CBT =======" 1>&2 done log "Exiting CBT" "$@" exit $exitCode