SBT: Plugin Dependencies and Project Class Paths

How to add an external dependency to the SBT plugin and make it available for both the project class and the plugin?

In particular, I have a simple plugin that should run our TestNG test packages and do some post processing. Here is a simplified version:

import sbt._ import java.util.ArrayList import Keys._ import org.testng._ object RunTestSuitesPlugin extends Plugin { lazy val runTestSuites = TaskKey[Unit]("run-test-suites", "runs TestNG test suites") lazy val testSuites = SettingKey[Seq[String]]("test-suites", "list of test suites to run") class JavaListWrapper[T](val seq: Seq[T]) { def toJavaList = seq.foldLeft(new java.util.ArrayList[T](seq.size)) { (al, e) => al.add(e); al } } implicit def listToJavaList[T](l: Seq[T]) = new JavaListWrapper(l) def runTestSuitesTask = runTestSuites <<= (target, streams, testSuites) map { (targetDirectory, taskStream, suites) => import taskStream.log log.info("running test suites: " + suites) runSuites(suites) } private def runSuites(testSuites: Seq[String]) = { var tester = new TestNG tester.setTestSuites(testSuites.toJavaList) tester.run() } def testSuiteSettings = { inConfig(Compile)(Seq( runTestSuitesTask, testSuites := Seq("testsuites/mysuite.xml"), libraryDependencies += "org.testng" % "testng" % "5.14")) } } 

The problem is that when I add this plugin to the project and run it using run-test-suites , it does not work with java.lang.NoClassDefFoundError: org / testng / TestNG , although showing the full class shows that testng. jar is on the way to classes.

So, for some reason, the class path used when executing the plugin is different from the one in my project, since I can make the plugin dependency in both places?

+4
source share
2 answers

The bug was fixed by adding TestNG directly to unmanaged Jars in compilation in a project that uses the plugin.

I did not find any resources explaining the structure of the SBT class path during the execution of the plugin, so we will be very grateful for any attempt to explain why this step is necessary.

+1
source

I will try to answer, but I am not very familiar with the internal details of sbt.

Usually, the path for the build system (unlike your program) is in the project, as described here . Usually this should be in project/plugins.sbt . It sounds right, since there is no reason for the program you are developing to take care of which libraries your build system uses, and vice versa.

When your plugin runs the application code, it may not be so simple, and problems with classpath / classloader may occur. I'm not sure if this will work. Typically, your plugin should implement a testing platform, and not define its own task. Testing documentation for sbt is limited.

The testing framework should implement org.scalatools.testing.Framework in the test-interface . Your assembly will take this into account after adding

 testFrameworks += new TestFramework("full.class.name") 

When you run a regular test command, it allows each structure to recognize the test classes it deals with (two available criteria: extending the base class or annotation) and running them. The frame runs in the assembly, it is provided with a class loader to access the application code.

You can take a look at the implementation of the framework for junit (comes with sbt). There is also a TestNG implementation . I donโ€™t know this, according to his document, itโ€™s a little unorthodox, I hope this works for you.

+1
source

All Articles