I have an existing scala project using SBT which has several modules.
I'd like to start adding new modules in kotlin - I don't require the ability to add kotlin to existing modules (but it could be nice if possible)
I can create new dedicated modules for the new kotlin code if that is a necessity as long as it is possible for the existing scala code to call out to the newly added kotlin modules (visa versa is nice to have but can live without that "kotlin calling scala" if impossible to do that)
Is this feasible and practical thing to do? If possible, how would this be done?
.
├── build.sbt
............
├── Module1ScalaWithJava (EXISTING)
│ ├── src
│ │ ├── main
│ │ │ ├── java
│ │ │ ├── resources
│ │ │ └── scala
├── Module2ScalaOnly (EXISTING)
│ ├── src
│ │ ├── main
│ │ │ └── scala
│ │ └── test
│ │ └── scala
├── NewModuleKotlinOnly (I WANT THIS)
│ ├── src
│ │ ├── main
│ │ │ └── ???KOTLIN????
As mentioned in comments, you can add kotlin module using kotlin-plugin.
Add line to your project/plugins.sbt file (or create it):
addSbtPlugin("com.hanhuy.sbt" % "kotlin-plugin" % "2.0.0")
And you will be able to add kotlin modules to your sbt-project. I would advice you using only build.sbt file for adding modules. I will demonstrate you how to do that below.
I created simple multi-module project with scala and kotlin modules which are depends on each other.
Here my build.sbt:
name := "kotlin-scala"
version := "0.1"
scalaVersion := "2.13.4"
lazy val scalaFirst =
project
.in(file("scala-first"))
lazy val kotlinFirst =
project
.in(file("kotlin-first"))
.settings(
libraryDependencies ++= Seq(
"org.junit.jupiter" % "junit-jupiter-api" % "5.7.0"
).map(_ % Test)
)
lazy val scalaSecond =
project
.in(file("scala-second"))
.dependsOn(kotlinFirst % "compile->compile;test->test")
.settings(
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.2.3"
).map(_ % Test)
)
lazy val kotlinSecond =
project
.in(file("kotlin-second"))
.dependsOn(scalaFirst % "compile->compile;test->test")
.settings(
libraryDependencies ++= Seq(
"org.junit.jupiter" % "junit-jupiter-api" % "5.7.0"
).map(_ % Test)
)
lazy val kotlinScalaSubmodule =
project
.in(file("kotlin-scala-submodule"))
.dependsOn(kotlinFirst % "compile->compile;test->test")
.dependsOn(scalaFirst % "compile->compile;test->test")
.settings(
libraryDependencies ++= Seq(
"org.junit.jupiter" % "junit-jupiter-api" % "5.7.0"
).map(_ % Test)
)
lazy val scalaKotlinSubmodule =
project
.in(file("scala-kotlin-submodule"))
.dependsOn(scalaFirst % "compile->compile;test->test")
.dependsOn(kotlinFirst % "compile->compile;test->test")
.settings(
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.2.3"
).map(_ % Test)
)
build.properties contains:
sbt.version = 1.3.2
My project structure:
Here I have some dependencies between modules:
+---------------+ +--------------+
| kotlin-first | | scala-first |
+---------------+ +--------------+
^ \ / ^
| \/ |
+--------------+ || +---------------+
| scala-second | || | kotlin-second |
+--------------+ || +---------------+
||
/ \
+------------------+ +------------------+
| scala-kotlin-sub | | kotlin-scala-sub |
+------------------+ +------------------+
full project you can find on github
Also I write just some unit-tests to demonstrate that it works properly.
Tested on:
java open-jdk 1.8,
scala version 2.13.4.
sbt version 1.3.2
Intellij IDEA build 2020.2.3 with jetBrains scala plugin works properly with this project.
Related
Im trying to implement the simple server-client application using scalaPB's official example. The scala code is found on their gitHub
However, when I try to run it, I keep getting the error object helloworld is not a member of package io.grpc.examples.helloworld when I try to import anything using import io.grpc.examples.helloworld.helloworld.{foo}.
My build.sbt file:
name := "Distributed sorting"
version := "0.1"
scalaVersion := "2.13.7"
libraryDependencies ++= Seq(
"io.grpc" % "grpc-netty" % scalapb.compiler.Version.grpcJavaVersion,
"com.thesamet.scalapb" %% "scalapb-runtime-grpc" % scalapb.compiler.Version.scalapbVersion
)
Compile / PB.targets := Seq(
scalapb.gen() -> (Compile / sourceManaged).value / "scalapb"
)
My files look like this:
├── build.sbt
├── project
│ ├── build.properties
│ ├── scalapb.sbt
│ └── target
├── ...
└── src/main
├── protobuf
├── hello.proto
└── scala/io/grpc/examples/helloworld
├── HelloWorldClient.scala
└── HelloWorldServer.scala
Firstly, I recommend using Akka gRPC rather than using ScalaPB directly. The documentation is pretty clear and there is a giter8 config that can be used to create a sample project using sbt new.
Secondly, the code in that gitHub does not look like official sample code. It says it has been "translated from grpc java" which is probably not what you want.
Finally, on the specific issue you are seeing, the stubs generated by scalaPB are in a package whose name is given in the proto file. The example file has
package com.example.protos
So the stubs will be in com.example.protos.Greeter.
I'm trying to define a multi-project build with a consequent number of subprojects:
.
├── build.sbt
├── project/
│ ├── dependencies.scala
│ ├── tasks.scala
│ └── settings.scala
├── lib_1/
│ └── src/
├── ...
└── lib_n/
└── src/
Those subprojects are currently defined in build.sbt:
val outputJarFolder = "/some/path/"
lazy val comonSettings = /* ... */
lazy val lib_1 = (project in file ("lib1")).settings(
name:="LibOne",
commonSettings,
libraryDependencies ++= Seq(scalaTest, jsonLib, scalaXML, commonsIo),
Compile/packageBin/artifactPath := file(outputJarFolder + "lib1.jar")
)
// ... more libs ...
lazy val lib_n = (project in file ("libn")).settings(
name:="LibLast",
commonSettings,
Compile/packageBin/artifactPath := file(outputJarFolder + "libn.jar")
)
.depensOn(lib_2, lib_12)
How can I define those subprojects in another file than build.sbt in order to "unclog" that file? I want to still be able to define them in their lexicographic order (so lazy is a must). I'm working with sbt version 1.2.8 and scala 2.10.
I've tried:
Putting the declaration of those lib_k variables in a scala file and importing it --> sbt says: "classes cannot be lazy".
Putting those declaration in an object (or in a class and instantiate it in build.sbt) --> sbt projects doesn't list any subproject.
sbt documentation mentions it, but doesn't emphasize too much (perhaps to avoid encouragement for too much variation in how builds are defined in the absence of a common convention):
The build definition is described in build.sbt (actually any files named *.sbt) in the project’s base directory.
So you can split your build.sbt file into several separate .sbt files in the root of the project with different names.
I also recommend reading documentation on Organizing the build.
I am learning Scala with this coursera course task here that provides SBT file. I download its objsets.zip here. Then I unzip it end and enter into it and type sbt and then console. I try to load the file src/main/scala/objsets/TweetSet.scala on commandline but I am getting a lot of errors.
scala> :load src/main/scala/objsets/TweetSet.scala
Loading src/main/scala/objsets/TweetSet.scala...
<console>:1: error: illegal start of definition
package objsets
^
<console>:10: error: not found: value TweetReader
import TweetReader._
^
import common._
defined class Tweet
<console>:2: error: illegal start of statement (no modifiers allowed here)
override def toString: String =
^
the course uses Eclipse Scala IDE but I would like to learn to use Vim for Scala development, my favorite editor. I find Eclipse hard to use. So
How can I load the scala files in the scala interpreter on the commandline under SBT? Does there exist some favourable tools for developing the scala project on a text editor such as Vim without having to leave the editor or commandline themselves?
SBT files and the directory looks like this
$ tree src/
src/
├── main
│ └── scala
│ ├── common
│ │ └── package.scala
│ └── objsets
│ ├── TweetData.scala
│ ├── TweetReader.scala
│ ├── TweetSet.scala
│ └── testing.sc
└── test
└── scala
└── objsets
└── TweetSetSuite.scala
7 directories, 6 files
$ cat build.sbt assignment.sbt
name := course.value + "-" + assignment.value
scalaVersion := "2.11.7"
scalacOptions ++= Seq("-deprecation")
// grading libraries
libraryDependencies += "junit" % "junit" % "4.10" % Test
// for funsets
libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.4"
// include the common dir
commonSourcePackages += "common"
courseId := "bRPXgjY9EeW6RApRXdjJPw"
course := "progfun1"
assignment := "objsets"
assignmentInfo := AssignmentInfo(
key = "6PTXvD99EeWAiCIAC7Pj9w",
itemId = "d1FGp",
premiumItemId = Some("Ogg05"),
partId = "7hlkb",
styleSheet = Some((_: File) / "scalastyle" / "scalastyle_config.xml")
)
:load copies the contents of a file into the REPL line by line. That means that you end up trying to define a package (which is not allowed in the REPL), and then you try to import things that aren't visible, etc. If you use :load on a file that has a format useable by the REPL, it will work. In most cases, this means replacing the package line(s) with imports.
There's no need to use :load anyway. sbt console will place you in a REPL that has the project on its classpath. sbt consoleQuick will place you in a REPL that only has the dependencies on the classpath.
For your second question, you are meant to use sbt as a background process. In your terminal emulator, you'll have one tab running vim on your files, and in the other tab, you'll have sbt. In the tab with sbt, you can run ~compile, which recompiles your code every time you save a file in Vim. This replicates how IDEs show compiler errors/warnings as you type.
How can I accumulate all the discoveredMainClasses of a project, along with its dependent sub projects in SBT?
For example, I have a project that looks like
├── bar
│ └── src
│ └── main
│ └── scala
│ └── BarMain.scala
├── build.sbt
├── foo
│ └── src
│ └── main
│ └── scala
│ └── FooMain.scala
├── project
│ └── build.properties
└── root
With one root project that aggregate(foo, bar), I get the following for discoveredMainClasses:
[info] foo/compile:discoveredMainClasses
[info] List(MainFoo)
[info] bar/compile:discoveredMainClasses
[info] List(MainBar)
[info] root/compile:discoveredMainClasses
[info] List()
With one root that only dependsOn(foo, bar) I get
> show discoveredMainClasses
[info] *
How can I have show root/discoveredMainClasses contain both MainFoo and MainBar?
For context, I have other tasks that depend on the output from discoveredMainClasses namely the makeBashScripts in native-packager
The core idea is to create a module that depends on all all the sub modules you want to include and configure all settings on this module.
This results in a build.sbt like this
lazy val root = project.in(file("."))
// package the root module, but not the sub modules
.enablePlugins(JavaAppPackaging)
.settings(
name := "application",
// add the discoveredMainClasses to this project
discoveredMainClasses in Compile ++= (discoveredMainClasses in (client, Compile)).value,
discoveredMainClasses in Compile ++= (discoveredMainClasses in (server, Compile)).value
)
// include these modules in the resulting package
.dependsOn(client, server)
lazy val client = project.in(file("client"))
.settings(
name := "client"
)
lazy val server = project.in(file("server"))
.settings(
name := "server"
)
The (discoveredMainClasses in (client, Compile)).value accesses the discoveredMainClasses from the client project in the Compile scope.
You can build and run your applications with
$ sbt universal:stage
$ ./target/universal/stage/bin/client-app
$ ./target/universal/stage/bin/server-app
A running example can be found here.
cheers,
Muki
An alternative way to #Muki's answer would be to define a ScopeFilter that includes everything but root and accumulate main classes that way. This has the advantage of not having to repeat client, server everywhere.
The resulting build.sbt:
lazy val allCompileButRootFilter =
ScopeFilter(inAggregates(ThisProject, includeRoot = false), inConfigurations(Compile))
lazy val root = project.in(file("."))
.aggregate(client, server)
.enablePlugins(JavaAppPackaging)
.settings(
discoveredMainClasses in Compile ++=
discoveredMainClasses.all(allCompileButRootFilter).value.flatten,
...
)
I just want to create a directory layout for my scala project with sbt and sbteclipse. Following is my sbt file.
import com.typesafe.sbteclipse.plugin.EclipsePlugin.EclipseKeys
name := "BGS"
organization := "com.example"
version := "1.0.0"
scalaVersion := "2.9.2"
scalacOptions ++= Seq("-deprecation")
EclipseKeys.createSrc := EclipseCreateSrc.Default + EclipseCreateSrc.Resource
EclipseKeys.projectFlavor := EclipseProjectFlavor.Scala
scalaSource in Compile <<= (sourceDirectory in Compile)(_ / "scala")
scalaSource in Test <<= (sourceDirectory in Test)(_ / "scala")
libraryDependencies += "org.scalatest" %% "scalatest" % "1.8" % "test"
libraryDependencies += "junit" % "junit" % "4.10" % "test"
unmanagedBase <<= baseDirectory { base => base / "lib" }
unmanagedJars in Compile <<= baseDirectory map { base => (base ** "*.jar").classpath }
In this sbt file, I had to use folling to lines to force creation of Scala directories:
scalaSource in Compile <<= (sourceDirectory in Compile)(_ / "scala")
scalaSource in Test <<= (sourceDirectory in Test)(_ / "scala")
Furthermore, after running "eclipse" from sbt console, I imported the project to Eclipse, but I could not create Scala class. Eclipse project icon has "J" letter attached to it indicating it is a Java project :-?
Why does sbt and sbteclipse default to Java?
I am running sbt version 0.12 (latest version as of Nov 2012), scala 2.9.2
For your information, what I am aiming to do is use sbt to create working project with following directory structure:
├── build.sbt
├── lib
│ ├── biojava3-core-3.0.4.jar
├── project
│ ├── plugins.sbt
│ ├── project
│ │ └── target
│ └── target
│ ├── config-classes
│ ├── scala-2.9.2
│ └── streams
├── src
│ ├── main
│ │ ├── java
│ │ ├── resources
│ │ └── scala
│ └── test
│ ├── java
│ ├── resources
│ └── scala
├── target
│ ├── scala-2.9.2
│ │ ├── cache
│ │ ├── classes
│ │ └── test-classes
│ └── streams
│ └── compile
└── test
Since I haven't got any desirable answers so far, I am trending into Typesafe Stack. I removed manually-installed scala and sbt and I am using everything from Typesafe Stack. I will update more when I am done testing with this way.
Here is the final update as I promised:
I followed instruction in this link and it worked perfectly:
http://typesafe.com/resources/typesafe-stack/downloading-installing.html
Detailed information in case somebody want to follow for Mac OSX:
Basically, I first I switched macport to homebrew following instruction here:
http://bitboxer.de/2010/06/03/moving-from-macports-to-homebrew/
Then I did:
brew install scala sbt maven giter8
Then create a Scala project from command line:
g8 typesafehub/scala-sbt
Finally I followed instruction here to add sbteclipse and convert the project to eclipse:
https://github.com/typesafehub/sbteclipse
Everything works as I expect.
Why do you want sbt to create those directors? why not jsut create them yourself. It's a one time event.
You shouldnt need any of the following lines
EclipseKeys.createSrc := EclipseCreateSrc.Default + EclipseCreateSrc.Resource
EclipseKeys.projectFlavor := EclipseProjectFlavor.Scala
scalaSource in Compile <<= (sourceDirectory in Compile)(_ / "scala")
scalaSource in Test <<= (sourceDirectory in Test)(_ / "scala")
I normally just do mkdir -p src/main/scala whenever I start a new project.