Migrating from Build.scala to build.sbt - override def rootProject

441 views
Skip to first unread message

Ben McCann

unread,
Mar 10, 2014, 6:30:54 PM3/10/14
to sbt...@googlegroups.com

Apologies for posting to the developers mailing list, but this seems to be a question that only the experts know the answer to.

In my Build.scala, I have:

override def rootProject = Some(frontendProject)

I'm trying to convert to the newer build.sbt format, but don't know the equivalent of this line. How do I set the project for sbt to load by default when using build.sbt?

Thanks,

-Ben


Josh Suereth

unread,
Mar 10, 2014, 6:58:39 PM3/10/14
to sbt...@googlegroups.com
The root project is the project in the root directory.  You can specify this in build.sbt via:

val root = project.in(file("."))


Hmm, it seems we neglected a migration guide for 0.12 -> 0.13.  I'll see if I can eek one out in the next few days of 0.13.2-RC1, before the rush of new bugs/tasks overwhelms me.


--
You received this message because you are subscribed to the Google Groups "sbt-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/a77cbf42-ee49-468e-b747-b15085ece376%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Josh Suereth

unread,
Mar 10, 2014, 6:59:17 PM3/10/14
to sbt...@googlegroups.com
I should clarify:

A root project is *only* provided for you if you haven't specified a project to be in the root directory.

In Build.scala the rules are more complicated.

Ben McCann

unread,
Mar 15, 2014, 9:23:53 PM3/15/14
to sbt...@googlegroups.com
Thanks Josh. I'm not sure that's what I was referring to though. In Build.scala there was a way to specify the project that's loaded when you start SBT, which was "override def rootProject". I think I figured out in build.sbt, but it's drastically more complicated:

onLoad in Global := { Command.process("project root", _: State) } compose (onLoad in Global).value

Josh Suereth

unread,
Mar 26, 2014, 2:15:57 PM3/26/14
to sbt...@googlegroups.com
On Sat, Mar 15, 2014 at 9:23 PM, Ben McCann <benjamin...@gmail.com> wrote:
Thanks Josh. I'm not sure that's what I was referring to though. In Build.scala there was a way to specify the project that's loaded when you start SBT, which was "override def rootProject". I think I figured out in build.sbt, but it's drastically more complicated:

onLoad in Global := { Command.process("project root", _: State) } compose (onLoad in Global).value



