Skip to content

Commit b1bbae5

Browse files
Port sbt project naming macro for tlCrossRootProject - resolves #169
1 parent bc913e6 commit b1bbae5

File tree

4 files changed

+88
-6
lines changed

4 files changed

+88
-6
lines changed

.editorconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
end_of_line = lf
6+
insert_final_newline = true
7+
trim_trailing_whitespace = true
8+
9+
[*.{scala,sbt}]
10+
indent_style = space
11+
indent_size = 2

ci/src/main/scala/org/typelevel/sbt/CrossRootProject.scala

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,13 @@ object CrossRootProject {
9999
def unapply(root: CrossRootProject): Some[(Project, Project, Project, Project)] =
100100
Some((root.all, root.jvm, root.js, root.native))
101101

102-
private[sbt] def apply(): CrossRootProject = new CrossRootProject(
103-
Project("root", file(".")),
104-
Project("rootJVM", file(".jvm")),
105-
Project("rootJS", file(".js")),
106-
Project("rootNative", file(".native"))
102+
def apply(): CrossRootProject = CrossRootProject("root")
103+
104+
def apply(name: String): CrossRootProject = new CrossRootProject(
105+
Project(name, file(".")),
106+
Project(s"${name}JVM", file(".jvm")),
107+
Project(s"${name}JS", file(".js")),
108+
Project(s"${name}Native", file(".native"))
107109
).enablePlugins(NoPublishPlugin, TypelevelCiCrossPlugin)
108110
}
109111

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2022 Typelevel
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.typelevel.sbt
18+
19+
import scala.annotation.tailrec
20+
import scala.reflect.macros.blackbox
21+
22+
private[sbt] object CrossRootProjectMacros {
23+
24+
// Copied from sbt.std.KeyMacro
25+
def definingValName(c: blackbox.Context, invalidEnclosingTree: String => String): String = {
26+
import c.universe.{Apply => ApplyTree, _}
27+
val methodName = c.macroApplication.symbol.name
28+
def processName(n: Name): String =
29+
n.decodedName
30+
.toString
31+
.trim // trim is not strictly correct, but macros don't expose the API necessary
32+
@tailrec def enclosingVal(trees: List[c.Tree]): String = {
33+
trees match {
34+
case ValDef(_, name, _, _) :: _ => processName(name)
35+
case (_: ApplyTree | _: Select | _: TypeApply) :: xs => enclosingVal(xs)
36+
// lazy val x: X = <methodName> has this form for some reason (only when the explicit type is present, though)
37+
case Block(_, _) :: DefDef(mods, name, _, _, _, _) :: _ if mods.hasFlag(Flag.LAZY) =>
38+
processName(name)
39+
case _ =>
40+
c.error(c.enclosingPosition, invalidEnclosingTree(methodName.decodedName.toString))
41+
"<error>"
42+
}
43+
}
44+
enclosingVal(enclosingTrees(c).toList)
45+
}
46+
47+
// Copied from sbt.std.KeyMacro
48+
def enclosingTrees(c: blackbox.Context): Seq[c.Tree] =
49+
c.asInstanceOf[reflect.macros.runtime.Context]
50+
.callsiteTyper
51+
.context
52+
.enclosingContextChain
53+
.map(_.tree.asInstanceOf[c.Tree])
54+
55+
def crossRootProjectImpl(c: blackbox.Context): c.Expr[CrossRootProject] = {
56+
import c.universe._
57+
58+
val enclosingValName = definingValName(
59+
c,
60+
methodName =>
61+
s"""$methodName must be directly assigned to a val, such as `val x = $methodName`. Alternatively, you can use `org.typelevel.sbt.CrossRootProject.apply`"""
62+
)
63+
64+
val name = c.Expr[String](Literal(Constant(enclosingValName)))
65+
66+
reify { CrossRootProject(name.splice) }
67+
}
68+
}

ci/src/main/scala/org/typelevel/sbt/TypelevelCiPlugin.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ import org.typelevel.sbt.gha.GenerativePlugin
2121
import org.typelevel.sbt.gha.GitHubActionsPlugin
2222
import org.typelevel.sbt.gha.GenerativePlugin.autoImport._
2323
import com.typesafe.tools.mima.plugin.MimaPlugin
24+
import scala.language.experimental.macros
2425

2526
object TypelevelCiPlugin extends AutoPlugin {
2627

2728
override def requires = GitHubActionsPlugin && GenerativePlugin && MimaPlugin
2829
override def trigger = allRequirements
2930

3031
object autoImport {
31-
def tlCrossRootProject: CrossRootProject = CrossRootProject()
32+
def tlCrossRootProject: CrossRootProject = macro CrossRootProjectMacros.crossRootProjectImpl
3233
}
3334

3435
override def buildSettings = Seq(

0 commit comments

Comments
 (0)