akauppi
akauppi

Reputation: 18066

Something in a regex broke my build.sbt script (sbt 0.13, Scala 2.10.2)

I have a function in the build.sbt script to automatically detect latest Java Runtime on OS X. This worked great, until some update (sbt or Scala) that doesn't like it any more. I cannot even do an sbt clean but get an error (below).

The function is (simply pasting this to your build.sbt and running sbt clean is enough to test this - the code doesn't need to get executed):

javaHome := {
  var s = System.getenv("JAVA_HOME")
  if (s==null) {
    // Try to detect the latest JDK
    //
    // OS X: "/Library/Java/JavaVirtualMachines/jdk1.xxx.jdk/Contents/Home" with greatest id (i.e. "7.0_11")
    // Linux: tbd
    // Windows: tbd
    //
    val base= new File("/Library/Java/JavaVirtualMachines")
    assert( base.isDirectory, "Java JDKs not found at: "+ base )
    //
    // Get the latest version number available
    //
    // Note: JDK 7 has i.e. "jdk1.7.0_11.jdk"
    //       JDK 8 (early access) has "jdk1.8.0.jdk" (no underscore)
    //
    // For re explanation, see: http://stackoverflow.com/questions/8213837/optional-grouping-in-scala-regular-expressions
    // Triple quotes """ means '\' does not need to be escaped.
    //
    val re = """^jdk(\d+)\.(\d+)\.(\d+)(?:_([\d]*))?\.jdk$""".r
    var best = 0  // latest version so far
    base.listFiles.filter(_.isDirectory).map( (f: File) => {    // 'dir.name' i.e. "jdk1.7.0_11.jdk", "jdk1.8.0.jdk"
      try {
        val re(a,b,c,d) = f.name    // unapplies the caught parts
        val abcd= (((a.toInt)*100 + b.toInt)*100 + c.toInt)*100 + (Option(d) getOrElse "0").toInt
        println( "Found JVM: ", a, b, c, d )
        if (b=="8") {
          throw new RuntimeException( "Cannot use JavaFX 8 due to ScalaFX: please set 'JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.7.xxx.jdk/Contents/Home' env.var." )
        } else if (abcd>best) {
          best= abcd
          s= base+"/"+f.name+"/Contents/Home/"   // postfix in OS X
        }
      } catch { 
        // not really supposed to be having non-matching directories there (just skip them)
        case e: MatchError => None
      }
    } )
    //
    if (s==null) {
      throw new RuntimeException( "No JDK found at: "+ base )
    }
  }
  //
  println( "*** Using Java JDK: "+ s )
  val dir = new File(s)
  if (!dir.isDirectory) {
    throw new RuntimeException( "No JDK found at: "+ s )
  }
  //
  Some(dir)  // 'sbt' 'javaHome' value is ': Option[java.io.File]'
}

What I get running this is:

$ sbt clean
[info] Loading project definition from /Users/asko/Hg/ScalaSim/project
**error: symbol value re does not exist in $cdeec6b653504a14ad9f.$sbtdef**
[error] scala.reflect.internal.FatalError: 
[error]      while compiling: /Users/asko/Hg/ScalaSim/build.sbt
[error]         during phase: icode
[error]      library version: version 2.10.2
[error]     compiler version: version 2.10.2
[error]   reconstructed args: -classpath /Users/asko/Hg/ScalaSim/project/target/scala-2.10/sbt-0.13/classes:/Users/asko/.ivy2/cache/scala_2.10/sbt_0.13/com.github.retronym/sbt-onejar/jars/sbt-onejar-0.8.jar:/Users/asko/.ivy2/cache/scala_2.10/sbt_0.13/com.eed3si9n/sbt-assembly/jars/sbt-assembly-0.9.1.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/actions-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/api-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/apply-macro-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/cache-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/classfile-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/classpath-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/collections-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/command-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/compile-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/compiler-integration-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/compiler-ivy-integration-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/completion-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/control-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/cross-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/incremental-compiler-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/io-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/ivy-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/ivy-2.3.0-rc1.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/jline-2.11.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/jsch-0.1.46.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/launcher-interface-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/logging-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/main-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/main-settings-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/persist-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/process-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/relation-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/run-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/sbinary_2.10-0.4.2.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/sbt-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/scala-reflect-2.10.2.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/task-system-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/tasks-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/test-agent-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/test-interface-1.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/testing-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/tracking-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/org.scala-sbt/sbt/0.13.0/xsbti/interface-0.13.0.jar:/Users/asko/.sbt/boot/scala-2.10.2/lib/jansi.jar:/Users/asko/.sbt/boot/scala-2.10.2/lib/jline.jar:/Users/asko/.sbt/boot/scala-2.10.2/lib/scala-compiler.jar:/Users/asko/.sbt/boot/scala-2.10.2/lib/scala-library.jar:/Users/asko/.sbt/boot/scala-2.10.2/lib/scala-reflect.jar:/Users/asko/Hg/ScalaSim/project/target/config-classes
[error] 
[error]   last tree to typer: Ident(re$1)
[error]               symbol: value re$1 (flags: <param> <synthetic> <triedcooking>)
[error]    symbol definition: re$1: util.matching.Regex
[error]                  tpe: util.matching.Regex
[error]        symbol owners: value re$1 -> constructor $cdeec6b653504a14ad9f$$anonfun$$sbtdef$1 -> anonymous class sbtdef$1 -> package <empty>
[error]       context owners: anonymous class sbtdef$1 -> package <empty>
[error] 
[error] == Enclosing template or block ==
[error] 
...
[error] 
[error] == Expanded type of tree ==
[error] 
[error] TypeRef(TypeSymbol(class Regex extends Serializable))
[error] 
[error] symbol value re does not exist in $cdeec6b653504a14ad9f.$sbtdef
[error] Use 'last' for the full log.
Project loading failed: (r)etry, (q)uit, (l)ast, or (i)gnore? 

Can anyone tell me what's going on here? Is it me or is it sbt?

Upvotes: 2

Views: 636

Answers (2)

Eugene Yokota
Eugene Yokota

Reputation: 95684

I think there's a bug in sbt 0.13's macro handling (or macro in general). Take val re out and it works fine:

val re = """^jdk(\d+)\.(\d+)\.(\d+)(?:_([\d]*))?\.jdk$""".r

javaHome := {
  var s = System.getenv("JAVA_HOME")
  if (s==null) {
    // Try to detect the latest JDK
    //
    // OS X: "/Library/Java/JavaVirtualMachines/jdk1.xxx.jdk/Contents/Home" with greatest id (i.e. "7.0_11")
    // Linux: tbd
    // Windows: tbd
    //
    val base= new File("/Library/Java/JavaVirtualMachines")
    assert( base.isDirectory, "Java JDKs not found at: "+ base )
    //
    // Get the latest version number available
    //
    // Note: JDK 7 has i.e. "jdk1.7.0_11.jdk"
    //       JDK 8 (early access) has "jdk1.8.0.jdk" (no underscore)
    //
    // For re explanation, see: http://stackoverflow.com/questions/8213837/optional-grouping-in-scala-regular-expressions
    // Triple quotes """ means '\' does not need to be escaped.
    //
    var best = 0  // latest version so far
    base.listFiles.filter(_.isDirectory).map( (f: File) => {    // 'dir.name' i.e. "jdk1.7.0_11.jdk", "jdk1.8.0.jdk"
      try {
        val re(a,b,c,d) = f.name    // unapplies the caught parts
        val abcd= (((a.toInt)*100 + b.toInt)*100 + c.toInt)*100 + (Option(d) getOrElse "0").toInt
        println( "Found JVM: ", a, b, c, d )
        if (b=="8") {
          throw new RuntimeException( "Cannot use JavaFX 8 due to ScalaFX: please set 'JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.7.xxx.jdk/Contents/Home' env.var." )
        } else if (abcd>best) {
          best= abcd
          s= base+"/"+f.name+"/Contents/Home/"   // postfix in OS X
        }
      } catch { 
        // not really supposed to be having non-matching directories there (just skip them)
        case e: MatchError => None
      }
    } )
    //
    if (s==null) {
      throw new RuntimeException( "No JDK found at: "+ base )
    }
  }
  //
  println( "*** Using Java JDK: "+ s )
  val dir = new File(s)
  if (!dir.isDirectory) {
    throw new RuntimeException( "No JDK found at: "+ s )
  }
  //
  Some(dir)  // 'sbt' 'javaHome' value is ': Option[java.io.File]'
}

Here's what I got:

*** Using Java JDK: /Library/Java/Home/

Upvotes: 2

Daniel C. Sobral
Daniel C. Sobral

Reputation: 297285

It is sbt.

Very interesting! SBT 0.13 moved to using macros, so it can simplify declarations, but you have stumbled upon something that has stopped working because of that. Please report this as a bug on SBT.

Upvotes: 1

Related Questions