Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
scottfrazer committed Oct 16, 2015
2 parents 8be932e + de491ac commit 053c7d9
Show file tree
Hide file tree
Showing 63 changed files with 1,337 additions and 944 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ jdk:
before_script:
- mysql -e "create database IF NOT EXISTS cromwell_test;"

script: sbt -Dbackend.shared-filesystem.localization.0=copy clean coverage nodocker:test
script: sbt -Dbackend.shared-filesystem.localization.0=copy clean coverage nodocker:test assembly
after_success: sbt coveralls
33 changes: 26 additions & 7 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,32 @@ import sbtrelease.ReleasePlugin._

name := "cromwell"

version := "0.12"
version := "0.13"

organization := "org.broadinstitute"

scalaVersion := "2.11.7"

val lenthallV = "0.13"

val sprayV = "1.3.2"

val DowngradedSprayV = "1.3.1"

val akkaV = "2.3.12"

val slickV = "3.1.0"

val googleClientApiV = "1.20.0"

resolvers ++= Seq(
"Broad Artifactory Releases" at "https://artifactory.broadinstitute.org/artifactory/libs-release/",
"Broad Artifactory Snapshots" at "https://artifactory.broadinstitute.org/artifactory/libs-snapshot/")

libraryDependencies ++= Seq(
"com.gettyimages" %% "spray-swagger" % "0.5.1",
"org.broadinstitute" %% "lenthall" % lenthallV,
"com.typesafe.scala-logging" %% "scala-logging" % "3.1.0",
"org.joda" % "joda-convert" % "1.8.1",
"org.webjars" % "swagger-ui" % "2.1.1",
"io.spray" %% "spray-can" % sprayV,
"io.spray" %% "spray-routing" % sprayV,
Expand All @@ -33,14 +43,14 @@ libraryDependencies ++= Seq(
"ch.qos.logback" % "logback-classic" % "1.1.3",
"ch.qos.logback" % "logback-access" % "1.1.3",
"org.codehaus.janino" % "janino" % "2.7.8",
"com.typesafe.slick" %% "slick" % "3.0.0",
"com.zaxxer" % "HikariCP" % "2.3.3",
"com.typesafe.slick" %% "slick" % slickV,
"com.typesafe.slick" %% "slick-hikaricp" % slickV,
"org.hsqldb" % "hsqldb" % "2.3.2",
"com.google.gcloud" % "gcloud-java" % "latest.integration",
"com.google.api-client" % "google-api-client-java6" % googleClientApiV,
"com.google.api-client" % "google-api-client-jackson2" % googleClientApiV,
"com.google.oauth-client" % "google-oauth-client" % googleClientApiV,
"mysql" % "mysql-connector-java" % "5.1.35",
"mysql" % "mysql-connector-java" % "5.1.36",
"org.scalaz" % "scalaz-core_2.11" % "7.1.3",
//---------- Test libraries -------------------//
"io.spray" %% "spray-testkit" % sprayV % Test,
Expand Down Expand Up @@ -81,14 +91,20 @@ val customMergeStrategy: String => MergeStrategy = {
MergeStrategy.filterDistinctLines
case _ => MergeStrategy.deduplicate
}
case "asm-license.txt" | "overview.html" =>
case "asm-license.txt" | "overview.html" | "cobertura.properties" =>
MergeStrategy.discard
case _ => MergeStrategy.deduplicate
}

assemblyMergeStrategy in assembly := customMergeStrategy

scalacOptions ++= Seq("-deprecation", "-unchecked", "-feature")
// The reason why -Xmax-classfile-name is set is because this will fail
// to build on Docker otherwise. The reason why it's 200 is because it
// fails if the value is too close to 256 (even 254 fails). For more info:
//
// https://github.com/sbt/sbt-assembly/issues/69
// https://github.com/scala/pickling/issues/10
scalacOptions ++= Seq("-deprecation", "-unchecked", "-feature", "-Xmax-classfile-name", "200")

lazy val DockerTest = config("docker") extend Test

Expand All @@ -107,3 +123,6 @@ testOptions in DockerTest += Tests.Argument("-n", "DockerTest")
testOptions in NoDockerTest += Tests.Argument("-l", "DockerTest")

test in assembly := {}

parallelExecution := false

27 changes: 16 additions & 11 deletions src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ akka {
loggers = ["akka.event.slf4j.Slf4jLogger"]
}

swagger {
docsPath = "swagger/cromwell.yaml"
uiVersion = "2.1.1"
}

spray.can {
server {
request-timeout = 40s
Expand Down Expand Up @@ -94,30 +99,30 @@ database {

main {
hsqldb {
url = "jdbc:hsqldb:mem:${slick.uniqueSchema};shutdown=false;hsqldb.tx=mvcc"
driver = "org.hsqldb.jdbcDriver"
slick.driver = "slick.driver.HsqldbDriver"
db.url = "jdbc:hsqldb:mem:${slick.uniqueSchema};shutdown=false;hsqldb.tx=mvcc"
db.driver = "org.hsqldb.jdbcDriver"
driver = "slick.driver.HsqldbDriver$"
slick.createSchema = true
}
}

test {
hsqldb {
url = "jdbc:hsqldb:mem:testdb;shutdown=false;hsqldb.tx=mvcc"
driver = "org.hsqldb.jdbcDriver"
slick.driver = "slick.driver.HsqldbDriver"
db.url = "jdbc:hsqldb:mem:testdb;shutdown=false;hsqldb.tx=mvcc"
db.driver = "org.hsqldb.jdbcDriver"
driver = "slick.driver.HsqldbDriver$"
liquibase = {
changelog = "src/main/migrations/changelog.xml"
connection = "liquibase.database.jvm.HsqlConnection"
}
}

mysql {
url = "jdbc:mysql://localhost/cromwell_test"
user = "travis"
password = ""
driver = "com.mysql.jdbc.Driver"
slick.driver = "slick.driver.MySQLDriver"
db.url = "jdbc:mysql://localhost/cromwell_test"
db.user = "travis"
db.password = ""
db.driver = "com.mysql.jdbc.Driver"
driver = "slick.driver.MySQLDriver$"
liquibase = {
changelog = "src/main/migrations/changelog.xml"
}
Expand Down
34 changes: 25 additions & 9 deletions src/main/scala/cromwell/binding/AstTools.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,28 @@ object AstTools {
}

def wdlValue(wdlType: WdlType, wdlSyntaxErrorFormatter: WdlSyntaxErrorFormatter): WdlValue = {

def astToMap(ast: Ast) = {
val mapType = wdlType.asInstanceOf[WdlMapType]
val elements = ast.getAttribute("map").asInstanceOf[AstList].asScala.toVector.map({ kvnode =>
val k = kvnode.asInstanceOf[Ast].getAttribute("key").wdlValue(mapType.keyType, wdlSyntaxErrorFormatter)
val v = kvnode.asInstanceOf[Ast].getAttribute("value").wdlValue(mapType.valueType, wdlSyntaxErrorFormatter)
k -> v
}).toMap

WdlMap(mapType, elements)
}

def astToObject(ast: Ast) = {
val elements = ast.getAttribute("map").asInstanceOf[AstList].asScala.toVector.map({ kvnode =>
val k = kvnode.asInstanceOf[Ast].getAttribute("key").sourceString
val v = kvnode.asInstanceOf[Ast].getAttribute("value").wdlValue(WdlStringType, wdlSyntaxErrorFormatter)
k -> v
}).toMap

WdlObject(elements)
}

astNode match {
case t: Terminal if t.getTerminalStr == "string" && wdlType == WdlStringType => WdlString(t.getSourceString)
case t: Terminal if t.getTerminalStr == "string" && wdlType == WdlFileType => WdlFile(t.getSourceString)
Expand All @@ -61,21 +83,15 @@ object AstTools {
case "true" => WdlBoolean.True
case "false" => WdlBoolean.False
}
// TODO: The below cases, ArrayLiteral and MapLiteral are brittle. They recursively call this wdlValue().
// TODO: The below cases, ArrayLiteral and MapLiteral, ObjectLiteral are brittle. They recursively call this wdlValue().
// However, those recursive calls might contain full-on expressions instead of just other literals. This
// whole thing ought to be part of the regular expression evaluator, though I imagine that's non-trivial.
case a: Ast if a.getName == "ArrayLiteral" && wdlType.isInstanceOf[WdlArrayType] =>
val arrType = wdlType.asInstanceOf[WdlArrayType]
val elements = a.getAttribute("values").astListAsVector map {node => node.wdlValue(arrType.memberType, wdlSyntaxErrorFormatter)}
WdlArray(arrType, elements)
case a: Ast if a.getName == "MapLiteral" && wdlType.isInstanceOf[WdlMapType] =>
val mapType = wdlType.asInstanceOf[WdlMapType]
val elements = a.getAttribute("map").asInstanceOf[AstList].asScala.toVector.map({ kvnode =>
val k = kvnode.asInstanceOf[Ast].getAttribute("key").wdlValue(mapType.keyType, wdlSyntaxErrorFormatter)
val v = kvnode.asInstanceOf[Ast].getAttribute("value").wdlValue(mapType.valueType, wdlSyntaxErrorFormatter)
k -> v
}).toMap
WdlMap(mapType, elements)
case a: Ast if a.getName == "MapLiteral" && wdlType.isInstanceOf[WdlMapType] => astToMap(a)
case a: Ast if a.getName == "ObjectLiteral" && wdlType == WdlObjectType => astToObject(a)
case _ => throw new SyntaxError(s"Could not convert AST to a $wdlType (${Option(astNode).getOrElse("No AST").toString})")
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/main/scala/cromwell/binding/WdlExpression.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ object WdlExpression {
def isMemberAccess: Boolean = ast.getName == "MemberAccess"
def isArrayLiteral: Boolean = ast.getName == "ArrayLiteral"
def isMapLiteral: Boolean = ast.getName == "MapLiteral"
def isObjectLiteral: Boolean = ast.getName == "ObjectLiteral"
def isArrayOrMapLookup: Boolean = ast.getName == "ArrayOrMapLookup"
def params = ast.getAttribute("params").asInstanceOf[AstList].asScala.toVector
def name = ast.getAttribute("name").asInstanceOf[Terminal].getSourceString
Expand Down Expand Up @@ -70,7 +71,8 @@ object WdlExpression {
"read_float",
"read_boolean",
"read_lines",
"read_map"
"read_map",
"read_object"
)

def evaluate(ast: AstNode, lookup: ScopedLookupFunction, functions: WdlFunctions[WdlValue]): Try[WdlValue] =
Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/cromwell/binding/WdlNamespace.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package cromwell.binding
import java.io.File

import cromwell.binding.AstTools.{AstNodeName, EnhancedAstNode, EnhancedAstSeq}
import cromwell.binding.expression.NoFunctions
import cromwell.binding.expression.WdlStandardLibraryFunctions
import cromwell.binding.types._
import cromwell.binding.values._
import cromwell.parser.WdlParser._
Expand Down Expand Up @@ -107,13 +107,13 @@ case class NamespaceWithWorkflow(importedAs: Option[String],
* For the declarations that have an expression attached to it already, evaluate the expression
* and return the value for storage in the symbol store
*/
def staticDeclarationsRecursive(userInputs: WorkflowCoercedInputs): Try[WorkflowCoercedInputs] = {
def staticDeclarationsRecursive(userInputs: WorkflowCoercedInputs, wdlFunctions: WdlStandardLibraryFunctions): Try[WorkflowCoercedInputs] = {
import scala.collection.mutable
val collected = mutable.Map[String, WdlValue]()
val allDeclarations = workflow.declarations ++ workflow.calls.flatMap {_.task.declarations}

val evaluatedDeclarations = allDeclarations.filter {_.expression.isDefined}.map {decl =>
val value = decl.expression.get.evaluate(declarationLookupFunction(decl, collected.toMap ++ userInputs), new NoFunctions)
val value = decl.expression.get.evaluate(declarationLookupFunction(decl, collected.toMap ++ userInputs), wdlFunctions)
collected += (decl.fullyQualifiedName -> value.get)
val coercedValue = value match {
case Success(s) => decl.wdlType.coerceRawValue(s)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ case class ParameterCommandPart(attributes: Map[String, String], expression: Wdl
case Some(d) => throw new UnsupportedOperationException(s"Parameter ${v.variable} is required, but no value is specified")
case None => throw new UnsupportedOperationException(s"This should not happen: could not find declaration for ${v.variable}")
}
case _ => throw new UnsupportedOperationException(s"Could not evaluate expression: ${expression.toString}")
case e => throw new UnsupportedOperationException(s"Could not evaluate expression: ${expression.toWdlString}", e)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,22 @@ trait WdlStandardLibraryFunctions extends WdlFunctions[WdlValue] {
protected def read_object(params: Seq[Try[WdlValue]]): Try[WdlObject] = fail("read_objects")
protected def read_objects(params: Seq[Try[WdlValue]]): Try[WdlArray] = fail("read_objects")
protected def read_json(params: Seq[Try[WdlValue]]): Try[WdlValue] = fail("read_json")
protected def read_int(params: Seq[Try[WdlValue]]): Try[WdlInteger] = fail("read_int")
protected def read_string(params: Seq[Try[WdlValue]]): Try[WdlString] = fail("read_string")
protected def read_float(params: Seq[Try[WdlValue]]): Try[WdlFloat] = fail("read_float")
protected def read_boolean(params: Seq[Try[WdlValue]]): Try[WdlBoolean] = fail("read_boolean")
/**
* Try to read an integer from the file referenced by the specified `WdlValue`.
*/
protected def read_int(params: Seq[Try[WdlValue]]): Try[WdlInteger] =
read_string(params) map { s => WdlInteger(s.value.trim.toInt) }
/**
* Try to read a float from the file referenced by the specified `WdlValue`.
*/
protected def read_float(params: Seq[Try[WdlValue]]): Try[WdlFloat] =
read_string(params) map { s => WdlFloat(s.value.trim.toDouble) }
/**
* Try to read a boolean from the file referenced by the specified `WdlValue`.
*/
protected def read_boolean(params: Seq[Try[WdlValue]]): Try[WdlBoolean] =
read_string(params) map { s => WdlBoolean(java.lang.Boolean.parseBoolean(s.value.trim.toLowerCase)) }
protected def write_lines(params: Seq[Try[WdlValue]]): Try[WdlFile] = fail("write_lines")
protected def write_tsv(params: Seq[Try[WdlValue]]): Try[WdlFile] = fail("write_tsv")
protected def write_map(params: Seq[Try[WdlValue]]): Try[WdlFile] = fail("write_map")
Expand Down
13 changes: 12 additions & 1 deletion src/main/scala/cromwell/binding/types/WdlArrayType.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package cromwell.binding.types

import cromwell.binding.values.{WdlArray, WdlFile, WdlString}
import cromwell.binding.values.{WdlValue, WdlArray, WdlFile, WdlString}
import spray.json.JsArray

case class WdlArrayType(memberType: WdlType) extends WdlType {
Expand All @@ -27,3 +27,14 @@ case class WdlArrayType(memberType: WdlType) extends WdlType {
case _ => false
}
}

object WdlArrayType {

implicit class WdlArrayEnhanced(wdlType: WdlType) extends WdlType {

override protected def coercion: PartialFunction[Any, WdlValue] = wdlType.coercion
override def toWdlString: String = wdlType.toWdlString

def isAnArrayOf(genericType: WdlType) = wdlType.isInstanceOf[WdlArrayType] && wdlType.asInstanceOf[WdlArrayType].memberType == genericType
}
}
2 changes: 2 additions & 0 deletions src/main/scala/cromwell/binding/types/WdlMapType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ case class WdlMapType(keyType: WdlType, valueType: WdlType) extends WdlType {
case m: Map[_, _] if m.isEmpty => WdlMap(WdlMapType(keyType, valueType), Map())
case js: JsObject if js.fields.nonEmpty => WdlMap.coerceMap(js.fields, this)
case wdlMap: WdlMap => WdlMap.coerceMap(wdlMap.value, this)
case o: WdlObject => WdlMap.coerceMap(o.value, this)
}

override def isCoerceableFrom(otherType: WdlType): Boolean = otherType match {
case m: WdlMapType => keyType.isCoerceableFrom(m.keyType) && valueType.isCoerceableFrom(m.valueType)
case WdlObjectType => keyType.isCoerceableFrom(WdlStringType) && valueType.isCoerceableFrom(WdlStringType)
case _ => false
}
}
40 changes: 38 additions & 2 deletions src/main/scala/cromwell/binding/types/WdlObjectType.scala
Original file line number Diff line number Diff line change
@@ -1,16 +1,52 @@
package cromwell.binding.types

import cromwell.binding.Call
import cromwell.binding.values.{WdlCallOutputsObject, WdlObject}
import cromwell.binding.values._
import spray.json.JsObject

import scala.util.{Try, Failure, Success}

case object WdlObjectType extends WdlType {
val toWdlString: String = "Object"

private def handleCoercionFailures(tries: Try[_]*) = {
val errorMessages = tries collect {
case Failure(f) => f.getMessage
} mkString ","

throw new UnsupportedOperationException(s"Coercion failed: $errorMessages")
}

override protected def coercion = {
case o: WdlObject => o
case m: WdlMap if isMapCoercable(m) =>
val coercedMap = m.value map {
case (k, v) => toWdlString(k) -> toWdlString(v)
} collect {
case (Success(k), Success(v)) => k.value -> v
case (k, v) => handleCoercionFailures(k, v)
}

WdlObject(coercedMap)
case js: JsObject =>
val coercedMap = WdlMap.coerceMap(js.fields, WdlMapType(WdlStringType, WdlAnyType)).value map {
// get is safe because coerceMap above would have failed already if k was not coerceable to WdlString
case (k, v) => toWdlString(k).get.value -> v
}

WdlObject(coercedMap)
}

private def toWdlString(v: WdlValue) = WdlStringType.coerceRawValue(v).map(_.asInstanceOf[WdlString])

override def isCoerceableFrom(otherType: WdlType) = otherType match {
case WdlObjectType => true
case t: WdlMapType if isMapTypeCoercable(t) => true
case _ => false
}

override def fromWdlString(rawString: String) = ???
def isMapTypeCoercable(t: WdlMapType) = WdlStringType.isCoerceableFrom(t.keyType) && WdlStringType.isCoerceableFrom(t.valueType)
def isMapCoercable(m: WdlMap) = isMapTypeCoercable(m.wdlType)
}

case class WdlCallOutputsObjectType(call: Call) extends WdlType {
Expand Down
1 change: 1 addition & 0 deletions src/main/scala/cromwell/binding/types/WdlType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ trait WdlType {
* @param wdlSource source code representing the WdlValue
* @return The WdlValue
*/
//TODO: return a Try ?
def fromWdlString(wdlSource: WdlSource): WdlValue = {
val tokens = WdlType.parser.lex(wdlSource, "string")
val terminalMap = tokens.asScala.toVector.map {(_, wdlSource)}.toMap
Expand Down
3 changes: 2 additions & 1 deletion src/main/scala/cromwell/binding/values/WdlArray.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package cromwell.binding.values

import cromwell.binding.types.{WdlArrayType, WdlPrimitiveType}
import cromwell.binding.types.{WdlObjectType, WdlArrayType, WdlPrimitiveType}

import scala.util.{Failure, Success, Try}

Expand All @@ -25,6 +25,7 @@ case class WdlArray(wdlType: WdlArrayType, value: Seq[WdlValue]) extends WdlValu
def tsvSerialize: Try[String] = {
wdlType.memberType match {
case t: WdlPrimitiveType => Success(value.map(_.valueString).mkString("\n"))
case WdlObjectType => WdlObject.tsvSerializeArray(value map { _.asInstanceOf[WdlObject] })
case _ => Failure(new UnsupportedOperationException("Can only TSV serialize an Array[Primitive]"))
}
}
Expand Down
Loading

0 comments on commit 053c7d9

Please sign in to comment.