diff --git a/build.sbt b/build.sbt index 011cdab..450d72e 100644 --- a/build.sbt +++ b/build.sbt @@ -82,24 +82,31 @@ val scalaOpts = scalacOptions ++= ((isSnapshot.value, scalaVersion.value) match "-Wvalue-discard", "-Wunused:_", ) + case (_, ScalaVersions.scala_3) => Seq( + "-no-indent", + "-explain", + ) case (_, _) => Seq.empty }) lazy val sbtmeta = (project in file("sbtmeta")) .settings( - crossScalaVersions := Seq(ScalaVersions.scala_213, ScalaVersions.scala_212), + crossScalaVersions := Seq(ScalaVersions.scala_3, ScalaVersions.scala_213, ScalaVersions.scala_212), scalaVersion := crossScalaVersions.value.head, - libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided, + libraryDependencies ++= { + if (scalaVersion.value.startsWith("2")) + Seq("org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided) + else Seq.empty + }, scalaOpts, ) lazy val sbtgen = (project in file("sbtgen")) .dependsOn(sbtmeta) .settings( - crossScalaVersions := Seq(ScalaVersions.scala_213, ScalaVersions.scala_212), + crossScalaVersions := Seq(ScalaVersions.scala_3, ScalaVersions.scala_213, ScalaVersions.scala_212), scalaVersion := crossScalaVersions.value.head, - // libraryDependencies += "com.github.scopt" %% "scopt" % "4.0.0-RC2", - libraryDependencies += "com.github.scopt" %% "scopt" % "3.7.1", + libraryDependencies += "com.github.scopt" %% "scopt" % "4.1.0", libraryDependencies += "org.scala-lang.modules" %% "scala-collection-compat" % "2.11.0", (ThisBuild / libraryDependencies) += "org.scalatest" %% "scalatest" % "3.2.18" % Test, scalacOptions ++= Seq( @@ -216,7 +223,7 @@ lazy val `izumi-sbtgen` = (project in file(".")) action = { st: State => val extracted = Project.extract(st) val ref = extracted.get(`sbt-tests` / thisProjectRef) - extracted.runInputTask((ref / (Global / scripted)), "", st)._1 + extracted.runInputTask(ref / (Global / scripted), "", st)._1 } ), setReleaseVersion, // : ReleaseStep diff --git a/project/ScalaVersions.scala b/project/ScalaVersions.scala index d30270d..1784ee7 100644 --- a/project/ScalaVersions.scala +++ b/project/ScalaVersions.scala @@ -1,6 +1,7 @@ object ScalaVersions { val scala_212 = "2.12.18" val scala_213 = "2.13.12" + val scala_3 = "3.2.2" val scalaJsVersion = "1.15.0" val scalaNativeVersion = "0.4.17" diff --git a/sbtmeta/src/main/scala/izumi/sbtgen/sbtmeta/ProjectAttributeMacro.scala b/sbtmeta/src/main/scala-2/izumi/sbtgen/sbtmeta/ProjectAttributeMacro.scala similarity index 66% rename from sbtmeta/src/main/scala/izumi/sbtgen/sbtmeta/ProjectAttributeMacro.scala rename to sbtmeta/src/main/scala-2/izumi/sbtgen/sbtmeta/ProjectAttributeMacro.scala index 166cc73..f389e96 100644 --- a/sbtmeta/src/main/scala/izumi/sbtgen/sbtmeta/ProjectAttributeMacro.scala +++ b/sbtmeta/src/main/scala-2/izumi/sbtgen/sbtmeta/ProjectAttributeMacro.scala @@ -1,46 +1,20 @@ package izumi.sbtgen.sbtmeta import java.nio.file.{Path, Paths} -import java.time.LocalDateTime import scala.annotation.tailrec import scala.reflect.macros.blackbox object ProjectAttributeMacro { - def buildTimestampMacro(c: blackbox.Context)(): c.Expr[LocalDateTime] = { - import c.universe._ - - val time = LocalDateTime.now() - c.Expr[LocalDateTime] { - q"{_root_.java.time.LocalDateTime.of(${time.getYear}, ${time.getMonthValue}, ${time.getDayOfMonth}, ${time.getHour}, ${time.getMinute}, ${time.getSecond}, ${time.getNano})}" - } - } - def extractAttrMacro(c: blackbox.Context)(name: c.Expr[String]): c.Expr[Option[String]] = { val nameStr = TreeTools.stringLiteral(c)(c.universe)(name.tree) extractAttr(c, nameStr, force = true) } - def extractProjectGroupIdMacro(c: blackbox.Context)(): c.Expr[Option[String]] = { - extractAttr(c, "product-group") - } - - def extractSbtVersionMacro(c: blackbox.Context)(): c.Expr[Option[String]] = { - extractAttr(c, "sbt-version") - } - - def extractScalatestVersionMacro(c: blackbox.Context)(): c.Expr[Option[String]] = { - extractAttr(c, "scalatest-version") - } - def extractScalaVersionsMacro(c: blackbox.Context)(): c.Expr[Option[String]] = { extractAttr(c, "scala-versions") } - def extractScalaVersionMacro(c: blackbox.Context)(): c.Expr[Option[String]] = { - extractAttr(c, "scala-version") - } - def extractProjectVersionMacro(c: blackbox.Context)(): c.Expr[Option[String]] = { extractAttr(c, "product-version") } diff --git a/sbtmeta/src/main/scala-2/izumi/sbtgen/sbtmeta/SbtgenMeta.scala b/sbtmeta/src/main/scala-2/izumi/sbtgen/sbtmeta/SbtgenMeta.scala new file mode 100644 index 0000000..587fc43 --- /dev/null +++ b/sbtmeta/src/main/scala-2/izumi/sbtgen/sbtmeta/SbtgenMeta.scala @@ -0,0 +1,13 @@ +package izumi.sbtgen.sbtmeta + +import scala.language.experimental.macros + +object SbtgenMeta { + def projectRoot(): Option[String] = macro ProjectAttributeMacro.findProjectRootMacro + + def extractSbtProjectVersion(): Option[String] = macro ProjectAttributeMacro.extractProjectVersionMacro + + def extractScalaVersions(): Option[String] = macro ProjectAttributeMacro.extractScalaVersionsMacro + + def extractMandatory(name: String): Option[String] = macro ProjectAttributeMacro.extractAttrMacro +} diff --git a/sbtmeta/src/main/scala/izumi/sbtgen/sbtmeta/TreeTools.scala b/sbtmeta/src/main/scala-2/izumi/sbtgen/sbtmeta/TreeTools.scala similarity index 100% rename from sbtmeta/src/main/scala/izumi/sbtgen/sbtmeta/TreeTools.scala rename to sbtmeta/src/main/scala-2/izumi/sbtgen/sbtmeta/TreeTools.scala diff --git a/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/BuildAttributes.scala b/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/BuildAttributes.scala new file mode 100644 index 0000000..ec7122e --- /dev/null +++ b/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/BuildAttributes.scala @@ -0,0 +1,9 @@ +package izumi.sbtgen.sbtmeta + +import java.time.LocalDateTime + +object BuildAttributes { + + inline def sbtProjectRoot(): Option[String] = ${ BuildAttributesImpl.sbtProjectRoot() } + +} diff --git a/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/BuildAttributesImpl.scala b/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/BuildAttributesImpl.scala new file mode 100644 index 0000000..1166ecf --- /dev/null +++ b/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/BuildAttributesImpl.scala @@ -0,0 +1,36 @@ +package izumi.sbtgen.sbtmeta + +import java.nio.file.Path +import java.time.LocalDateTime + +import scala.annotation.tailrec +import scala.quoted.{Expr, Quotes} + +object BuildAttributesImpl { + + def sbtProjectRoot()(using quotes: Quotes): Expr[Option[String]] = { + import quotes.reflect.* + + val result = SourceFile.current.getJPath + .flatMap(findProjectRoot) + .map(_.toFile.getCanonicalPath) + + Expr(result) + } + + @tailrec + private def findProjectRoot(cp: Path): Option[Path] = { + if (cp.resolve("build.sbt").toFile.exists()) { + Some(cp) + } else { + val parent = cp.getParent + + if (parent == null || parent == cp.getRoot) { + None + } else { + findProjectRoot(parent) + } + } + } + +} diff --git a/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/MacroParameters.scala b/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/MacroParameters.scala new file mode 100644 index 0000000..e958516 --- /dev/null +++ b/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/MacroParameters.scala @@ -0,0 +1,15 @@ +package izumi.sbtgen.sbtmeta + +import scala.quoted.{Expr, Quotes, Type} + +object MacroParameters { + + inline def scalaCrossVersions(): Option[String] = macroSetting("scala-versions") + + inline def artifactVersion(): Option[String] = macroSetting("product-version") + + inline def macroSetting(inline name: String): Option[String] = { + ${ MacroParametersImpl.extractString('{ name }) } + } + +} diff --git a/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/MacroParametersImpl.scala b/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/MacroParametersImpl.scala new file mode 100644 index 0000000..a38c0db --- /dev/null +++ b/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/MacroParametersImpl.scala @@ -0,0 +1,28 @@ +package izumi.sbtgen.sbtmeta + +import scala.annotation.experimental +import scala.quoted.{Expr, Quotes, Type} + +@experimental +object MacroParametersImpl { + + def extractString(name: Expr[String])(using quotes: Quotes): Expr[Option[String]] = { + Expr(extract(name.valueOrAbort)) + } + + def extractBool(name: Expr[String])(using quotes: Quotes): Expr[Option[Boolean]] = { + val value = extract(name.valueOrAbort) + val isTrue = value.map(_.toLowerCase).map(v => v == "true" || v == "1") + Expr(isTrue) + } + + private def extract(name: String)(using quotes: Quotes): Option[String] = { + import quotes.reflect.* + val prefix = s"$name=" + val value = CompilationInfo.XmacroSettings.filter(_.startsWith(prefix)).map(_.stripPrefix(prefix)).lastOption + if (value.isEmpty) { + report.info(s"Undefined macro parameter $name, add `-Xmacro-settings:$prefix` into `scalac` options") + } + value + } +} diff --git a/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/SbtgenMeta.scala b/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/SbtgenMeta.scala new file mode 100644 index 0000000..d56ed07 --- /dev/null +++ b/sbtmeta/src/main/scala-3/izumi.sbtgen.sbtmeta/SbtgenMeta.scala @@ -0,0 +1,11 @@ +package izumi.sbtgen.sbtmeta + +object SbtgenMeta { + inline def projectRoot(): Option[String] = ${ BuildAttributesImpl.sbtProjectRoot() } + + inline def extractSbtProjectVersion(): Option[String] = MacroParameters.artifactVersion() + + inline def extractScalaVersions(): Option[String] = MacroParameters.scalaCrossVersions() + + inline def extractMandatory(inline name: String): Option[String] = MacroParameters.macroSetting(name) +} diff --git a/sbtmeta/src/main/scala/izumi/sbtgen/sbtmeta/SbtgenMeta.scala b/sbtmeta/src/main/scala/izumi/sbtgen/sbtmeta/SbtgenMeta.scala deleted file mode 100644 index 6e5811c..0000000 --- a/sbtmeta/src/main/scala/izumi/sbtgen/sbtmeta/SbtgenMeta.scala +++ /dev/null @@ -1,27 +0,0 @@ -package izumi.sbtgen.sbtmeta - -import java.time.LocalDateTime - -import scala.language.experimental.macros - -object SbtgenMeta { - def buildTimestamp(): LocalDateTime = macro ProjectAttributeMacro.buildTimestampMacro - - def projectRoot(): Option[String] = macro ProjectAttributeMacro.findProjectRootMacro - - def extractSbtProjectGroupId(): Option[String] = macro ProjectAttributeMacro.extractProjectGroupIdMacro - - def extractSbtProjectVersion(): Option[String] = macro ProjectAttributeMacro.extractProjectVersionMacro - - def extractSbtVersion(): Option[String] = macro ProjectAttributeMacro.extractSbtVersionMacro - - def extractScalatestVersion(): Option[String] = macro ProjectAttributeMacro.extractScalatestVersionMacro - - def extractScalaVersion(): Option[String] = macro ProjectAttributeMacro.extractScalaVersionMacro - - def extractScalaVersions(): Option[String] = macro ProjectAttributeMacro.extractScalaVersionsMacro - - def extract(name: String): Option[String] = macro ProjectAttributeMacro.extractAttrMacro - - def extractMandatory(name: String): Option[String] = macro ProjectAttributeMacro.extractAttrMacro -}