mirror of https://github.com/sbt/sbt.git
commit
b865d88060
|
|
@ -234,6 +234,7 @@ Modifiers
|
|||
isImplicit: Boolean
|
||||
isLazy: Boolean
|
||||
isSynthetic: Boolean
|
||||
isMacro: Boolean
|
||||
}}}
|
||||
|
||||
{{{
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ object APIUtil
|
|||
val modifiersToByte = (m: Modifiers) => {
|
||||
import m._
|
||||
def x(b: Boolean, bit: Int) = if(b) 1 << bit else 0
|
||||
( x(isAbstract, 0) | x(isOverride, 1) | x(isFinal, 2) | x(isSealed, 3) | x(isImplicit, 4) | x(isLazy, 5) ).toByte
|
||||
( x(isAbstract, 0) | x(isOverride, 1) | x(isFinal, 2) | x(isSealed, 3) | x(isImplicit, 4) | x(isLazy, 5) | x(isMacro, 6) ).toByte
|
||||
}
|
||||
val byteToModifiers = (b: Byte) => {
|
||||
def x(bit: Int) = (b & (1 << bit)) != 0
|
||||
new Modifiers( x(0), x(1), x(2), x(3), x(4), x(5) )
|
||||
new Modifiers( x(0), x(1), x(2), x(3), x(4), x(5), x(6) )
|
||||
}
|
||||
|
||||
def verifyTypeParameters(s: SourceAPI): Boolean =
|
||||
|
|
@ -43,6 +43,25 @@ object APIUtil
|
|||
super.visitParameterRef(ref)
|
||||
}
|
||||
}
|
||||
|
||||
def hasMacro(s: SourceAPI): Boolean =
|
||||
{
|
||||
val check = new HasMacro
|
||||
check.visitAPI(s)
|
||||
check.hasMacro
|
||||
}
|
||||
|
||||
private[this] class HasMacro extends Visit
|
||||
{
|
||||
var hasMacro = false
|
||||
|
||||
override def visitModifiers(m: Modifiers)
|
||||
{
|
||||
hasMacro ||= m.isMacro
|
||||
super.visitModifiers(m)
|
||||
}
|
||||
}
|
||||
|
||||
def minimize(api: SourceAPI): SourceAPI =
|
||||
new SourceAPI(api.packages, minimizeDefinitions(api.definitions))
|
||||
def minimizeDefinitions(ds: Array[Definition]): Array[Definition] =
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ object ClassToAPI
|
|||
def modifiers(i: Int): api.Modifiers =
|
||||
{
|
||||
import Modifier.{isAbstract, isFinal}
|
||||
new api.Modifiers( isAbstract(i), false, isFinal(i), false, false, false)
|
||||
new api.Modifiers( isAbstract(i), false, isFinal(i), false, false, false, false)
|
||||
}
|
||||
def access(i: Int, pkg: Option[String]): api.Access =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -43,7 +43,9 @@ object TopLevel
|
|||
/** Checks the API of two source files for equality.*/
|
||||
object SameAPI
|
||||
{
|
||||
def apply(a: Source, b: Source): Boolean = a.apiHash == b.apiHash && (a.hash.length > 0 && b.hash.length > 0) && apply(a.api, b.api)
|
||||
def apply(a: Source, b: Source): Boolean =
|
||||
a.apiHash == b.apiHash && (a.hash.length > 0 && b.hash.length > 0) && apply(a.api, b.api)
|
||||
|
||||
def apply(a: SourceAPI, b: SourceAPI): Boolean =
|
||||
{
|
||||
val start = System.currentTimeMillis
|
||||
|
|
@ -208,6 +210,7 @@ class SameAPI(tagsA: TypeVars, tagsB: TypeVars, includePrivate: Boolean, include
|
|||
setIf(bs, isSealed, 3)
|
||||
setIf(bs, isImplicit, 4)
|
||||
setIf(bs, isLazy, 5)
|
||||
setIf(bs, isMacro, 6)
|
||||
bs.toImmutable
|
||||
}
|
||||
def setIf(bs: mutable.BitSet, flag: Boolean, i: Int): Unit =
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ object APIs
|
|||
|
||||
val emptyAPI = new xsbti.api.SourceAPI(Array(), Array())
|
||||
val emptyCompilation = new xsbti.api.Compilation(-1, "")
|
||||
val emptySource = new xsbti.api.Source(emptyCompilation, Array(), emptyAPI, 0)
|
||||
val emptySource = new xsbti.api.Source(emptyCompilation, Array(), emptyAPI, 0, false)
|
||||
def getAPI[T](map: Map[T, Source], src: T): Source = map.getOrElse(src, emptySource)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
|
|||
private[this] val sourceDeps = new HashMap[File, Set[File]]
|
||||
private[this] val extSrcDeps = new ListBuffer[(File, String, Source)]
|
||||
private[this] val binaryClassName = new HashMap[File, String]
|
||||
// source files containing a macro def.
|
||||
private[this] val macroSources = Set[File]()
|
||||
|
||||
private def add[A,B](map: Map[A,Set[B]], a: A, b: B): Unit =
|
||||
map.getOrElseUpdate(a, new HashSet[B]) += b
|
||||
|
|
@ -99,7 +101,12 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
|
|||
classToSource.put(module, source)
|
||||
}
|
||||
|
||||
def api(sourceFile: File, source: SourceAPI) { apis(sourceFile) = (xsbt.api.HashAPI(source), xsbt.api.APIUtil.minimize(source)) }
|
||||
def api(sourceFile: File, source: SourceAPI) {
|
||||
import xsbt.api.{APIUtil, HashAPI}
|
||||
if (APIUtil.hasMacro(source)) macroSources += sourceFile
|
||||
apis(sourceFile) = (HashAPI(source), APIUtil.minimize(source))
|
||||
}
|
||||
|
||||
def endSource(sourcePath: File): Unit =
|
||||
assert(apis.contains(sourcePath))
|
||||
|
||||
|
|
@ -110,7 +117,9 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
|
|||
(base /: apis) { case (a, (src, api) ) =>
|
||||
val stamp = current.internalSource(src)
|
||||
val hash = stamp match { case h: Hash => h.value; case _ => new Array[Byte](0) }
|
||||
val s = new xsbti.api.Source(compilation, hash, api._2, api._1)
|
||||
// TODO store this in Relations, rather than Source.
|
||||
val hasMacro: Boolean = macroSources.contains(src)
|
||||
val s = new xsbti.api.Source(compilation, hash, api._2, api._1, hasMacro)
|
||||
a.addSource(src, s, stamp, sourceDeps.getOrElse(src, Nil: Iterable[File]))
|
||||
}
|
||||
def addExternals(base: Analysis): Analysis = (base /: extSrcDeps) { case (a, (source, name, api)) => a.addExternalDep(source, name, api) }
|
||||
|
|
|
|||
|
|
@ -59,7 +59,12 @@ object Incremental
|
|||
|
||||
new APIChanges(modifiedAPIs, changedNames)
|
||||
}
|
||||
def sameSource(a: Source, b: Source): Boolean = shortcutSameSource(a, b) || SameAPI(a,b)
|
||||
def sameSource(a: Source, b: Source): Boolean = {
|
||||
// Clients of a modified source file (ie, one that doesn't satisfy `shortcutSameSource`) containing macros must be recompiled.
|
||||
val hasMacro = a.hasMacro || b.hasMacro
|
||||
shortcutSameSource(a, b) || (!hasMacro && SameAPI(a,b))
|
||||
}
|
||||
|
||||
def shortcutSameSource(a: Source, b: Source): Boolean = !a.hash.isEmpty && !b.hash.isEmpty && sameCompilation(a.compilation, b.compilation) && (a.hash deepEquals b.hash)
|
||||
def sameCompilation(a: Compilation, b: Compilation): Boolean = a.startTime == b.startTime && a.target == b.target
|
||||
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
|
|||
{
|
||||
import Flags._
|
||||
new xsbti.api.Modifiers(s.hasFlag(ABSTRACT) || s.hasFlag(DEFERRED), s.hasFlag(OVERRIDE),
|
||||
s.isFinal, s.hasFlag(SEALED), isImplicit(s), s.hasFlag(LAZY))
|
||||
s.isFinal, s.hasFlag(SEALED), isImplicit(s), s.hasFlag(LAZY), hasMacro(s))
|
||||
}
|
||||
|
||||
private def isImplicit(s: Symbol) = s.hasFlag(Flags.IMPLICIT)
|
||||
|
|
|
|||
|
|
@ -130,12 +130,21 @@ abstract class Compat
|
|||
def LOCALCHILD = sourceCompatibilityOnly
|
||||
|
||||
def NullaryMethodType = NullaryMethodTpe
|
||||
|
||||
def MACRO = DummyValue
|
||||
}
|
||||
// in 2.9, NullaryMethodType was added to Type
|
||||
object NullaryMethodTpe {
|
||||
def unapply(t: Type): Option[Type] = None
|
||||
}
|
||||
|
||||
val DummyValue = 0
|
||||
def hasMacro(s: Symbol): Boolean =
|
||||
{
|
||||
val MACRO = Flags.MACRO // will be DummyValue for versions before 2.10
|
||||
MACRO != DummyValue && s.hasFlag(MACRO)
|
||||
}
|
||||
|
||||
private[this] def sourceCompatibilityOnly: Nothing = throw new RuntimeException("For source compatibility only: should not get here.")
|
||||
|
||||
private[this] final implicit def miscCompat(n: AnyRef): MiscCompat = new MiscCompat
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ Source
|
|||
hash: Byte*
|
||||
api: SourceAPI
|
||||
apiHash: Int
|
||||
hasMacro: Boolean
|
||||
|
||||
SourceAPI
|
||||
packages : Package*
|
||||
|
|
|
|||
|
|
@ -8,13 +8,14 @@ public final class Modifiers implements java.io.Serializable
|
|||
private static final int SealedBit = 3;
|
||||
private static final int ImplicitBit = 4;
|
||||
private static final int LazyBit = 5;
|
||||
private static final int MacroBit = 6;
|
||||
|
||||
private static final int flag(boolean set, int bit)
|
||||
{
|
||||
return set ? (1 << bit) : 0;
|
||||
}
|
||||
|
||||
public Modifiers(boolean isAbstract, boolean isOverride, boolean isFinal, boolean isSealed, boolean isImplicit, boolean isLazy)
|
||||
public Modifiers(boolean isAbstract, boolean isOverride, boolean isFinal, boolean isSealed, boolean isImplicit, boolean isLazy, boolean isMacro)
|
||||
{
|
||||
this.flags = (byte)(
|
||||
flag(isAbstract, AbstractBit) |
|
||||
|
|
@ -22,7 +23,8 @@ public final class Modifiers implements java.io.Serializable
|
|||
flag(isFinal, FinalBit) |
|
||||
flag(isSealed, SealedBit) |
|
||||
flag(isImplicit, ImplicitBit) |
|
||||
flag(isLazy, LazyBit)
|
||||
flag(isLazy, LazyBit) |
|
||||
flag(isMacro, MacroBit)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -62,8 +64,12 @@ public final class Modifiers implements java.io.Serializable
|
|||
{
|
||||
return flag(LazyBit);
|
||||
}
|
||||
public final boolean isMacro()
|
||||
{
|
||||
return flag(MacroBit);
|
||||
}
|
||||
public String toString()
|
||||
{
|
||||
return "Modifiers(" + "isAbstract: " + isAbstract() + ", " + "isOverride: " + isOverride() + ", " + "isFinal: " + isFinal() + ", " + "isSealed: " + isSealed() + ", " + "isImplicit: " + isImplicit() + ", " + "isLazy: " + isLazy()+ ")";
|
||||
return "Modifiers(" + "isAbstract: " + isAbstract() + ", " + "isOverride: " + isOverride() + ", " + "isFinal: " + isFinal() + ", " + "isSealed: " + isSealed() + ", " + "isImplicit: " + isImplicit() + ", " + "isLazy: " + isLazy() + ", " + "isMacro: " + isMacro()+ ")";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,7 +143,8 @@ object Sbt extends Build
|
|||
val loader = classpath.ClasspathUtilities.toLoader(scriptedSbtClasspath.files, scriptedSbtInstance.loader)
|
||||
val m = ModuleUtilities.getObject("sbt.test.ScriptedTests", loader)
|
||||
val r = m.getClass.getMethod("run", classOf[File], classOf[Boolean], classOf[String], classOf[String], classOf[String], classOf[Array[String]], classOf[File], classOf[Array[String]])
|
||||
try { r.invoke(m, sourcePath, true: java.lang.Boolean, v, sv, ssv, args.toArray[String], launcher, Array[String]()) }
|
||||
val launcherVmOptions = Array("-XX:MaxPermSize=256M") // increased after a failure in scripted source-dependencies/macro
|
||||
try { r.invoke(m, sourcePath, true: java.lang.Boolean, v, sv, ssv, args.toArray[String], launcher, launcherVmOptions) }
|
||||
catch { case ite: java.lang.reflect.InvocationTargetException => throw ite.getCause }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
package macro
|
||||
|
||||
object Client {
|
||||
Provider.tree(0)
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package macro
|
||||
|
||||
object Provider {
|
||||
def macro tree(args: Any) = reify(args)
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package macro
|
||||
|
||||
object Provider {
|
||||
def macro tree(args: Any) = sys.error("no macro for you!")
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import sbt._
|
||||
import Keys._
|
||||
|
||||
object build extends Build {
|
||||
val defaultSettings = Seq(
|
||||
scalaVersion := "2.10.0-M2",
|
||||
scalacOptions += "-Xmacros"
|
||||
)
|
||||
|
||||
lazy val root = Project(
|
||||
base = file("."),
|
||||
id = "macro",
|
||||
aggregate = Seq(macroProvider, macroClient),
|
||||
settings = Defaults.defaultSettings ++ defaultSettings
|
||||
)
|
||||
|
||||
lazy val macroProvider = Project(
|
||||
base = file("macro-provider"),
|
||||
id = "macro-provider",
|
||||
settings = Defaults.defaultSettings ++ defaultSettings
|
||||
)
|
||||
|
||||
lazy val macroClient = Project(
|
||||
base = file("macro-client"),
|
||||
id = "macro-client",
|
||||
dependencies = Seq(macroProvider),
|
||||
settings = Defaults.defaultSettings ++ defaultSettings
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
> compile
|
||||
|
||||
# replace macro with one that throws an error
|
||||
|
||||
$ copy-file macro-provider/changes/Provider.scala macro-provider/Provider.scala
|
||||
|
||||
> macro-provider/compile
|
||||
|
||||
-> macro-client/compile
|
||||
|
||||
> clean
|
||||
|
||||
-> compile
|
||||
Loading…
Reference in New Issue