sbt test:doc Could not find any member to link - scala

I'm attempting to run sbt test:doc and I'm seeing a number of warnings similar to below:
[warn] /Users/tleese/code/my/stuff/src/test/scala/com/my/stuff/common/tests/util/NumberExtractorsSpecs.scala:9: Could not find any member to link for "com.my.stuff.common.util.IntExtractor".
The problem appears to be that Scaladoc references from test sources to main sources are not able to link correctly. Any idea what I might be doing wrong or need to configure?
Below are the relevant sections of my Build.scala:
val docScalacOptions = Seq("-groups", "-implicits", "-external-urls:[urls]")
scalacOptions in (Compile, doc) ++= docScalacOptions
scalacOptions in (Test, doc) ++= docScalacOptions
autoAPIMappings := true

Not sure if this is a satisfactory solution, but...
Scaladoc currently expects pairs of jar and URL to get the external linking to work. You can force sbt to link internal dependencies using JARs using exportJars. Compare the value of
$ show test:fullClasspath
before and after setting exportJars. Next, grab the name of the JAR that's being used and link it to the URL you'll be uploading it to.
scalaVersion := "2.11.0"
autoAPIMappings := true
exportJars := true
scalacOptions in (Test, doc) ++= Opts.doc.externalAPI((
file(s"${(packageBin in Compile).value}") -> url("http://example.com/")) :: Nil)
Now I see that test:doc a Scaladoc with links to http://example.com/index.html#foo.IntExtractor from my foo.IntExtractor.

Using ideas from Eugene's answer I made a following snippet.
It uses apiMapping sbt variable as adviced in sbt manual.
Unfortunately it doesn't tell how to deal with managed dependencies, even the subsection title says so.
// External documentation
/* You can print computed classpath by `show compile:fullClassPath`.
* From that list you can check jar name (that is not so obvious with play dependencies etc).
*/
val documentationSettings = Seq(
autoAPIMappings := true,
apiMappings ++= {
// Lookup the path to jar (it's probably somewhere under ~/.ivy/cache) from computed classpath
val classpath = (fullClasspath in Compile).value
def findJar(name: String): File = {
val regex = ("/" + name + "[^/]*.jar$").r
classpath.find { jar => regex.findFirstIn(jar.data.toString).nonEmpty }.get.data // fail hard if not found
}
// Define external documentation paths
Map(
findJar("scala-library") -> url("http://scala-lang.org/api/" + currentScalaVersion + "/"),
findJar("play-json") -> url("https://playframework.com/documentation/2.3.x/api/scala/index.html")
)
}
)

This is a modification of the answer by #phadej. Unfortunately, that answer only works on Unix/Linux because it assumes that the path separator is a /. On Windows, the path separator is \.
The following works on all platforms, and is slightly more idiomatic IMHO:
/* You can print the classpath with `show compile:fullClassPath` in the SBT REPL.
* From that list you can find the name of the jar for the managed dependency.
*/
lazy val documentationSettings = Seq(
autoAPIMappings := true,
apiMappings ++= {
// Lookup the path to jar from the classpath
val classpath = (fullClasspath in Compile).value
def findJar(nameBeginsWith: String): File = {
classpath.find { attributed: Attributed[java.io.File] => (attributed.data ** s"$nameBeginsWith*.jar").get.nonEmpty }.get.data // fail hard if not found
}
// Define external documentation paths
Map(
findJar("scala-library") -> url("http://scala-lang.org/api/" + currentScalaVersion + "/"),
findJar("play-json") -> url("https://playframework.com/documentation/2.3.x/api/scala/index.html")
)
}
)

Related

How to override libraryDependencies in a sbt plugin?

How would one override the libraryDependencies ?
I tried:
Keys.libraryDependencies in Compile := {
val libraryDependencies = (Keys.libraryDependencies in Compile).value
val allLibraries = UpdateDependencies(libraryDependencies)
allLibraries
}
So that seem to work, when I add print statement, the allLibraries is correct.
However, in the next steps, it doesn't seem to have the right values:
Keys.update in Compile := Def.taskDyn {
val u = (Keys.update in Compile).value
Def.task {
val allModules= u.configurations.flatMap(_.allModules)
log.info(s"Read ${allModules.size} modules:")
u
}
}.value
The print statement only have a few modules instead of all the one I would have added in the previous step.
Anyone have a solution ? Thanks !
So I understand where my problem was.
I was not understanding correctly how settings and tasks were working together.
settings are only evaluated once when sbt start.
and tasks are only evaluated once when sbt start a task / command which will require it.
So you cannot read and then rewrite settings like that.
It was so convoluted, I even wrote a whole article about it

sbt-assembly does not pick up configuration specific settings

