Другой интересный вопрос @ Ким-Стебель.
Моя первая идея, которая не затрагивает ваш вопрос, заключается в том, что ваш компилятор может настроить путь класса макросов с помощью findMacroClassLoader. REPL использует это благодаря @extempore.
Это было бы полезно для идиомы, такой как требование («myfoo.jar») {mymacro}, возможно.
Вопрос в том, можно ли обновить путь к классу компилятора. Это может быть возможно путем отбрасывания из вашего контекстного юниверса в компилятор, для которого isCompilerUniverse == true. Тогда вы можете platform.updateClassPath.
Update с кодом:
Вот что-то нравится эта идея использования специального заполнителя в классе путь для требуемого макроса.
Необычным способом, упомянутым ниже, было бы использование пользовательского пути класса, который может сообщать обо всех необходимых классах в этом месте. Старый код, упомянутый ниже, был для виртуального каталога в пути к классу.
Этот быстрый и дрянной способ использует файловую систему для распаковки вашей банки и просто просит компилятор выполнить ее повторное сканирование.
Заполнитель должен быть фактическим директором из-за ограничения в invalidateClassPathEntries, который хочет проверить, находится ли путь канонического файла в пути к классу.
package alacs
import scala.language.experimental.macros
import scala.reflect.macros.Context
import scala.sys.process._
import java.io.File
/** A require macro to dynamically fudge the compilation classpath. */
object PathMaker {
// special place to unpack required libs, must be on the initial classpath
val entry = "required"
// whether to report updated syms without overly verbose -verbose
val talky = true
def require(c: Context)(name: c.Expr[String]): c.Expr[Unit] = {
import c.universe._
val st = c.universe.asInstanceOf[scala.reflect.internal.SymbolTable]
if (st.isCompilerUniverse) {
val Literal(Constant(what: String)) = name.tree
if (update(what)) {
val global = st.asInstanceOf[scala.tools.nsc.Global]
val (updated, _) = global invalidateClassPathEntries entry
c.info(c.enclosingPosition, s"Updated symbols $updated", force = talky)
} else {
c.abort(c.enclosingPosition, s"Couldn't unpack '$what' into '$entry'")
}
}
reify {() }
}
// figure out where name is, and update the special class path entry
def update(name: String): Boolean = {
// Process doesn't parse the command, it just splits on space,
// something about working on Windows
//val status = s"sh -c \"mkdir $entry ; (cd $entry ; jar xf ../$name)\"".!
// but Process can set cwd for you
val command = s"jar xf ../$name"
val status = Process(command, new File(entry)).!
(status == 0)
}
}
Необходимое требует API:
package alacs
import scala.language.experimental.macros
object Require {
def require(name: String): Unit = macro PathMaker.require
}
Использование:
package sample
import alacs.Require._
/** Sample app requiring something not on the class path. */
object Test extends App {
require("special.jar")
import special._
Console println Special(7, "seven")
}
Что-то упаковано в special.jar
package special
case class Special(i: Int, s: String)
Испытано таким образом:
rm -rf required
mkdir required
skalac pathmaker.scala
skalac -cp .:required require.scala sample.scala
skala -cp .:special.jar sample.Test
[email protected]:~/tmp/pathmaker$ . ./b
Unpack special.jar
sample.scala:8: Updated symbols List(package special)
require("special.jar")
^
Special(7,seven)
Макрос не пробирает его по пути выполнения, что является сложной задачей.
Но я предполагаю, что требуемый макрос может делать удобные вещи, например, условно захватывать разные версии фляги с различными характеристиками времени компиляции (константы и т. Д.).
Update, просто проверить, что это довольно дикий:
require("fast.jar")
import constants._
Console println speed
require("slow.jar")
Console println speed
где
package object constants {
//final val speed = 55
final val speed = 85
}
$ skalac -d fast.jar constants.scala
и запустить его с встраиваемыми константами
85
55
Новый нюанс: это мой первый макрос, и я смотрю invalidateClassPathEntries для другого приложения, поэтому я не изучал ограничения y и др.
Обновление: одно ограничение контролирует, когда макрос расширяется. Я хотел показать, как что-то компилирует против старого API против нового API, и должен был обернуть код в блоках, которые доступны, прежде чем необходимо уверены символы:
require("oldfoo.jar")
locally {
import foo._
// something
require("newfoo.jar")
// try again
}
Старого нюанс: Извините за туманный ответ, я знаю, вы не соблюдаете этого; Я попробую позже, но, может быть, кто-то сделает шаг вперед с ясностью.
Раньше я подключился к реализации «платформы» для компилятора global, но это, надеюсь, излишнее для этого варианта использования. В этот момент вы можете делать все, что хотите, с помощью classpath, но я думаю, что вам нужно что-то более готовое.
Вы можете попробовать адаптировать это решение http://stackoverflow.com/questions/1010919/adding-files-to-java-classpath-at-runtime –
Речь идет об изменении пути к среде выполнения, а не в том, что мне нужно. –
Но если он находится в определении макроса (_not_ сгенерированный код), он должен запускаться компилятором и изменять путь_its_ runtime classpath. –