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 d9a5025  Add support for Test / packageDaffodilBin
d9a5025 is described below

commit d9a502559edcbee5439574d879d475e96f0e745f
Author: Steve Lawrence <[email protected]>
AuthorDate: Tue Feb 24 11:23:16 2026 -0500

    Add support for Test / packageDaffodilBin
    
    Move the packageDaffodilBin and related tasks that generate saved
    parsers into the Compile and Test configs. This allows use of
    configuration scoped tasks (e.g. fullClasspath) to build saved parsers
    using resources that include Compile or Test resources.
    
    With this change, projects that require test resources can define this
    in their build.sbt:
    
        Test / daffodilPackageBinInfos := ...
    
    And then run this to create a saved parser using test resources:
    
        Test / packageDaffodilBin
    
    Note that if Test resources aren't needed this works exactly the same as
    before.
    
    Closes #165
---
 .../scala/org/apache/daffodil/DaffodilPlugin.scala | 277 ++++++++++++---------
 .../sbt-daffodil/saved-parsers-07/build.sbt        |  28 +++
 .../saved-parsers-07/project/plugins.sbt           |  20 ++
 .../src/test/resources/test.dfdl.xsd               |  37 +++
 .../sbt-daffodil/saved-parsers-07/test.script      |  29 +++
 5 files changed, 269 insertions(+), 122 deletions(-)

diff --git a/src/main/scala/org/apache/daffodil/DaffodilPlugin.scala 
b/src/main/scala/org/apache/daffodil/DaffodilPlugin.scala
index 8a83f6e..497ca49 100644
--- a/src/main/scala/org/apache/daffodil/DaffodilPlugin.scala
+++ b/src/main/scala/org/apache/daffodil/DaffodilPlugin.scala
@@ -176,11 +176,30 @@ object DaffodilPlugin extends AutoPlugin {
   }
 
   /**
-   * generate an artifact classifier name using the optional name and daffodil 
version
+   * Generate a classifier used for packageDaffodilBin artifacts, using the 
optional
+   * DaffodilBinInfo.name, the daffodil version, and the SBT configuration. 
The result is of the
+   * form:
+   *
+   *   [name-]daffodilXYZ[-config]
+   *
+   * Where [name-] is omitted if optName is None, daffodilXYZ is the daffodil 
version ID, and
+   * [-config] is omitted if it is Compile. Note that the config addition is 
important to
+   * indicate that non-compile resources were available when building a saved 
parser. For
+   * example, Test / packageDaffodilBin will have a "test" appended to the 
classifier,
+   * indicating it was built with test resources available and so probably 
should not be used in
+   * production.
    */
