blob: 40b55af1d736efd15938bf77c850eb5e20c1737e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.github.retronym.sbtxjc.SbtXjcPlugin
import Classpaths.managedJars
import java.io.ByteArrayOutputStream
import java.io.ByteArrayInputStream
import javax.xml.transform.TransformerFactory
import javax.xml.transform.stream.StreamResult
import javax.xml.transform.stream.StreamSource
//Fixes build issues on java11+
run / fork := true
Global / lintUnusedKeysOnLoad := false
lazy val commonSettings =
Seq(
version := IO.read((ThisBuild / baseDirectory).value / "VERSION").trim,
dependencyOverrides ++= Seq(
"org.apache.commons" % "commons-lang3" % "3.20.0"
),
fork := true, // needed to pass javaOptions to tests, for example
licenses += ("Apache-2.0", new URL("https://www.apache.org/licenses/LICENSE-2.0.txt")),
maintainer := "Apache Daffodil <dev@daffodil.apache.org>",
organization := "org.apache.daffodil",
// remove the -Xcheckinit option added by the sbt tpoletcat plugin. This
// option leads to non-reproducible builds
scalacOptions --= Seq("-Xcheckinit"),
startYear := Some(2021)
)
lazy val ratSettings = Seq(
ratLicenses := Seq(
("MIT ", Rat.MIT_LICENSE_NAME, Rat.MIT_LICENSE_TEXT_MICROSOFT),
("CC0 ", Rat.CREATIVE_COMMONS_LICENSE_NAME, Rat.CREATIVE_COMMONS_LICENSE_TEXT),
("MIT ", Rat.MIT_LICENSE_NAME, Rat.MIT_LICENSE_TEXT_DELTAXML)
),
ratLicenseFamilies := Seq(
Rat.MIT_LICENSE_NAME,
Rat.CREATIVE_COMMONS_LICENSE_NAME
),
ratExcludes := Rat.excludes,
ratFailBinaries := true
)
/* Workaround: certain reflection (used by JAXB) isn't allowed by default in JDK 17:
* https://docs.oracle.com/en/java/javase/17/migrate/migrating-jdk-8-later-jdk-releases.html#GUID-7BB28E4D-99B3-4078-BDC4-FC24180CE82B
*
* While we can handle this JVM quirk at build time, at runtime we won't know
* a user's JVM version. We'll provide documentation and an extension setting
* to add these flags to the extension-launched debugger backend.
*/
lazy val extraJvmOptions: Seq[String] =
if (scala.util.Properties.isJavaAtLeast("17"))
Seq(
"--add-opens",
"java.base/java.lang=ALL-UNNAMED"
)
else Seq()
lazy val xjcSettings =
Seq(
libraryDependencies ++= Seq(
"com.sun.xml.bind" % "jaxb-impl" % "2.3.9",
"javax.activation" % "activation" % "1.1.1",
"org.glassfish.jaxb" % "jaxb-xjc" % "2.3.9"
),
Test / javaOptions ++= extraJvmOptions, // tests use JAXB at runtime
xjcCommandLine += "-nv",
xjcCommandLine += "-p",
xjcCommandLine += "org.apache.daffodil.tdml",
xjcCommandLine += "-no-header",
xjcJvmOpts ++= extraJvmOptions,
xjcLibs := Seq(
"com.sun.xml.bind" % "jaxb-impl" % "2.3.9",
"org.glassfish.jaxb" % "jaxb-xjc" % "2.3.9",
"javax.activation" % "activation" % "1.1.1"
),
Compile / doc / sources := Seq(file("")),
Compile / xjc / sources := {
val stream = (Compile / xjc / streams).value
// We are going to extract XSD files from Daffodil jars needed by xjc to generate JAXB
// classes
lazy val xjcSourceDir = crossTarget.value / "xjc"
// Get the daffodil-lib or daffodil-core jar from the dependencies, this is the only jar we need to extract
// files from
val daffodilJarWithTdmlXsd = managedJars(Test, Set[String]("jar"), update.value)
.map(_.data)
.find(_.getName.contains(getDaffodilJarName(scalaBinaryVersion.value)))
.get
// cache the results of jar extraction so we don't keep extracting files (which would
// trigger xjc again) everytime we compile.
val cachedFun = FileFunction.cached(stream.cacheDirectory / "xjcSources") { _ =>
// Extract the DFDL TDML schema file used for JAXB generation.
IO.unzip(
daffodilJarWithTdmlXsd,
xjcSourceDir,
NameFilter.fnToNameFilter(f => f == "org/apache/daffodil/xsd/tdml.xsd")
)
// The TDML schema supports embedded DFDL schemas and configurations, and it references
// the schema for DFDL schema when doing so. This DFDL schema is pretty complex, which
// requires extra complexity like the need for an xjc bindings file and also hits edge
// cases where xjc generates non-deterministic java code, leading to non-reproducible
// builds.
//
// Fortunately, VS Code does not need embedded DFDL schemas or config converted to
// specific objects, so we use XSLT to replace those parts of the schema with <any>
// elements. This allows JAXB to read TDML files containing embedded DFDL schemas, but
// they are just converted to generic XML Objects and avoids those complex edge cases.
val tdmlFile = xjcSourceDir / "org" / "apache" / "daffodil" / "xsd" / "tdml.xsd"
val tdmlXslt = """
|<xsl:stylesheet
| xmlns:xs="http://www.w3.org/2001/XMLSchema"
| xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
| <xsl:template match="@*|node()">
| <xsl:copy>
| <xsl:apply-templates select="@*|node()"/>
| </xsl:copy>
| </xsl:template>
| <xsl:template match="xs:complexType[@name='defineSchemaType']">
| <xs:complexType name='defineSchemaType'>
| <xs:sequence>
| <xs:any />
| </xs:sequence>
| <xs:anyAttribute />
| </xs:complexType>
| </xsl:template>
| <xsl:template match="xs:complexType[@name='defineConfigType']">
| <xs:complexType name='defineConfigType'>
| <xs:sequence>
| <xs:any />
| </xs:sequence>
| <xs:anyAttribute />
| </xs:complexType>
| </xsl:template>
|</xsl:stylesheet>""".stripMargin
val xslt = new StreamSource(new ByteArrayInputStream(tdmlXslt.getBytes))
val input = new StreamSource(tdmlFile)
val output = new ByteArrayOutputStream()
val factory = TransformerFactory.newInstance()
val transformer = factory.newTransformer(xslt);
transformer.transform(input, new StreamResult(output))
IO.write(tdmlFile, output.toByteArray())
val xsdFiles = (xjcSourceDir ** "*.xsd").get
xsdFiles.toSet
}
// only invalidate the cache if the daffodil lib jar changed and so there could be a
// chance the tdml.xsd file has been updated
cachedFun(Set(daffodilJarWithTdmlXsd)).toSeq
}
)
lazy val `daffodil-debugger` = project
.in(file("."))
.settings(commonSettings, ratSettings)
.settings(publish / skip := true)
.aggregate(debugger.projectRefs: _*)
/** Since using projectMatrix, there will be a debugger, debugger2_12 and debugger3 target. The debugger target is for
* Daffodil 3.11.0 and Scala 2.13. The debugger2_12 target is for Daffodil 3.10.0 abd older and Scala 2.12. The
* debugger3 target is for Daffodil 4.0.0 and newer and Scala 3. (only availabe when using JDK 17+)
*
* When running something like "sbt test" that will run all targets. To use a single target do one of: sbt
* debugger/test OR sbt debugger2_12/test OR sbt debugger3/test. Based on which version of the debugger you are
* targeting.
*/
lazy val debugger =
(projectMatrix in (file("debugger")))
.enablePlugins(BuildInfoPlugin, JavaAppPackaging, UniversalPlugin, ClasspathJarPlugin, SbtXjcPlugin)
.settings(commonSettings)
.settings(xjcSettings)
.settings(
name := "daffodil-debugger",
scalacOptions ++= buildScalacOptions(scalaBinaryVersion.value),
javacOptions ++= buildJavacOptions(scalaBinaryVersion.value),
libraryDependencies ++= Seq(
/* NOTE: To support Java 8:
* logback-classic can not go above version 1.2.11.
* com.microsoft.java.debug.core can not go above version 0.34.0.
* jansi can not go above version 1.18.
*/
// scala-steward:off
"ch.qos.logback" % "logback-classic" % "1.2.11",
"com.microsoft.java" % "com.microsoft.java.debug.core" % "0.34.0",
"org.fusesource.jansi" % "jansi" % "1.18",
// scala-steward:on
"co.fs2" %% "fs2-io" % "3.12.2",
"com.monovore" %% "decline-effect" % "2.5.0",
"org.typelevel" %% "log4cats-slf4j" % "2.7.1",
"org.scalameta" %% "munit" % "1.2.1" % Test
),
libraryDependencies ++= getPlatformSpecificLibraries(scalaBinaryVersion.value),
buildInfoPackage := "org.apache.daffodil.debugger.dap",
buildInfoKeys := Seq[BuildInfoKey](
name,
version,
scalaVersion,
sbtVersion
),
packageName := s"${name.value}-${scalaBinaryVersion.value}"
)
.jvmPlatform(
scalaVersions =
Seq("2.12.20", "2.13.16") ++ (if (scala.util.Properties.isJavaAtLeast("17")) Seq("3.3.6") else Seq())
)
def getPlatformSpecificLibraries(scalaBinaryVersion: String) =
scalaBinaryVersion match {
case "2.12" =>
Seq(
// scala-steward:off
"org.apache.daffodil" %% "daffodil-sapi" % "3.10.0" % "provided,test",
"org.apache.daffodil" %% "daffodil-runtime1" % "3.10.0" % "provided,test",
"org.apache.daffodil" %% "daffodil-lib" % "3.10.0" % Test,
// scala-steward:on
"org.scala-lang.modules" %% "scala-collection-compat" % "2.14.0"
)
case "2.13" =>
Seq(
// scala-steward:off
"org.apache.daffodil" %% "daffodil-sapi" % "3.11.0" % "provided,test",
"org.apache.daffodil" %% "daffodil-runtime1" % "3.11.0" % "provided,test",
"org.apache.daffodil" %% "daffodil-lib" % "3.11.0" % Test
// scala-steward:on
)
case "3" =>
Seq(
"org.apache.daffodil" %% "daffodil-core" % "4.0.0" % "provided,test"
)
}
def getMinSupportedJavaVersion(scalaBinaryVersion: String): String =
if (scalaBinaryVersion == "3") "17"
else if (scala.util.Properties.isJavaAtLeast("21")) "11"
else "8"
def buildJavacOptions(scalaBinaryVersion: String) = {
val jdkCompat = getMinSupportedJavaVersion(scalaBinaryVersion)
if (jdkCompat == "8")
Seq("-source", jdkCompat, "-target", jdkCompat)
else
Seq("--release", jdkCompat)
}
def buildScalacOptions(scalaBinaryVersion: String) = {
val jdkCompat = getMinSupportedJavaVersion(scalaBinaryVersion)
val commonSettings = Seq(if (scalaBinaryVersion == "2.12") "-Ypartial-unification" else "")
if (scalaBinaryVersion == "2.12" && jdkCompat == "8")
commonSettings ++ Seq(s"--target:jvm-${jdkCompat}")
else
commonSettings ++ Seq("--release", jdkCompat)
}
def getDaffodilJarName(scalaBinaryVersion: String) =
if (scalaBinaryVersion == "3") "daffodil-core"
else "daffodil-lib"