This is an automated email from the ASF dual-hosted git repository.
slawrence pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil-sbt.git
The following commit(s) were added to refs/heads/main by this push:
new ef87057 Add new daffodilPlugin and daffodilBin ModuleID implicit
enrichments
ef87057 is described below
commit ef87057d1cdc8d89f8c7419cedb0a162b0c5e7ec
Author: Steve Lawrence <[email protected]>
AuthorDate: Tue Jan 6 15:35:37 2026 -0500
Add new daffodilPlugin and daffodilBin ModuleID implicit enrichments
This adds two new implicit enrichments to ModuleId, which allows
projects to more easily specify dependencies to Daffodil plugins or
saved parsers without needing to know the details of how to reference
them (e.g. Artifacts, classifiers, types, extensions).
For example, depending on a plugin and saved parser now looks like this:
libraryDependences ++= Seq(
"org.example" % "dfdl-plugin" % "1.0.0"
daffodilPlugin(daffodilVersion.value),
"org.example" % "dfdl-fmt" % "1.0.0"
daffodilBin(daffodilVersion.value)
)
This causes SBT to download the the specified plugin and saved parser,
allowing for easy testing or creationg of relase bundles.
Note that this also removes the daffodilPluginDependences setting--the
daffodilPlugin enrichment provides the same core functionality.
Also updates the cross-version-03 testto use the new enrichments. A new
"bundle" project is added to the test to depend on saved parsers and
show how to copy the plugin and parsers to a directory. This mimicks one
possible way to automate creating releases of pre-compiled artifacts.
Closes #146
---
README.md | 53 ++++++++++++---
.../scala/org/apache/daffodil/DaffodilPlugin.scala | 78 +++++++++++++++-------
.../sbt-daffodil/cross-versions-03/build.sbt | 32 ++++++++-
.../sbt-daffodil/cross-versions-03/test.script | 22 +++++-
4 files changed, 145 insertions(+), 40 deletions(-)
diff --git a/README.md b/README.md
index 845c104..a714bef 100644
--- a/README.md
+++ b/README.md
@@ -228,6 +228,36 @@ IntelliJ, do not trigger during builds. So you must either
run `sbt Test/compile
to manually trigger the resource generator, or let SBT handle builds by
enabling the "Use SBT shell for builds" option.
+### Saved Parsers as Dependencies
+
+It can sometimes be useful to add a saved parser as a dependency, causing SBT
+to download a saved parser published to a repository. This plugin enriches
+`ModuleID` with the `daffodilBin(...)` function to simplify this. The function
+requires a version to specify Daffodil compatibility of the saved parser--it
+should usually be `daffodilVersion.value`. For example:
+
+```scala
+libraryDependencies ++= Seq(
+ "org.example" % "dfdl-fmt" % "1.0.0" daffodilBin(daffodilVersion.value)
+)
+```
+
+This example adds a dependency to a saved parser for dfdl-fmt compatible with
+`daffodilVersion`.
+
+Note that because saved parsers are not jars, SBT will only download the saved
+parser--it will not do anything else like add it to the classpath. It is left
+to users to find and reference the file if needed. As an example, directories
+containing saved parsers could be added to the `test` classpath like this:
+
+```scala
+Test / dependencyClasspath ++= {
+ val savedParsers = update.value.matching(artifactFilter(`type` = "parser"))
+ val savedParserDirs = savedParser.map(_.toParentFile)
+ savedParserDirs
+}
+```
+
### Daffodil Plugins
#### Projects that Build Plugins
@@ -257,22 +287,25 @@ versions of Daffodil.
#### Projects that Use Plugins
-Projects that use Daffodil charset, layer, or user defined function plugin
-should specify the dependency using the `daffodilPluginDependencies` setting.
-Note that the `daffodilXYZ` classifier should not be included and `%%` should
-not be used even if the plugin is written in Scala. For example:
+Projects that use a Daffodil charset, layer, or user defined function plugin
+should add the plugin to the `libraryDependencies` setting with the
+`daffodilPlugin(...)` enrichment to `ModuleID`. The function requires a version
+to specify Daffodil compatibility of the plugin--it should usually be
+`daffodilVersion.value`. For example:
```scala
-daffodilPluginDependencies := Seq(
- "org.example" % "daffodil-layer-plugin" % "1.0.0"
+libraryDependencies ++= Seq(
+ "org.example" % "daffodil-layer-plugin" % "1.0.0"
daffodilPlugin(daffodilVersion.value)
)
```
+Note that `%%` should not be used even if the plugin is written in Scala.
+
The appropriate `daffodilXYZ` classifier will be added to the dependency based
-on the `daffodilVersion` of the project. The plugin dependency will also be
-added in the `"provided"` scope--this avoids potential issues with transitive
-dependencies, but does mean that if a schema project depends on a plugin, even
-transitively, it must specify it in `daffodilPluginDependencies`.
+on the provided version. The plugin dependency will also be added in the
+`"provided"` scope--this avoids potential issues with transitive dependencies,
+but does mean that if a schema project depends on a plugin, even transitively,
+it must specify the `libraryDependencies` entry.
### Flat Directory Layout
diff --git a/src/main/scala/org/apache/daffodil/DaffodilPlugin.scala
b/src/main/scala/org/apache/daffodil/DaffodilPlugin.scala
index fe6f7ad..92b07da 100644
--- a/src/main/scala/org/apache/daffodil/DaffodilPlugin.scala
+++ b/src/main/scala/org/apache/daffodil/DaffodilPlugin.scala
@@ -63,9 +63,6 @@ object DaffodilPlugin extends AutoPlugin {
val daffodilTdmlUsesPackageBin = settingKey[Boolean](
"Whether or not TDML files use the saved parsers created by
daffodilPackageBin"
)
- val daffodilPluginDependencies = settingKey[Seq[ModuleID]](
- "Dependendies to Daffodil plugins. This should not include the Daffodil
version suffix"
- )
/**
* Class to define daffodilPackageBinInfos, auto-imported to simplify sbt
configs
@@ -99,6 +96,59 @@ object DaffodilPlugin extends AutoPlugin {
DaffodilProject(root, crossDaffodilVersions)
}
}
+
+ /**
+ * Provides enrichments to ModuleID to provide daffodil specific
capabilities
+ */
+ implicit class DaffodilModuleIDOps(private val moduleId: ModuleID) extends
AnyVal {
+
+ /**
+ * Specify that a ModuleID is to a saved parser compatible with a
specific version of
+ * Daffodil. For example:
+ *
+ * libraryDependences ++= Seq(
+ * "org.example" % "dfdl-fmt" % "1.0.0"
daffodilBin(daffodilVersion.value)
+ * )
+ *
+ * This adds the dfdl-fmt-1.0.0-daffodilXYZ.bin saved parser as a
dependency. Note that
+ * SBT will not add this to the classpath like normal dependencies
(because it's not a
+ * jar), but the path can be extracted from the SBT "update" task. This
also adds it to
+ * the "provided" scope so that other schema projects can depend on this
schema project
+ * with a potentially different version of Daffodil and saved parser.
This does mean that
+ * if schema project A depends on saved parser from Foo, and schema
project B depends on
+ * project A, then project B must also list Foo as a dependency with
daffodilBin(...) if
+ * it needs the saved parser.
+ */
+ def daffodilBin(daffodilVersion: String): ModuleID = {
+ moduleId
+ .artifacts(
+ Artifact(moduleId.name, "parser", "bin",
daffodilVersionId(daffodilVersion))
+ )
+ .withConfigurations(Some("provided"))
+ }
+
+ /**
+ * Specify that a ModuleID is to a plugin compatible with a specific
version of Daffodil.
+ * For example:
+ *
+ * libraryDependences ++= Seq(
+ * "org.example" % "dfdl-plugin" % "1.0.0"
daffodilPlugin(daffodilVersion.value)
+ * )
+ *
+ * This adds the dfdl-plugin-1.0.0-daffodilXYZ.jar as a dependency and
adds it to the
+ * classpath. This also adds it to the "provided" scope so that other
schema projects can
+ * depend on this schema project with a potentially different version of
Daffodil and
+ * plugin. This does mean that if schema project A depends on plugin
Foo, and schema
+ * project B depends on project A, then project B must also list Foo as
a dependency with
+ * daffodilPlugin(...).
+ */
+ def daffodilPlugin(daffodilVersion: String): ModuleID = {
+ moduleId
+ .artifacts(Artifact(moduleId.name,
daffodilVersionId(daffodilVersion)))
+ .withConfigurations(Some("provided"))
+ }
+ }
+
}
val sbtDaffodilPluginVersion =
this.getClass.getPackage.getImplementationVersion()
@@ -344,23 +394,6 @@ object DaffodilPlugin extends AutoPlugin {
}
},
- /**
- * Modify libraryDependencies to add dependencies to Daffodil plugins this
project has. The
- * plugin dependencies are modified to use a version compatible with
daffodilVersion by
- * specifying the appropriate classifer based on the daffodil version. It
also adds plugin
- * dependencies to the "provided" scope so that other schema projects can
depend on this
- * schema project with a potentially different version of Daffodil and the
plugins. This
- * does mean that if schema project A depends on plugin Foo, and schema
project B depends on
- * project A, then project B must also list Foo as a
daffodilPluginDependency.
- */
- libraryDependencies ++= {
- daffodilPluginDependencies.value.map { pluginModuleID =>
- pluginModuleID
- .classifier(daffodilVersionId(daffodilVersion.value))
- .withConfigurations(Some("provided"))
- }
- },
-
/**
* If we are building plugins, we want to make sure they are built with
compatability for
* the minimum version of Java that daffodilVersion supports, regardless
of the javac
@@ -433,11 +466,6 @@ object DaffodilPlugin extends AutoPlugin {
daffodilPackageBinInfos := Seq(),
daffodilPackageBinVersions := Seq(),
- /**
- * Default to no Daffodil plugins
- */
- daffodilPluginDependencies := Seq(),
-
/**
* define and configure a custom Ivy configuration with dependencies to
the Daffodil
* versions we need, getting us easy access to the Daffodil jars and its
dependencies
diff --git a/src/sbt-test/sbt-daffodil/cross-versions-03/build.sbt
b/src/sbt-test/sbt-daffodil/cross-versions-03/build.sbt
index aaa0946..f58ac2b 100644
--- a/src/sbt-test/sbt-daffodil/cross-versions-03/build.sbt
+++ b/src/sbt-test/sbt-daffodil/cross-versions-03/build.sbt
@@ -25,7 +25,7 @@ val plugin = (project in file("plugin"))
daffodilVersion := "3.10.0",
daffodilBuildsCharset := true,
)
- .daffodilProject(crossDaffodilVersions = Seq("3.1.0", "3.11.0"))
+ .daffodilProject(crossDaffodilVersions = Seq("3.11.0", "4.0.0"))
val schema = (project in file("schema"))
.settings(
@@ -33,8 +33,34 @@ val schema = (project in file("schema"))
version := "0.1",
organization := "com.example",
daffodilVersion := "3.10.0",
- daffodilPluginDependencies := Seq(
- "com.example" % "test-plugin" % "0.1"
+ libraryDependencies ++= Seq(
+ "com.example" % "test-plugin" % "0.1"
daffodilPlugin(daffodilVersion.value)
),
+ daffodilPackageBinInfos := Seq(
+ DaffodilBinInfo("/com/example/test.dfdl.xsd")
+ )
)
.daffodilProject(crossDaffodilVersions = Seq("3.11.0", "4.0.0"))
+
+val bundle = (project in file("bundle"))
+ .settings(
+ name := "test-bundle",
+ version := "0.1",
+ organization := "com.example",
+ daffodilVersion := "3.10.0",
+ libraryDependencies ++= Seq(
+ "com.example" % "test-plugin" % "0.1"
daffodilPlugin(daffodilVersion.value),
+ "com.example" % "test-schema" % "0.1" daffodilBin(daffodilVersion.value)
+ ),
+ Compile / resourceGenerators += Def.task {
+ // copy plugins and saved parsers to a "release" directory
+ val srcFiles = update.value.matching(moduleFilter("com.example"))
+ val destDir = (Compile / baseDirectory).value / "target" / "release"
+ val destFiles = srcFiles.map { src =>
+ val dest = destDir / src.getName()
+ IO.copyFile(src, dest)
+ dest
+ }
+ destFiles
+ }.taskValue
+ ).daffodilProject(crossDaffodilVersions = Seq("3.11.0", "4.0.0"))
diff --git a/src/sbt-test/sbt-daffodil/cross-versions-03/test.script
b/src/sbt-test/sbt-daffodil/cross-versions-03/test.script
index bb8a766..e579ff1 100644
--- a/src/sbt-test/sbt-daffodil/cross-versions-03/test.script
+++ b/src/sbt-test/sbt-daffodil/cross-versions-03/test.script
@@ -29,5 +29,23 @@
# test only 3.11.0 with the plugin
> schema_daffodil3110/test
-# test only 4.0.0 with the plugin, this fails because the plugin was not build
for 4.0.0
--> schema_daffodil400/test
+# test only 4.0.0 with the plugin
+> schema_daffodil400/test
+
+# publish the schema and all versions of saved parsers
+> schema/publish
+
+# run bundle resource generators, which should copy the plugin and saved
+# parsers to a "release" directory
+> bundle/package
+> bundle_daffodil3110/package
+> bundle_daffodil400/package
+
+$ exists bundle/target/release/test-plugin-0.1-daffodil400.jar
+$ exists bundle/target/release/test-schema-0.1-daffodil400.bin
+
+$ exists bundle/target/release/test-plugin-0.1-daffodil3100.jar
+$ exists bundle/target/release/test-schema-0.1-daffodil3100.bin
+
+$ exists bundle/target/release/test-plugin-0.1-daffodil3110.jar
+$ exists bundle/target/release/test-schema-0.1-daffodil3110.bin