-  def classifierName(optName: Option[String], daffodilVersion: String): String 
= {
+  def packageBinClassifier(
+    optName: Option[String],
+    daffodilVersion: String,
+    config: Configuration
+  ): String = {
     val versionId = daffodilVersionId(daffodilVersion)
-    (optName.toSeq ++ Seq(versionId)).mkString("-")
+    val configClassifier = config match {
+      case Compile => None
+      case _ => Some(config.name)
+    }
+    (optName.toSeq ++ Seq(versionId) ++ configClassifier.toSeq).mkString("-")
   }
 
   /**
@@ -459,8 +478,127 @@ object DaffodilPlugin extends AutoPlugin {
     },
 
     /**
-     * define the artifacts, products, and the packageDaffodilBin task that 
creates the artifacts/products
+     * SBT ignores the packageDaffodilBin / logLevel setting, so we use it as 
the logLevel to
+     * provide to daffodil when saving a parser. We default to warn because 
Daffodils "info" is
+     * usually too verbose
+     */
+    packageDaffodilBin / logLevel := Level.Warn,
+
+    /**
+     * JVM options used for the forked process to build saved parsers
+     *
+     * Defaults to just setting various system properties to configure loggers 
that might be
+     * used by different daffodil versions
+     */
+    packageDaffodilBin / javaOptions := Seq(
+      s"-Dorg.slf4j.simpleLogger.logFile=System.out",
+      s"-Dorg.slf4j.simpleLogger.defaultLogLevel=${(packageDaffodilBin / 
logLevel).value}",
+      s"-Dorg.apache.logging.log4j.level=${(packageDaffodilBin / 
logLevel).value}"
+    ),
+
+    /**
+     * These two settings tell sbt about the packageDaffodilBin artifacts and 
the task that
+     * generates the artifacts so it knows to generate and publish them when
+     * publish/publihLocal/publishM2 is run. We intentionally only include 
Compile artifacts
+     * since Test artifacts are not intended to be published or used outside 
of very limited
+     * uses
+     */
+    artifacts ++= {
+      if ((Compile / packageDaffodilBin / publishArtifact).value) {
+        (Compile / packageDaffodilBin / artifacts).value
+      } else {
+        Seq()
+      }
+    },
+    packagedArtifacts := {
+      if ((Compile / packageDaffodilBin / publishArtifact).value) {
+        val arts = (Compile / packageDaffodilBin / artifacts).value
+        // the products val is not used, but this is required to build saved 
parsers as a side effect
+        val prods = (Compile / packageDaffodilBin / products).value
+        val artFileTuples = arts.map { art =>
+          val artName = 
s"${art.name}-${version.value}-${art.classifier.get}.${art.extension}"
+          val artFile = target.value / artName
+          (art -> artFile)
+        }
+        packagedArtifacts.value ++ artFileTuples
+      } else {
+        packagedArtifacts.value
+      }
+    },
+
+    /**
+    * If daffodilTdmlUsesPackageBin is true, we create a resource generator to 
build the saved
+    * parsers and adds them as a resource for the TDML files to find and use. 
Note that we use a
+    * resourceGenerator since other methods make it difficult to convince 
IntelliJ to put the
+    * files on the test classpath. See below Test/packageBin/mappings for 
related changes. Note
+    * that because this is a Test resource generator, it cannot have access to 
Test /
+    * packageCompileBin without creating a circular dependency. So only 
Compile /
+    * packageDaffodilBin can be made avilable for TDML tests
+     */
+    Test / resourceGenerators += Def.taskIf {
+      if (daffodilTdmlUsesPackageBin.value) {
+
+        if (!daffodilPackageBinVersions.value.contains(daffodilVersion.value)) 
{
+          throw new MessageOnlyException(
+            s"daffodilPackageBinVersions 
(${daffodilPackageBinVersions.value.mkString(", ")}) must contain 
daffodilVersion (${daffodilVersion.value}) if daffodilTdmlUsesPackageBin is 
true"
+          )
+        }
+
+        // force creation of saved parsers, there isn't currently a way to 
build them for just
+        // daffodilVersion
+        val allSavedParsers = (Compile / packageDaffodilBin / products).value
+
+        // copy the saved parsers for the current daffodilVersion to the root 
of the
+        // resourceManaged directory, and consider those our generated 
resources
+        val destDir = (Test / resourceManaged).value
+        val tdmlParserFiles = daffodilPackageBinInfos.value.map { dbi =>
+          val sourceClassifier = packageBinClassifier(dbi.name, 
daffodilVersion.value, Compile)
+          val source = target.value / 
s"${name.value}-${version.value}-${sourceClassifier}.bin"
+          val destClassifier = dbi.name.map { "-" + _ }.getOrElse("")
+          val dest = destDir / s"${name.value}${destClassifier}.bin"
+          IO.copyFile(source, dest)
+          dest
+        }
+        tdmlParserFiles
+      } else {
+        Seq()
+      }
+    }.taskValue,
+
+    /**
+     * The above resource generator creates saved parsers as test resources so 
that tests can
+     * find them on the classpath. But this means the parsers will also be 
packaged in test
+     * jars. Saved parsers are already published as artifacts, so there's no 
reason to also
+     * include them in jars--remove them from the mapping that says which 
files to put in jars.
      */