A) you don't really want to have multiple directories with a "baseDirectory" that are the same, therefore there should be just one in file(".").
B) If there *are* multiple projects in the same directory, I believe sbt picks the one with the "earliest" name (where early => alphabetically).
C) You can also do `onLoad in Global := { "project root" :: (_: State) } compose (onLoad in Global).value.

In either case, feel free to open an enhancement ticket about this if it's causing signfiicant issues.  We can probably create a setting the `reload` command will read when setting the initial project, rather than always using "root".

Aleh Aleshka

unread,
Mar 27, 2014, 8:45:47 AM3/27/14
to sbt...@googlegroups.com
Ben, what's the reason for this migration?
Thanks, Aleh

Jacek Laskowski

unread,
Mar 29, 2014, 6:04:10 PM3/29/14
to sbt...@googlegroups.com
Hi,

I stepped forward and reported the enhancement as https://github.com/sbt/sbt/issues/1224

Jacek

Justin du coeur

unread,
Sep 1, 2014, 12:31:11 PM9/1/14
to sbt...@googlegroups.com
Pardon a followup from a lurker, but this thread is the closest I've found in Googling around for a solution.  I've got a case where I'm trying to set the rootProject to something *other* than ".".

Suffice it to say, I'm working on setting up a scala.js project, which is complex because you have multiple compiler chains involved, with one directory for compile-to-JVM, one for compile-to-JS and one for shared code, all of this driven by Play.  The example I'm following uses Build.scala to say:

override def rootProject = Some(scalajvm)

Where the "scalajvm" Project is in the subdirectory named "scalajvm".  That's the main Play driver, and it in turn depends on the parallel scalajs project to generate the Javascript output files.  The root directory "." is really just a common folder holding the overall build.sbt; the scalajvm project is where the action is.

(I also tried to aggregate scalajvm into a root project at the top, but that's failing -- it looks like the Play plugin doesn't cope happily with being rooted in a subproject.  A naive attempt fails because of not finding the mainClass; fixing that causes further errors.)

I'm trying to build a purely build.sbt version of this example (which is entirely written in Build.scala), since the consensus seems to be that that's the preferred approach nowadays, and the above line is the sticking point.  I've confirmed that the Command.process() workaround works, but it looks a bit suboptimal -- a subshell feels like a bit of a hack.  Is there really no way to override rootProject from a .sbt file?  (Or any better way to declare that the root project is in a subdirectory?)

I suspect this situation is going to become more common as scala.js gains steam.  I'm hoping we can come up with a best-practice recommendation for how to structure one's build.sbt for scala.js with Play, since it is a pretty obvious combination...

Justin du coeur

unread,
Sep 1, 2014, 1:41:39 PM9/1/14
to sbt...@googlegroups.com
Following up my previous post, as I continue to experiment -- the best I've been able to find is a hybrid Build.scala/build.sbt, and it illustrates the heart of the problem, which is forward references.  Far as I can tell (please tell me if I'm wrong), the Build.scala can't refer to the scalajvm project declared in build.sbt, which is why I've been having trouble declaring the rootProject without winding up dragging much of my build.sbt into Build.scala.  I've hacked around that with what amounts to an interface through a variable.  In Build.scala I say:

  var scalajvmProject:Option[Project] = None

  override def rootProject = { scalajvmProject }

and in build.sbt I have:

  val dummy = { scalajvmProject = Some(scalajvm); true }

That doesn't thrill me (especially having to fake out the system with that dummy val), but it avoids the subshell and seems to get things working.  It's certainly inelegant, though -- does anyone have a better suggestion?  I tried doing this through a Setting[Option[Project]], but since rootProject is not itself a Setting I found it unobvious how to dereference that...

Josh Suereth

unread,
Sep 2, 2014, 8:32:49 AM9/2/14
to sbt...@googlegroups.com
On Mon, Sep 1, 2014 at 12:31 PM, Justin du coeur <jduc...@gmail.com> wrote:
Pardon a followup from a lurker, but this thread is the closest I've found in Googling around for a solution.  I've got a case where I'm trying to set the rootProject to something *other* than ".".

Suffice it to say, I'm working on setting up a scala.js project, which is complex because you have multiple compiler chains involved, with one directory for compile-to-JVM, one for compile-to-JS and one for shared code, all of this driven by Play.  The example I'm following uses Build.scala to say:

override def rootProject = Some(scalajvm)

Where the "scalajvm" Project is in the subdirectory named "scalajvm".  That's the main Play driver, and it in turn depends on the parallel scalajs project to generate the Javascript output files.  The root directory "." is really just a common folder holding the overall build.sbt; the scalajvm project is where the action is.

(I also tried to aggregate scalajvm into a root project at the top, but that's failing -- it looks like the Play plugin doesn't cope happily with being rooted in a subproject.  A naive attempt fails because of not finding the mainClass; fixing that causes further errors.)

I'm trying to build a purely build.sbt version of this example (which is entirely written in Build.scala), since the consensus seems to be that that's the preferred approach nowadays, and the above line is the sticking point.  I've confirmed that the Command.process() workaround works, but it looks a bit suboptimal -- a subshell feels like a bit of a hack.  Is there really no way to override rootProject from a .sbt file?  (Or any better way to declare that the root project is in a subdirectory?)

I suspect this situation is going to become more common as scala.js gains steam.  I'm hoping we can come up with a best-practice recommendation for how to structure one's build.sbt for scala.js with Play, since it is a pretty obvious combination...




One recommendation I have is:   If you're using a multi-project build, leave the root project as a "dummy" and have more than one sub-project, one for play and one for the scala-js project.

The reason for this is around aggregation: IF the root project is also a legitimate project, then trying to run `compile`/`test` on JUST that project is impossible.

I.e. if you use two subprojects, you can have:

scalajs-project/compile   # Build just one project.

*or*

compile  # Aggregates


Otherwise, specifying which project is root in build.sbt does not exist yet.  Any chance we can see more of the build.sbt/build.scala used for Scala.js?  Perhaps we can work on a canonical setup/example.

Justin du coeur

unread,
Sep 2, 2014, 9:01:01 AM9/2/14
to sbt...@googlegroups.com
On Tuesday, September 2, 2014 8:32:49 AM UTC-4, Josh Suereth wrote:
Otherwise, specifying which project is root in build.sbt does not exist yet.  Any chance we can see more of the build.sbt/build.scala used for Scala.js?  Perhaps we can work on a canonical setup/example.

Well, the example I am starting from (which is a skeleton example of combining play and scala.js) can be found here:

https://github.com/vmunier/play-with-scalajs-example

I actually did try to set up a dummy root project in my experiments, but hit all sorts of problems with the PlayScala plugin -- trying to enable that on an aggregated subproject hit various roadblocks.  That said, I'm an sbt novice (I've done lots of work in the Typesafe stack, but always with relatively vanilla projects), so it's quite possible that I was missing something obvious...

Josh Suereth

unread,
Sep 3, 2014, 11:17:30 AM9/3/14
to sbt...@googlegroups.com
Well, this is  kind of a big issue: https://github.com/vmunier/play-with-scalajs-example/blob/master/project/Build.scala#L75-L86

Commands are relative to projects, and this hack is quite epic.   We may want to reach out between scalajs / play / sbt communities to ensure the right hooks are inplace to avoid hacks like the above, which I'm positive was causing you issues.


--
You received this message because you are subscribed to the Google Groups "sbt-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.

Justin du coeur

unread,
Sep 3, 2014, 3:12:55 PM9/3/14
to sbt...@googlegroups.com
On Wednesday, September 3, 2014 11:17:30 AM UTC-4, Josh Suereth wrote:
Well, this is  kind of a big issue: https://github.com/vmunier/play-with-scalajs-example/blob/master/project/Build.scala#L75-L86

Commands are relative to projects, and this hack is quite epic.   We may want to reach out between scalajs / play / sbt communities to ensure the right hooks are inplace to avoid hacks like the above, which I'm positive was causing you issues.

Could well be.  For now, I'm following this model closely -- I eventually hit enough roadblocks in trying to get build.sbt working that I fell back to Vincent's Build.scala version, which is at least functional.  (Hacked though it is, it's the best I've found so far.  AFAIK, the quoted lines are trying to make the super-optimizer run during "start", but I don't use that particular command myself: I'm using either "run" or "stage", which are a bit more straightforward.)

As scala.js gains steam, I suspect this configuration is going to be increasingly common, so it may be worth ironing this stuff out.  I'm a tad peripheral here -- just an application developer -- but I'm happy to help if I can...

Josh Suereth

unread,
Sep 3, 2014, 3:48:59 PM9/3/14
to sbt...@googlegroups.com
Yeah, bringing this to our attention is highly appreciated. I just reached out to the Scala.js folk to see where we have the communicatoin.  I'll be meeting with a few shortly, so we're going to see what we can do to fix this.

Anything you learn in the meantime, this thread appears to be the best place until there is a scala.js ML.


--
You received this message because you are subscribed to the Google Groups "sbt-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.

Justin du coeur

unread,
Sep 3, 2014, 5:06:11 PM9/3/14
to sbt...@googlegroups.com
On Wednesday, September 3, 2014 3:48:59 PM UTC-4, Josh Suereth wrote:
Anything you learn in the meantime, this thread appears to be the best place until there is a scala.js ML.

Actually, there's a pretty active scala.js list: https://groups.google.com/forum/#!forum/scala-js  We've actually been discussing tweaks to this particular project today, although more focused on the scala.js content itself rather than the sbt side...

Josh Suereth

unread,
Sep 4, 2014, 2:22:45 PM9/4/14
to sbt...@googlegroups.com
Thanks for the link.  I just joined the ML.   We'll have to look into this sbt-portion of the project and see what we can do.


--
You received this message because you are subscribed to the Google Groups "sbt-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.

Ben McCann

unread,
Nov 20, 2014, 7:19:10 PM11/20/14
to sbt...@googlegroups.com
I just ran into a problem with the solution for this that I was using. Here's my build file:

lazy val data = project

lazy val frontend = (project in file("frontend"))
    .enablePlugins(PlayJava, SbtWeb)
    .dependsOn(data).aggregate(data)

// Switch to the frontend project on startup
onLoad in Global := { Command.process("project frontend", _: State) } compose (onLoad in Global).value


Now I have another project in which I'm trying to reference the data project with a ProjectRef:

lazy val dataProject = ProjectRef(file("../connectifier"), "data")


This works great except for the line in the original project, which sets the current project on startup to "project frontend". It's trying to switch to the frontend project in my external project. Can I wrap the command in some kind of if-statement or something so that it's only executed in my original project and not the new project?

Thanks,
Ben
Reply all
Reply to author
Forward
0 new messages