diff --git a/.gitignore b/.gitignore
index 2f7896d..402069d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
target/
+/pack/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..929c43e
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,18 @@
+sudo: false
+language: scala
+scala:
+ - 2.11.12
+ - 2.12.4
+jdk:
+ - oraclejdk8
+env:
+ - SCALAJS_VERSION=1.0.0-M2
+script:
+ - ./scripts/assemble-cli.sh $SCALAJS_VERSION $TRAVIS_SCALA_VERSION
+cache:
+ directories:
+ - $HOME/.ivy2/cache
+ - $HOME/.sbt
+before_cache:
+ - find $HOME/.ivy2/cache -name "ivydata-*.properties" -print -delete
+ - find $HOME/.sbt -name "*.lock" -print -delete
diff --git a/build.sbt b/build.sbt
new file mode 100644
index 0000000..08fd12e
--- /dev/null
+++ b/build.sbt
@@ -0,0 +1,196 @@
+// Configurable settings
+val scalaJSVersion =
+ settingKey[String]("Version of Scala.js for which to build to CLI")
+val scalaJSScalaVersions =
+ settingKey[Seq[String]]("All the minor versions of Scala for which to build the CLI")
+
+// Computed settings
+val scalaJSBinaryVersion =
+ settingKey[String]("Binary version of Scala.js")
+
+// Custom tasks
+val cliLibJars =
+ taskKey[Seq[File]]("All the .jars that must go to the lib/ directory of the CLI")
+val cliPack =
+ taskKey[File]("Pack the CLI for the current configuration")
+
+// Duplicated from the Scala.js sbt plugin
+def binaryScalaJSVersion(full: String): String = {
+ val ReleaseVersion = raw"""(\d+)\.(\d+)\.(\d+)""".r
+ val MinorSnapshotVersion = raw"""(\d+)\.(\d+)\.([1-9]\d*)-SNAPSHOT""".r
+ full match {
+ case ReleaseVersion(major, _, _) => major
+ case MinorSnapshotVersion(major, _, _) => major
+ case _ => full
+ }
+}
+
+inThisBuild(Def.settings(
+ version := "1.0.0-SNAPSHOT",
+ organization := "org.scala-js",
+
+ crossScalaVersions := Seq("2.12.4", "2.11.12"),
+ scalaVersion := crossScalaVersions.value.head,
+ scalacOptions ++= Seq("-deprecation", "-feature", "-Xfatal-warnings"),
+
+ scalaJSVersion := "1.0.0-M2",
+ scalaJSBinaryVersion := binaryScalaJSVersion(scalaJSVersion.value),
+
+ scalaJSScalaVersions := Seq(
+ "2.11.0",
+ "2.11.1",
+ "2.11.2",
+ "2.11.4",
+ "2.11.5",
+ "2.11.6",
+ "2.11.7",
+ "2.11.8",
+ "2.11.11",
+ "2.11.12",
+ "2.12.1",
+ "2.12.2",
+ "2.12.3",
+ "2.12.4",
+ ),
+
+ homepage := Some(url("https://www.scala-js.org/")),
+ licenses += ("BSD New",
+ url("https://github.com/scala-js/scala-js-env-cli/blob/master/LICENSE")),
+ scmInfo := Some(ScmInfo(
+ url("https://github.com/scala-js/scala-js-cli"),
+ "scm:git:git@github.com:scala-js/scala-js-cli.git",
+ Some("scm:git:git@github.com:scala-js/scala-js-cli.git"))),
+))
+
+val commonSettings = Def.settings(
+ publishMavenStyle := true,
+ publishTo := {
+ val nexus = "https://oss.sonatype.org/"
+ if (isSnapshot.value)
+ Some("snapshots" at nexus + "content/repositories/snapshots")
+ else
+ Some("releases" at nexus + "service/local/staging/deploy/maven2")
+ },
+ pomExtra := (
+
+
+ sjrd
+ Sébastien Doeraene
+ https://github.com/sjrd/
+
+
+ gzm0
+ Tobias Schlatter
+ https://github.com/gzm0/
+
+
+ nicolasstucki
+ Nicolas Stucki
+ https://github.com/nicolasstucki/
+
+
+ ),
+ pomIncludeRepository := { _ => false },
+)
+
+lazy val `scalajs-cli`: Project = project.in(file(".")).
+ settings(
+ commonSettings,
+
+ libraryDependencies ++= Seq(
+ "org.scala-js" %% "scalajs-tools" % scalaJSVersion.value,
+ "com.github.scopt" %% "scopt" % "3.5.0",
+ ),
+
+ // assembly options
+ mainClass in assembly := None, // don't want an executable JAR
+ assemblyOption in assembly ~= { _.copy(includeScala = false) },
+ assemblyJarName in assembly :=
+ s"${normalizedName.value}-assembly_${scalaBinaryVersion.value}-${scalaJSVersion.value}.jar",
+
+ cliLibJars := {
+ val s = streams.value
+ val log = s.log
+
+ val sjsOrg = organization.value
+ val scalaBinVer = scalaBinaryVersion.value
+ val sjsVer = scalaJSVersion.value
+
+ val scalaFullVers = scalaJSScalaVersions.value.filter { full =>
+ CrossVersion.binaryScalaVersion(full) == scalaBinVer
+ }
+
+ val cliAssemblyJar = assembly.value
+
+ val stdLibModuleID =
+ sjsOrg % s"scalajs-library_$scalaBinVer" % sjsVer
+ val compilerPluginModuleIDs =
+ scalaFullVers.map(v => sjsOrg % s"scalajs-compiler_$v" % sjsVer)
+ val allModuleIDs = (stdLibModuleID +: compilerPluginModuleIDs).toVector
+ val allModuleIDsIntransitive = allModuleIDs.map(_.intransitive())
+
+ val resolvedLibJars = {
+ val retrieveDir = s.cacheDirectory / "cli-lib-jars"
+ val lm = {
+ import sbt.librarymanagement.ivy._
+ val ivyConfig = InlineIvyConfiguration().withLog(log)
+ IvyDependencyResolution(ivyConfig)
+ }
+ val dummyModuleName =
+ s"clilibjars-$sjsVer-$scalaBinVer-" + scalaFullVers.mkString("-")
+ val dummyModuleID = sjsOrg % dummyModuleName % version.value
+ val descriptor =
+ lm.moduleDescriptor(dummyModuleID, allModuleIDsIntransitive, scalaModuleInfo = None)
+ val maybeFiles = lm.retrieve(descriptor, retrieveDir, log)
+ maybeFiles.fold({ unresolvedWarn =>
+ throw unresolvedWarn.resolveException
+ }, { files =>
+ files
+ }).distinct
+ }
+
+ cliAssemblyJar +: resolvedLibJars
+ },
+
+ target in cliPack := baseDirectory.value / "pack",
+ moduleName in cliPack :=
+ s"scalajs_${scalaBinaryVersion.value}-${scalaJSVersion.value}",
+ crossTarget in cliPack :=
+ (target in cliPack).value / (moduleName in cliPack).value,
+
+ cliPack := {
+ val scalaBinVer = scalaBinaryVersion.value
+ val sjsVer = scalaJSVersion.value
+
+ val trg = (crossTarget in cliPack).value
+ val trgLib = trg / "lib"
+ val trgBin = trg / "bin"
+
+ if (trg.exists)
+ IO.delete(trg)
+
+ IO.createDirectory(trgLib)
+ val libJars = cliLibJars.value
+ for (libJar <- libJars) {
+ IO.copyFile(libJar, trgLib / libJar.getName)
+ }
+
+ IO.createDirectory(trgBin)
+ val scriptDir = (resourceDirectory in Compile).value
+ for {
+ scriptFile <- IO.listFiles(scriptDir)
+ if !scriptFile.getPath.endsWith("~")
+ } {
+ val content = IO.read(scriptFile)
+ val processedContent = content
+ .replaceAllLiterally("@SCALA_BIN_VER@", scalaBinVer)
+ .replaceAllLiterally("@SCALAJS_VER@", sjsVer)
+ val dest = trgBin / scriptFile.getName
+ IO.write(dest, processedContent)
+ if (scriptFile.canExecute)
+ dest.setExecutable(/* executable = */ true, /* ownerOnly = */ false)
+ }
+
+ trg
+ },
+ )
diff --git a/project/build.properties b/project/build.properties
new file mode 100644
index 0000000..394cb75
--- /dev/null
+++ b/project/build.properties
@@ -0,0 +1 @@
+sbt.version=1.0.4
diff --git a/project/plugins.sbt b/project/plugins.sbt
new file mode 100644
index 0000000..15a88b0
--- /dev/null
+++ b/project/plugins.sbt
@@ -0,0 +1 @@
+addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5")
diff --git a/scripts/assemble-cli.sh b/scripts/assemble-cli.sh
new file mode 100755
index 0000000..2a5a4d1
--- /dev/null
+++ b/scripts/assemble-cli.sh
@@ -0,0 +1,52 @@
+#! /bin/sh
+
+set -e
+
+# Assembles the CLI tools for a given Scala binary version.
+
+if [ $# -lt 2 ]; then
+ echo "Usage: $(basename $0) " >&2
+ exit 1
+fi
+
+SCALAJS_VER=$1
+
+BASEVER=$2
+case $BASEVER in
+ 2.11.*)
+ BINVER="2.11"
+ ;;
+ 2.12.*)
+ BINVER="2.12"
+ ;;
+ *)
+ echo "Invalid Scala version $BINVER" >&2
+ exit 2
+esac
+
+# Build and lay out the contents of the archives
+sbt \
+ "clean" \
+ "++$BASEVER!" \
+ "set scalaJSVersion in ThisBuild := \"$SCALAJS_VER\"" \
+ "cliPack" \
+ || exit $?
+
+# Base Scala.js project directory.
+BASE="$(dirname $0)/.."
+
+# Aritfact name (no extension).
+NAME=scalajs_$BINVER-$SCALAJS_VER
+
+# Target directories
+TRG_BASE="$BASE/pack"
+TRG_VER="$TRG_BASE/$NAME"
+
+# Tar and zip the whole thing up
+(
+ cd $TRG_BASE
+ tar cfz $NAME.tgz $NAME
+
+ if [ -f $NAME.zip ]; then rm $NAME.zip; fi
+ zip -r $NAME.zip -r $NAME
+)
diff --git a/src/main/resources/scalajsc b/src/main/resources/scalajsc
new file mode 100755
index 0000000..7fd1100
--- /dev/null
+++ b/src/main/resources/scalajsc
@@ -0,0 +1,16 @@
+#! /bin/sh
+
+SCALA_BIN_VER="@SCALA_BIN_VER@"
+SCALAJS_VER="@SCALAJS_VER@"
+SCALA_VER=$(scalac -version 2>&1 | grep -o '[0-9]\.[0-9][0-9]\.[0-9]')
+
+if [ "$(echo $SCALA_VER | cut -b 1-4)" != "$SCALA_BIN_VER" ]; then
+ echo "This bundle of Scala.js CLI is for $SCALA_BIN_VER. Your scala version is $SCALA_VER!" >&2
+ exit 1
+fi
+
+BASE="$(dirname $0)/.."
+PLUGIN="$BASE/lib/scalajs-compiler_$SCALA_VER-$SCALAJS_VER.jar"
+JSLIB="$BASE/lib/scalajs-library_$SCALA_BIN_VER-$SCALAJS_VER.jar"
+
+scalac -classpath "$JSLIB" "-Xplugin:$PLUGIN" "$@"
diff --git a/src/main/resources/scalajsc.bat b/src/main/resources/scalajsc.bat
new file mode 100644
index 0000000..767c5df
--- /dev/null
+++ b/src/main/resources/scalajsc.bat
@@ -0,0 +1,14 @@
+@ECHO OFF
+set SCALA_BIN_VER=@SCALA_BIN_VER@
+set SCALAJS_VER=@SCALAJS_VER@
+
+for /F "tokens=5" %%i in (' scala -version 2^>^&1 1^>nul ') do set SCALA_VER=%%i
+
+if NOT "%SCALA_VER:~0,4%" == "%SCALA_BIN_VER%" (
+ echo "This bundle of Scala.js CLI is for %SCALA_BIN_VER%. Your scala version is %SCALA_VER%!" 1>&2
+) else (
+ set PLUGIN=%~dp0\..\lib\scalajs-compiler_%SCALA_VER%-%SCALAJS_VER%.jar
+ set JSLIB=%~dp0\..\lib\scalajs-library_%SCALA_BIN_VER%-%SCALAJS_VER%.jar
+
+ scalac -classpath "%JSLIB%" "-Xplugin:%PLUGIN%" %*
+)
diff --git a/src/main/resources/scalajsld b/src/main/resources/scalajsld
new file mode 100755
index 0000000..b194859
--- /dev/null
+++ b/src/main/resources/scalajsld
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+SCALA_BIN_VER="@SCALA_BIN_VER@"
+SCALAJS_VER="@SCALAJS_VER@"
+
+BASE="$(dirname $0)/.."
+CLILIB="$BASE/lib/scalajs-cli-assembly_$SCALA_BIN_VER-$SCALAJS_VER.jar"
+JSLIB="$BASE/lib/scalajs-library_$SCALA_BIN_VER-$SCALAJS_VER.jar"
+
+scala -classpath "$CLILIB" org.scalajs.cli.Scalajsld --stdlib "$JSLIB" "$@"
diff --git a/src/main/resources/scalajsld.bat b/src/main/resources/scalajsld.bat
new file mode 100644
index 0000000..0308d55
--- /dev/null
+++ b/src/main/resources/scalajsld.bat
@@ -0,0 +1,8 @@
+@ECHO OFF
+set SCALA_BIN_VER=@SCALA_BIN_VER@
+set SCALAJS_VER=@SCALAJS_VER@
+
+set CLILIB="%~dp0\..\lib\scalajs-cli-assembly_%SCALA_BIN_VER%-%SCALAJS_VER%.jar"
+set JSLIB="%~dp0\..\lib\scalajs-library_%SCALA_BIN_VER%-%SCALAJS_VER%.jar"
+
+scala -classpath %CLILIB% org.scalajs.cli.Scalajsld --stdlib %JSLIB% %*
diff --git a/src/main/resources/scalajsp b/src/main/resources/scalajsp
new file mode 100755
index 0000000..f98f259
--- /dev/null
+++ b/src/main/resources/scalajsp
@@ -0,0 +1,9 @@
+#! /bin/sh
+
+SCALA_BIN_VER="@SCALA_BIN_VER@"
+SCALAJS_VER="@SCALAJS_VER@"
+
+BASE="$(dirname $0)/.."
+CLILIB="$BASE/lib/scalajs-cli-assembly_$SCALA_BIN_VER-$SCALAJS_VER.jar"
+
+scala -classpath "$CLILIB" org.scalajs.cli.Scalajsp "$@"
diff --git a/src/main/resources/scalajsp.bat b/src/main/resources/scalajsp.bat
new file mode 100644
index 0000000..1fc4ad6
--- /dev/null
+++ b/src/main/resources/scalajsp.bat
@@ -0,0 +1,7 @@
+@ECHO OFF
+set SCALA_BIN_VER=@SCALA_BIN_VER@
+set SCALAJS_VER=@SCALAJS_VER@
+
+set CLILIB="%~dp0\..\lib\scalajs-cli-assembly_%SCALA_BIN_VER%-%SCALAJS_VER%.jar"
+
+scala -classpath %CLILIB% org.scalajs.cli.Scalajsp %*
diff --git a/src/main/scala/org/scalajs/cli/Scalajsld.scala b/src/main/scala/org/scalajs/cli/Scalajsld.scala
new file mode 100644
index 0000000..ef53b80
--- /dev/null
+++ b/src/main/scala/org/scalajs/cli/Scalajsld.scala
@@ -0,0 +1,185 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js CLI **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package org.scalajs.cli
+
+import org.scalajs.core.ir.ScalaJSVersions
+
+import org.scalajs.core.tools.io._
+import org.scalajs.core.tools.logging._
+
+import org.scalajs.core.tools.linker._
+import org.scalajs.core.tools.linker.standard._
+
+import CheckedBehavior.Compliant
+
+import scala.collection.immutable.Seq
+
+import java.io.File
+import java.net.URI
+
+object Scalajsld {
+
+ private case class Options(
+ cp: Seq[File] = Seq.empty,
+ moduleInitializers: Seq[ModuleInitializer] = Seq.empty,
+ output: File = null,
+ semantics: Semantics = Semantics.Defaults,
+ outputMode: OutputMode = OutputMode.ECMAScript51Isolated,
+ moduleKind: ModuleKind = ModuleKind.NoModule,
+ noOpt: Boolean = false,
+ fullOpt: Boolean = false,
+ prettyPrint: Boolean = false,
+ sourceMap: Boolean = false,
+ relativizeSourceMap: Option[URI] = None,
+ checkIR: Boolean = false,
+ stdLib: Option[File] = None,
+ logLevel: Level = Level.Info
+ )
+
+ private implicit object MainMethodRead extends scopt.Read[ModuleInitializer] {
+ val arity = 1
+ val reads = { (s: String) =>
+ val lastDot = s.lastIndexOf('.')
+ if (lastDot < 0)
+ throw new IllegalArgumentException(s"$s is not a valid main method")
+ ModuleInitializer.mainMethodWithArgs(s.substring(0, lastDot),
+ s.substring(lastDot + 1))
+ }
+ }
+
+ private implicit object OutputModeRead extends scopt.Read[OutputMode] {
+ val arity = 1
+ val reads = { (s: String) =>
+ OutputMode.All.find(_.toString() == s).getOrElse(
+ throw new IllegalArgumentException(s"$s is not a valid output mode"))
+ }
+ }
+
+ private implicit object ModuleKindRead extends scopt.Read[ModuleKind] {
+ val arity = 1
+ val reads = { (s: String) =>
+ ModuleKind.All.find(_.toString() == s).getOrElse(
+ throw new IllegalArgumentException(s"$s is not a valid module kind"))
+ }
+ }
+
+ def main(args: Array[String]): Unit = {
+ val parser = new scopt.OptionParser[Options]("scalajsld") {
+ head("scalajsld", ScalaJSVersions.current)
+ arg[File](" ...")
+ .unbounded()
+ .action { (x, c) => c.copy(cp = c.cp :+ x) }
+ .text("Entries of Scala.js classpath to link")
+ opt[ModuleInitializer]("mainMethod")
+ .valueName("")
+ .abbr("mm")
+ .unbounded()
+ .action { (x, c) => c.copy(moduleInitializers = c.moduleInitializers :+ x) }
+ .text("Execute the specified main(Array[String]) method on startup")
+ opt[File]('o', "output")
+ .valueName("")
+ .required()
+ .action { (x, c) => c.copy(output = x) }
+ .text("Output file of linker (required)")
+ opt[Unit]('f', "fastOpt")
+ .action { (_, c) => c.copy(noOpt = false, fullOpt = false) }
+ .text("Optimize code (this is the default)")
+ opt[Unit]('n', "noOpt")
+ .action { (_, c) => c.copy(noOpt = true, fullOpt = false) }
+ .text("Don't optimize code")
+ opt[Unit]('u', "fullOpt")
+ .action { (_, c) => c.copy(noOpt = false, fullOpt = true) }
+ .text("Fully optimize code (uses Google Closure Compiler)")
+ opt[Unit]('p', "prettyPrint")
+ .action { (_, c) => c.copy(prettyPrint = true) }
+ .text("Pretty print full opted code (meaningful with -u)")
+ opt[Unit]('s', "sourceMap")
+ .action { (_, c) => c.copy(sourceMap = true) }
+ .text("Produce a source map for the produced code")
+ opt[Unit]("compliantAsInstanceOfs")
+ .action { (_, c) => c.copy(semantics =
+ c.semantics.withAsInstanceOfs(Compliant))
+ }
+ .text("Use compliant asInstanceOfs")
+ opt[OutputMode]('m', "outputMode")
+ .action { (mode, c) => c.copy(outputMode = mode) }
+ .text("Output mode " + OutputMode.All.mkString("(", ", ", ")"))
+ opt[ModuleKind]('k', "moduleKind")
+ .action { (kind, c) => c.copy(moduleKind = kind) }
+ .text("Module kind " + ModuleKind.All.mkString("(", ", ", ")"))
+ opt[Unit]('c', "checkIR")
+ .action { (_, c) => c.copy(checkIR = true) }
+ .text("Check IR before optimizing")
+ opt[File]('r', "relativizeSourceMap")
+ .valueName("")
+ .action { (x, c) => c.copy(relativizeSourceMap = Some(x.toURI)) }
+ .text("Relativize source map with respect to given path (meaningful with -s)")
+ opt[Unit]("noStdlib")
+ .action { (_, c) => c.copy(stdLib = None) }
+ .text("Don't automatically include Scala.js standard library")
+ opt[File]("stdlib")
+ .valueName("")
+ .hidden()
+ .action { (x, c) => c.copy(stdLib = Some(x)) }
+ .text("Location of Scala.js standard libarary. This is set by the " +
+ "runner script and automatically prepended to the classpath. " +
+ "Use -n to not include it.")
+ opt[Unit]('d', "debug")
+ .action { (_, c) => c.copy(logLevel = Level.Debug) }
+ .text("Debug mode: Show full log")
+ opt[Unit]('q', "quiet")
+ .action { (_, c) => c.copy(logLevel = Level.Warn) }
+ .text("Only show warnings & errors")
+ opt[Unit]("really-quiet")
+ .abbr("qq")
+ .action { (_, c) => c.copy(logLevel = Level.Error) }
+ .text("Only show errors")
+ version("version")
+ .abbr("v")
+ .text("Show scalajsld version")
+ help("help")
+ .abbr("h")
+ .text("prints this usage text")
+
+ override def showUsageOnError = true
+ }
+
+ for (options <- parser.parse(args, Options())) {
+ val classpath = options.stdLib.toList ++ options.cp
+ val irContainers = FileScalaJSIRContainer.fromClasspath(classpath)
+ val moduleInitializers = options.moduleInitializers
+
+ val semantics =
+ if (options.fullOpt) options.semantics.optimized
+ else options.semantics
+
+ val config = StandardLinker.Config()
+ .withSemantics(semantics)
+ .withModuleKind(options.moduleKind)
+ .withOutputMode(options.outputMode)
+ .withCheckIR(options.checkIR)
+ .withOptimizer(!options.noOpt)
+ .withParallel(true)
+ .withSourceMap(options.sourceMap)
+ .withRelativizeSourceMapBase(options.relativizeSourceMap)
+ .withClosureCompiler(options.fullOpt)
+ .withPrettyPrint(options.prettyPrint)
+ .withBatchMode(true)
+
+ val linker = StandardLinker(config)
+ val logger = new ScalaConsoleLogger(options.logLevel)
+ val outFile = WritableFileVirtualJSFile(options.output)
+ val cache = (new IRFileCache).newCache
+
+ linker.link(cache.cached(irContainers), moduleInitializers, outFile,
+ logger)
+ }
+ }
+}
diff --git a/src/main/scala/org/scalajs/cli/Scalajsp.scala b/src/main/scala/org/scalajs/cli/Scalajsp.scala
new file mode 100644
index 0000000..e63b4a8
--- /dev/null
+++ b/src/main/scala/org/scalajs/cli/Scalajsp.scala
@@ -0,0 +1,125 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js CLI **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package org.scalajs.cli
+
+import org.scalajs.core.ir
+import ir.ScalaJSVersions
+import ir.Trees.{Tree, ClassDef}
+import ir.Printers.IRTreePrinter
+
+import org.scalajs.core.tools.io._
+import scala.collection.immutable.Seq
+
+import java.io.{Console => _, _}
+import java.util.zip.{ZipFile, ZipEntry}
+
+object Scalajsp {
+
+ private case class Options(
+ jar: Option[File] = None,
+ fileNames: Seq[String] = Seq.empty
+ )
+
+ def main(args: Array[String]): Unit = {
+ val parser = new scopt.OptionParser[Options]("scalajsp") {
+ head("scalajsp", ScalaJSVersions.current)
+ arg[String](" ...")
+ .unbounded()
+ .action { (x, c) => c.copy(fileNames = c.fileNames :+ x) }
+ .text("*.sjsir file to display content of")
+ opt[File]('j', "jar")
+ .valueName("")
+ .action { (x, c) => c.copy(jar = Some(x)) }
+ .text("Read *.sjsir file(s) from the given JAR.")
+ opt[Unit]('s', "supported")
+ .action { (_,_) => printSupported(); exit(0) }
+ .text("Show supported Scala.js IR versions")
+ version("version")
+ .abbr("v")
+ .text("Show scalajsp version")
+ help("help")
+ .abbr("h")
+ .text("prints this usage text")
+
+ override def showUsageOnError = true
+ }
+
+ for {
+ options <- parser.parse(args, Options())
+ fileName <- options.fileNames
+ } {
+ val vfile = options.jar.map { jar =>
+ readFromJar(jar, fileName)
+ }.getOrElse {
+ readFromFile(fileName)
+ }
+
+ displayFileContent(vfile, options)
+ }
+ }
+
+ private def printSupported(): Unit = {
+ import ScalaJSVersions._
+ println(s"Emitted Scala.js IR version is: $binaryEmitted")
+ println("Supported Scala.js IR versions are")
+ binarySupported.foreach(v => println(s"* $v"))
+ }
+
+ private def displayFileContent(vfile: VirtualScalaJSIRFile,
+ opts: Options): Unit = {
+ new IRTreePrinter(stdout).print(vfile.tree)
+ stdout.write('\n')
+ stdout.flush()
+ }
+
+ private def fail(msg: String): Nothing = {
+ Console.err.println(msg)
+ exit(1)
+ }
+
+ private def exit(code: Int): Nothing = {
+ System.exit(code)
+ throw new AssertionError("unreachable")
+ }
+
+ private def readFromFile(fileName: String) = {
+ val file = new File(fileName)
+
+ if (!file.exists)
+ fail(s"No such file: $fileName")
+ else if (!file.canRead)
+ fail(s"Unable to read file: $fileName")
+ else
+ FileVirtualScalaJSIRFile(file)
+ }
+
+ private def readFromJar(jar: File, name: String) = {
+ val jarFile =
+ try { new ZipFile(jar) }
+ catch { case _: FileNotFoundException => fail(s"No such JAR: $jar") }
+ try {
+ val entry = jarFile.getEntry(name)
+ if (entry == null) {
+ fail(s"No such file in jar: $name")
+ } else {
+ val name = jarFile.getName + "#" + entry.getName
+ val content =
+ IO.readInputStreamToByteArray(jarFile.getInputStream(entry))
+ new MemVirtualSerializedScalaJSIRFile(name).withContent(content)
+ }
+ } finally {
+ jarFile.close()
+ }
+ }
+
+ private val stdout =
+ new BufferedWriter(new OutputStreamWriter(Console.out, "UTF-8"))
+
+}