+    Test / packageBin / mappings := {
+      val existingMappings = (Test / packageBin / mappings).value
+      if (daffodilTdmlUsesPackageBin.value) {
+        val tdmlParserNames = daffodilPackageBinInfos.value.map { dbi =>
+          val destClassifier = dbi.name.map { "-" + _ }.getOrElse("")
+          s"${name.value}${destClassifier}.bin"
+        }
+        existingMappings.filterNot { case (_, name) => 
tdmlParserNames.contains(name) }
+      } else {
+        existingMappings
+      }
+    }
+  ) ++
+    inConfig(Compile)(packageDaffodilBinSettings) ++
+    inConfig(Test)(packageDaffodilBinSettings) ++
+    inConfig(Compile)(flatLayoutSettings("src")) ++
+    inConfig(Test)(flatLayoutSettings("test"))
+
+  /**
+   * Define the artifacts, products, and packageDaffodilBin task that creates 
the saved parsers
+   * and makes them available to SBT. Note that these settings must be 
configuration scoped
+   * (e.g. Compile/Test) because they access other tasks/settings that are 
configuration scoped,
+   * such as fullClasspath. This requires that these tasks to be accessed like 
"Compile /
+   * packageDaffodilBin / products" which creates a saved parser built using 
either compile or
+   * test resources. Note that users can still reference the 
packageDaffodilBin task without a
+   * configuration and it defaults to the Compile task.
+   */
+  def packageDaffodilBinSettings = Seq(
     packageDaffodilBin / artifacts := {
       val logger = sLog.value
       if (daffodilPackageBinVersions.value.length > 1) {
@@ -470,11 +608,7 @@ object DaffodilPlugin extends AutoPlugin {
       }
       daffodilPackageBinVersions.value.flatMap { daffodilVersion =>
         daffodilPackageBinInfos.value.map { dbi =>
-          // each artifact has the same name as the jar, in the "parser" type, 
"bin" extension,
-          // and daffodil version specific classifier. If dbi.name is Some, it 
is prepended
-          // to the classifier separated by a hyphen. Note that publishing as 
maven style will
-          // only use the name, extension, and classifier
-          val classifier = classifierName(dbi.name, daffodilVersion)
+          val classifier = packageBinClassifier(dbi.name, daffodilVersion, 
configuration.value)
           Artifact(name.value, "parser", "bin", Some(classifier), Vector(), 
None)
         }
       }.toSeq
@@ -486,8 +620,13 @@ object DaffodilPlugin extends AutoPlugin {
       // options to provide to the forked JVM process used to save a processor
       val jvmArgs = (packageDaffodilBin / javaOptions).value
 
-      // get all dependencies and resources of this project
-      val projectClasspath = (Compile / fullClasspath).value.files
+      // get all dependencies and resources of this configuration needed to 
build a saved
+      // parser. If in the Test configuration or in some Compile 
configurations, fullClasspath
+      // could contain the daffodil-slf4j-logger, which causes a warning 
because it conflicts
+      // with the slf4j-simple logger added to the daffodilXYZ ivy config, so 
we remove that
+      // here to avoid the warning.
+      val projectClasspath = fullClasspath.value.files
+        .filterNot { _.name.startsWith("daffodil-slf4j-logger") }
 
       val mainClass = "org.apache.daffodil.DaffodilSaver"
 
@@ -507,9 +646,10 @@ object DaffodilPlugin extends AutoPlugin {
         throw new MessageOnlyException(msg)
       }
 
+      val config = configuration.value
       val ivyConfigs = ivyConfigurations.value
-      val classpathTypesVal = (Compile / classpathTypes).value
-      val updateVal = (Compile / update).value
+      val classpathTypesVal = classpathTypes.value
+      val updateVal = update.value
 
       // FileFunction.cached creates a function that accepts files to watch. 
If any have
       // changed, cachedFun will call the function passing in the watched 
files to regenerate
@@ -533,7 +673,7 @@ object DaffodilPlugin extends AutoPlugin {
           val classpathFiles = daffodilJars ++ projectClasspath
 
           daffodilPackageBinInfos.value.map { dbi =>
-            val classifier = classifierName(dbi.name, daffodilVersion)
+            val classifier = packageBinClassifier(dbi.name, daffodilVersion, 
config)
             val targetName = 
s"${name.value}-${version.value}-${classifier}.bin"
             val targetFile = target.value / targetName
 
@@ -617,25 +757,6 @@ object DaffodilPlugin extends AutoPlugin {
       val savedParsers = cachedFun(filesToWatch)
       savedParsers.toSeq
     },
-
-    /**
-     * SBT ignores the packageDaffodilBin / logLevel setting, so we use it as 
the logLevel to
-     * provide to daffodil when saving a parser. We default to warn because 
Daffodils "info" is
-     * usually too verbose
-     */
-    packageDaffodilBin / logLevel := Level.Warn,
-
-    /**
-     * JVM options used for the forked process to build saved parsers
-     *
-     * Defaults to just setting various system properties to configure loggers 
that might be
-     * used by different daffodil versions
-     */
-    packageDaffodilBin / javaOptions := Seq(
-      s"-Dorg.slf4j.simpleLogger.logFile=System.out",
-      s"-Dorg.slf4j.simpleLogger.defaultLogLevel=${(packageDaffodilBin / 
logLevel).value}",
-      s"-Dorg.apache.logging.log4j.level=${(packageDaffodilBin / 
logLevel).value}"
-    ),
     packageDaffodilBin := {
       val logger = streams.value.log
 
@@ -650,96 +771,8 @@ object DaffodilPlugin extends AutoPlugin {
         )
       }
       prods
-    },
-
-    /**
-     * These two settings tell sbt about the artifacts and the task that 
generates the artifacts
-     * so it knows to generate and publish them when 
publish/publihLocal/publishM2 is run
-     */
-    artifacts ++= {
-      if ((packageDaffodilBin / publishArtifact).value) {
-        (packageDaffodilBin / artifacts).value
-      } else {
-        Seq()
-      }
-    },
-    packagedArtifacts := {
-      if ((packageDaffodilBin / publishArtifact).value) {
-        val arts = (packageDaffodilBin / artifacts).value
-        val files = (packageDaffodilBin / products).value
-
-        // the artifacts and associated files are not necessarily in the same 
order. For each
-        // artifact, we need to find the associated file (the one that ends 
with the same
-        // classifier and extension) and update the packagedArtifacts setting 
with that pair
-        val updatedPackagedArtifacts =
-          arts.foldLeft(packagedArtifacts.value) { case (pa, art) =>
-            val suffix = s"-${art.classifier.get}.${art.extension}"
-            val file = files.find { _.getName.endsWith(suffix) }.get
-            pa.updated(art, file)
-          }
-        updatedPackagedArtifacts
-      } else {
-        packagedArtifacts.value
-      }
-    },
-
-    /**
-    * If daffodilTdmlUsesPackageBin is true, we create a resource generator to 
build the saved
-    * parsers and adds them as a resource for the TDML files to find and use. 
Note that we use a
-    * resourceGenerator since other methods make it difficult to convince 
IntelliJ to put the
-    * files on the test classpath. See below Test/packageBin/mappings for 
related changes.
-     */
-    Test / resourceGenerators += Def.taskIf {
-      if (daffodilTdmlUsesPackageBin.value) {
-
-        if (!daffodilPackageBinVersions.value.contains(daffodilVersion.value)) 
{
-          throw new MessageOnlyException(
-            s"daffodilPackageBinVersions 
(${daffodilPackageBinVersions.value.mkString(", ")}) must contain 
daffodilVersion (${daffodilVersion.value}) if daffodilTdmlUsesPackageBin is 
true"
-          )
-        }
-
-        // force creation of saved parsers, there isn't currently a way to 
build them for just
-        // daffodilVersion
-        val allSavedParsers = (packageDaffodilBin / products).value
-
-        // copy the saved parsers for the current daffodilVersion to the root 
of the
-        // resourceManaged directory, and consider those our generated 
resources
-        val destDir = (Test / resourceManaged).value
-        val tdmlParserFiles = daffodilPackageBinInfos.value.map { dbi =>
-          val sourceClassifier = classifierName(dbi.name, 
daffodilVersion.value)
-          val source = target.value / 
s"${name.value}-${version.value}-${sourceClassifier}.bin"
-          val destClassifier = dbi.name.map { "-" + _ }.getOrElse("")
-          val dest = destDir / s"${name.value}${destClassifier}.bin"
-          IO.copyFile(source, dest)
-          dest
-        }
-        tdmlParserFiles
-      } else {
-        Seq()
-      }
-    }.taskValue,
-
-    /**
-     * The above resource generator creates saved parsers as test resources so 
that tests can
-     * find them on the classpath. But this means the parsers will also be 
packaged in test
-     * jars. Saved parsers are already published as artifacts, so there's no 
reason to also
-     * include them in jars--remove them from the mapping that says which 
files to put in jars.
-     */
-    Test / packageBin / mappings := {
-      val existingMappings = (Test / packageBin / mappings).value
-      if (daffodilTdmlUsesPackageBin.value) {
-        val tdmlParserNames = daffodilPackageBinInfos.value.map { dbi =>
-          val destClassifier = dbi.name.map { "-" + _ }.getOrElse("")
-          s"${name.value}${destClassifier}.bin"
-        }
-        existingMappings.filterNot { case (_, name) => 
tdmlParserNames.contains(name) }
-      } else {
-        existingMappings
-      }
     }
-  ) ++
-    inConfig(Compile)(flatLayoutSettings("src")) ++
-    inConfig(Test)(flatLayoutSettings("test"))
+  )
 
   /**
    * If daffodilFlatLayout is true, returns settings to make a flat directory 
layout. All
diff --git a/src/sbt-test/sbt-daffodil/saved-parsers-07/build.sbt 
b/src/sbt-test/sbt-daffodil/saved-parsers-07/build.sbt
new file mode 100644
index 0000000..d567693
--- /dev/null
+++ b/src/sbt-test/sbt-daffodil/saved-parsers-07/build.sbt
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+val test = (project in file("."))
+  .settings(
+    version := "0.1",
+    name := "test",
+    organization := "com.example",
+    Test / daffodilPackageBinInfos := Seq(
+      DaffodilBinInfo("/test.dfdl.xsd"),
+    ),
+    daffodilVersion := "4.1.0"
+  )
+  .daffodilProject()
diff --git a/src/sbt-test/sbt-daffodil/saved-parsers-07/project/plugins.sbt 
b/src/sbt-test/sbt-daffodil/saved-parsers-07/project/plugins.sbt
new file mode 100644
index 0000000..eaf249b
--- /dev/null
+++ b/src/sbt-test/sbt-daffodil/saved-parsers-07/project/plugins.sbt
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+addSbtPlugin("org.apache.daffodil" % "sbt-daffodil" % 
sys.props("plugin.version"))
diff --git 
a/src/sbt-test/sbt-daffodil/saved-parsers-07/src/test/resources/test.dfdl.xsd 
b/src/sbt-test/sbt-daffodil/saved-parsers-07/src/test/resources/test.dfdl.xsd
new file mode 100644
index 0000000..dbab88c
--- /dev/null
+++ 
b/src/sbt-test/sbt-daffodil/saved-parsers-07/src/test/resources/test.dfdl.xsd
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+
+<schema
+  xmlns="http://www.w3.org/2001/XMLSchema"; 
+  xmlns:xs="http://www.w3.org/2001/XMLSchema"; 
+  xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/";
+  xmlns:ex="http://example.com";
+  targetNamespace="http://example.com";
+  elementFormDefault="unqualified">
+
+  <include 
schemaLocation="/org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+
+  <annotation>
+    <appinfo source="http://www.ogf.org/dfdl/";>
+      <dfdl:format ref="ex:GeneralFormat" />
+    </appinfo>
+  </annotation>
+
+  <element name="test01" type="xs:string" dfdl:lengthKind="delimited" />
+
+</schema>
diff --git a/src/sbt-test/sbt-daffodil/saved-parsers-07/test.script 
b/src/sbt-test/sbt-daffodil/saved-parsers-07/test.script
new file mode 100644
index 0000000..ee6053a
--- /dev/null
+++ b/src/sbt-test/sbt-daffodil/saved-parsers-07/test.script
@@ -0,0 +1,29 @@
+## 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.
+## 
+
+> set publishTo := Some(Resolver.file("file", new File("target/ivy-publish/")))
+
+> packageDaffodilBin
+$ absent target/test-0.1-daffodil410.bin
+$ absent target/test-0.1-daffodil410-test.bin
+
+> Test / packageDaffodilBin
+$ absent target/test-0.1-daffodil410.bin
+$ exists target/test-0.1-daffodil410-test.bin
+
+> publish

Reply via email to