#!/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