I am updating an old 0.7.x build file from the tool sbt that thankfully removed the reference to "simple" from its name in the meantime.
Something that once worked, does not do so any longer. I had different config entries for platform specific assembly tasks. These include specific filters that for some reason are now called assemblyExcludedJars instead of excludedJars, and specific jar names that for some reason are now called assemblyJarName instead of jarName.
Basically:
val Foo = config("foo") extend Compile
lazy val assemblyFoo = TaskKey[File]("assembly-foo")
lazy val root = Project(id = "root", base = file("."))
// .configs(Foo) // needed? doesn't change anything
.settings(
inConfig(Foo)(inTask(assembly) {
assemblyJarName := "wtf.jar"
}),
scalaVersion := "2.11.7",
assemblyFoo <<= assembly in Foo
)
Now I would expect that if I run sbt assembly-foo or sbt foo:assembly, it would produce a file wtf.jar. But I am getting the default root-assembly-0.1-snapshot.jar. The same problem happens when I try to specify assemblyExcludedJars, they are simply ignored and still included.
If I remove the inConfig it works:
lazy val root = Project(id = "root", base = file("."))
.settings(
inTask(assembly) {
assemblyJarName := "wtf.jar"
},
scalaVersion := "2.11.7",
assemblyFoo <<= assembly in Foo
)
But now I cannot use different jar names for different configurations (which is the whole point).
As described in a blog post by one of sbt's authors and the author of sbt-assembly, this should work. It was also written in this Stackoverflow question. But the example requires an antique version of sbt-assembly (0.9.0 from 2013, before auto plugins etc.) and doesn't seem to apply to the current versions.
If one defines a new configuration, one has to redefine (?) all the tasks one is about to use. Apparently for sbt-assembly, this means running baseAssemblySettings:
val Foo = config("foo") extend Compile
lazy val assemblyFoo = TaskKey[File]("assembly-foo")
lazy val root = Project(id = "root", base = file("."))
.settings(
inConfig(Foo)(baseAssemblySettings /* !!! */ ++ inTask(assembly) {
jarName := "wtf.jar"
}),
scalaVersion := "2.11.7",
assemblyFoo := (assembly in Foo).value
)
Tested with sbt 0.13.9 and sbt-assembly 0.14.1.

sbt: publish generated sources

I have a project where part of the sources are generated (sourceGenerators in Compile). I noticed that (in most scenarios reasonably) these sources are not published with publishLocal or publishSigned. In this case this is unfortunate because when you use this project/library as a dependency, you cannot look up the sources, for example in IntelliJ, even if the other sources of the project have been downloaded.
Can I configure sbt's publishing settings to include the generated sources in the Maven -sources.jar?
So, just to be complete, this was my solution based on #pfn's answer:
mappings in (Compile, packageSrc) ++= {
val base = (sourceManaged in Compile).value
val files = (managedSources in Compile).value
files.map { f => (f, f.relativeTo(base).get.getPath) }
}
mappings in (Compile,packageSrc) := (managedSources in Compile).value map (s => (s,s.getName)),
Just like #0__'s answer, but ported to the 'new' sbt syntax, i.e. without deprecation warnings.
Compile/packageSrc/mappings ++= {
val base = (Compile/sourceManaged).value
val files = (Compile/managedSources).value
files.map(f => (f, f.relativeTo(base).get.getPath))
}

Error while using aspectj with Scala

