Last active
July 8, 2017 21:38
-
-
Save frgomes/27710d31f1788f1f784b to your computer and use it in GitHub Desktop.
Scala SBT Scripting - Run Scala program from SBT and parses command line arguments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| #-*- mode: scala; -*- | |
| exec java \ | |
| -Dsbt.main.class=sbt.ScriptMain \ | |
| -Dsbt.boot.directory=~/.sbt/boot \ | |
| -Dsbt.log.noformat=true \ | |
| -jar $(which sbt-launch.jar) $0 "+ $*" # see: https://github.com/sbt/sbt/issues/2257 | |
| !# | |
| /*** | |
| scalaVersion := "2.11.7" | |
| resolvers ++= | |
| Seq( | |
| Resolver.jcenterRepo, | |
| Resolver.url("frgomes resolver", url("http://dl.bintray.com/frgomes/sbt-plugins")) | |
| (Resolver.ivyStylePatterns)) | |
| addSbtPlugin("info.rgomes" %% "sbt-snippets" % "0.1.0") | |
| libraryDependencies ++= | |
| Seq( | |
| "org.scala-lang.modules" %% "scala-xml" % "1.0.5", | |
| "com.github.scopt" %% "scopt" % "3.3.0", | |
| "org.ccil.cowan.tagsoup" % "tagsoup" % "1.2.1" ) | |
| */ | |
| case class Cmd(pkgname: String = null, clsname: String = null, keep: Boolean = false, input: String = null, output: String = null) | |
| class CLI(args : Array[String]) { | |
| // FIXME: http://stackoverflow.com/questions/7163364/how-to-handle-in-file-paths | |
| private val re = "^~".r | |
| private val home = System.getProperty("user.home") | |
| private val parser = | |
| new scopt.OptionParser[Cmd]("translator") { | |
| head("""usage: translator [<input>] [<output>] | |
| |synopsis: | |
| | Translate a HTML file to ScalaJS, employing Scalatags and Dejawu. | |
| | More info on ScalaJS: https://github.com/scala-js/scala-js | |
| | Scalatags: https://github.com/lihaoyi/scalatags | |
| | Dejawu: https://github.com/frgomes/dejawu | |
| |options:""".stripMargin) | |
| opt[Unit]('k', "keep-markup").valueName("[keep-markup]") | |
| .optional | |
| .action { (o, c) => c.copy( keep = true ) } | |
| .text("""Keep the markup as it is, without employing dejawu tags.""") | |
| opt[String]('m', "main-class").valueName("[main-class]") | |
| .action { (o, c) => c.copy( clsname = o ) } | |
| .text("""Fully qualified class name""") | |
| arg[String]("<input>") | |
| .optional() | |
| .action { (o, c) => c.copy( input = re.replaceFirstIn(o, home) ) } | |
| .text("""Input .html file or "-" (without quotes) for stdin.""") | |
| arg[String]("<output>") | |
| .optional() | |
| .action { (o, c) => c.copy( output = re.replaceFirstIn(o, home) ) } | |
| .text("""Output .scala file or "-" (without quotes) for stdout.""") | |
| } | |
| private def validate(c : Option[Cmd]) : Option[Cmd] = | |
| if(!c.isDefined) c else | |
| if(c.get.clsname != null && c.get.clsname.trim.length > 0) { | |
| val parts = c.get.clsname.trim.split('.') | |
| val pkgname : String = if(parts.length < 2) "" else parts.reverse.tail.reverse.mkString(".") | |
| val clsname : String = parts.length match { | |
| case 0 => "Noname" | |
| case 1 => c.get.clsname.trim | |
| case _ => parts.last } | |
| Option(Cmd(pkgname, clsname, c.get.keep, c.get.input, c.get.output)) } | |
| else { Option(Cmd(null, null, c.get.keep, c.get.input, c.get.output)) } | |
| val parse : Option[Cmd] = validate(parser.parse(args, Cmd())) | |
| } | |
| object Translator { | |
| def main(args: Array[String]) { | |
| val cmd = new CLI(args).parse | |
| //TODO val tool = new Translator | |
| //TODO if (cmd.isDefined) tool.translate(cmd.get) | |
| } | |
| } | |
| println("---------------------------------------------------------------------------------------------") | |
| Translator.main(args diff Seq("+")) // see: https://github.com/sbt/sbt/issues/2257 | |
| println("---------------------------------------------------------------------------------------------") | |
| val fqcn = "org.dejawu.demos.ThemePreviewer" | |
| val html = "https://raw.githubusercontent.com/dojo/demos/master/themePreviewer/demo.html" | |
| val scala = "ThemePreviewer.scala" | |
| Translator.main(Array("-k", "-m", fqcn, html, scala)) | |
| println("---------------------------------------------------------------------------------------------") | |
| # in case you'd like to parse from <stdin> | |
| if(args.contains("-")) { | |
| val sc = new java.util.Scanner(System.in); | |
| while(sc.hasNext()){ | |
| val inputs = sc.nextLine.split(" ") | |
| Translator.main(inputs) | |
| } | |
| } | |
| println("done.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
IMPORTANT
LEAVE THIS FILE WITHOUT A EXTENSION.
It has an extension here just for the sake of syntax highlighting.
Brief lesson of magic
This file is a shell script which calls Java which calls SBT which executes an embedded Scala code.
Notice that we employ the entry point
sbt.ScriptMainwhich requires a SBT script. In this case, we pass this file itself as input. SBT will start reading after the!#mark.Just after the
!#mark, SBT finds a block between/*** ... */like this below:This is a block made of SBT statements and definitions, pretty much what you would put into a
build.sbtfile.After this block, SBT considers everything to be your Scala code to be executed.
Notice that we pass command line arguments informed to the shell script as standard input to the Scala script. This way, we allow our Scala script to interpret command line arguments as if it was a shell script or a Python script parsing arguments passed to it.
Beware of bug sbt/sbt#2257
In a nutshell, this bug means that you cannot pass long command line arguments.
The source code shown in this gist is already circumventing the difficulty.
More information at: sbt/sbt#2257
Usage example
$ mv translate.scala translate
$ chmod 755 translate
$ ./translate --help