SBT build, run main class from subproject on Compile and run
Asked Answered
H

2

11

I have a simple build tool Multi-Project problem...

I have the following directory structure represents my java sbt projects:

/project1
/project2
/project3

So all projects share a common direct parent folder. Projects 2 and 3 are referenced in project 1's build.sbt like this:

.dependsOn(project2, project3)
.aggregate(project2, project3)

lazy val project2 = ProjectRef(file("../project2"), "project2")

lazy val project3 = ProjectRef(file("../project3"), "project3")

This way there's a dependency between project1 and the others.

All is fine to this point and everything works as it should.

But now I want to execute the main method from project2 before anything else is executed. When I execute the "run" task from the parent (project1), I want a specific class from project2 to have its main method executed. How do I do this? The sbt documentation explains that "Aggregation means that running a task on the aggregate project will also run it on the aggregated projects.": http://www.scala-sbt.org/0.13.5/docs/Getting-Started/Multi-Project.html#aggregation

I'm not seeing my main class on projet2 been executed. I also added this to project2's build.sbt:

mainClass in (Compile, run) := Some("Main")

The goal of the projet is to generate code at Compiletime and runtime. Project2's job is to generate Java and Javascript code. The could should be generated before the other projects are built.

Is that possible? If not, I'll have to settle for running project2 independently from the other projects.

=]

Hygrograph answered 24/8, 2014 at 21:59 Comment(3)
Surprising that you haven't gotten any suggestions to this. Do you remember, were you able to solve it, somehow?Napkin
https://mcmap.net/q/946740/-sbt-run-task-on-subproject seems to have the solution: run in Compile <<= (run in Compile in project2)Napkin
@Misael Neto were you able to do this? If yes how?Lanark
M
6

if i have a structure as indicated below:

+ root +--- backend +--- frontend and a build.sbt project similar as indicated in http://www.scala-sbt.org/0.13/docs/Multi-Project.html , lets say:

lazy val commonSettings = Seq(
  version := "0.1.0-SNAPSHOT",
  scalaVersion := "2.12.1",
  resolvers := allResolvers,
  libraryDependencies := AllLibraryDependencies
)

lazy val client = (project in file("client")).
  //  .enablePlugins(PlayScala)
  settings(commonSettings: _*).
  settings(
    name := "client"
  )
  .aggregate(common, frontend, backend)
  .dependsOn(common, frontend, backend)


lazy val common = (project in file("common")).
  settings(commonSettings: _*).
  settings(
    name := "common"
  )

lazy val frontend = (project in file("frontend")).
  settings(commonSettings: _*).
  settings(
    name := "frontend"
  )
  .aggregate(common)
  .dependsOn(common)

lazy val backend = (project in file("backend")).
  settings(commonSettings: _*).
  settings(
    name := "backend"
  )
  .aggregate(common)
  .dependsOn(common)

`

Then to execute i.e a class inside frontend project, this command has worked for me:

sbt "frontend/runMain sample.cluster.transformation.frontend.TransformationFrontendApp 2551"

Malefactor answered 18/5, 2017 at 15:26 Comment(0)
V
4

By default run / aggregate is false:

sbt:root> inspect run / aggregate
[info] Setting: Boolean = false
[info] Description:
[info]  Configures task aggregation.
[info] Provided by:
[info]  Zero / run / aggregate
[info] Defined at:
[info]  (sbt.Defaults.disableAggregate) Defaults.scala:1920
[info] Delegates:
[info]  run / aggregate
[info]  aggregate
[info]  ThisBuild / run / aggregate
[info]  ThisBuild / aggregate
[info]  Zero / run / aggregate
[info]  Global / aggregate
[info] Related:
[info]  Global / aggregate
[info]  Zero / run / aggregate
[info]  Zero / consoleQuick / aggregate
[info]  dependencyCheckPurge / aggregate
[info]  refinedConsole / dependencyCheckUpdateOnly / aggregate
[info]  refinedConsole / dependencyCheckAggregate / aggregate
[info]  server / dependencyCheckListSettings / aggregate
[info]  Zero / changedInputFiles / aggregate
[info]  refinedConsole / dependencyCheckListSettings / aggregate
[info]  refinedConsole / dependencyCheckPurge / aggregate
[info] ...

So the run task will not be aggregated.

Even if you set that to true then you are unlikely to get what you want since the order of aggregated tasks is undefined.

Creating task dependencies is done by evaluating one from another (as @akauppi pointed out in old SBT syntax):

lazy val project1 = (project in file("project1")
  .settings(
    Compile / run := (project2 / Compile / run).evaluated
  )

Now it gets a bit more tricky if you also want to invoke run on project1.

A naive approach would be:

lazy val project1 = (project in file("project1")
  .settings(
    Compile / run := {
      (project2 / Compile / run).evaluated
      (project1 / Compile / run).evaluated
    }
  )

But there is no guarantee that this will run project2 first.

I haven't found a way to get it to work.

I tried using:

(project2 / Compile / run).flatMap((project1 / Compile / run).toTask("").taskValue

But you get an illegal dynamic reference error.

I also tried:

Def.sequential((server / Compile / run).toTask(""), (Compile / run).toTask("")).value

But that just gives an undefined setting error at runtime.

🤷‍♂️

Varix answered 12/12, 2019 at 22:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.