sbt 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. #!/usr/bin/env bash
  2. #
  3. # A more capable sbt runner, coincidentally also called sbt.
  4. # Author: Paul Phillips <[email protected]>
  5. # todo - make this dynamic
  6. declare -r sbt_release_version=0.12.0
  7. declare -r sbt_snapshot_version=0.13.0-SNAPSHOT
  8. unset sbt_jar sbt_dir sbt_create sbt_snapshot sbt_launch_dir
  9. unset scala_version java_home sbt_explicit_version
  10. unset verbose debug quiet
  11. for arg in "$@"; do
  12. case $arg in
  13. -q|-quiet) quiet=1 ;;
  14. *) ;;
  15. esac
  16. done
  17. build_props_sbt () {
  18. if [[ -f project/build.properties ]]; then
  19. versionLine=$(grep ^sbt.version project/build.properties)
  20. versionString=${versionLine##sbt.version=}
  21. echo "$versionString"
  22. fi
  23. }
  24. update_build_props_sbt () {
  25. local ver="$1"
  26. local old=$(build_props_sbt)
  27. if [[ $ver == $old ]]; then
  28. return
  29. elif [[ -f project/build.properties ]]; then
  30. perl -pi -e "s/^sbt\.version=.*\$/sbt.version=${ver}/" project/build.properties
  31. grep -q '^sbt.version=' project/build.properties || echo "sbt.version=${ver}" >> project/build.properties
  32. echo !!!
  33. echo !!! Updated file project/build.properties setting sbt.version to: $ver
  34. echo !!! Previous value was: $old
  35. echo !!!
  36. fi
  37. }
  38. sbt_version () {
  39. if [[ -n $sbt_explicit_version ]]; then
  40. echo $sbt_explicit_version
  41. else
  42. local v=$(build_props_sbt)
  43. if [[ -n $v ]]; then
  44. echo $v
  45. else
  46. echo $sbt_release_version
  47. fi
  48. fi
  49. }
  50. echoerr () {
  51. [[ -z $quiet ]] && echo 1>&2 "$@"
  52. }
  53. vlog () {
  54. [[ $verbose || $debug ]] && echoerr "$@"
  55. }
  56. dlog () {
  57. [[ $debug ]] && echoerr "$@"
  58. }
  59. # this seems to cover the bases on OSX, and someone will
  60. # have to tell me about the others.
  61. get_script_path () {
  62. local path="$1"
  63. [[ -L "$path" ]] || { echo "$path" ; return; }
  64. local target=$(readlink "$path")
  65. if [[ "${target:0:1}" == "/" ]]; then
  66. echo "$target"
  67. else
  68. echo "$(dirname $path)/$target"
  69. fi
  70. }
  71. # a ham-fisted attempt to move some memory settings in concert
  72. # so they need not be dicked around with individually.
  73. get_mem_opts () {
  74. local mem=${1:-1536}
  75. local perm=$(( $mem / 4 ))
  76. (( $perm > 256 )) || perm=256
  77. (( $perm < 1024 )) || perm=1024
  78. local codecache=$(( $perm / 2 ))
  79. echo "-Xms${mem}m -Xmx${mem}m -XX:MaxPermSize=${perm}m -XX:ReservedCodeCacheSize=${codecache}m"
  80. }
  81. die() {
  82. echo "Aborting: $@"
  83. exit 1
  84. }
  85. make_url () {
  86. groupid="$1"
  87. category="$2"
  88. version="$3"
  89. echo "http://typesafe.artifactoryonline.com/typesafe/ivy-$category/$groupid/sbt-launch/$version/sbt-launch.jar"
  90. }
  91. declare -r default_jvm_opts="-Dfile.encoding=UTF8"
  92. declare -r default_sbt_opts="-XX:+CMSClassUnloadingEnabled"
  93. declare -r default_sbt_mem=1536
  94. declare -r noshare_opts="-Dsbt.global.base=project/.sbtboot -Dsbt.boot.directory=project/.boot -Dsbt.ivy.home=project/.ivy"
  95. declare -r sbt_opts_file=".sbtopts"
  96. declare -r jvm_opts_file=".jvmopts"
  97. declare -r latest_28="2.8.2"
  98. declare -r latest_29="2.9.2"
  99. declare -r latest_210="2.10.0-SNAPSHOT"
  100. declare -r script_path=$(get_script_path "$BASH_SOURCE")
  101. declare -r script_dir="$(dirname $script_path)"
  102. declare -r script_name="$(basename $script_path)"
  103. # some non-read-onlies set with defaults
  104. declare java_cmd=java
  105. declare sbt_launch_dir="$script_dir/.lib"
  106. declare sbt_universal_launcher="$script_dir/lib/sbt-launch.jar"
  107. declare sbt_mem=$default_sbt_mem
  108. declare sbt_jar=$sbt_universal_launcher
  109. # pull -J and -D options to give to java.
  110. declare -a residual_args
  111. declare -a java_args
  112. declare -a scalac_args
  113. declare -a sbt_commands
  114. build_props_scala () {
  115. if [[ -f project/build.properties ]]; then
  116. versionLine=$(grep ^build.scala.versions project/build.properties)
  117. versionString=${versionLine##build.scala.versions=}
  118. echo ${versionString%% .*}
  119. fi
  120. }
  121. execRunner () {
  122. # print the arguments one to a line, quoting any containing spaces
  123. [[ $verbose || $debug ]] && echo "# Executing command line:" && {
  124. for arg; do
  125. if printf "%s\n" "$arg" | grep -q ' '; then
  126. printf "\"%s\"\n" "$arg"
  127. else
  128. printf "%s\n" "$arg"
  129. fi
  130. done
  131. echo ""
  132. }
  133. exec "$@"
  134. }
  135. sbt_groupid () {
  136. case $(sbt_version) in
  137. 0.7.*) echo org.scala-tools.sbt ;;
  138. 0.10.*) echo org.scala-tools.sbt ;;
  139. 0.11.[12]) echo org.scala-tools.sbt ;;
  140. *) echo org.scala-sbt ;;
  141. esac
  142. }
  143. sbt_artifactory_list () {
  144. local version0=$(sbt_version)
  145. local version=${version0%-SNAPSHOT}
  146. local url="http://typesafe.artifactoryonline.com/typesafe/ivy-snapshots/$(sbt_groupid)/sbt-launch/"
  147. dlog "Looking for snapshot list at: $url "
  148. curl -s --list-only "$url" | \
  149. grep -F $version | \
  150. perl -e 'print reverse <>' | \
  151. perl -pe 's#^<a href="([^"/]+).*#$1#;'
  152. }
  153. make_release_url () {
  154. make_url $(sbt_groupid) releases $(sbt_version)
  155. }
  156. # argument is e.g. 0.13.0-SNAPSHOT
  157. # finds the actual version (with the build id) at artifactory
  158. make_snapshot_url () {
  159. for ver in $(sbt_artifactory_list); do
  160. local url=$(make_url $(sbt_groupid) snapshots $ver)
  161. dlog "Testing $url"
  162. curl -s --head "$url" >/dev/null
  163. dlog "curl returned: $?"
  164. echo "$url"
  165. return
  166. done
  167. }
  168. jar_url () {
  169. case $(sbt_version) in
  170. 0.7.*) echo "http://simple-build-tool.googlecode.com/files/sbt-launch-0.7.7.jar" ;;
  171. *-SNAPSHOT) make_snapshot_url ;;
  172. *) make_release_url ;;
  173. esac
  174. }
  175. jar_file () {
  176. echo "$sbt_launch_dir/$1/sbt-launch.jar"
  177. }
  178. download_url () {
  179. local url="$1"
  180. local jar="$2"
  181. echo "Downloading sbt launcher $(sbt_version):"
  182. echo " From $url"
  183. echo " To $jar"
  184. mkdir -p $(dirname "$jar") && {
  185. if which curl >/dev/null; then
  186. curl --fail --silent "$url" --output "$jar"
  187. elif which wget >/dev/null; then
  188. wget --quiet -O "$jar" "$url"
  189. fi
  190. } && [[ -f "$jar" ]]
  191. }
  192. acquire_sbt_jar () {
  193. sbt_url="$(jar_url)"
  194. sbt_jar="$(jar_file $(sbt_version))"
  195. [[ -f "$sbt_jar" ]] || download_url "$sbt_url" "$sbt_jar"
  196. }
  197. usage () {
  198. cat <<EOM
  199. Usage: $script_name [options]
  200. -h | -help print this message
  201. -v | -verbose this runner is chattier
  202. -d | -debug set sbt log level to Debug
  203. -q | -quiet set sbt log level to Error
  204. -no-colors disable ANSI color codes
  205. -sbt-create start sbt even if current directory contains no sbt project
  206. -sbt-dir <path> path to global settings/plugins directory (default: ~/.sbt/<version>)
  207. -sbt-boot <path> path to shared boot directory (default: ~/.sbt/boot in 0.11+)
  208. -ivy <path> path to local Ivy repository (default: ~/.ivy2)
  209. -mem <integer> set memory options (default: $sbt_mem, which is
  210. $(get_mem_opts $sbt_mem) )
  211. -no-share use all local caches; no sharing
  212. -offline put sbt in offline mode
  213. -jvm-debug <port> Turn on JVM debugging, open at the given port.
  214. -batch Disable interactive mode
  215. # sbt version (default: from project/build.properties if present, else latest release)
  216. !!! The only way to accomplish this pre-0.12.0 if there is a build.properties file which
  217. !!! contains an sbt.version property is to update the file on disk. That's what this does.
  218. -sbt-version <version> use the specified version of sbt
  219. -sbt-jar <path> use the specified jar as the sbt launcher
  220. -sbt-snapshot use a snapshot version of sbt
  221. -sbt-launch-dir <path> directory to hold sbt launchers (default: $sbt_launch_dir)
  222. # scala version (default: as chosen by sbt)
  223. -28 use $latest_28
  224. -29 use $latest_29
  225. -210 use $latest_210
  226. -scala-home <path> use the scala build at the specified directory
  227. -scala-version <version> use the specified version of scala
  228. # java version (default: java from PATH, currently $(java -version |& grep version))
  229. -java-home <path> alternate JAVA_HOME
  230. # jvm options and output control
  231. JAVA_OPTS environment variable holding jvm args, if unset uses "$default_jvm_opts"
  232. SBT_OPTS environment variable holding jvm args, if unset uses "$default_sbt_opts"
  233. .jvmopts if file is in sbt root, it is prepended to the args given to the jvm
  234. .sbtopts if file is in sbt root, it is prepended to the args given to **sbt**
  235. -Dkey=val pass -Dkey=val directly to the jvm
  236. -J-X pass option -X directly to the jvm (-J is stripped)
  237. -S-X add -X to sbt's scalacOptions (-J is stripped)
  238. In the case of duplicated or conflicting options, the order above
  239. shows precedence: JAVA_OPTS lowest, command line options highest.
  240. EOM
  241. }
  242. addJava () {
  243. dlog "[addJava] arg = '$1'"
  244. java_args=( "${java_args[@]}" "$1" )
  245. }
  246. addSbt () {
  247. dlog "[addSbt] arg = '$1'"
  248. sbt_commands=( "${sbt_commands[@]}" "$1" )
  249. }
  250. addScalac () {
  251. dlog "[addScalac] arg = '$1'"
  252. scalac_args=( "${scalac_args[@]}" "$1" )
  253. }
  254. addResidual () {
  255. dlog "[residual] arg = '$1'"
  256. residual_args=( "${residual_args[@]}" "$1" )
  257. }
  258. addResolver () {
  259. addSbt "set resolvers in ThisBuild += $1"
  260. }
  261. addDebugger () {
  262. addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1"
  263. }
  264. jrebelAgent () {
  265. SCALATRA_PROJECT_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
  266. if [ -z "$SCALATRA_JREBEL" ];
  267. then echo -n '';
  268. else echo -n "-javaagent:$SCALATRA_JREBEL -Dscalatra_project_root=${SCALATRA_PROJECT_ROOT}";
  269. fi
  270. }
  271. get_jvm_opts () {
  272. echo "${JAVA_OPTS:-$default_jvm_opts}"
  273. echo "`jrebelAgent` ${SBT_OPTS:-$default_sbt_opts}"
  274. [[ -f "$jvm_opts_file" ]] && cat "$jvm_opts_file"
  275. }
  276. process_args ()
  277. {
  278. require_arg () {
  279. local type="$1"
  280. local opt="$2"
  281. local arg="$3"
  282. if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
  283. die "$opt requires <$type> argument"
  284. fi
  285. }
  286. while [[ $# -gt 0 ]]; do
  287. case "$1" in
  288. -h|-help) usage; exit 1 ;;
  289. -v|-verbose) verbose=1 && shift ;;
  290. -d|-debug) debug=1 && shift ;;
  291. -q|-quiet) quiet=1 && shift ;;
  292. -ivy) require_arg path "$1" "$2" && addJava "-Dsbt.ivy.home=$2" && shift 2 ;;
  293. -mem) require_arg integer "$1" "$2" && sbt_mem="$2" && shift 2 ;;
  294. -no-colors) addJava "-Dsbt.log.noformat=true" && shift ;;
  295. -no-share) addJava "$noshare_opts" && shift ;;
  296. -sbt-boot) require_arg path "$1" "$2" && addJava "-Dsbt.boot.directory=$2" && shift 2 ;;
  297. -sbt-dir) require_arg path "$1" "$2" && sbt_dir="$2" && shift 2 ;;
  298. -debug-inc) addJava "-Dxsbt.inc.debug=true" && shift ;;
  299. -offline) addSbt "set offline := true" && shift ;;
  300. -jvm-debug) require_arg port "$1" "$2" && addDebugger $2 && shift 2 ;;
  301. -batch) exec </dev/null && shift ;;
  302. -sbt-create) sbt_create=true && shift ;;
  303. -sbt-snapshot) sbt_explicit_version=$sbt_snapshot_version && shift ;;
  304. -sbt-jar) require_arg path "$1" "$2" && sbt_jar="$2" && shift 2 ;;
  305. -sbt-version) require_arg version "$1" "$2" && sbt_explicit_version="$2" && shift 2 ;;
  306. -sbt-launch-dir) require_arg path "$1" "$2" && sbt_launch_dir="$2" && shift 2 ;;
  307. -scala-version) require_arg version "$1" "$2" && addSbt "set scalaVersion := \"$2\"" && shift 2 ;;
  308. -scala-home) require_arg path "$1" "$2" && addSbt "set scalaHome in ThisBuild := Some(file(\"$2\"))" && shift 2 ;;
  309. -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;;
  310. -D*) addJava "$1" && shift ;;
  311. -J*) addJava "${1:2}" && shift ;;
  312. -S*) addScalac "${1:2}" && shift ;;
  313. -28) addSbt "++ $latest_28" && shift ;;
  314. -29) addSbt "++ $latest_29" && shift ;;
  315. -210) addSbt "++ $latest_210" && shift ;;
  316. *) addResidual "$1" && shift ;;
  317. esac
  318. done
  319. [[ $debug ]] && {
  320. case $(sbt_version) in
  321. 0.7.*) addSbt "debug" ;;
  322. *) addSbt "set logLevel in Global := Level.Debug" ;;
  323. esac
  324. }
  325. [[ $quiet ]] && {
  326. case $(sbt_version) in
  327. 0.7.*) ;;
  328. *) addSbt "set logLevel in Global := Level.Error" ;;
  329. esac
  330. }
  331. }
  332. # if .sbtopts exists, prepend its contents to $@ so it can be processed by this runner
  333. [[ -f "$sbt_opts_file" ]] && {
  334. sbtargs=()
  335. while IFS= read -r arg; do
  336. sbtargs=( "${sbtargs[@]}" "$arg" )
  337. done <"$sbt_opts_file"
  338. set -- "${sbtargs[@]}" "$@"
  339. }
  340. # process the combined args, then reset "$@" to the residuals
  341. process_args "$@"
  342. set -- "${residual_args[@]}"
  343. argumentCount=$#
  344. # set scalacOptions if we were given any -S opts
  345. [[ ${#scalac_args[@]} -eq 0 ]] || addSbt "set scalacOptions in ThisBuild += \"${scalac_args[@]}\""
  346. # Update build.properties no disk to set explicit version - sbt gives us no choice
  347. [[ -n "$sbt_explicit_version" ]] && update_build_props_sbt "$sbt_explicit_version"
  348. echoerr "Detected sbt version $(sbt_version)"
  349. [[ -n "$scala_version" ]] && echo "Overriding scala version to $scala_version"
  350. # no args - alert them there's stuff in here
  351. (( $argumentCount > 0 )) || echo "Starting $script_name: invoke with -help for other options"
  352. # verify this is an sbt dir or -create was given
  353. [[ -f ./build.sbt || -d ./project || -n "$sbt_create" ]] || {
  354. cat <<EOM
  355. $(pwd) doesn't appear to be an sbt project.
  356. If you want to start sbt anyway, run:
  357. $0 -sbt-create
  358. EOM
  359. exit 1
  360. }
  361. # pick up completion if present; todo
  362. [[ -f .sbt_completion.sh ]] && source .sbt_completion.sh
  363. # no jar? download it.
  364. [[ -f "$sbt_jar" ]] || acquire_sbt_jar || {
  365. # still no jar? uh-oh.
  366. echo "Download failed. Obtain the jar manually and place it at $sbt_jar"
  367. exit 1
  368. }
  369. [[ -n "$sbt_dir" ]] || {
  370. sbt_dir=~/.sbt/$(sbt_version)
  371. addJava "-Dsbt.global.base=$sbt_dir"
  372. echoerr "Using $sbt_dir as sbt dir, -sbt-dir to override."
  373. }
  374. # since sbt 0.7 doesn't understand iflast
  375. (( ${#residual_args[@]} == 0 )) && residual_args=( "shell" )
  376. if [ ! -z "$SCALATRA_JREBEL" ]; then
  377. if [ ! -r "$SCALATRA_JREBEL" ]; then
  378. echo "################################################################################"
  379. echo " SCALATRA WARNING"
  380. echo "Cannot find and/or read $SCALATRA_JREBEL."
  381. echo 'Update $SCALATRA_JREBEL to a new JRebel jar path, or unset the variable.'
  382. echo "################################################################################"
  383. exit 1
  384. fi
  385. fi
  386. # run sbt
  387. execRunner "$java_cmd" \
  388. $(get_mem_opts $sbt_mem) \
  389. $(get_jvm_opts) \
  390. ${java_args[@]} \
  391. -jar "$sbt_jar" \
  392. "${sbt_commands[@]}" \
  393. "${residual_args[@]}"