import org.enso.build.BenchTasks._ import org.enso.build.WithDebugCommand import sbt.Keys.{libraryDependencies, scalacOptions} import sbt.addCompilerPlugin import sbtassembly.AssemblyPlugin.defaultUniversalScript import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType} import java.io.File // ============================================================================ // === Global Configuration =================================================== // ============================================================================ val scalacVersion = "2.13.5" val rustVersion = "1.40.0-nightly (b520af6fd 2019-11-03)" val graalVersion = "21.0.0.2" val javaVersion = "11" val ensoVersion = "0.2.7-SNAPSHOT" // Note [Engine And Launcher Version] /* Note [Engine And Launcher Version] * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Currently both Engine and Launcher versions are tied to each other - each new * releases contains the Engine and the Launcher and thus the version number is * shared. If the version numbers ever diverge, make sure to update the build * scripts at .github/workflows accordingly. */ organization in ThisBuild := "org.enso" scalaVersion in ThisBuild := scalacVersion lazy val gatherLicenses = taskKey[Unit]("Gathers licensing information for relevant dependencies") gatherLicenses := { val _ = GatherLicenses.run.value } lazy val verifyLicensePackages = taskKey[Unit]( "Verifies if the license package has been generated, " + "has no warnings and is up-to-date with dependencies." ) verifyLicensePackages := GatherLicenses.verifyReports.value lazy val verifyGeneratedPackage = inputKey[Unit]( "Verifies if the license package in a generated distribution is " + "up-to-date with the one from the report." ) verifyGeneratedPackage := GatherLicenses.verifyGeneratedPackage.evaluated GatherLicenses.distributions := Seq( Distribution( "launcher", file("distribution/launcher/THIRD-PARTY"), Distribution.sbtProjects(launcher) ), Distribution( "engine", file("distribution/engine/THIRD-PARTY"), Distribution.sbtProjects( runtime, `engine-runner`, `language-server` ) ), Distribution( "project-manager", file("distribution/project-manager/THIRD-PARTY"), Distribution.sbtProjects(`project-manager`) ), Distribution( "std-lib-Base", file("distribution/std-lib/Base/THIRD-PARTY"), Distribution.sbtProjects(`std-bits`) ), Distribution( "std-lib-Table", file("distribution/std-lib/Table/THIRD-PARTY"), Distribution.sbtProjects(table) ), Distribution( "std-lib-Database", file("distribution/std-lib/Database/THIRD-PARTY"), Distribution.sbtProjects(database) ) ) GatherLicenses.licenseConfigurations := Set("compile") GatherLicenses.configurationRoot := file("tools/legal-review") lazy val openLegalReviewReport = taskKey[Unit]( "Gathers licensing information for relevant dependencies and opens the " + "report in review mode in the browser." ) openLegalReviewReport := { val _ = gatherLicenses.value GatherLicenses.runReportServer() } lazy val analyzeDependency = inputKey[Unit]("...") analyzeDependency := GatherLicenses.analyzeDependency.evaluated val packageBuilder = new DistributionPackage.Builder( ensoVersion = ensoVersion, graalVersion = graalVersion, graalJavaVersion = javaVersion, artifactRoot = file("built-distribution") ) Global / onChangedBuildSource := ReloadOnSourceChanges // ============================================================================ // === Compiler Options ======================================================= // ============================================================================ javacOptions in ThisBuild ++= Seq( "-encoding", // Provide explicit encoding (the next line) "UTF-8", // Specify character encoding used by Java source files. "-deprecation" // Shows a description of each use or override of a deprecated member or class. ) scalacOptions in ThisBuild ++= Seq( "-deprecation", // Emit warning and location for usages of deprecated APIs. "-encoding", // Provide explicit encoding (the next line) "utf-8", // Specify character encoding used by Scala source files. "-explaintypes", // Explain type errors in more detail. "-feature", // Emit warning and location for usages of features that should be imported explicitly. "-language:existentials", // Existential types (besides wildcard types) can be written and inferred "-language:experimental.macros", // Allow macro definition (besides implementation and application) "-language:higherKinds", // Allow higher-kinded types "-language:implicitConversions", // Allow definition of implicit functions called views "-unchecked", // Enable additional warnings where generated code depends on assumptions. "-Xcheckinit", // Wrap field accessors to throw an exception on uninitialized access. "-Xlint:adapted-args", // Warn if an argument list is modified to match the receiver. "-Xlint:constant", // Evaluation of a constant arithmetic expression results in an error. "-Xlint:delayedinit-select", // Selecting member of DelayedInit. "-Xlint:doc-detached", // A Scaladoc comment appears to be detached from its element. "-Xlint:inaccessible", // Warn about inaccessible types in method signatures. "-Xlint:infer-any", // Warn when a type argument is inferred to be `Any`. "-Xlint:missing-interpolator", // A string literal appears to be missing an interpolator id. "-Xlint:nullary-unit", // Warn when nullary methods return Unit. "-Xlint:option-implicit", // Option.apply used implicit view. "-Xlint:package-object-classes", // Class or object defined in package object. "-Xlint:poly-implicit-overload", // Parameterized overloaded implicit methods are not visible as view bounds. "-Xlint:private-shadow", // A private field (or class parameter) shadows a superclass field. "-Xlint:stars-align", // Pattern sequence wildcard must align with sequence component. "-Xlint:type-parameter-shadow", // A local type parameter shadows a type already in scope. "-Xmacro-settings:-logging@org.enso", // Disable the debug logging globally. "-Ywarn-dead-code", // Warn when dead code is identified. "-Ywarn-extra-implicit", // Warn when more than one implicit parameter section is defined. "-Ywarn-numeric-widen", // Warn when numerics are widened. "-Ywarn-unused:imports", // Warn if an import selector is not referenced. "-Ywarn-unused:implicits", // Warn if an implicit parameter is unused. "-Ywarn-unused:locals", // Warn if a local definition is unused. "-Ywarn-unused:patvars", // Warn if a variable bound in a pattern is unused. "-Ywarn-unused:privates", // Warn if a private member is unused. "-Ywarn-unused:params", // Warn if a value parameter is unused. "-Xfatal-warnings" // Make warnings fatal so they don't make it onto main (use @nowarn for local suppression) ) val jsSettings = Seq( scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.ESModule) } ) scalacOptions in (Compile, console) ~= (_ filterNot (_ == "-Xfatal-warnings")) // ============================================================================ // === Benchmark Configuration ================================================ // ============================================================================ lazy val Benchmark = config("bench") extend sbt.Test // Native Image Generation lazy val rebuildNativeImage = taskKey[Unit]("Force to rebuild native image") lazy val buildNativeImage = taskKey[Unit]("Ensure that the Native Image is built.") // Bootstrap task lazy val bootstrap = taskKey[Unit]("Prepares Truffle JARs that are required by the sbt JVM") bootstrap := {} // ============================================================================ // === Global Project ========================================================= // ============================================================================ lazy val enso = (project in file(".")) .settings(version := "0.1") .aggregate( `core-definition`, `interpreter-dsl`, `json-rpc-server-test`, `json-rpc-server`, `language-server`, `parser-service`, `polyglot-api`, `project-manager`, `syntax-definition`.jvm, `text-buffer`, flexer.jvm, graph, logger.jvm, pkg, cli, `logging-service`, `akka-native`, `version-output`, `engine-runner`, runtime, searcher, launcher, `runtime-version-manager`, `runtime-version-manager-test`, syntax.jvm, testkit ) .settings(Global / concurrentRestrictions += Tags.exclusive(Exclusive)) .settings( commands ++= Seq(packageBuilder.makePackages, packageBuilder.makeBundles) ) // ============================================================================ // === Dependency Versions ==================================================== // ============================================================================ /* Note [Dependency Versions] * ~~~~~~~~~~~~~~~~~~~~~~~~~~ * Please maintain the following section in alphabetical order for the bundles * of dependencies. Additionally, please keep the 'Other' subsection in * alphabetical order. * * Furthermore, please keep the following in mind: * - Wherever possible, we should use the same version of a dependency * throughout the project. * - If you need to include a new dependency, please define its version in this * section. * - If that version is not the latest, please include a note explaining why * this is the case. * - If, for some reason, you need to use a dependency version other than the * global one, please include a note explaining why this is the case, and the * circumstances under which the dependency could be upgraded to use the * global version */ // === Akka =================================================================== def akkaPkg(name: String) = akkaURL %% s"akka-$name" % akkaVersion def akkaHTTPPkg(name: String) = akkaURL %% s"akka-$name" % akkaHTTPVersion val akkaURL = "com.typesafe.akka" val akkaVersion = "2.6.6" val akkaHTTPVersion = "10.2.0-RC1" val akkaMockSchedulerVersion = "0.5.5" val logbackClassicVersion = "1.2.3" val akkaActor = akkaPkg("actor") val akkaStream = akkaPkg("stream") val akkaTyped = akkaPkg("actor-typed") val akkaTestkit = akkaPkg("testkit") val akkaSLF4J = akkaPkg("slf4j") val akkaTestkitTyped = akkaPkg("actor-testkit-typed") % Test val akkaHttp = akkaHTTPPkg("http") val akkaSpray = akkaHTTPPkg("http-spray-json") val akkaTest = Seq( "ch.qos.logback" % "logback-classic" % logbackClassicVersion % Test ) val akka = Seq( akkaActor, akkaStream, akkaHttp, akkaSpray, akkaTyped ) // === Cats =================================================================== val catsVersion = "2.2.0-M3" val kittensVersion = "2.1.0" val cats = { Seq( "org.typelevel" %% "cats-core" % catsVersion, "org.typelevel" %% "cats-effect" % catsVersion, "org.typelevel" %% "cats-free" % catsVersion, "org.typelevel" %% "cats-macros" % catsVersion, "org.typelevel" %% "kittens" % kittensVersion ) } // === Circe ================================================================== val circeVersion = "0.14.0-M1" val circeYamlVersion = "0.13.1" val enumeratumCirceVersion = "1.6.1" val circeGenericExtrasVersion = "0.13.0" val circe = Seq("circe-core", "circe-generic", "circe-parser") .map("io.circe" %% _ % circeVersion) // === Commons ================================================================ val commonsCollectionsVersion = "4.4" val commonsLangVersion = "3.10" val commonsIoVersion = "2.7" val commonsTextVersion = "1.8" val commonsMathVersion = "3.6.1" val commonsCompressVersion = "1.20" val commonsCliVersion = "1.4" val commons = Seq( "org.apache.commons" % "commons-collections4" % commonsCollectionsVersion, "org.apache.commons" % "commons-lang3" % commonsLangVersion, "commons-io" % "commons-io" % commonsIoVersion, "org.apache.commons" % "commons-text" % commonsTextVersion, "org.apache.commons" % "commons-math3" % commonsMathVersion, "commons-cli" % "commons-cli" % commonsCliVersion ) // === Jackson ================================================================ val jacksonVersion = "2.11.1" val jackson = Seq( "com.fasterxml.jackson.dataformat" % "jackson-dataformat-cbor" % jacksonVersion, "com.fasterxml.jackson.core" % "jackson-databind" % jacksonVersion, "com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion ) // === JAXB ================================================================ val jaxbVersion = "2.3.3" val jaxb = Seq( "jakarta.xml.bind" % "jakarta.xml.bind-api" % jaxbVersion % Benchmark, "com.sun.xml.bind" % "jaxb-impl" % jaxbVersion % Benchmark ) // === JMH ==================================================================== val jmhVersion = "1.23" val jmh = Seq( "org.openjdk.jmh" % "jmh-core" % jmhVersion % Benchmark, "org.openjdk.jmh" % "jmh-generator-annprocess" % jmhVersion % Benchmark ) // === Monocle ================================================================ val monocleVersion = "2.0.5" val monocle = { Seq( "com.github.julien-truffaut" %% "monocle-core" % monocleVersion, "com.github.julien-truffaut" %% "monocle-macro" % monocleVersion, "com.github.julien-truffaut" %% "monocle-law" % monocleVersion % "test" ) } // === Scala Compiler ========================================================= val scalaCompiler = Seq( "org.scala-lang" % "scala-reflect" % scalacVersion, "org.scala-lang" % "scala-compiler" % scalacVersion ) // === Splain ================================================================= val splainVersion = "0.5.8" val splainOptions = Seq( "-P:splain:infix:true", "-P:splain:foundreq:true", "-P:splain:implicits:true", "-P:splain:tree:true" ) // === std-lib ================================================================ val icuVersion = "67.1" // === ZIO ==================================================================== val zioVersion = "1.0.1" val zioInteropCatsVersion = "2.1.4.0" val zio = Seq( "dev.zio" %% "zio" % zioVersion, "dev.zio" %% "zio-interop-cats" % zioInteropCatsVersion ) // === Other ================================================================== val bcpkixJdk15Version = "1.65" val bumpVersion = "0.1.3" val declineVersion = "1.2.0" val directoryWatcherVersion = "0.9.10" val flatbuffersVersion = "1.12.0" val guavaVersion = "29.0-jre" val jlineVersion = "3.15.0" val kindProjectorVersion = "0.11.3" val mockitoScalaVersion = "1.14.8" val newtypeVersion = "0.4.4" val pprintVersion = "0.5.9" val pureconfigVersion = "0.13.0" val refinedVersion = "0.9.14" val scalacheckVersion = "1.14.3" val scalacticVersion = "3.3.0-SNAP2" val scalaLoggingVersion = "3.9.2" val scalameterVersion = "0.19" val scalatagsVersion = "0.9.1" val scalatestVersion = "3.3.0-SNAP2" val shapelessVersion = "2.4.0-M1" val slf4jVersion = "1.7.30" val slickVersion = "3.3.2" val sqliteVersion = "3.31.1" val tikaVersion = "1.24.1" val typesafeConfigVersion = "1.4.0" // ============================================================================ // === Internal Libraries ===================================================== // ============================================================================ lazy val logger = crossProject(JVMPlatform, JSPlatform) .withoutSuffixFor(JVMPlatform) .crossType(CrossType.Pure) .in(file("lib/scala/logger")) .settings( version := "0.1", libraryDependencies ++= scalaCompiler ) .jsSettings(jsSettings) lazy val flexer = crossProject(JVMPlatform, JSPlatform) .withoutSuffixFor(JVMPlatform) .crossType(CrossType.Pure) .in(file("lib/scala/flexer")) .dependsOn(logger) .settings( version := "0.1", resolvers += Resolver.sonatypeRepo("releases"), libraryDependencies ++= scalaCompiler ++ Seq( "com.google.guava" % "guava" % guavaVersion exclude ("com.google.code.findbugs", "jsr305"), "org.typelevel" %%% "cats-core" % catsVersion, "org.typelevel" %%% "kittens" % kittensVersion ) ) .jsSettings(jsSettings) lazy val `syntax-definition` = crossProject(JVMPlatform, JSPlatform) .withoutSuffixFor(JVMPlatform) .crossType(CrossType.Pure) .in(file("lib/scala/syntax/definition")) .dependsOn(logger, flexer) .settings( scalacOptions ++= Seq("-Ypatmat-exhaust-depth", "off"), libraryDependencies ++= monocle ++ scalaCompiler ++ Seq( "org.typelevel" %%% "cats-core" % catsVersion, "org.typelevel" %%% "kittens" % kittensVersion, "com.lihaoyi" %%% "scalatags" % scalatagsVersion, "io.circe" %%% "circe-core" % circeVersion, "io.circe" %%% "circe-generic" % circeVersion, "io.circe" %%% "circe-parser" % circeVersion ) ) .jsSettings(jsSettings) lazy val syntax = crossProject(JVMPlatform, JSPlatform) .withoutSuffixFor(JVMPlatform) .crossType(CrossType.Full) .in(file("lib/scala/syntax/specialization")) .dependsOn(logger, flexer, `syntax-definition`) .configs(Test) .configs(Benchmark) .settings( commands += WithDebugCommand.withDebug, Test / fork := true, testFrameworks := Nil, scalacOptions ++= Seq("-Ypatmat-exhaust-depth", "off"), mainClass in (Compile, run) := Some("org.enso.syntax.text.Main"), version := "0.1", logBuffered := false, libraryDependencies ++= Seq( "org.scalatest" %%% "scalatest" % scalatestVersion % Test, "com.lihaoyi" %%% "pprint" % pprintVersion, "io.circe" %%% "circe-core" % circeVersion, "io.circe" %%% "circe-generic" % circeVersion, "io.circe" %%% "circe-parser" % circeVersion ), (Compile / compile) := (Compile / compile) .dependsOn(RecompileParser.run(`syntax-definition`)) .value, (Test / compile) := (Test / compile) .dependsOn(RecompileParser.run(`syntax-definition`)) .value, (Benchmark / compile) := (Benchmark / compile) .dependsOn(RecompileParser.run(`syntax-definition`)) .value ) .jvmSettings( inConfig(Benchmark)(Defaults.testSettings), unmanagedSourceDirectories in Benchmark += baseDirectory.value.getParentFile / "shared/src/bench/scala", libraryDependencies += "com.storm-enroute" %% "scalameter" % scalameterVersion % "bench", testFrameworks := List( new TestFramework("org.scalatest.tools.Framework"), new TestFramework("org.scalameter.ScalaMeterFramework") ), bench := (test in Benchmark).tag(Exclusive).value ) .jsSettings( scalaJSUseMainModuleInitializer := false, scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.ESModule) }, testFrameworks := List(new TestFramework("org.scalatest.tools.Framework")), Compile / fullOptJS / artifactPath := file("target/scala-parser.js") ) lazy val `lexer-bench` = (project in file("lib/scala/syntax/specialization/lexer-bench")) .settings( commands += WithDebugCommand.withDebug, inConfig(Compile)(truffleRunOptionsSettings), inConfig(Benchmark)(Defaults.testSettings), parallelExecution in Test := false, logBuffered in Test := false, Test / fork := true, libraryDependencies ++= jmh ) .configs(Test) .configs(Benchmark) .dependsOn(syntax.jvm) .dependsOn(flexer.jvm) .settings( javaOptions ++= Seq( "-Xms4096m", "-Xmx4096m", "-XX:+FlightRecorder" ), mainClass in Benchmark := Some("org.openjdk.jmh.Main"), bench := Def.task { (run in Benchmark).toTask("").value }, benchOnly := Def.inputTaskDyn { import complete.Parsers.spaceDelimited val name = spaceDelimited("").parsed match { case List(name) => name case _ => throw new IllegalArgumentException("Expected one argument.") } Def.task { (testOnly in Benchmark).toTask(" -- -z " + name).value } }.evaluated, parallelExecution in Benchmark := false ) lazy val `parser-service` = (project in file("lib/scala/parser-service")) .dependsOn(syntax.jvm) .settings( libraryDependencies ++= akka ++ akkaTest, mainClass := Some("org.enso.ParserServiceMain") ) lazy val `text-buffer` = project .in(file("lib/scala/text-buffer")) .configs(Test) .settings( libraryDependencies ++= Seq( "org.typelevel" %% "cats-core" % catsVersion, "org.bouncycastle" % "bcpkix-jdk15on" % bcpkixJdk15Version, "org.scalatest" %% "scalatest" % scalatestVersion % Test, "org.scalacheck" %% "scalacheck" % scalacheckVersion % Test ) ) lazy val graph = (project in file("lib/scala/graph/")) .dependsOn(logger.jvm) .configs(Test) .settings( version := "0.1", resolvers ++= Seq( Resolver.sonatypeRepo("releases"), Resolver.sonatypeRepo("snapshots") ), scalacOptions += "-Ymacro-annotations", libraryDependencies ++= scalaCompiler ++ Seq( "com.chuusai" %% "shapeless" % shapelessVersion, "io.estatico" %% "newtype" % newtypeVersion, "org.scalatest" %% "scalatest" % scalatestVersion % Test, "org.scalacheck" %% "scalacheck" % scalacheckVersion % Test, "com.github.julien-truffaut" %% "monocle-core" % monocleVersion, "org.apache.commons" % "commons-lang3" % commonsLangVersion ), addCompilerPlugin( "org.typelevel" %% "kind-projector" % kindProjectorVersion cross CrossVersion.full ), addCompilerPlugin( "io.tryp" % "splain" % splainVersion cross CrossVersion.patch ), scalacOptions ++= splainOptions ) lazy val pkg = (project in file("lib/scala/pkg")) .settings( mainClass in (Compile, run) := Some("org.enso.pkg.Main"), version := "0.1", libraryDependencies ++= circe ++ Seq( "org.scalatest" %% "scalatest" % scalatestVersion % Test, "nl.gn0s1s" %% "bump" % bumpVersion, "io.circe" %% "circe-yaml" % circeYamlVersion, // separate from other circe deps because its independent project with its own versioning "commons-io" % "commons-io" % commonsIoVersion ) ) lazy val `akka-native` = project .in(file("lib/scala/akka-native")) .configs(Test) .settings( version := "0.1", libraryDependencies ++= Seq( akkaActor ), // Note [Native Image Workaround for GraalVM 20.2] libraryDependencies += "org.graalvm.nativeimage" % "svm" % graalVersion % "provided" ) lazy val `logging-service` = project .in(file("lib/scala/logging-service")) .configs(Test) .settings( version := "0.1", libraryDependencies ++= Seq( "org.slf4j" % "slf4j-api" % slf4jVersion, "com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion, akkaStream, akkaHttp, "io.circe" %%% "circe-core" % circeVersion, "io.circe" %%% "circe-parser" % circeVersion, "org.scalatest" %% "scalatest" % scalatestVersion % Test, "org.graalvm.nativeimage" % "svm" % graalVersion % "provided" ) ) .settings( if (Platform.isWindows) (Compile / unmanagedSourceDirectories) += (Compile / sourceDirectory).value / "java-windows" else (Compile / unmanagedSourceDirectories) += (Compile / sourceDirectory).value / "java-unix" ) .dependsOn(`akka-native`) lazy val cli = project .in(file("lib/scala/cli")) .configs(Test) .settings( version := "0.1", libraryDependencies ++= Seq( "org.scalatest" %% "scalatest" % scalatestVersion % Test, "org.typelevel" %% "cats-core" % catsVersion ), parallelExecution in Test := false ) lazy val `version-output` = (project in file("lib/scala/version-output")) .settings( version := "0.1" ) .settings( Compile / sourceGenerators += Def.task { val file = (Compile / sourceManaged).value / "buildinfo" / "Info.scala" BuildInfo .writeBuildInfoFile( file, state.value.log, ensoVersion, scalacVersion, graalVersion ) }.taskValue ) lazy val `project-manager` = (project in file("lib/scala/project-manager")) .settings( (Compile / mainClass) := Some("org.enso.projectmanager.boot.ProjectManager") ) .settings( (Compile / run / fork) := true, (Test / fork) := true, (Compile / run / connectInput) := true, libraryDependencies ++= akka ++ Seq(akkaTestkit % Test), libraryDependencies ++= circe, libraryDependencies ++= Seq( "com.typesafe" % "config" % typesafeConfigVersion, "com.github.pureconfig" %% "pureconfig" % pureconfigVersion, "com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion, "dev.zio" %% "zio" % zioVersion, "dev.zio" %% "zio-interop-cats" % zioInteropCatsVersion, "commons-cli" % "commons-cli" % commonsCliVersion, "commons-io" % "commons-io" % commonsIoVersion, "org.apache.commons" % "commons-lang3" % commonsLangVersion, "com.beachape" %% "enumeratum-circe" % enumeratumCirceVersion, "com.miguno.akka" %% "akka-mock-scheduler" % akkaMockSchedulerVersion % Test, "org.mockito" %% "mockito-scala" % mockitoScalaVersion % Test ), addCompilerPlugin( "org.typelevel" %% "kind-projector" % kindProjectorVersion cross CrossVersion.full ) ) .settings( assemblyJarName in assembly := "project-manager.jar", test in assembly := {}, assemblyOutputPath in assembly := file("project-manager.jar"), assemblyMergeStrategy in assembly := { case PathList("META-INF", file, xs @ _*) if file.endsWith(".DSA") => MergeStrategy.discard case PathList("META-INF", file, xs @ _*) if file.endsWith(".SF") => MergeStrategy.discard case PathList("META-INF", "MANIFEST.MF", xs @ _*) => MergeStrategy.discard case "application.conf" => MergeStrategy.concat case "reference.conf" => MergeStrategy.concat case _ => MergeStrategy.first }, (Test / test) := (Test / test).dependsOn(`engine-runner` / assembly).value, rebuildNativeImage := NativeImage .buildNativeImage( "project-manager", staticOnLinux = true, additionalOptions = Seq( "--enable-all-security-services" // Note [HTTPS in the Native Images] ) ) .dependsOn(VerifyReflectionSetup.run) .dependsOn(assembly) .value, buildNativeImage := NativeImage .incrementalNativeImageBuild( rebuildNativeImage, "project-manager" ) .value ) .dependsOn(`akka-native`) .dependsOn(`version-output`) .dependsOn(pkg) .dependsOn(`polyglot-api`) .dependsOn(`runtime-version-manager`) .dependsOn(`json-rpc-server`) .dependsOn(`json-rpc-server-test` % Test) .dependsOn(testkit % Test) .dependsOn(`runtime-version-manager-test` % Test) /* Note [Classpath Separation] * ~~~~~~~~~~~~~~~~~~~~~~~~~~ * Projects using the language runtime do not depend on it directly, but instead * the language runtime is put on the Truffle classpath, rather than the * standard classpath. This is the recommended way of handling this and we * strive to use such structure everywhere. * See * https://www.graalvm.org/docs/graalvm-as-a-platform/implement-language#graalvm * * Currently the only exception to this are the tests of the runtime project * which have classpath separation disabled, because they need direct access to * the runtime's instruments. * * To ensure correct handling of dependencies by sbt, the classpath appended to * Java options, should be based on `(runtime / Compile / fullClasspath).value` * wherever possible. Using a key from the runtime project enables sbt to see * the dependency. * * Assembly tasks that build JAR files which need `runtime.jar` to run should * also add a dependency on `runtime / assembly`. */ lazy val `json-rpc-server` = project .in(file("lib/scala/json-rpc-server")) .settings( libraryDependencies ++= akka ++ akkaTest, libraryDependencies ++= circe, libraryDependencies ++= Seq( "io.circe" %% "circe-literal" % circeVersion, akkaTestkit % Test, "org.scalatest" %% "scalatest" % scalatestVersion % Test ) ) lazy val `json-rpc-server-test` = project .in(file("lib/scala/json-rpc-server-test")) .settings( libraryDependencies ++= akka, libraryDependencies ++= circe, libraryDependencies ++= Seq( "io.circe" %% "circe-literal" % circeVersion, akkaTestkit, "org.scalatest" %% "scalatest" % scalatestVersion ) ) .dependsOn(`json-rpc-server`) lazy val testkit = project .in(file("lib/scala/testkit")) .settings( libraryDependencies ++= Seq( "org.apache.commons" % "commons-lang3" % commonsLangVersion, "org.scalatest" %% "scalatest" % scalatestVersion ) ) lazy val `core-definition` = (project in file("lib/scala/core-definition")) .configs(Benchmark) .settings( version := "0.1", inConfig(Compile)(truffleRunOptionsSettings), inConfig(Benchmark)(Defaults.testSettings), parallelExecution in Test := false, logBuffered in Test := false, scalacOptions += "-Ymacro-annotations", libraryDependencies ++= jmh ++ Seq( "com.chuusai" %% "shapeless" % shapelessVersion, "org.scalacheck" %% "scalacheck" % scalacheckVersion % Test, "org.scalactic" %% "scalactic" % scalacticVersion % Test, "org.scalatest" %% "scalatest" % scalatestVersion % Test, "org.typelevel" %% "cats-core" % catsVersion, "com.github.julien-truffaut" %% "monocle-core" % monocleVersion ), addCompilerPlugin( "org.typelevel" %% "kind-projector" % kindProjectorVersion cross CrossVersion.full ), addCompilerPlugin( "io.tryp" % "splain" % splainVersion cross CrossVersion.patch ), scalacOptions ++= splainOptions ) .dependsOn(graph) .dependsOn(syntax.jvm) lazy val searcher = project .in(file("lib/scala/searcher")) .configs(Test) .settings( libraryDependencies ++= jmh ++ Seq( "com.typesafe.slick" %% "slick" % slickVersion, "org.xerial" % "sqlite-jdbc" % sqliteVersion, "ch.qos.logback" % "logback-classic" % logbackClassicVersion % Test, "org.scalatest" %% "scalatest" % scalatestVersion % Test ) ) .configs(Benchmark) .settings( inConfig(Benchmark)(Defaults.testSettings), fork in Benchmark := true ) .dependsOn(testkit % Test) .dependsOn(`polyglot-api`) lazy val `interpreter-dsl` = (project in file("lib/scala/interpreter-dsl")) .settings( version := "0.1", libraryDependencies += "com.google.auto.service" % "auto-service" % "1.0-rc7" exclude ("com.google.code.findbugs", "jsr305") ) // ============================================================================ // === Sub-Projects =========================================================== // ============================================================================ val truffleRunOptions = Seq( "-Dpolyglot.engine.IterativePartialEscape=true", "-Dpolyglot.engine.BackgroundCompilation=false" ) val truffleRunOptionsSettings = Seq( fork := true, javaOptions ++= truffleRunOptions ) lazy val `polyglot-api` = project .in(file("engine/polyglot-api")) .settings( Test / fork := true, Test / javaOptions ++= { // Note [Classpath Separation] val runtimeClasspath = (LocalProject("runtime") / Compile / fullClasspath).value .map(_.data) .mkString(File.pathSeparator) Seq(s"-Dtruffle.class.path.append=$runtimeClasspath") }, libraryDependencies ++= Seq( "org.graalvm.sdk" % "polyglot-tck" % graalVersion % "provided", "com.google.flatbuffers" % "flatbuffers-java" % flatbuffersVersion, "org.scalatest" %% "scalatest" % scalatestVersion % Test, "org.scalacheck" %% "scalacheck" % scalacheckVersion % Test ), libraryDependencies ++= jackson, addCompilerPlugin( "org.typelevel" %% "kind-projector" % kindProjectorVersion cross CrossVersion.full ), addCompilerPlugin( "io.tryp" % "splain" % splainVersion cross CrossVersion.patch ), scalacOptions ++= splainOptions, GenerateFlatbuffers.flatcVersion := flatbuffersVersion, sourceGenerators in Compile += GenerateFlatbuffers.task ) .dependsOn(pkg) .dependsOn(`text-buffer`) lazy val `language-server` = (project in file("engine/language-server")) .settings( libraryDependencies ++= akka ++ circe ++ Seq( "com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion, "io.circe" %% "circe-generic-extras" % circeGenericExtrasVersion, "io.circe" %% "circe-literal" % circeVersion, "dev.zio" %% "zio" % zioVersion, "io.methvin" % "directory-watcher" % directoryWatcherVersion, "com.beachape" %% "enumeratum-circe" % enumeratumCirceVersion, "com.google.flatbuffers" % "flatbuffers-java" % flatbuffersVersion, "commons-io" % "commons-io" % commonsIoVersion, akkaTestkit % Test, "com.typesafe.akka" %% "akka-http-testkit" % akkaHTTPVersion % Test, "org.scalatest" %% "scalatest" % scalatestVersion % Test, "org.scalacheck" %% "scalacheck" % scalacheckVersion % Test, "org.graalvm.sdk" % "polyglot-tck" % graalVersion % "provided" ), testOptions in Test += Tests .Argument(TestFrameworks.ScalaCheck, "-minSuccessfulTests", "1000"), GenerateFlatbuffers.flatcVersion := flatbuffersVersion, sourceGenerators in Compile += GenerateFlatbuffers.task ) .configs(Benchmark) .settings( inConfig(Benchmark)(Defaults.testSettings), bench := (test in Benchmark).value, libraryDependencies += "com.storm-enroute" %% "scalameter" % scalameterVersion % "bench", testFrameworks ++= List( new TestFramework("org.scalameter.ScalaMeterFramework") ) ) .dependsOn(`polyglot-api`) .dependsOn(`json-rpc-server`) .dependsOn(`json-rpc-server-test` % Test) .dependsOn(`text-buffer`) .dependsOn(`searcher`) .dependsOn(testkit % Test) .dependsOn(`logging-service`) lazy val ast = (project in file("lib/scala/ast")) .settings( version := ensoVersion, Cargo.rustVersion := rustVersion, Compile / sourceGenerators += GenerateAST.task ) lazy val parser = (project in file("lib/scala/parser")) .settings( fork := true, Cargo.rustVersion := rustVersion, Compile / compile / compileInputs := (Compile / compile / compileInputs) .dependsOn(Cargo("build --project parser")) .value, javaOptions += { val root = baseDirectory.value.getParentFile.getParentFile.getParentFile s"-Djava.library.path=$root/target/rust/debug" }, libraryDependencies ++= Seq( "com.storm-enroute" %% "scalameter" % scalameterVersion % "bench", "org.scalatest" %%% "scalatest" % scalatestVersion % Test ), testFrameworks := List( new TestFramework("org.scalatest.tools.Framework"), new TestFramework("org.scalameter.ScalaMeterFramework") ) ) .dependsOn(ast) lazy val cleanInstruments = taskKey[Unit]( "Cleans fragile class files to force a full recompilation and preserve" + "consistency of instrumentation configuration." ) lazy val runtime = (project in file("engine/runtime")) .configs(Benchmark) .settings( version := ensoVersion, commands += WithDebugCommand.withDebug, cleanInstruments := FixInstrumentsGeneration.cleanInstruments.value, inConfig(Compile)(truffleRunOptionsSettings), inConfig(Benchmark)(Defaults.testSettings), parallelExecution in Test := false, logBuffered in Test := false, scalacOptions += "-Ymacro-annotations", scalacOptions ++= Seq("-Ypatmat-exhaust-depth", "off"), libraryDependencies ++= jmh ++ jaxb ++ Seq( "com.chuusai" %% "shapeless" % shapelessVersion, "org.apache.commons" % "commons-lang3" % commonsLangVersion, "org.apache.tika" % "tika-core" % tikaVersion, "org.graalvm.sdk" % "graal-sdk" % graalVersion % "provided", "org.graalvm.sdk" % "polyglot-tck" % graalVersion % "provided", "org.graalvm.truffle" % "truffle-api" % graalVersion % "provided", "org.graalvm.truffle" % "truffle-dsl-processor" % graalVersion % "provided", "org.graalvm.truffle" % "truffle-tck" % graalVersion % "provided", "org.graalvm.truffle" % "truffle-tck-common" % graalVersion % "provided", "org.scalacheck" %% "scalacheck" % scalacheckVersion % Test, "org.scalactic" %% "scalactic" % scalacticVersion % Test, "org.scalatest" %% "scalatest" % scalatestVersion % Test, "org.graalvm.truffle" % "truffle-api" % graalVersion % Benchmark, "org.typelevel" %% "cats-core" % catsVersion, "eu.timepit" %% "refined" % refinedVersion ), // Note [Unmanaged Classpath] Compile / unmanagedClasspath += (`core-definition` / Compile / packageBin).value, Test / unmanagedClasspath += (`core-definition` / Compile / packageBin).value, Compile / compile / compileInputs := (Compile / compile / compileInputs) .dependsOn(CopyTruffleJAR.preCompileTask) .value, Compile / compile := FixInstrumentsGeneration.patchedCompile .dependsOn(FixInstrumentsGeneration.preCompileTask) .dependsOn(`core-definition` / Compile / packageBin) .value, // Note [Classpath Separation] Test / javaOptions ++= Seq( "-Dgraalvm.locatorDisabled=true", s"--upgrade-module-path=${file("engine/runtime/build-cache/truffle-api.jar").absolutePath}" ), bootstrap := CopyTruffleJAR.bootstrapJARs.value, Global / onLoad := EnvironmentCheck.addVersionCheck( graalVersion, javaVersion )((Global / onLoad).value) ) .settings( (Compile / javacOptions) ++= Seq( "-s", (Compile / sourceManaged).value.getAbsolutePath ), addCompilerPlugin( "org.typelevel" %% "kind-projector" % kindProjectorVersion cross CrossVersion.full ), addCompilerPlugin( "io.tryp" % "splain" % splainVersion cross CrossVersion.patch ), scalacOptions ++= splainOptions ) .settings( (Compile / compile) := (Compile / compile) .dependsOn(Def.task { (Compile / sourceManaged).value.mkdirs }) .value ) .settings( (Runtime / compile) := (Runtime / compile) .dependsOn(`std-bits` / Compile / packageBin) .dependsOn(table / Compile / packageBin) .dependsOn(database / Compile / packageBin) .value ) .settings( bench := (test in Benchmark).tag(Exclusive).value, benchOnly := Def.inputTaskDyn { import complete.Parsers.spaceDelimited val name = spaceDelimited("").parsed match { case List(name) => name case _ => throw new IllegalArgumentException("Expected one argument.") } Def.task { (testOnly in Benchmark).toTask(" -- -z " + name).value } }.evaluated, parallelExecution in Benchmark := false ) .settings( assemblyJarName in assembly := "runtime.jar", test in assembly := {}, assemblyOutputPath in assembly := file("runtime.jar"), assemblyMergeStrategy in assembly := { case PathList("META-INF", file, xs @ _*) if file.endsWith(".DSA") => MergeStrategy.discard case PathList("META-INF", file, xs @ _*) if file.endsWith(".SF") => MergeStrategy.discard case PathList("META-INF", "MANIFEST.MF", xs @ _*) => MergeStrategy.discard case _ => MergeStrategy.first } ) .dependsOn(pkg) .dependsOn(`interpreter-dsl`) .dependsOn(syntax.jvm) .dependsOn(graph) .dependsOn(`polyglot-api`) .dependsOn(`text-buffer`) .dependsOn(searcher) .dependsOn(testkit % Test) /* Note [Unmanaged Classpath] * ~~~~~~~~~~~~~~~~~~~~~~~~~~ * As the definition of the core primitives in `core_definition` is achieved * entirely using the graph macros, this means that the IDE experience for those * using these primitives is very poor. * * To get around this, we want to treat the core definition as a .jar dependency * to force the IDE to depend on bytecode for its diagnostics, rather than the * source code (as this means it sees the macros expanded). A standard workflow * with local publishing would not recompile the definition automatically on * changes, so the `unmanagedClasspath` route allows us to get automatic * recompilation but still convince the IDE that it is a .jar dependency. */ lazy val `engine-runner` = project .in(file("engine/runner")) .settings( javaOptions ++= { // Note [Classpath Separation] val runtimeClasspath = (runtime / Compile / fullClasspath).value .map(_.data) .mkString(File.pathSeparator) Seq(s"-Dtruffle.class.path.append=$runtimeClasspath") }, mainClass in (Compile, run) := Some("org.enso.runner.Main"), mainClass in assembly := (Compile / run / mainClass).value, assemblyJarName in assembly := "runner.jar", test in assembly := {}, assemblyOutputPath in assembly := file("runner.jar"), assemblyMergeStrategy in assembly := { case PathList("META-INF", file, xs @ _*) if file.endsWith(".DSA") => MergeStrategy.discard case PathList("META-INF", file, xs @ _*) if file.endsWith(".SF") => MergeStrategy.discard case PathList("META-INF", "MANIFEST.MF", xs @ _*) => MergeStrategy.discard case "application.conf" => MergeStrategy.concat case "reference.conf" => MergeStrategy.concat case x => MergeStrategy.first }, assemblyOption in assembly := (assemblyOption in assembly).value .copy( prependShellScript = Some( defaultUniversalScript( shebang = false, javaOpts = truffleRunOptions ++ Seq("-Dtruffle.class.path.append=runtime.jar") ) ) ), commands += WithDebugCommand.withDebug, inConfig(Compile)(truffleRunOptionsSettings), libraryDependencies ++= Seq( "org.graalvm.sdk" % "polyglot-tck" % graalVersion % "provided", "org.graalvm.truffle" % "truffle-api" % graalVersion % "provided", "commons-cli" % "commons-cli" % commonsCliVersion, "com.monovore" %% "decline" % declineVersion, "org.jline" % "jline" % jlineVersion, "org.typelevel" %% "cats-core" % catsVersion ), connectInput in run := true ) .settings( assembly := assembly .dependsOn(runtime / assembly) .value ) .dependsOn(`version-output`) .dependsOn(pkg) .dependsOn(`language-server`) .dependsOn(`polyglot-api`) .dependsOn(`logging-service`) lazy val launcher = project .in(file("engine/launcher")) .configs(Test) .settings( resolvers += Resolver.bintrayRepo("gn0s1s", "releases"), libraryDependencies ++= Seq( "com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion, "org.typelevel" %% "cats-core" % catsVersion, "nl.gn0s1s" %% "bump" % bumpVersion, "org.apache.commons" % "commons-compress" % commonsCompressVersion, "org.scalatest" %% "scalatest" % scalatestVersion % Test, akkaSLF4J ) ) .settings( rebuildNativeImage := NativeImage .buildNativeImage( "enso", staticOnLinux = true, additionalOptions = Seq( "--enable-all-security-services", // Note [HTTPS in the Native Images] "-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog", "-H:IncludeResources=.*Main.enso$" ), initializeAtRuntime = Seq( // Note [WSLoggerManager Shutdown Hook] "org.enso.loggingservice.WSLoggerManager$" ) ) .dependsOn(assembly) .dependsOn(VerifyReflectionSetup.run) .value, buildNativeImage := NativeImage .incrementalNativeImageBuild( rebuildNativeImage, "enso" ) .value, test in assembly := {}, assemblyOutputPath in assembly := file("launcher.jar") ) .settings( (Test / test) := (Test / test) .dependsOn(buildNativeImage) .dependsOn(LauncherShimsForTest.prepare(rustcVersion = rustVersion)) .value, parallelExecution in Test := false ) .dependsOn(cli) .dependsOn(`runtime-version-manager`) .dependsOn(`version-output`) .dependsOn(pkg) .dependsOn(`logging-service`) .dependsOn(`runtime-version-manager-test` % Test) lazy val `runtime-version-manager` = project .in(file("lib/scala/runtime-version-manager")) .configs(Test) .settings( resolvers += Resolver.bintrayRepo("gn0s1s", "releases"), libraryDependencies ++= Seq( "com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion, "org.typelevel" %% "cats-core" % catsVersion, "nl.gn0s1s" %% "bump" % bumpVersion, "org.apache.commons" % "commons-compress" % commonsCompressVersion, "org.scalatest" %% "scalatest" % scalatestVersion % Test, akkaHttp ) ) .dependsOn(pkg) .dependsOn(`logging-service`) .dependsOn(cli) .dependsOn(`version-output`) lazy val `runtime-version-manager-test` = project .in(file("lib/scala/runtime-version-manager-test")) .configs(Test) .settings( libraryDependencies ++= Seq( "com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion, "org.scalatest" %% "scalatest" % scalatestVersion, "commons-io" % "commons-io" % commonsIoVersion ) ) .settings(parallelExecution in Test := false) .settings( (Test / test) := (Test / test) .dependsOn(`locking-test-helper` / assembly) .value ) .dependsOn(`runtime-version-manager`) .dependsOn(`logging-service`) .dependsOn(testkit) lazy val `locking-test-helper` = project .in(file("lib/scala/locking-test-helper")) .settings( test in assembly := {}, assemblyOutputPath in assembly := file("locking-test-helper.jar") ) val `std-lib-root` = file("distribution/std-lib/") val `std-lib-polyglot-root` = `std-lib-root` / "Base" / "polyglot" / "java" val `table-polyglot-root` = `std-lib-root` / "Table" / "polyglot" / "java" val `database-polyglot-root` = `std-lib-root` / "Database" / "polyglot" / "java" lazy val `std-bits` = project .in(file("std-bits")) .settings( autoScalaLibrary := false, Compile / packageBin / artifactPath := `std-lib-polyglot-root` / "std-bits.jar", libraryDependencies ++= Seq( "com.ibm.icu" % "icu4j" % icuVersion ), Compile / packageBin := Def.task { val result = (Compile / packageBin).value val _ = StdBits .copyDependencies( `std-lib-polyglot-root`, Some("std-bits.jar"), ignoreScalaLibrary = true ) .value result }.value ) lazy val table = project .in(file("table")) .settings( autoScalaLibrary := false, Compile / packageBin / artifactPath := `table-polyglot-root` / "table.jar", libraryDependencies ++= Seq( "com.univocity" % "univocity-parsers" % "2.9.0" ), Compile / packageBin := Def.task { val result = (Compile / packageBin).value val _ = StdBits .copyDependencies( `table-polyglot-root`, Some("table.jar"), ignoreScalaLibrary = true ) .value result }.value ) lazy val database = project .in(file("database")) .settings( autoScalaLibrary := false, libraryDependencies ++= Seq( "org.xerial" % "sqlite-jdbc" % "3.34.0" ), Compile / packageBin := Def.task { val result = (Compile / packageBin).value val _ = StdBits .copyDependencies( `database-polyglot-root`, None, ignoreScalaLibrary = true ) .value result }.value ) /* Note [HTTPS in the Native Images] * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * The launcher and project-manager use Akka Http for making web requests. They * do not use Java's stdlib implementation, because there is a bug (not fixed in * JDK 11) (https://bugs.openjdk.java.net/browse/JDK-8231449) in its HTTPS * handling that causes long running requests to freeze forever. However, Akka * Http still needs the stdlib's SSL implementation which is not included in the * Native Images by default (because of its size). The * `--enable-all-security-services` flag is used to ensure it is available in * the built executable. */ /* Note [Native Image Workaround for GraalVM 20.2] * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * In GraalVM 20.2 the Native Image build of even simple Scala programs has * started to fail on a call to `Statics.releaseFence`. It has been reported as * a bug in the GraalVM repository: https://github.com/oracle/graal/issues/2770 * * A proposed workaround for this bug is to substitute the original function * with a different implementation that does not use the problematic * MethodHandle. This is implemented in class * `org.enso.launcher.workarounds.ReplacementStatics` using * `org.enso.launcher.workarounds.Unsafe` which gives access to * `sun.misc.Unsafe` which contains a low-level function corresponding to the * required "release fence". * * To allow for that substitution, the launcher code requires annotations from * the `svm` module and that is why this additional dependency is needed as long * as that workaround is in-place. The dependency is marked as "provided" * because it is included within the native-image build. */ /* Note [WSLoggerManager Shutdown Hook] * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * As the WSLoggerManager registers a shutdown hook when its initialized to * ensure that logs are not lost in case of logging service initialization * failure, it has to be initialized at runtime, as otherwise if the * initialization was done at build time, the shutdown hook would actually also * run at build time and have no effect at runtime. */ lazy val engineDistributionRoot = settingKey[File]("Root of built engine distribution") lazy val launcherDistributionRoot = settingKey[File]("Root of built launcher distribution") lazy val projectManagerDistributionRoot = settingKey[File]("Root of built project manager distribution") engineDistributionRoot := packageBuilder.localArtifact("engine") / s"enso-$ensoVersion" launcherDistributionRoot := packageBuilder.localArtifact("launcher") / "enso" projectManagerDistributionRoot := packageBuilder.localArtifact("project-manager") / "enso" lazy val buildEngineDistribution = taskKey[Unit]("Builds the engine distribution") buildEngineDistribution := { val _ = (`engine-runner` / assembly).value val root = engineDistributionRoot.value val log = streams.value.log val cacheFactory = streams.value.cacheStoreFactory DistributionPackage.createEnginePackage( distributionRoot = root, cacheFactory = cacheFactory, graalVersion = graalVersion, javaVersion = javaVersion ) log.info(s"Engine package created at $root") } lazy val buildLauncherDistribution = taskKey[Unit]("Builds the launcher distribution") buildLauncherDistribution := { val _ = (launcher / buildNativeImage).value val root = launcherDistributionRoot.value val log = streams.value.log val cacheFactory = streams.value.cacheStoreFactory DistributionPackage.createLauncherPackage(root, cacheFactory) log.info(s"Launcher package created at $root") } lazy val buildProjectManagerDistribution = taskKey[Unit]("Builds the project manager distribution") buildProjectManagerDistribution := { val _ = (`project-manager` / buildNativeImage).value val root = projectManagerDistributionRoot.value val log = streams.value.log val cacheFactory = streams.value.cacheStoreFactory DistributionPackage.createProjectManagerPackage(root, cacheFactory) log.info(s"Project Manager package created at $root") }