Sbt: call subproject's source code from root project

458 views Asked by At

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.

1

There are 1 answers

6
Dmytro Mitin On BEST ANSWER

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.