From 4521f43995e8847638d1f90d055e52070b3d8777 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 9 May 2025 21:21:58 -0400 Subject: [PATCH] Support debug output for macros It's sometimes useful to get the output of the macro-generated code. This adds a mechanism to allow plugins.sbt to pass in scalacOptions, which we can later check from the macro. 1. -Xmacro-settings:sbt:Vprint prints out the code. 2. Adding -Xmacro-settings:sbt:print-tree-structure prints out the tree structure. --- .../sbt/internal/util/appmacro/Cont.scala | 6 ++++- .../internal/util/appmacro/ContextUtil.scala | 13 ++++++++++ .../src/main/java/xsbti/Attic.java | 26 +++++++++++++++++++ main/src/main/scala/sbt/internal/Load.scala | 3 +++ 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 internal/util-interface/src/main/java/xsbti/Attic.java diff --git a/core-macros/src/main/scala/sbt/internal/util/appmacro/Cont.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/Cont.scala index 8e773dd54..6db4069f6 100644 --- a/core-macros/src/main/scala/sbt/internal/util/appmacro/Cont.scala +++ b/core-macros/src/main/scala/sbt/internal/util/appmacro/Cont.scala @@ -436,8 +436,12 @@ trait Cont: val exprWithConfig = cacheConfigExprOpt.map(config => '{ $config; $expr }).getOrElse(expr) val body = transformWrappers(exprWithConfig.asTerm, record, Symbol.spliceOwner) - inputBuf.toList match + val r = inputBuf.toList match case Nil => pure(body) case x :: Nil => genMap(body, x) case xs => genMapN(body, xs) + if hasVprintMacroSetting then + if hasPrintTreeMacroSetting then Console.err.println(Printer.TreeStructure.show(r.asTerm)) + else Console.err.println(r.show) + r end Cont diff --git a/core-macros/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala index 72b4fddf4..ec608ef72 100644 --- a/core-macros/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala +++ b/core-macros/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala @@ -9,10 +9,12 @@ package sbt.internal.util package appmacro +import scala.jdk.CollectionConverters.* import scala.quoted.* import scala.collection.mutable import sbt.util.cacheLevel import sbt.util.CacheLevelTag +import xsbti.Attic trait ContextUtil[C <: Quotes & scala.Singleton](val valStart: Int): val qctx: C @@ -166,4 +168,15 @@ trait ContextUtil[C <: Quotes & scala.Singleton](val valStart: Int): end traverser traverser.traverseTree(tree)(Symbol.spliceOwner) defs.toSet + + private lazy val atticValues = Attic.getItems().asScala.toSet + def hasVprintMacroSetting: Boolean = + atticValues.contains("-Xmacro-settings:sbt:Vprint") + def hasPrintTreeMacroSetting: Boolean = + atticValues.contains("-Xmacro-settings:sbt:print-tree-structure") +end ContextUtil + +object ContextUtil: + def appendScalacOptions(options: Seq[String]): Unit = + Attic.appendItems(options.asJava); end ContextUtil diff --git a/internal/util-interface/src/main/java/xsbti/Attic.java b/internal/util-interface/src/main/java/xsbti/Attic.java new file mode 100644 index 000000000..f25bf7249 --- /dev/null +++ b/internal/util-interface/src/main/java/xsbti/Attic.java @@ -0,0 +1,26 @@ +package xsbti; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** A global in-memory storage to pass information. */ +public class Attic { + private static List _items = new ArrayList(); + + /** + * This is used to collect scalacOptions of metabuild. This works around the fact that + * CompilationInfo.XmacroSettings is experimental. + */ + public static void appendItems(Collection values) { + _items.addAll(values); + } + + /** + * This is used to return scalacOptions of metabuild. This works around the fact that + * CompilationInfo.XmacroSettings is experimental. + */ + public static Collection getItems() { + return _items; + } +} diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala index 8c7196542..e8d2ed0d1 100755 --- a/main/src/main/scala/sbt/internal/Load.scala +++ b/main/src/main/scala/sbt/internal/Load.scala @@ -23,6 +23,7 @@ import sbt.internal.inc.{ MappedFileConverter, ScalaInstance, ZincLmUtil, ZincUt import sbt.internal.util.Attributed.data import sbt.internal.util.Types.const import sbt.internal.util.Attributed +import sbt.internal.util.appmacro.ContextUtil import sbt.internal.server.BuildServerEvalReporter import sbt.io.{ GlobFilter, IO } import sbt.librarymanagement.ivy.{ InlineIvyConfiguration, IvyDependencyResolution, IvyPaths } @@ -776,6 +777,8 @@ private[sbt] object Load { } val converter = config.converter + ContextUtil.appendScalacOptions(plugs.pluginData.scalacOptions) + // NOTE - because we create an eval here, we need a clean-eval later for this URI. lazy val eval = timed("Load.loadUnit: mkEval", log) { def mkReporter(): EvalReporter = plugs.pluginData.buildTarget match {