|
#!/usr/bin/env bash |
|
# |
|
# A more capable sbt runner, coincidentally also called sbt. |
|
# Author: Paul Phillips <[email protected]> |
|
|
|
# todo - make this dynamic |
|
declare -r sbt_release_version=0.12.0 |
|
declare -r sbt_snapshot_version=0.13.0-SNAPSHOT |
|
|
|
unset sbt_jar sbt_dir sbt_create sbt_snapshot sbt_launch_dir |
|
unset scala_version java_home sbt_explicit_version |
|
unset verbose debug quiet |
|
|
|
for arg in "$@"; do |
|
case $arg in |
|
-q|-quiet) quiet=1 ;; |
|
*) ;; |
|
esac |
|
done |
|
|
|
build_props_sbt () { |
|
if [[ -f project/build.properties ]]; then |
|
versionLine=$(grep ^sbt.version project/build.properties) |
|
versionString=${versionLine##sbt.version=} |
|
echo "$versionString" |
|
fi |
|
} |
|
|
|
update_build_props_sbt () { |
|
local ver="$1" |
|
local old=$(build_props_sbt) |
|
|
|
if [[ $ver == $old ]]; then |
|
return |
|
elif [[ -f project/build.properties ]]; then |
|
perl -pi -e "s/^sbt\.version=.*\$/sbt.version=${ver}/" project/build.properties |
|
grep -q '^sbt.version=' project/build.properties || echo "sbt.version=${ver}" >> project/build.properties |
|
|
|
echo !!! |
|
echo !!! Updated file project/build.properties setting sbt.version to: $ver |
|
echo !!! Previous value was: $old |
|
echo !!! |
|
fi |
|
} |
|
|
|
sbt_version () { |
|
if [[ -n $sbt_explicit_version ]]; then |
|
echo $sbt_explicit_version |
|
else |
|
local v=$(build_props_sbt) |
|
if [[ -n $v ]]; then |
|
echo $v |
|
else |
|
echo $sbt_release_version |
|
fi |
|
fi |
|
} |
|
|
|
echoerr () { |
|
[[ -z $quiet ]] && echo 1>&2 "$@" |
|
} |
|
vlog () { |
|
[[ $verbose || $debug ]] && echoerr "$@" |
|
} |
|
dlog () { |
|
[[ $debug ]] && echoerr "$@" |
|
} |
|
|
|
# this seems to cover the bases on OSX, and someone will |
|
# have to tell me about the others. |
|
get_script_path () { |
|
local path="$1" |
|
[[ -L "$path" ]] || { echo "$path" ; return; } |
|
|
|
local target=$(readlink "$path") |
|
if [[ "${target:0:1}" == "/" ]]; then |
|
echo "$target" |
|
else |
|
echo "$(dirname $path)/$target" |
|
fi |
|
} |
|
|
|
# a ham-fisted attempt to move some memory settings in concert |
|
# so they need not be dicked around with individually. |
|
get_mem_opts () { |
|
local mem=${1:-1536} |
|
local perm=$(( $mem / 4 )) |
|
(( $perm > 256 )) || perm=256 |
|
(( $perm < 1024 )) || perm=1024 |
|
local codecache=$(( $perm / 2 )) |
|
|
|
echo "-Xms${mem}m -Xmx${mem}m -XX:MaxPermSize=${perm}m -XX:ReservedCodeCacheSize=${codecache}m" |
|
} |
|
|
|
die() { |
|
echo "Aborting: $@" |
|
exit 1 |
|
} |
|
|
|
make_url () { |
|
groupid="$1" |
|
category="$2" |
|
version="$3" |
|
|
|
echo "http://typesafe.artifactoryonline.com/typesafe/ivy-$category/$groupid/sbt-launch/$version/sbt-launch.jar" |
|
} |
|
|
|
declare -r default_jvm_opts="-Dfile.encoding=UTF8" |
|
declare -r default_sbt_opts="-XX:+CMSClassUnloadingEnabled" |
|
declare -r default_sbt_mem=1536 |
|
declare -r noshare_opts="-Dsbt.global.base=project/.sbtboot -Dsbt.boot.directory=project/.boot -Dsbt.ivy.home=project/.ivy" |
|
declare -r sbt_opts_file=".sbtopts" |
|
declare -r jvm_opts_file=".jvmopts" |
|
declare -r latest_28="2.8.2" |
|
declare -r latest_29="2.9.2" |
|
declare -r latest_210="2.10.0-SNAPSHOT" |
|
|
|
declare -r script_path=$(get_script_path "$BASH_SOURCE") |
|
declare -r script_dir="$(dirname $script_path)" |
|
declare -r script_name="$(basename $script_path)" |
|
|
|
# some non-read-onlies set with defaults |
|
declare java_cmd=java |
|
declare sbt_launch_dir="$script_dir/.lib" |
|
declare sbt_universal_launcher="$script_dir/lib/sbt-launch.jar" |
|
declare sbt_mem=$default_sbt_mem |
|
declare sbt_jar=$sbt_universal_launcher |
|
|
|
# pull -J and -D options to give to java. |
|
declare -a residual_args |
|
declare -a java_args |
|
declare -a scalac_args |
|
declare -a sbt_commands |
|
|
|
build_props_scala () { |
|
if [[ -f project/build.properties ]]; then |
|
versionLine=$(grep ^build.scala.versions project/build.properties) |
|
versionString=${versionLine##build.scala.versions=} |
|
echo ${versionString%% .*} |
|
fi |
|
} |
|
|
|
execRunner () { |
|
# print the arguments one to a line, quoting any containing spaces |
|
[[ $verbose || $debug ]] && echo "# Executing command line:" && { |
|
for arg; do |
|
if printf "%s\n" "$arg" | grep -q ' '; then |
|
printf "\"%s\"\n" "$arg" |
|
else |
|
printf "%s\n" "$arg" |
|
fi |
|
done |
|
echo "" |
|
} |
|
|
|
exec "$@" |
|
} |
|
|
|
sbt_groupid () { |
|
case $(sbt_version) in |
|
0.7.*) echo org.scala-tools.sbt ;; |
|
0.10.*) echo org.scala-tools.sbt ;; |
|
0.11.[12]) echo org.scala-tools.sbt ;; |
|
*) echo org.scala-sbt ;; |
|
esac |
|
} |
|
|
|
sbt_artifactory_list () { |
|
local version0=$(sbt_version) |
|
local version=${version0%-SNAPSHOT} |
|
local url="http://typesafe.artifactoryonline.com/typesafe/ivy-snapshots/$(sbt_groupid)/sbt-launch/" |
|
dlog "Looking for snapshot list at: $url " |
|
|
|
curl -s --list-only "$url" | \ |
|
grep -F $version | \ |
|
perl -e 'print reverse <>' | \ |
|
perl -pe 's#^<a href="([^"/]+).*#$1#;' |
|
} |
|
|
|
make_release_url () { |
|
make_url $(sbt_groupid) releases $(sbt_version) |
|
} |
|
|
|
# argument is e.g. 0.13.0-SNAPSHOT |
|
# finds the actual version (with the build id) at artifactory |
|
make_snapshot_url () { |
|
for ver in $(sbt_artifactory_list); do |
|
local url=$(make_url $(sbt_groupid) snapshots $ver) |
|
dlog "Testing $url" |
|
curl -s --head "$url" >/dev/null |
|
dlog "curl returned: $?" |
|
echo "$url" |
|
return |
|
done |
|
} |
|
|
|
jar_url () { |
|
case $(sbt_version) in |
|
0.7.*) echo "http://simple-build-tool.googlecode.com/files/sbt-launch-0.7.7.jar" ;; |
|
*-SNAPSHOT) make_snapshot_url ;; |
|
*) make_release_url ;; |
|
esac |
|
} |
|
|
|
jar_file () { |
|
echo "$sbt_launch_dir/$1/sbt-launch.jar" |
|
} |
|
|
|
download_url () { |
|
local url="$1" |
|
local jar="$2" |
|
|
|
echo "Downloading sbt launcher $(sbt_version):" |
|
echo " From $url" |
|
echo " To $jar" |
|
|
|
mkdir -p $(dirname "$jar") && { |
|
if which curl >/dev/null; then |
|
curl --fail --silent "$url" --output "$jar" |
|
elif which wget >/dev/null; then |
|
wget --quiet -O "$jar" "$url" |
|
fi |
|
} && [[ -f "$jar" ]] |
|
} |
|
|
|
acquire_sbt_jar () { |
|
sbt_url="$(jar_url)" |
|
sbt_jar="$(jar_file $(sbt_version))" |
|
|
|
[[ -f "$sbt_jar" ]] || download_url "$sbt_url" "$sbt_jar" |
|
} |
|
|
|
usage () { |
|
cat <<EOM |
|
Usage: $script_name [options] |
|
|
|
-h | -help print this message |
|
-v | -verbose this runner is chattier |
|
-d | -debug set sbt log level to Debug |
|
-q | -quiet set sbt log level to Error |
|
-no-colors disable ANSI color codes |
|
-sbt-create start sbt even if current directory contains no sbt project |
|
-sbt-dir <path> path to global settings/plugins directory (default: ~/.sbt/<version>) |
|
-sbt-boot <path> path to shared boot directory (default: ~/.sbt/boot in 0.11+) |
|
-ivy <path> path to local Ivy repository (default: ~/.ivy2) |
|
-mem <integer> set memory options (default: $sbt_mem, which is |
|
$(get_mem_opts $sbt_mem) ) |
|
-no-share use all local caches; no sharing |
|
-offline put sbt in offline mode |
|
-jvm-debug <port> Turn on JVM debugging, open at the given port. |
|
-batch Disable interactive mode |
|
|
|
# sbt version (default: from project/build.properties if present, else latest release) |
|
!!! The only way to accomplish this pre-0.12.0 if there is a build.properties file which |
|
!!! contains an sbt.version property is to update the file on disk. That's what this does. |
|
-sbt-version <version> use the specified version of sbt |
|
-sbt-jar <path> use the specified jar as the sbt launcher |
|
-sbt-snapshot use a snapshot version of sbt |
|
-sbt-launch-dir <path> directory to hold sbt launchers (default: $sbt_launch_dir) |
|
|
|
# scala version (default: as chosen by sbt) |
|
-28 use $latest_28 |
|
-29 use $latest_29 |
|
-210 use $latest_210 |
|
-scala-home <path> use the scala build at the specified directory |
|
-scala-version <version> use the specified version of scala |
|
|
|
# java version (default: java from PATH, currently $(java -version |& grep version)) |
|
-java-home <path> alternate JAVA_HOME |
|
|
|
# jvm options and output control |
|
JAVA_OPTS environment variable holding jvm args, if unset uses "$default_jvm_opts" |
|
SBT_OPTS environment variable holding jvm args, if unset uses "$default_sbt_opts" |
|
.jvmopts if file is in sbt root, it is prepended to the args given to the jvm |
|
.sbtopts if file is in sbt root, it is prepended to the args given to **sbt** |
|
-Dkey=val pass -Dkey=val directly to the jvm |
|
-J-X pass option -X directly to the jvm (-J is stripped) |
|
-S-X add -X to sbt's scalacOptions (-J is stripped) |
|
|
|
In the case of duplicated or conflicting options, the order above |
|
shows precedence: JAVA_OPTS lowest, command line options highest. |
|
EOM |
|
} |
|
|
|
addJava () { |
|
dlog "[addJava] arg = '$1'" |
|
java_args=( "${java_args[@]}" "$1" ) |
|
} |
|
addSbt () { |
|
dlog "[addSbt] arg = '$1'" |
|
sbt_commands=( "${sbt_commands[@]}" "$1" ) |
|
} |
|
addScalac () { |
|
dlog "[addScalac] arg = '$1'" |
|
scalac_args=( "${scalac_args[@]}" "$1" ) |
|
} |
|
addResidual () { |
|
dlog "[residual] arg = '$1'" |
|
residual_args=( "${residual_args[@]}" "$1" ) |
|
} |
|
addResolver () { |
|
addSbt "set resolvers in ThisBuild += $1" |
|
} |
|
addDebugger () { |
|
addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1" |
|
} |
|
get_jvm_opts () { |
|
# echo "${JAVA_OPTS:-$default_jvm_opts}" |
|
# echo "${SBT_OPTS:-$default_sbt_opts}" |
|
|
|
[[ -f "$jvm_opts_file" ]] && cat "$jvm_opts_file" |
|
} |
|
|
|
process_args () |
|
{ |
|
require_arg () { |
|
local type="$1" |
|
local opt="$2" |
|
local arg="$3" |
|
|
|
if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then |
|
die "$opt requires <$type> argument" |
|
fi |
|
} |
|
while [[ $# -gt 0 ]]; do |
|
case "$1" in |
|
-h|-help) usage; exit 1 ;; |
|
-v|-verbose) verbose=1 && shift ;; |
|
-d|-debug) debug=1 && shift ;; |
|
-q|-quiet) quiet=1 && shift ;; |
|
|
|
-ivy) require_arg path "$1" "$2" && addJava "-Dsbt.ivy.home=$2" && shift 2 ;; |
|
-mem) require_arg integer "$1" "$2" && sbt_mem="$2" && shift 2 ;; |
|
-no-colors) addJava "-Dsbt.log.noformat=true" && shift ;; |
|
-no-share) addJava "$noshare_opts" && shift ;; |
|
-sbt-boot) require_arg path "$1" "$2" && addJava "-Dsbt.boot.directory=$2" && shift 2 ;; |
|
-sbt-dir) require_arg path "$1" "$2" && sbt_dir="$2" && shift 2 ;; |
|
-debug-inc) addJava "-Dxsbt.inc.debug=true" && shift ;; |
|
-offline) addSbt "set offline := true" && shift ;; |
|
-jvm-debug) require_arg port "$1" "$2" && addDebugger $2 && shift 2 ;; |
|
-batch) exec </dev/null && shift ;; |
|
|
|
-sbt-create) sbt_create=true && shift ;; |
|
-sbt-snapshot) sbt_explicit_version=$sbt_snapshot_version && shift ;; |
|
-sbt-jar) require_arg path "$1" "$2" && sbt_jar="$2" && shift 2 ;; |
|
-sbt-version) require_arg version "$1" "$2" && sbt_explicit_version="$2" && shift 2 ;; |
|
-sbt-launch-dir) require_arg path "$1" "$2" && sbt_launch_dir="$2" && shift 2 ;; |
|
-scala-version) require_arg version "$1" "$2" && addSbt "set scalaVersion := \"$2\"" && shift 2 ;; |
|
-scala-home) require_arg path "$1" "$2" && addSbt "set scalaHome in ThisBuild := Some(file(\"$2\"))" && shift 2 ;; |
|
-java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;; |
|
|
|
-D*) addJava "$1" && shift ;; |
|
-J*) addJava "${1:2}" && shift ;; |
|
-S*) addScalac "${1:2}" && shift ;; |
|
-28) addSbt "++ $latest_28" && shift ;; |
|
-29) addSbt "++ $latest_29" && shift ;; |
|
-210) addSbt "++ $latest_210" && shift ;; |
|
|
|
*) addResidual "$1" && shift ;; |
|
esac |
|
done |
|
|
|
[[ $debug ]] && { |
|
case $(sbt_version) in |
|
0.7.*) addSbt "debug" ;; |
|
*) addSbt "set logLevel in Global := Level.Debug" ;; |
|
esac |
|
} |
|
[[ $quiet ]] && { |
|
case $(sbt_version) in |
|
0.7.*) ;; |
|
*) addSbt "set logLevel in Global := Level.Error" ;; |
|
esac |
|
} |
|
} |
|
|
|
# if .sbtopts exists, prepend its contents to $@ so it can be processed by this runner |
|
[[ -f "$sbt_opts_file" ]] && { |
|
sbtargs=() |
|
while IFS= read -r arg; do |
|
sbtargs=( "${sbtargs[@]}" "$arg" ) |
|
done <"$sbt_opts_file" |
|
|
|
set -- "${sbtargs[@]}" "$@" |
|
} |
|
|
|
# process the combined args, then reset "$@" to the residuals |
|
process_args "$@" |
|
set -- "${residual_args[@]}" |
|
argumentCount=$# |
|
|
|
# set scalacOptions if we were given any -S opts |
|
[[ ${#scalac_args[@]} -eq 0 ]] || addSbt "set scalacOptions in ThisBuild += \"${scalac_args[@]}\"" |
|
|
|
# Update build.properties no disk to set explicit version - sbt gives us no choice |
|
[[ -n "$sbt_explicit_version" ]] && update_build_props_sbt "$sbt_explicit_version" |
|
echoerr "Detected sbt version $(sbt_version)" |
|
|
|
[[ -n "$scala_version" ]] && echo "Overriding scala version to $scala_version" |
|
|
|
# no args - alert them there's stuff in here |
|
(( $argumentCount > 0 )) || echo "Starting $script_name: invoke with -help for other options" |
|
|
|
# verify this is an sbt dir or -create was given |
|
[[ -f ./build.sbt || -d ./project || -n "$sbt_create" ]] || { |
|
cat <<EOM |
|
$(pwd) doesn't appear to be an sbt project. |
|
If you want to start sbt anyway, run: |
|
$0 -sbt-create |
|
|
|
EOM |
|
exit 1 |
|
} |
|
|
|
# pick up completion if present; todo |
|
[[ -f .sbt_completion.sh ]] && source .sbt_completion.sh |
|
|
|
# no jar? download it. |
|
[[ -f "$sbt_jar" ]] || acquire_sbt_jar || { |
|
# still no jar? uh-oh. |
|
echo "Download failed. Obtain the jar manually and place it at $sbt_jar" |
|
exit 1 |
|
} |
|
|
|
[[ -n "$sbt_dir" ]] || { |
|
sbt_dir=~/.sbt/$(sbt_version) |
|
addJava "-Dsbt.global.base=$sbt_dir" |
|
echoerr "Using $sbt_dir as sbt dir, -sbt-dir to override." |
|
} |
|
|
|
# since sbt 0.7 doesn't understand iflast |
|
(( ${#residual_args[@]} == 0 )) && residual_args=( "shell" ) |
|
|
|
# run sbt |
|
execRunner "$java_cmd" \ |
|
$(get_mem_opts $sbt_mem) \ |
|
$(get_jvm_opts) \ |
|
${java_args[@]} \ |
|
-jar "$sbt_jar" \ |
|
"${sbt_commands[@]}" \ |
|
"${residual_args[@]}" |