I have a scala project, but the imports don't work as designed. I tried everything here, but nothing seems to fix the issue. My project is as follows:
- src
- main
- scala
- importtest
ImportTest.scala
Main.scala
build.sbt
Imported class:
#/src/main/scala/importtest/ImportTest.scala
package importtest
class ImportTest {
def run(): Unit = {
System.out.println("boo!")
}
}
My main class is:
#/src/main/scala/Main.scala
import importtest.ImportTest
object Main {
def main(): Unit = {
val i = ImportTest()
}
}
My SBT build is:
name := "ImportTest"
version := "0.1"
scalaVersion := "2.12.6"
When I try to build, I get:
Error:(5, 13) not found: value ImportTest
val i = ImportTest()
What is going wrong here? Why can't I import the ImportTest class?
Also, not sure if this helps, but IntelliJ will autocomplete the package name, but it cant autocomplete the class within the package - it marks it as unresolved.
You are initializing ImportTest() as if it was a case class.
Because its a regular class, you need to use "new".
Change the initialization to:
val i = new ImportTest()
Related
I'm using SBT to build my project. I want to analyze some classes from my source code using either Scala or Java reflection during the build process.
How do I define an SBT task that loads a single known class or all classes from my source code?
import sbt._
val loadedClasses = taskKey[Seq[Class[_]]]("All classes from the source")
val classToLoad = settingKey[String]("Scala class name to load")
val loadedClass = taskKey[Seq[Class[_]]]("Loaded classToLoad")
You can use the output of the fullClasspathAsJars SBT task to get access to the JARs produced from you source code. This task doesn't include JARs of the dependencies. Then you can create a ClassLoader to load classes from those JARs:
import java.net.URLClassLoader
val classLoader = taskKey[ClassLoader]("Class loader for source classes")
classLoader := {
val jarUrls = (Compile / fullClasspathAsJars).value.map(_.data.toURI.toURL).toArray
new URLClassLoader(jarUrls, ClassLoader.getSystemClassLoader)
}
Then if you know the name of your class in the JAR, you can use this ClassLoader to load it.
Note the difference between Scala class names and class names in the JAR. Scala class names may be mangled, and one Scala class can produce several classes in the JAR. For example my.company.Box.MyClass class from the following snippet produces two JAR classes: my.company.Box$MyClass and my.company.Box$MyClass$, the latter being the class of the companion object.
package my.company
object Box {
case class MyClass()
}
So if you want to specify a class by its Scala name or to list all classes defined in the source, you have to use the output of the compile SBT task. This task produces a CompileAnalysis object which is part of internal SBT API and is prone to change in the future. The following code works as of SBT 1.3.10.
To load a class by its Scala name:
import sbt.internal.inc.Analysis
import xsbti.compile.CompileAnalysis
def loadClass(
scalaClassName: String,
classLoader: ClassLoader,
compilation: CompileAnalysis
): List[Class[_]] = {
compilation match {
case analysis: Analysis =>
analysis.relations.productClassName
.forward(scalaClassName)
.map(classLoader.loadClass)
.toList
}
}
classToLoad := "my.company.Box.MyClass"
loadedClass := loadClass(
classToLoad.value,
classLoader.value,
(Compile / compile).value)
To list all classes from the source code:
def loadAllClasses(
classLoader: ClassLoader,
compilation: CompileAnalysis,
): List[Class[_]] = {
val fullClassNames = compilation match {
case analysis: Analysis =>
analysis.relations.allSources.flatMap { source =>
// Scala class names
val classNames = analysis.relations.classNames(source)
val getProductName = analysis.relations.productClassName
classNames.flatMap { className =>
// Class names in the JAR
val productNames = getProductName.forward(className)
if (productNames.isEmpty) Set(className) else productNames
}
}.toList
}
fullClassNames.map(className => classLoader.loadClass(className))
}
loadedClasses := loadAllClasses(
classLoader.value,
(Compile / compile).value)
Based on Reference scala file from build.sbt add the following to project/build.sbt
Compile / unmanagedSourceDirectories += baseDirectory.value / ".." / "src" / "main" / "scala"
and then scala-reflect on project's sources from within build.sbt like so
val reflectScalaClasses = taskKey[Unit]("Reflect on project sources from within sbt")
reflectScalaClasses := {
import scala.reflect.runtime.universe._
println(typeOf[example.Hello])
}
where
src
├── main
│ └── scala
│ └── example
│ ├── Hello.scala
I am migrating a Build.scala file to a build.sbt file.
In the Build.scala file, there are print statements that print out vals (of type String) defined in the body of the Build.scala file.
project/Build.scala:
import sbt._
import Keys._
object HelloBuild extends Build {
val foo = System.getProperty("foo")
println(foo)
}
How do I migrate these print statements to the build.sbt file?
You can't just print it. You are declaring your build, but when it will be printed is different story. Probably it should be within a task for instance. From docs:
myTask := {
val log = streams.value.log
val propertyFoo = System.getProperty("foo")
log.info(s"property foo = $propertyFoo")
}
I have a following problem with sbt. From sbt console I can get value of baseDirectory global setting.
> baseDirectory
[info] /home/georginaumov/Documents/hello
>
I added one task from build.sbt
lazy val printBaseDirectory: TaskKey[Unit] = TaskKey[Unit]("printBaseDirectory", "Print baseDirectory for the project", KeyRanks.ATask)
printBaseDirectory <<= streams map Tasks.printBaseDirectory
This is code for Tasks singleton object.
import sbt.Keys.TaskStreams
import sbt._
object Tasks {
def printBaseDirectory(streams: TaskStreams): Unit = {
streams.log.info("Here I want to print value of baseDirectory")
}
}
But I cannot get the value. I tried many things and in fact the problem is that I cannot get java.io.File from sbt.SettingKey[java.io.File].
How to solve the problem?
I tried
printBaseDirectory <<= streams map Tasks.printBaseDirectory(baseDirectory)
and
def printBaseDirectory(baseDir: sbt.File)(streams: TaskStreams): Unit = {
streams.log.info("Here I want to print value of baseDirectory")
}
into the singleton object but get following error:
error: type mismatch;
[error] Type error in expression
found : sbt.SettingKey[java.io.File]
required: sbt.File
(which expands to) java.io.File
Edit:
Many thanks to Martin. I wrote a article on my blog for people with a similar problem in the future.
I think what you want is to define Tasks as:
import sbt.Keys.TaskStreams
import sbt._
object Tasks {
def printBaseDirectory(streams: TaskStreams, dir: File): Unit = {
streams.log.info(dir.getAbsolutePath)
}
}
and in build.sbt:
lazy val printBaseDirectory: TaskKey[Unit] = TaskKey[Unit]("printBaseDirectory", "Print baseDirectory for the project", KeyRanks.ATask)
printBaseDirectory <<= (streams, baseDirectory) map Tasks.printBaseDirectory
Or:
lazy val printBaseDirectory: TaskKey[Unit] = TaskKey[Unit]("printBaseDirectory", "Print baseDirectory for the project", KeyRanks.ATask)
printBaseDirectory := {
Tasks.printBaseDirectory(streams.value, baseDirectory.value)
}
Using the .value macro is the preferred way.
I am starting out in Scala with SBT, making a Hello World program.
Here's my project layout:
I've made sure to download the very latest JDK and Scala, and configure my Project Settings. Here's my build.sbt:
name := "Coursera_Scala"
version := "1.0"
scalaVersion := "2.11.6"
libraryDependencies += "org.scalatest" % "scalatest_2.11" % "2.2.4" % "test"
Hello.scala itself compiles okay:
package demo
class Hello {
def sayHelloTo(name: String) = "Hello, $name!"
}
However, my accompanying HelloTest.scala does not. Here's the test:
package demo
import org.scalatest.FunSuite
class HelloTest extends FunSuite {
test("testSayHello") {
val result = new Hello.sayHelloTo("Scala")
assert(result == "Hello, Scala!")
}
}
Here's the error:
Error:(8, 22) not found: value Hello
val result = new Hello.sayHello("Scala")
^
In addition to the compile error, Intellij shows errors "Cannot resolve symbol" for the symbols Hello, assert, and ==. This leads me to believe that the build is set up incorrectly, but then wouldn't there be an error on the import?
The problem is this expression:
new Hello.sayHelloTo("Scala")
This creates a new instance of the class sayHelloTo defined on the value Hello. However, there is no value Hello, just the class Hello.
You want to write this:
(new Hello).sayHelloTo("Scala")
This creates a new instance of the class Hello and calls sayHelloTo on the instance.
Or you can use new Hello().sayHelloTo(...). Writing () should create a new instance and then call the method.
If I define a SBT task key outside of my build.sbt file as a Scala class in the project folder, how can I import that task
So in ./project/MyTask.scala I have;
import sbt.Keys._
import sbt._
object MyTask {
lazy val uname = settingKey[String]("Your name")
lazy val printHi = taskKey[Unit]("print Hi")
printHi := { println(s"hi ${name.value}") }
}
Then in ./build.sbt I have;
import MyTask._
uname := "Joe"
Then when I run sbt printHi I get an error that the task cannot be found. Running show uname also works. When I define printHi in build.sbt directly without the object import everything works as expected.
I need so somehow add this task to the build.sbt file. How can I do this?
The issue is that your expression printHi := { println(s"hi ${name.value}") } isn't associated to anything.
First off, everything in sbt is a transformation, in this case (:=) overrides any previous setting of printHi to the definition you give (println(s"hi ${name.value}")). But by not associating that expression (which is a Setting[Task[Unit]]) to anything (for instance to a project, or as a value that then gets attached to a project) it just gets evaluated in the construction of the MyTask object and then thrown away.
One way to do this is to put that setting (printHi := println(s"hi ${name.value}")), in a Seq[Setting[_]] that you then pull into build.sbt:
project/MyTask.scala
import sbt._, Keys._
object MyTask {
val printHi = taskKey[Unit]("prints Hi")
val myTaskSettings = Seq[Setting[_]](
printHi := println(s"hi ${name.value}")
)
}
build.sbt
import MyTask._
myTaskSettings
Another way is to define MyTask to be a mini plugin that lives in project/. You can see an example of this in PgpCommonSettings.