From abc2d28ce0dc124f57faf8ac8cf9ea9c65feca53 Mon Sep 17 00:00:00 2001 From: Andy Scott Date: Mon, 30 Apr 2018 23:47:02 -0700 Subject: [PATCH] Add prelim macro tracker [WIP] --- rules/scala/macros/BUILD | 33 +++++++++++++ rules/scala/macros/MacroSpoorPlugin.scala | 59 +++++++++++++++++++++++ rules/scala/macros/Test.scala | 8 +++ rules/scala/macros/scalac-plugin.xml | 4 ++ rules/scala/private/core.bzl | 12 +++-- 5 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 rules/scala/macros/BUILD create mode 100644 rules/scala/macros/MacroSpoorPlugin.scala create mode 100644 rules/scala/macros/Test.scala create mode 100644 rules/scala/macros/scalac-plugin.xml diff --git a/rules/scala/macros/BUILD b/rules/scala/macros/BUILD new file mode 100644 index 00000000..6c77831e --- /dev/null +++ b/rules/scala/macros/BUILD @@ -0,0 +1,33 @@ +load("//rules:scala.bzl", + "annex_scala_library", + "annex_scala_binary") + +annex_scala_library( + name = "macro-spoor-plugin", + srcs = glob(["**/*.scala"], exclude = ["**/*Test.scala"]), + scala = "@scala_annex_scala_2_12", + deps = [ + "@scala_annex_scala_2_12_scala_compiler//jar", + "@scala_annex_scala_2_12_scala_reflect//jar", + ], + deps_used_whitelist = [ + "@scala_annex_scala_2_12_scala_reflect//jar", + ], + resources = ["scalac-plugin.xml"], + visibility = ["//visibility:public"], +) + +annex_scala_binary( + name = "macro-spoor-plugin-usage", + srcs = glob(["**/*Test.scala"]), + scala = "@scala_annex_scala_2_12", + plugins = [ + ":macro-spoor-plugin", + ], + deps = [ + "@scala_annex_com_lihaoyi_sourcecode_2_12", + ], + deps_used_whitelist = [ + ], + visibility = ["//visibility:public"], +) diff --git a/rules/scala/macros/MacroSpoorPlugin.scala b/rules/scala/macros/MacroSpoorPlugin.scala new file mode 100644 index 00000000..46186c4b --- /dev/null +++ b/rules/scala/macros/MacroSpoorPlugin.scala @@ -0,0 +1,59 @@ +package anx + +import scala.annotation.tailrec + +import scala.tools.nsc.{Global ⇒ NscGlobal} +import scala.tools.nsc.plugins.{Plugin => NscPlugin, PluginComponent => NscPluginComponent} +import scala.reflect.internal.Mode + +import scala.reflect.io.NoAbstractFile +import scala.tools.nsc.io.AbstractFile + +import java.io.File + +final class MacroSpoorPlugin(val global: NscGlobal) extends NscPlugin { + import global._ + import analyzer.{MacroPlugin => NscMacroPlugin, _} + + override val name: String = "anx-macro-spoor" + override val description: String = "tracks which dependencies are needed to expand macros" + override val components: List[NscPluginComponent] = Nil + + analyzer.addMacroPlugin(MacroPlugin) + + private[this] object MacroPlugin extends NscMacroPlugin { + override def pluginsMacroRuntime(expandee: Tree): Option[MacroRuntime] = { + val macroDef = expandee.symbol + if (!fastTrack.contains(macroDef)) { + val file = classFile(macroDef.owner) + println("macro used file: " + file) + } + None + } + } + + // copied from xsbt.LocateClassFile.classFile + @tailrec private[this] def classFile(sym: Symbol): Option[AbstractFile] = { + if (sym hasFlag scala.tools.nsc.symtab.Flags.PACKAGE) None + else { + val file = sym.associatedFile + if (file == NoAbstractFile) { + if (isTopLevelModule(sym)) { + val linked = sym.companionClass + if (linked == NoSymbol) None + else classFile(linked) + } else None + } else Some(file) + } + } + + // copied from xsbt.ClassName.flatName + private[this] def flatname(s: Symbol, separator: Char) = + enteringPhase(currentRun.flattenPhase.next) { s fullName separator } + + // copied from xsbt.ClassName.isTopLevelModule + private[this] def isTopLevelModule(sym: Symbol): Boolean = + enteringPhase(currentRun.picklerPhase.next) { + sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass + } +} diff --git a/rules/scala/macros/Test.scala b/rules/scala/macros/Test.scala new file mode 100644 index 00000000..f4a1e1dd --- /dev/null +++ b/rules/scala/macros/Test.scala @@ -0,0 +1,8 @@ +package doof + +object Foo { + def main(args: Array[String]): Unit = { + val line = implicitly[sourcecode.Line] + assert(line.value == 6) + } +} diff --git a/rules/scala/macros/scalac-plugin.xml b/rules/scala/macros/scalac-plugin.xml new file mode 100644 index 00000000..568d7cc7 --- /dev/null +++ b/rules/scala/macros/scalac-plugin.xml @@ -0,0 +1,4 @@ + + anx-macro-spoor + anx.MacroSpoorPlugin + diff --git a/rules/scala/private/core.bzl b/rules/scala/private/core.bzl index 524bd9e9..3d636f77 100644 --- a/rules/scala/private/core.bzl +++ b/rules/scala/private/core.bzl @@ -84,7 +84,7 @@ def runner_common(ctx): ] compile_classpath = depset(order = "preorder", transitive = macro_classpath + [sdeps.transitive_compile_time_jars]) - zipper_inputs, _, zipper_manifests = ctx.resolve_command(tools = [ctx.attr._zipper]) + zipper_inputs, _, zipper_manifests = ctx.resolve_command(tools = [ctx.attr._zipper]) if ctx.files.resources: class_jar = ctx.actions.declare_file("{}/classes.zip".format(ctx.label.name)) @@ -94,8 +94,9 @@ def runner_common(ctx): args.add(resource_jar) args.set_param_file_format("multiline") args.use_param_file("@%s") + strip_prefix = ctx.label.package + "/" + ctx.attr.resource_strip_prefix for file in ctx.files.resources: - args.add("{}={}".format(_resource_path(file, ctx.attr.resource_strip_prefix), file.path)) + args.add("{}={}".format(_resource_path(file, strip_prefix), file.path)) ctx.actions.run( arguments = [args], executable = ctx.executable._zipper, @@ -292,10 +293,13 @@ def _labeled_groups(labeled_jars_list): return [_labeled_group(labeled_jars) for labeled_jars in labeled_jars_list] def _resource_path(file, strip_prefix): + print(file.short_path) + print(strip_prefix) if strip_prefix: if not file.short_path.startswith(strip_prefix): - fail("{} does not have prefix {}".format(file.short_path, strip_prefix)) - return file.short_path[len(strip_prefix) + 1 - int(file.short_path.endswith("/")):] + fail("{} does not have prefix {}".format(file.short_path, strip_prefix)) + return file.short_path[len(strip_prefix):] + # todo: consider removing custom automatic handling? conventional = [ "src/main/resources/", "src/test/resources/",