diff --git a/README.md b/README.md index f85d655..f0318d7 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,50 @@ -# plainsbt +# `sbt-nosbt` -`plainsbt` is a Scala plugin to make your complex, multi-module build definition maintainable like regular Scala code. +`sbt-nosbt` is an `sbt` plugin to make your complex, multi-module build definition more maintainable +by moving build definition from `.sbt` files to plain Scala files and providing a nice convention for +hierarchical organization of subprojects. ## Overview -`sbt` can be intimidating. This is mostly due to various layers of abstraction and "magic" that it uses. However, deep down, `sbt` build definition is ultimately just plain Scala code. This plugin aims to bring that plain Scala to the surface, removing at least some of the `sbt`'s magic. +`sbt` can be intimidating. This is mostly due to various layers of abstraction and "magic" that it uses. However, deep down, +`sbt` build definition is ultimately just plain Scala code. This plugin aims to bring that plain Scala to the surface, +removing at least some of the `sbt`'s magic. ### `.sbt` files -`sbt` requires your build to be defined in `.sbt` files, which are Scala-like files preprocessed in a special way. Most importantly, that preprocessing includes: +`sbt` requires your build to be defined in `.sbt` files, which are Scala-like files preprocessed in a special way. +Most importantly, that preprocessing includes: * automatic import of keys and other definitions from `sbt` core and plugins * extracting all project definitions by looking for all `lazy val`s (and `val`s) typed as `Project` -`.sbt` files may also refer to definitions in `project/*.scala` files, which are regular Scala files without any special treatment. While this allows you to move a lot of utility functions out of `.sbt` files, you are still forced to enumerate all your projects in `.sbt` files. Typically, this is a single `build.sbt` file. +`.sbt` files may also refer to definitions in `project/*.scala` files, which are regular Scala files without any special +treatment. While this allows you to move a lot of utility functions out of `.sbt` files, you are still forced to +enumerate all your projects in `.sbt` files. Typically, this is a single `build.sbt` file. ### Moving to plain Scala -`plainsbt` plugin allows you to move all your project definitions into plain Scala files. This removes all the special `.sbt` preprocessing and allows you to organize your build definition like regular Scala code by splitting it into multiple files that explicitly refer to each other. `plainsbt` also establishes a convention for project (and directory) hierarchy that makes it easier to define complex, multi-project builds. +The `nosbt` plugin allows you to move all your project definitions into plain Scala files. This removes all the +special `.sbt` preprocessing and allows you to organize your build definition like regular Scala code by splitting +it into multiple files that explicitly refer to each other. `sbt-nosbt` also establishes a convention for project +(and directory) hierarchy that makes it easier to define complex, multi-project builds. ## Usage example -The full example is available in an [example project repository](https://github.com/ghik/plainsbt-example) +The full example is available in an [example project repository](https://github.com/ghik/sbt-nosbt-example) ### Simple multi-project build -Add the `plainsbt` plugin to your `project/plugins.sbt`: +Add the `nosbt` plugin to your `project/plugins.sbt`: ```scala -addSbtPlugin("com.github.ghik" % "plainsbt" % "") +addSbtPlugin("com.github.ghik" % "sbt-nosbt" % "") ``` Now create a `project/MyProj.scala` file with definition of a `ProjectGroup`: ```scala -import com.github.ghik.plainsbt.ProjectGroup +import com.github.ghik.sbt.nosbt.ProjectGroup import sbt.Keys._ import sbt._ @@ -71,20 +81,29 @@ object MyProj extends ProjectGroup("myproj") { The above file is a complete definition of an `sbt` multi-project build, in plain Scala: -* The root project must be defined as `lazy val root` and implemented with `mkSubProject`. ID of this project will be the same as name of the `ProjectGroup`, i.e. `myproj`. Base directory of this project is the build root directory. -* All subprojects in the project group must be defined as `lazy val`s, just like you would do in an `.sbt` file. However, usage of `mkSubProject` makes sure that subprojects follow hierarchical naming and directory convention. - For example `lazy val api: Project = mkSubProject` will define a subproject with ID `myproj-api` and base directory `api/`. Note how this is different from the default `sbt` behaviour which would place the project in a directory corresponding directly to its ID (i.e. `myproj-api/`). +* The root project must be defined as `lazy val root` and implemented with `mkSubProject`. ID of this project will be + the same as name of the `ProjectGroup`, i.e. `myproj`. Base directory of this project is the build root directory. +* All subprojects in the project group must be defined as `lazy val`s, just like you would do in an `.sbt` file. + However, usage of `mkSubProject` makes sure that subprojects follow hierarchical naming and directory convention. + For example `lazy val api: Project = mkSubProject` will define a subproject with ID `myproj-api` and base directory + `api/`. Note how this is different from the default `sbt` behaviour which would place the project in a directory + corresponding directly to its ID (i.e. `myproj-api/`). * Settings shared by all the projects in your build can be defined by overriding `commonSettings`. - Note how this is **not** the same as defining settings in `Global` or `ThisBuild` scopes - `commonSettings` are applied **directly** on each and every project which is more reliable than `Global`/`ThisBuild` and generally more recommended. - There are also variations of `commonSettings`, e.g. `subprojectSettings`, `leafSubprojectSettings`, etc. which allow you to refine the exact set of projects that you want to apply settings on. Refer to `ProjectGroup`s API for details. + Note how this is **not** the same as defining settings in `Global` or `ThisBuild` scopes - `commonSettings` are + applied **directly** on each and every project which is more reliable than `Global`/`ThisBuild` and generally + more recommended. There are also variations of `commonSettings`, e.g. `subprojectSettings`, `leafSubprojectSettings`, + etc. which allow you to refine the exact set of projects that you want to apply settings on. Refer to `ProjectGroup`s + API for details. * Settings in `Global` scope can be set by overriding `globalSettings` * Settings in `ThisBuild` scope can be set by overriding `buildSettings`. -Because `MyProj.scala` is a regular Scala file, its contents may be split and reorganized as you wish, e.g. be extracting traits, subclasses, etc. into separate files. It becomes maitainable like plain Scala code. +Because `MyProj.scala` is a regular Scala file, its contents may be split and reorganized as you wish, e.g. be +extracting traits, subclasses, etc. into separate files. It becomes maitainable like plain Scala code. ### Bootstrapping -We also need to tell `sbt` that `MyProj.scala` is the entry point of the entire build definition. In order to do that, we need to create a minimal, "bootstrapping" `build.sbt` file: +We also need to tell `sbt` that `MyProj.scala` is the entry point of the entire build definition. In order to do that, +we need to create a minimal, "bootstrapping" `build.sbt` file: ```scala lazy val root = MyProj.root @@ -94,7 +113,8 @@ _et voila!_ ### Complex, multi-level hierarchies -Let's say your build is more complex. It is split into several "services", each one consisting of multiple subprojects. Let's say you want to achieve a project structure like this: +Let's say your build is more complex. It is split into several "services", each one consisting of multiple subprojects. +Let's say you want to achieve a project structure like this: ``` myproj @@ -127,7 +147,7 @@ myproj/ You can achieve this with the following set of definitions: ```scala -import com.github.ghik.plainsbt.ProjectGroup +import com.github.ghik.sbt.nosbt.ProjectGroup import sbt.Keys._ import sbt._ @@ -166,7 +186,8 @@ object BarService extends ProjectGroup("barservice", MyProj) { } ``` -Note how `Commons`, `FooService` and `BarService` declare `MyProj` as their _parent_ project group. The `MyProj` must also explicitly declare `lazy val`s referring to subgroups' root projects in order for `sbt` to see them. +Note how `Commons`, `FooService` and `BarService` declare `MyProj` as their _parent_ project group. The `MyProj` +must also explicitly declare `lazy val`s referring to subgroups' root projects in order for `sbt` to see them. Finally, the boostrapping `build.sbt` file: @@ -176,7 +197,10 @@ lazy val root = MyProj.root ## Caveats -* Settings defined in `Global` and `ThisBuild` scopes by overriding `globalSettings` and `buildSettings` have lower priority than if they would be defined directly in the `.sbt` file. This means they may get overwritten by settings from other `sbt` plugins in your build. If this is a problem, you can lift their priority back by referring to them explicitly in the `build.sbt` bootstrapping file: +* Settings defined in `Global` and `ThisBuild` scopes by overriding `globalSettings` and `buildSettings` have + lower priority than if they would be defined directly in the `.sbt` file. This means they may get overwritten by + settings from other `sbt` plugins in your build. If this is a problem, you can lift their priority back by + referring to them explicitly in the `build.sbt` bootstrapping file: ```scala inScope(Global)(MyProj.globalSettings) @@ -184,4 +208,5 @@ lazy val root = MyProj.root lazy val root = MyProj.root ``` - In order to avoid these problems altogether, prefer overriding `ProjectGroup.commonSettings` rather than using `ThisBuild`. + In order to avoid these problems altogether, prefer overriding `ProjectGroup.commonSettings` + rather than using `ThisBuild`. diff --git a/build.sbt b/build.sbt index be66b1b..030628a 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ Global / excludeLintKeys += ideBasePackages inThisBuild(Seq( organization := "com.github.ghik", - homepage := Some(url("https://github.com/ghik/plainsbt")), + homepage := Some(url("https://github.com/ghik/sbt-nosbt")), githubWorkflowTargetTags ++= Seq("v*"), githubWorkflowJavaVersions := Seq(JavaSpec.temurin("17")), @@ -22,7 +22,7 @@ inThisBuild(Seq( lazy val root = project.in(file(".")) .enablePlugins(SbtPlugin) .settings( - name := "plainsbt", + name := "sbt-nosbt", pluginCrossBuild / sbtVersion := { scalaBinaryVersion.value match { case "2.12" => "1.8.0" @@ -41,7 +41,7 @@ lazy val root = project.in(file(".")) projectInfo := ModuleInfo( nameFormal = "PlainSBT", description = "SBT plugin for organizing your build into plain Scala files", - homepage = Some(url("https://github.com/ghik/plainsbt")), + homepage = Some(url("https://github.com/ghik/sbt-nosbt")), startYear = Some(2023), licenses = Vector( "Apache License, Version 2.0" -> url("https://www.apache.org/licenses/LICENSE-2.0") @@ -49,9 +49,9 @@ lazy val root = project.in(file(".")) organizationName = "ghik", organizationHomepage = Some(url("https://github.com/ghik")), scmInfo = Some(ScmInfo( - browseUrl = url("https://github.com/ghik/plainsbt.git"), - connection = "scm:git:git@github.com:ghik/plainsbt.git", - devConnection = Some("scm:git:git@github.com:ghik/plainsbt.git") + browseUrl = url("https://github.com/ghik/sbt-nosbt.git"), + connection = "scm:git:git@github.com:ghik/sbt-nosbt.git", + devConnection = Some("scm:git:git@github.com:ghik/sbt-nosbt.git") )), developers = Vector( Developer("ghik", "Roman Janusz", "romeqjanoosh@gmail.com", url("https://github.com/ghik")) diff --git a/src/main/scala/com/github/ghik/plainsbt/Macros.scala b/src/main/scala/com/github/ghik/sbt/nosbt/Macros.scala similarity index 82% rename from src/main/scala/com/github/ghik/plainsbt/Macros.scala rename to src/main/scala/com/github/ghik/sbt/nosbt/Macros.scala index 3d65d91..26ade86 100644 --- a/src/main/scala/com/github/ghik/plainsbt/Macros.scala +++ b/src/main/scala/com/github/ghik/sbt/nosbt/Macros.scala @@ -1,4 +1,4 @@ -package com.github.ghik.plainsbt +package com.github.ghik.sbt.nosbt import scala.reflect.macros.blackbox @@ -6,7 +6,7 @@ class Macros(val c: blackbox.Context) { import c.universe.* - private def PlainsbtPkg = q"_root_.com.github.ghik.plainsbt" + private def NosbtPkg = q"_root_.com.github.ghik.sbt.nosbt" private def classBeingConstructed: ClassSymbol = { val ownerConstr = c.internal.enclosingOwner @@ -20,7 +20,7 @@ class Macros(val c: blackbox.Context) { val sbtProjectCls = c.mirror.staticClass("_root_.sbt.Project") val projectGroupTpe = - c.mirror.staticClass("_root_.com.github.ghik.plainsbt.ProjectGroup").toType + c.mirror.staticClass("_root_.com.github.ghik.sbt-nosbt.ProjectGroup").toType val rootProjectSym = projectGroupTpe.member(TermName("root")) @@ -42,5 +42,5 @@ class Macros(val c: blackbox.Context) { } def mkFreshProject: Tree = - q"$PlainsbtPkg.FreshProject(_root_.sbt.project)" + q"$NosbtPkg.FreshProject(_root_.sbt.project)" } diff --git a/src/main/scala/com/github/ghik/plainsbt/ProjectGroup.scala b/src/main/scala/com/github/ghik/sbt/nosbt/ProjectGroup.scala similarity index 98% rename from src/main/scala/com/github/ghik/plainsbt/ProjectGroup.scala rename to src/main/scala/com/github/ghik/sbt/nosbt/ProjectGroup.scala index 348d8a2..4d14c91 100644 --- a/src/main/scala/com/github/ghik/plainsbt/ProjectGroup.scala +++ b/src/main/scala/com/github/ghik/sbt/nosbt/ProjectGroup.scala @@ -1,7 +1,7 @@ -package com.github.ghik.plainsbt +package com.github.ghik.sbt.nosbt import com.avsystem.commons.* -import sbt.{Def, *} +import sbt.* import scala.language.experimental.macros