1

So my project structure looks like this: My root project contains the settings, tasks and configs. In addition is has a subprojects folder which contains all subprojects. I have created a task on the root project that depends on some code in a subproject X. Is it possible to call a method from subproject x inside that task definition?

My code looks like this :

lazy val rootSettings: Seq[Setting[_]] = Seq (someRootTask :=  { //I need to call an object from a subproject here..}) 

I tried to use the reflection api with no success:

import scala.reflect.runtime.{universe => u }
lazy val docSettings: Seq[Setting[_]] = Seq(
  rootTask := {
      val subproject = baseDirectory.in(playApp).value.getAbsolutePath
      val mirror = u.runtimeMirror(getClass.getClassLoader)
      val clazz = mirror.staticModule(subproject+"/" +"controllers.pckg.obj" ) 

      val cm = mirror.reflectModule(clazz)
      val instanceMirror = mirror.reflect(cm.instance)
      val methodName ="sayHi" 

      val methodSymbol = u.typeOf[instanceMirror.type].declaration(u.newTermName(methodName)).asMethod
      val method = instanceMirror.reflectMethod(methodSymbol)

      method.apply()
  }
)
// still can't point to the object i want to call. 

The code above throws an error. It can't find the object, i know its path but i can't reference to it as package.class from the root project.

Tomer Shetah
  • 8,413
  • 7
  • 27
  • 35
Helix112
  • 305
  • 3
  • 12
  • @Dmytro Mitin, thanks for the prompt reply, no it doesn't help because am defining the task in a . Scala file. so even if I add to the subproject's object to the unmanaged source dir in plugins. Sbt. the compiler throws a syntax error where I try to call thr object it can't reslove it. I can't even import it in the first place – Helix112 Dec 02 '20 at 08:27
  • Does not work with reflection neither. – Helix112 Dec 02 '20 at 08:56
  • 1
    You don't need reflection. – Dmytro Mitin Dec 02 '20 at 19:07

1 Answers1

1

Reference scala file from build.sbt

If I understood correctly your project looks like

root
  project
    build.properties
    build.sbt
  subproject1
    src
      main
        scala
          com.example.package1
            App.scala
  subproject2
    src
      main
        scala
          com.example.package2
  build.sbt

In project/build.sbt I can write

Compile / unmanagedSourceDirectories += baseDirectory.value / ".." / "subproject1" / "src" / "main" / "scala"

Suppose subproject1/src/main/scala/com/example/package1/App.scala is

package com.example.package1

object App {
  def foo(): Unit = println("foo")
}

Then in root build.sbt I can call foo

name := "sbtdemo"

version := "0.1"

ThisBuild / scalaVersion := "2.13.4"

lazy val sampleUnitTask = taskKey[Unit]("A sample unit task.")

lazy val rootSettings: Seq[Setting[_]] = Seq(
  sampleUnitTask := {
    com.example.package1.App.foo()
  }
)

lazy val root = project
  .in(file("."))
  .dependsOn(subproject1, subproject2)
  .settings(rootSettings)

lazy val subproject1 = project
  .in(file("subproject1"))

lazy val subproject2 = project
  .in(file("subproject2"))

If in sbt shell I run root/sampleUnitTask it prints foo.


I created Play project with sbt new playframework/play-scala-seed.g8. Everything seems to work. I added project/build.sbt and subproject1/src/main/scala/com/example/package1/App.scala as above. Then with the following root build.sbt

name := """playframeworkdemo"""
organization := "com.example"

version := "1.0-SNAPSHOT"

lazy val sampleUnitTask = taskKey[Unit]("A sample unit task.")

lazy val rootSettings: Seq[Setting[_]] = Seq(
  sampleUnitTask := {
    com.example.package1.App.foo()
  }
)

lazy val root = (project in file(".")).enablePlugins(PlayScala)
  .dependsOn(subproject1, subproject2)
  .settings(rootSettings)

ThisBuild / scalaVersion := "2.13.3"

libraryDependencies += guice
libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test

// Adds additional packages into Twirl
//TwirlKeys.templateImports += "com.example.controllers._"

// Adds additional packages into conf/routes
// play.sbt.routes.RoutesKeys.routesImport += "com.example.binders._"

lazy val subproject1 = project
  .in(file("subproject1"))

lazy val subproject2 = project
  .in(file("subproject2"))

root/sampleUnitTask executed in sbt shell prints foo.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • I have two questions : 1/ my subproject is a play app, I know that play overrides sbt default settings, so that sbt searches in app/ folder and not src/main/ Do I point to subproject/app/ instead of subproject/src/main in unmanagedSourceDirs ? 2/ Am not really using a .sbt file but Build.scala file. So if I want to call com.package dir there directly it cannot resolve the package name it needs to be imported somehow,or do I ignore that error at compile time? – Helix112 Dec 02 '20 at 20:43
  • I can't compile the project cause the package i want to reference in the task definition is unresolved – Helix112 Dec 02 '20 at 22:03
  • @Helix112 Regarding your question 1. For a directory like `src/main/scala` or `app` [responsible](https://www.scala-sbt.org/1.x/docs/Howto-Customizing-Paths.html) is parameter `scalaSource`. If in sbt shell you run `scalaSource` it prints `.../app` but `subproject1/scalaSource` prints `.../subproject1/src/main/scala`. So Play changes `scalaSource` for `root` project but not for subprojects you created if didn't change `scalaSource` for them. – Dmytro Mitin Dec 03 '20 at 01:01
  • @Helix112 So in `project/build.sbt` you should keep `Compile / unmanagedSourceDirectories += baseDirectory.value / ".." / "subproject1" / "src" / "main" / "scala"` if you didn't change `scalaSource` for `subproject1`. – Dmytro Mitin Dec 03 '20 at 01:02
  • @Helix112 Regarding your question 2 with `Build.sbt` or that something doesn't compile please prepare reproducible example (e.g. at Github). It's hard to debug blindly. – Dmytro Mitin Dec 03 '20 at 01:11
  • 1
    thanks for the informative answer. What i meant is that the subproject itself is a play app and not the root project. Anyway i've referenced to it as .../subproject1/app. Now after adding that path to unmanaged src Dir sbt throws a new error inside the subproject classes : object x is not a member of package com. Example. I'll try again later and come back to you – Helix112 Dec 03 '20 at 14:37