I am having an application in scala. I need to use AOP for one of the functionality. I used the plugin sbt-aspectj . Everything is working fine when I run using the sbt console. However, I am not able to make it work when using the executable jar. I tried the the sample code provided in the sbt-aspect git page. But, I am getting the errors as
[warn] warning incorrect classpath: D:\source\jvm\modules\scala\frameworks\aspectjTracer\target\scala-2.11\classes
[warn] Missing message: configure.invalidClasspathSection in: org.aspectj.ajdt.ajc.messages
[error] error no sources specified
.
[trace] Stack trace suppressed: run 'last aspectjTracer/aspectj:ajc' for the full output.
[error] (aspectjTracer/aspectj:ajc) org.aspectj.bridge.AbortException: ABORT
[error] Expected project ID
[error] Expected configuration
[error] Expected ':' (if selecting a configuration)
[error] Expected key
[error] Not a valid key: aspectjTracker (similar: aspectjSource, aspectj-source, aspectjDirectory)
[error] last aspectjTracker/aspectj:ajc
[error]
My Build.scala is given below :
object frameworkBuild extends Build {
import Dependencies._
val akkaV = "2.3.6"
val sprayV = "1.3.1"
val musterV = "0.3.0"
val common_settings = Defaults.defaultSettings ++
Seq(version := "1.3-SNAPSHOT",
organization := "com.reactore",
scalaVersion in ThisBuild := "2.11.2",
scalacOptions ++= Seq("-unchecked", "-feature", "-deprecation"),
libraryDependencies := frameworkDependencies ++ testLibraryDependencies,
publishMavenStyle := true,
)
connectInput in run := true
lazy val aspectJTracer = Project(
"aspectjTracer",
file("aspectjTracer"),
settings = common_settings ++ aspectjSettings ++ Seq(
// input compiled scala classes
inputs in Aspectj <+= compiledClasses,
// ignore warnings
lintProperties in Aspectj += "invalidAbsoluteTypeName = ignore",
lintProperties in Aspectj += "adviceDidNotMatch = ignore",
// replace regular products with compiled aspects
products in Compile <<= products in Aspectj
)
)
// test that the instrumentation works
lazy val instrumented = Project(
"instrumented",
file("instrumented"),
dependencies = Seq(aspectJTracer),
settings = common_settings ++ aspectjSettings ++ Seq(
// add the compiled aspects from tracer
binaries in Aspectj <++= products in Compile in aspectJTracer,
// weave this project's classes
inputs in Aspectj <+= compiledClasses,
products in Compile <<= products in Aspectj,
products in Runtime <<= products in Compile
)
)
lazy val frameworks = Project(id = "frameworks", base = file("."), settings = common_settings).aggregate( core, baseDomain,aspectJTracer,instrumented)
lazy val core = Project(id = "framework-core", base = file("framework-core"), settings = common_settings)
lazy val baseDomain = Project(id = "framework-base-domain", base = file("framework-base-domain"), settings = common_settings).dependsOn(core,aspectJTracer,instrumented)
}
Does anyone know how to fix this? I posted this in the sbt-aspectj github page and waiting for a response there as well. But I am in a little hurry to fix this. Your help will be really appreciated.
Finally the problem is resolved. I had added javaagent in the build.scala. But, while running with the sbt-one-jar, it was not taking that jar. So I have manually provided the javaagent as the aspectweaver jar file and it worked. However, it is almost taking 3-4 minutes to start the jar file with the aspect.
Sometime it is even taking 15 min to start the jar file due to the aspectjwaver. I am not sure if this is problem with aspectj or sbt-one-jar, I guess its with the one-jar.
has anyone else faced the same issue ? I don't see any activity in sbt-one-jar, so asking it here.

Compile with different settings in different commands

I have a project defined as follows:
lazy val tests = Project(
id = "tests",
base = file("tests")
) settings(
commands += testScalalib
) settings (
sharedSettings ++ useShowRawPluginSettings ++ usePluginSettings: _*
) settings (
libraryDependencies <+= (scalaVersion)("org.scala-lang" % "scala-reflect" % _),
libraryDependencies <+= (scalaVersion)("org.scala-lang" % "scala-compiler" % _),
libraryDependencies += "org.tukaani" % "xz" % "1.5",
scalacOptions ++= Seq()
)
I would like to have three different commands which will compile only some files inside this project. The testScalalib command added above for instance is supposed to compile only some specific files.
My best attempt so far is:
lazy val testScalalib: Command = Command.command("testScalalib") { state =>
val extracted = Project extract state
import extracted._
val newState = append(Seq(
(sources in Compile) <<= (sources in Compile).map(_ filter(f => !f.getAbsolutePath.contains("scalalibrary/") && f.name != "Typers.scala"))),
state)
runTask(compile in Compile, newState)
state
}
Unfortunately when I use the command, it still compiles the whole project, not just the specified files...
Do you have any idea how I should do that?
I think your best bet would be to create different configurations like compile and test, and have the appropriate settings values that would suit your needs. Read Scopes in the official sbt documentation and/or How to define another compilation scope in SBT?
I would not create additional commands, I would create an extra configuration, as #JacekLaskowski suggested, and based on the answer he had cited.
This is how you can do it (using Sbt 0.13.2) and Build.scala (you could of course do the same in build.sbt, and older Sbt version with different syntax)
import sbt._
import Keys._
object MyBuild extends Build {
lazy val Api = config("api")
val root = Project(id="root", base = file(".")).configs(Api).settings(custom: _*)
lazy val custom: Seq[Setting[_]] = inConfig(Api)(Defaults.configSettings ++ Seq(
unmanagedSourceDirectories := (unmanagedSourceDirectories in Compile).value,
classDirectory := (classDirectory in Compile).value,
dependencyClasspath := (dependencyClasspath in Compile).value,
unmanagedSources := {
unmanagedSources.value.filter(f => !f.getAbsolutePath.contains("scalalibrary/") && f.name != "Typers.scala")
}
))
}
now when you call compile everything will get compiled, but when you call api:compile only the classes matching the filter predicate.
Btw. You may want to also look into the possibility of defining different unmanagedSourceDirectories and/or defining includeFilter.