work on source api parts

This commit is contained in:
Mark Harrah 2010-01-22 20:17:49 -05:00
parent b4c5168d1f
commit 5f9f3729d1
16 changed files with 787 additions and 294 deletions

View File

@ -29,6 +29,7 @@ object APIFormat
try { objIn.readObject().asInstanceOf[Source] }
finally { objIn.close() }
}
catch { case _: java.io.EOFException | _: java.io.InvalidClassException => new xsbti.api.Source(Array(), Array())}
catch { case _: java.io.EOFException | _: java.io.InvalidClassException => emptySource}
}
def emptySource: Source = new Source(Array(), Array())
}

View File

@ -1,5 +1,5 @@
/* sbt -- Simple Build Tool
* Copyright 2009 Mark Harrah
* Copyright 2009, 2010 Mark Harrah
*/
package xsbt.api
@ -13,111 +13,88 @@ object SameAPI
def apply(a: Source, b: Source) =
{
val start = System.currentTimeMillis
val result = (new SameAPI).check(a,b)
println("\n=========== API #1 ================")
import DefaultShowAPI._
println(ShowAPI.show(a))
println("\n=========== API #2 ================")
println(ShowAPI.show(b))
val result = (new SameAPI(a,b)).check
val end = System.currentTimeMillis
println(" API comparison took: " + (end - start) / 1000.0 + " s")
result
}
}
private class SameAPI
private class SameAPI(a: Source, b: Source)
{
abstract class Result(val referencesMap: immutable.Map[Int, Int], val parameterMap: immutable.Map[Int, Int]) extends NotNull
private lazy val tagsA = TagTypeVariables(a)
private lazy val tagsB = TagTypeVariables(b)
def debug(flag: Boolean, msg: => String): Boolean =
{
def &&(o: => Result): Result
def value: Boolean
}
final object False extends Result(Map.empty, Map.empty)
{
def &&(o: => Result): Result = this
def value = false
}
final class True(references: immutable.Map[Int, Int], parameters: immutable.Map[Int, Int]) extends Result(references, parameters)
{
def value = true
def &&(o: => Result): Result = o match { case False => o; case t: True => newTrue(t) }
private def newTrue(t: True): True =
{
def equalIntersection(a: Map[Int, Int], b: Map[Int, Int]) =
{
val sameKeys = a.keySet ** b.keySet
assume(sameKeys.forall(key => a(key) == b(key)))
}
equalIntersection(referencesMap, t.referencesMap)
equalIntersection(parameterMap, t.parameterMap)
new True(referencesMap ++ t.referencesMap, parameterMap ++ t.parameterMap)
}
}
implicit def b2b(b: Boolean): Result = if(b) new True(Map.empty, Map.empty) else False
def debug(flag: Result, msg: => String): Result =
{
if(!flag.value) println(msg)
if(!flag) println(msg)
flag
}
def printMaps(b: Result) = println(mapStrings(b))
def mapStrings(b: Result) =
lazy val check: Boolean =
{
"parameter map:\n\t" +
b.parameterMap.mkString("\n\t") +
"\nreference map:\n\t" +
b.referencesMap.mkString("\n\t")
}
def check(a: Source, b: Source): Boolean =
{
val initialResult =
samePackages(a, b) &&
debug(sameDefinitions(a, b), "Definitions differed")
initialResult.value &&
debug(sameReferences(initialResult.referencesMap, initialResult.parameterMap), "References differed: \n" + mapStrings(initialResult)).value
samePackages(a, b) &&
debug(sameDefinitions(a, b), "Definitions differed")
}
def sameReferences(referencesMap: Map[Int,Int], parameterMap: Map[Int,Int]): Boolean =
referencesMap.forall { case (a, b) => parameterMap(a) == b }
def samePackages(a: Source, b: Source): Result =
def samePackages(a: Source, b: Source): Boolean =
sameStrings(packages(a), packages(b))
def packages(s: Source): Set[String] =
Set() ++ s.packages.map(_.name)
def sameDefinitions(a: Source, b: Source): Result =
def sameDefinitions(a: Source, b: Source): Boolean =
sameDefinitions(a.definitions, b.definitions)
def sameDefinitions(a: Seq[Definition], b: Seq[Definition]): Result =
def sameDefinitions(a: Seq[Definition], b: Seq[Definition]): Boolean =
{
val (avalues, atypes) = separateDefinitions(a)
val (bvalues, btypes) = separateDefinitions(b)
val (avalues, atypes) = separateDefinitions(filterDefinitions(a))
val (bvalues, btypes) = separateDefinitions(filterDefinitions(b))
debug(sameDefinitions(byName(avalues), byName(bvalues)), "Value definitions differed") &&
debug(sameDefinitions(byName(atypes), byName(btypes)), "Type definitions differed")
}
def separateDefinitions(s: Seq[Definition]): (Seq[Definition], Seq[Definition]) =
s.toArray.partition(isValueDefinition)
def sameDefinitions(a: scala.collection.Map[String, List[Definition]], b: scala.collection.Map[String, List[Definition]]): Result =
debug(sameStrings(a.keySet, b.keySet), "\tDefinition strings differed") && forall(zippedEntries(a,b))(tupled(sameNamedDefinitions))
def sameDefinitions(a: scala.collection.Map[String, List[Definition]], b: scala.collection.Map[String, List[Definition]]): Boolean =
debug(sameStrings(a.keySet, b.keySet), "\tDefinition strings differed") && zippedEntries(a,b).forall(tupled(sameNamedDefinitions))
def sameNamedDefinitions(a: List[Definition], b: List[Definition]): Result =
def filterDefinitions(d: Seq[Definition]) = d.filter(isNonPrivate)
def isNonPrivate(d: Definition): Boolean = isNonPrivate(d.access)
def isNonPrivate(a: Access): Boolean =
a match
{
case p: Private if p.qualifier.isInstanceOf[IdQualifier] => false
case _ => true
}
def sameNamedDefinitions(a: List[Definition], b: List[Definition]): Boolean =
{
println("Comparing " + a.size + " defs against " + b.size + " defs")
def sameDefs(a: List[Definition], b: List[Definition], v: Result): Result =
def show(x: List[Definition]) = x.map(DefaultShowAPI.apply).mkString("{\n\t", "\n\t", "\n}\n")
//println("Comparing \n\t" + show(a) + "\nagainst\n\t" + show(b))
def sameDefs(a: List[Definition], b: List[Definition]): Boolean =
{
println("\t " + a.size + " to " + b.size + "")
a match
{
case adef :: atail =>
def sameDef(seen: List[Definition], remaining: List[Definition]): Result =
def sameDef(seen: List[Definition], remaining: List[Definition]): Boolean =
remaining match
{
case Nil => debug(false, "Definition different in new API: \n" + adef.name )
case bdef :: btail =>
val eq = v && sameDefinitionContent(adef, bdef)
if(eq.value) printMaps(eq)
if(eq.value) sameDefs(atail, seen ::: btail, eq) else sameDef(bdef :: seen, btail)
val eq = sameDefinitionContent(adef, bdef)
if(eq) sameDefs(atail, seen ::: btail) else sameDef(bdef :: seen, btail)
}
sameDef(Nil, b)
case Nil => v
case Nil => true
}
}
//if(a.length > 1) println("Comparing\n" + a.mkString("\n\t") + "\nagainst\n" + b.mkString("\n\t") + "\n\n")
debug((a.length == b.length), "\t\tLength differed for " + a.headOption.map(_.name).getOrElse("empty")) && sameDefs(a, b, true)
debug((a.length == b.length), "\t\tLength differed for " + a.headOption.map(_.name).getOrElse("empty")) && sameDefs(a, b)
}
def isValueDefinition(d: Definition): Boolean =
@ -138,14 +115,14 @@ private class SameAPI
}
// doesn't check name
def sameDefinitionContent(a: Definition, b: Definition): Result =
def sameDefinitionContent(a: Definition, b: Definition): Boolean =
//a.name == b.name &&
debug(sameAccess(a.access, b.access), "Access differed") &&
debug(sameModifiers(a.modifiers, b.modifiers), "Modifiers differed") &&
debug(sameAnnotations(a.annotations, b.annotations), "Annotations differed") &&
debug(sameDefinitionSpecificAPI(a, b), "Definition-specific differed")
def sameAccess(a: Access, b: Access): Result =
def sameAccess(a: Access, b: Access): Boolean =
(a, b) match
{
case (_: Public, _: Public) => true
@ -154,9 +131,9 @@ private class SameAPI
case (qa: Pkg, qb: Pkg) => sameQualifier(qa, qb)
case _ => debug(false, "Different access categories")
}
def sameQualifier(a: Qualified, b: Qualified): Result =
def sameQualifier(a: Qualified, b: Qualified): Boolean =
sameQualifier(a.qualifier, b.qualifier)
def sameQualifier(a: Qualifier, b: Qualifier): Result =
def sameQualifier(a: Qualifier, b: Qualifier): Boolean =
(a, b) match
{
case (_: Unqualified, _: Unqualified) => true
@ -165,7 +142,7 @@ private class SameAPI
case _ => debug(false, "Different qualifier categories: " + a.getClass.getName + " -- " +b.getClass.getName)
}
def sameModifiers(a: Modifiers, b: Modifiers): Result =
def sameModifiers(a: Modifiers, b: Modifiers): Boolean =
bitSet(a) == bitSet(b)
def bitSet(m: Modifiers): immutable.BitSet =
@ -182,20 +159,20 @@ private class SameAPI
setIf(bs, isSynthetic, 7)
bs.toImmutable
}
def setIf(bs: mutable.BitSet, flag: Result, i: Int): Unit =
if(flag.value) bs += i
def setIf(bs: mutable.BitSet, flag: Boolean, i: Int): Unit =
if(flag) bs += i
def sameAnnotations(a: Seq[Annotation], b: Seq[Annotation]): Result =
def sameAnnotations(a: Seq[Annotation], b: Seq[Annotation]): Boolean =
sameSeq(a, b)(sameAnnotation)
def sameAnnotation(a: Annotation, b: Annotation): Result =
def sameAnnotation(a: Annotation, b: Annotation): Boolean =
debug(sameSimpleType(a.base, b.base), "Annotation base type differed") &&
debug(sameAnnotationArguments(a.arguments, b.arguments), "Annotation arguments differed (" + a + ") and (" + b + ")")
def sameAnnotationArguments(a: Seq[AnnotationArgument], b: Seq[AnnotationArgument]): Result =
def sameAnnotationArguments(a: Seq[AnnotationArgument], b: Seq[AnnotationArgument]): Boolean =
argumentMap(a) == argumentMap(b)
def argumentMap(a: Seq[AnnotationArgument]): Map[String,String] =
Map() ++ a.map(arg => (arg.name, arg.value))
def sameDefinitionSpecificAPI(a: Definition, b: Definition): Result =
def sameDefinitionSpecificAPI(a: Definition, b: Definition): Boolean =
(a, b) match
{
case (fa: FieldLike, fb: FieldLike) => sameFieldSpecificAPI(fa, fb)
@ -203,11 +180,11 @@ private class SameAPI
case _ => false
}
def sameParameterizedDefinition(a: ParameterizedDefinition, b: ParameterizedDefinition): Result =
def sameParameterizedDefinition(a: ParameterizedDefinition, b: ParameterizedDefinition): Boolean =
debug(sameTypeParameters(a.typeParameters, b.typeParameters), "Different type parameters for " + a.name) &&
sameParameterizedSpecificAPI(a, b)
def sameParameterizedSpecificAPI(a: ParameterizedDefinition, b: ParameterizedDefinition): Result =
def sameParameterizedSpecificAPI(a: ParameterizedDefinition, b: ParameterizedDefinition): Boolean =
(a, b) match
{
case (da: Def, db: Def) => sameDefSpecificAPI(da, db)
@ -217,19 +194,19 @@ private class SameAPI
case _ => false
}
def sameDefSpecificAPI(a: Def, b: Def): Result =
def sameDefSpecificAPI(a: Def, b: Def): Boolean =
debug(sameValueParameters(a.valueParameters, b.valueParameters), "Different def value parameters for " + a.name) &&
debug(sameType(a.returnType, b.returnType), "Different def return type for " + a.name)
def sameAliasSpecificAPI(a: TypeAlias, b: TypeAlias): Result =
def sameAliasSpecificAPI(a: TypeAlias, b: TypeAlias): Boolean =
debug(sameType(a.tpe, b.tpe), "Different alias type for " + a.name)
def sameDeclarationSpecificAPI(a: TypeDeclaration, b: TypeDeclaration): Result =
def sameDeclarationSpecificAPI(a: TypeDeclaration, b: TypeDeclaration): Boolean =
debug(sameType(a.lowerBound, b.lowerBound), "Different lower bound for declaration " + a.name) &&
debug(sameType(a.upperBound, b.upperBound), "Different upper bound for declaration " + a.name)
def sameFieldSpecificAPI(a: FieldLike, b: FieldLike): Result =
def sameFieldSpecificAPI(a: FieldLike, b: FieldLike): Boolean =
debug(sameFieldCategory(a, b), "Different field categories (" + a.name + "=" + a.getClass.getName + " -- " +a.name + "=" + a.getClass.getName + ")")&&
debug(sameType(a.tpe, b.tpe), "Different field type for " + a.name)
def sameFieldCategory(a: FieldLike, b: FieldLike): Result =
def sameFieldCategory(a: FieldLike, b: FieldLike): Boolean =
(a,b) match
{
case (_: Val, _: Val) => true
@ -237,48 +214,47 @@ private class SameAPI
case _=> false
}
def sameClassLikeSpecificAPI(a: ClassLike, b: ClassLike): Result =
def sameClassLikeSpecificAPI(a: ClassLike, b: ClassLike): Boolean =
sameDefinitionType(a.definitionType, b.definitionType) &&
sameType(a.selfType, b.selfType) &&
sameStructure(a.structure, b.structure)
def sameValueParameters(a: Seq[ParameterList], b: Seq[ParameterList]): Result =
def sameValueParameters(a: Seq[ParameterList], b: Seq[ParameterList]): Boolean =
sameSeq(a, b)(sameParameterList)
def sameParameterList(a: ParameterList, b: ParameterList): Result =
((a.isImplicit == b.isImplicit): Result) &&
def sameParameterList(a: ParameterList, b: ParameterList): Boolean =
(a.isImplicit == b.isImplicit) &&
sameParameters(a.parameters, b.parameters)
def sameParameters(a: Seq[MethodParameter], b: Seq[MethodParameter]): Result =
def sameParameters(a: Seq[MethodParameter], b: Seq[MethodParameter]): Boolean =
sameSeq(a, b)(sameMethodParameter)
def sameMethodParameter(a: MethodParameter, b: MethodParameter): Result =
((a.name == b.name): Result) &&
def sameMethodParameter(a: MethodParameter, b: MethodParameter): Boolean =
(a.name == b.name) &&
sameType(a.tpe, b.tpe) &&
(a.hasDefault == b.hasDefault) &&
sameParameterModifier(a.modifier, b.modifier)
def sameParameterModifier(a: ParameterModifier, b: ParameterModifier) =
a == b
def sameDefinitionType(a: DefinitionType, b: DefinitionType): Result =
def sameDefinitionType(a: DefinitionType, b: DefinitionType): Boolean =
a == b
def sameVariance(a: Variance, b: Variance): Result =
def sameVariance(a: Variance, b: Variance): Boolean =
a == b
def sameTypeParameters(a: Seq[TypeParameter], b: Seq[TypeParameter]): Result =
def sameTypeParameters(a: Seq[TypeParameter], b: Seq[TypeParameter]): Boolean =
debug(sameSeq(a, b)(sameTypeParameter), "Different type parameters")
def sameTypeParameter(a: TypeParameter, b: TypeParameter): Result =
def sameTypeParameter(a: TypeParameter, b: TypeParameter): Boolean =
sameTypeParameters(a.typeParameters, b.typeParameters) &&
debug(sameAnnotations(a.annotations, b.annotations), "Different type parameter annotations") &&
debug(sameVariance(a.variance, b.variance), "Different variance") &&
debug(sameType(a.lowerBound, b.lowerBound), "Different lower bound") &&
debug(sameType(a.upperBound, b.upperBound), "Different upper bound") &&
debug(mapSameParameters(a, b), "Different type parameter bindings")
debug(sameTags(a.id, b.id), "Different type parameter bindings")
def sameTags(a: Int, b: Int): Boolean = tagsA(a) == tagsB(b)
def mapSameParameters(a: TypeParameter, b: TypeParameter): Result =
mapParameters(a.id, b.id)
def mapParameters(a: Int, b: Int) = new True(Map.empty, Map( a -> b ))
def sameType(a: Type, b: Type): Result =
def sameType(a: Type, b: Type): Boolean =
(a, b) match
{
case (sa: SimpleType, sb: SimpleType) => debug(sameSimpleType(sa, sb), "Different simple types: " + sa + " and " + {Thread.dumpStack; sb})
case (sa: SimpleType, sb: SimpleType) => debug(sameSimpleType(sa, sb), "Different simple types: " + DefaultShowAPI(sa) + " and " + DefaultShowAPI(sb))
case (aa: Annotated, ab: Annotated) => debug(sameAnnotatedType(aa, ab), "Different annotated types")
case (sa: Structure, sb: Structure) => debug(sameStructure(sa, sb), "Different structure type")
case (ea: Existential, eb: Existential) => debug(sameExistentialType(ea, eb), "Different existential type")
@ -286,24 +262,24 @@ private class SameAPI
case _ => false
}
def sameExistentialType(a: Existential, b: Existential): Result =
def sameExistentialType(a: Existential, b: Existential): Boolean =
sameTypeParameters(a.clause, b.clause) &&
sameType(a.baseType, b.baseType)
def samePolymorphicType(a: Polymorphic, b: Polymorphic): Result =
def samePolymorphicType(a: Polymorphic, b: Polymorphic): Boolean =
sameTypeParameters(a.parameters, b.parameters) &&
sameType(a.baseType, b.baseType)
def sameAnnotatedType(a: Annotated, b: Annotated): Result =
def sameAnnotatedType(a: Annotated, b: Annotated): Boolean =
sameSimpleType(a.baseType, b.baseType) &&
sameAnnotations(a.annotations, b.annotations)
def sameStructure(a: Structure, b: Structure): Result =
def sameStructure(a: Structure, b: Structure): Boolean =
sameSeq(a.parents, b.parents)(sameType) &&
sameMembers(a.declarations, b.declarations) &&
sameMembers(a.declared, b.declared) &&
sameMembers(a.inherited, b.inherited)
def sameMembers(a: Seq[Definition], b: Seq[Definition]): Result =
def sameMembers(a: Seq[Definition], b: Seq[Definition]): Boolean =
sameDefinitions(a, b)
def sameSimpleType(a: SimpleType, b: SimpleType): Result =
def sameSimpleType(a: SimpleType, b: SimpleType): Boolean =
(a, b) match
{
case (pa: Projection, pb: Projection) => debug(sameProjection(pa, pb), "Different projection")
@ -314,23 +290,21 @@ private class SameAPI
case _ => debug(false, "Different category of simple type (" + a.getClass.getName + " and " + b.getClass.getName + ") for (" + a + " and " + b + ")")
}
def sameParameterized(a: Parameterized, b: Parameterized): Result =
def sameParameterized(a: Parameterized, b: Parameterized): Boolean =
sameSimpleType(a.baseType, b.baseType) &&
sameSeq(a.typeArguments, b.typeArguments)(sameType)
def sameParameterRef(a: ParameterRef, b: ParameterRef): Result =
mapReferences(a.id, b.id)
def mapReferences(a: Int, b: Int) = new True(Map( a -> b ), Map.empty)
def sameSingleton(a: Singleton, b: Singleton): Result =
def sameParameterRef(a: ParameterRef, b: ParameterRef): Boolean = sameTags(a.id, b.id)
def sameSingleton(a: Singleton, b: Singleton): Boolean =
samePath(a.path, b.path)
def sameProjection(a: Projection, b: Projection): Result =
def sameProjection(a: Projection, b: Projection): Boolean =
sameSimpleType(a.prefix, b.prefix) &&
(a.id == b.id)
def samePath(a: Path, b: Path): Result =
def samePath(a: Path, b: Path): Boolean =
samePathComponents(a.components, b.components)
def samePathComponents(a: Seq[PathComponent], b: Seq[PathComponent]): Result =
def samePathComponents(a: Seq[PathComponent], b: Seq[PathComponent]): Boolean =
sameSeq(a, b)(samePathComponent)
def samePathComponent(a: PathComponent, b: PathComponent): Result =
def samePathComponent(a: PathComponent, b: PathComponent): Boolean =
(a, b) match
{
case (_: This, _: This) => true
@ -338,25 +312,19 @@ private class SameAPI
case (ia: Id, ib: Id) => samePathId(ia, ib)
case _ => false
}
def samePathSuper(a: Super, b: Super): Result =
def samePathSuper(a: Super, b: Super): Boolean =
samePath(a.qualifier, b.qualifier)
def samePathId(a: Id, b: Id): Result =
def samePathId(a: Id, b: Id): Boolean =
a.id == b.id
// precondition: a.keySet == b.keySet
protected def zippedEntries[A,B](a: scala.collection.Map[A,B], b: scala.collection.Map[A,B]): Iterable[(B,B)] =
for( (key, avalue) <- a) yield (avalue, b(key))
def sameStrings(a: scala.collection.Set[String], b: scala.collection.Set[String]): Result =
def sameStrings(a: scala.collection.Set[String], b: scala.collection.Set[String]): Boolean =
a == b
final def sameSeq[T](a: Seq[T], b: Seq[T])(eq: (T,T) => Result): Result =
final def sameSeq[T](a: Seq[T], b: Seq[T])(eq: (T,T) => Boolean): Boolean =
sameArray(a.toArray, b.toArray)(eq)
final def sameArray[T](a: Array[T], b: Array[T])(eq: (T,T) => Result): Result =
((a.length == b.length): Result) && forall(a.zip(b))(tupled(eq))
final def forall[T](a: Iterable[T])(f: T => Result): Result = forallList(a.toList, true)(f)
final def forallList[T](a: List[T], v: Result)(f: T => Result): Result =
if(v.value) ( a match { case Nil => v; case x :: xs => forallList(xs, v && f(x) )(f) } )
else v
def defaultEquals[T <: AnyRef] = (a: T, b: T) => a == b
final def sameArray[T](a: Array[T], b: Array[T])(eq: (T,T) => Boolean): Boolean =
(a.length == b.length) && (a zip b).forall(tupled(eq))
}

302
compile/api/ShowAPI.scala Normal file
View File

@ -0,0 +1,302 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package xsbt.api
import xsbti.api._
trait Show[A]
{
def show(a: A): String
}
final class ShowLazy[A](delegate: => Show[A]) extends Show[A]
{
private lazy val s = delegate
def show(a: A) = s.show(a)
}
import ShowAPI._
object ShowAPI
{
def Show[T](implicit s: Show[T]): Show[T] = s
def show[T](t: T)(implicit s: Show[T]): String = s.show(t)
def bounds(lower: Type, upper: Type)(implicit t: Show[Type]): String =
">: " + t.show(lower) + " <: " + t.show(upper)
import ParameterModifier._
def parameterModifier(base: String, pm: ParameterModifier): String =
pm match
{
case Plain => base
case Repeated => base + "*"
case ByName => "=> " + base
}
def concat[A](list: Seq[A], as: Show[A], sep: String): String = mapSeq(list, as).mkString(sep)
def commas[A](list: Seq[A], as: Show[A]): String = concat(list, as, ", ")
def spaced[A](list: Seq[A], as: Show[A]): String = concat(list, as, " ")
def lines[A](list: Seq[A], as: Show[A]): String = mapSeq(list, as).mkString("", "\n", "\n")
def mapSeq[A](list: Seq[A], as: Show[A]): Seq[String] = list.map(as.show)
}
trait ShowBase
{
implicit def showAnnotation(implicit as: Show[AnnotationArgument], t: Show[Type]): Show[Annotation] =
new Show[Annotation] { def show(a: Annotation) = "@" + t.show(a.base) + (if(a.arguments.isEmpty) "" else "(" + commas(a.arguments, as) + ")") }
implicit def showAnnotationArgument: Show[AnnotationArgument] =
new Show[AnnotationArgument] { def show(a: AnnotationArgument) = a.name + " = " + a.value }
import Variance._
implicit def showVariance: Show[Variance] =
new Show[Variance] { def show(v: Variance) = v match { case Invariant => ""; case Covariant => "+"; case Contravariant => "-" } }
implicit def showSource(implicit ps: Show[Package], ds: Show[Definition]): Show[Source] =
new Show[Source] { def show(a: Source) = lines(a.packages, ps) + lines(a.definitions, ds) }
implicit def showPackage: Show[Package] =
new Show[Package] { def show(pkg: Package) = "package " + pkg.name }
implicit def showAccess(implicit sq: Show[Qualified]): Show[Access] =
new Show[Access]
{
def show(a: Access) =
a match
{
case p: Public => "public"
case q: Qualified => sq.show(q)
}
}
implicit def showQualified(implicit sq: Show[Qualifier]): Show[Qualified] =
new Show[Qualified]
{
def show(q: Qualified) =
((q match
{
case p: Protected => "protected"
case p: Private => "private"
case p: Pkg => "package"
})
+ sq.show(q.qualifier) )
}
implicit def showQualifier: Show[Qualifier] =
new Show[Qualifier]
{
def show(q: Qualifier) =
q match
{
case _: Unqualified => ""
case _: ThisQualifier => "[this]"
case i: IdQualifier => "[" + i.value + "]"
}
}
implicit def showModifiers: Show[Modifiers] =
new Show[Modifiers]
{
def show(m: Modifiers) =
{
val mods =
(m.isOverride, "override") ::
(m.isFinal, "final") ::
(m.isSealed, "sealed") ::
(m.isImplicit, "implicit") ::
(m.isAbstract || m.isDeferred, "abstract") ::
(m.isLazy, "lazy") ::
(m.isSynthetic, "synthetic") ::
Nil
mods.filter(_._1).map(_._2).mkString(" ")
}
}
implicit def showDefinitionType: Show[DefinitionType] =
new Show[DefinitionType] {
import DefinitionType._
def show(dt: DefinitionType) =
dt match
{
case Trait => "trait"
case ClassDef => "class"
case Module => "object"
case PackageModule => "package object"
}
}
}
trait ShowDefinitions
{
implicit def showVal(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], t: Show[Type]): Show[Val] =
new Show[Val] { def show(v: Val) = definitionBase(v, "val")(acs, ms, ans) + ": " + t.show(v.tpe) }
implicit def showVar(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], t: Show[Type]): Show[Var] =
new Show[Var] { def show(v: Var) = definitionBase(v, "var")(acs, ms, ans) + ": " + t.show(v.tpe) }
implicit def showDef(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], vp: Show[Seq[ParameterList]], t: Show[Type]): Show[Def] =
new Show[Def] { def show(d: Def) = parameterizedDef(d, "def")(acs, ms, ans, tp) + vp.show(d.valueParameters) + ": " + t.show(d.returnType) }
implicit def showClassLike(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], dt: Show[DefinitionType], s: Show[Structure], t: Show[Type]): Show[ClassLike] =
new Show[ClassLike] { def show(cl: ClassLike) = parameterizedDef(cl, dt.show(cl.definitionType))(acs, ms, ans, tp) + " requires " + t.show(cl.selfType) + " extends " + s.show(cl.structure) }
implicit def showTypeAlias(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], t: Show[Type]): Show[TypeAlias] =
new Show[TypeAlias] { def show(ta: TypeAlias) = parameterizedDef(ta, "type")(acs, ms, ans, tp) + " = " + t.show(ta.tpe) }
implicit def showTypeDeclaration(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], t: Show[Type]): Show[TypeDeclaration] =
new Show[TypeDeclaration] { def show(td: TypeDeclaration) = parameterizedDef(td, "type")(acs, ms, ans, tp) + bounds(td.lowerBound, td.upperBound) }
def parameterizedDef(d: ParameterizedDefinition, label: String)(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]]): String =
definitionBase(d, label)(acs, ms, ans) + tp.show(d.typeParameters)
def definitionBase(d: Definition, label: String)(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation]): String =
spaced(d.annotations, ans) + " " + acs.show(d.access) + " " + ms.show(d.modifiers) + " " + label + " " + d.name
}
trait ShowDefinition
{
implicit def showDefinition(implicit vl: Show[Val], vr: Show[Var], ds: Show[Def], cl: Show[ClassLike], ta: Show[TypeAlias], td: Show[TypeDeclaration]): Show[Definition] =
new Show[Definition]
{
def show(d: Definition) =
d match
{
case v: Val => vl.show(v)
case v: Var => vr.show(v)
case d: Def => ds.show(d)
case c: ClassLike => cl.show(c)
case t: TypeAlias => ta.show(t)
case t: TypeDeclaration => td.show(t)
}
}
}
trait ShowType
{
implicit def showType(implicit s: Show[SimpleType], a: Show[Annotated], st: Show[Structure], e: Show[Existential], po: Show[Polymorphic]): Show[Type] =
new Show[Type]
{
def show(t: Type) =
t match
{
case q: SimpleType => s.show(q)
case q: Annotated => a.show(q)
case q: Structure => st.show(q)
case q: Existential => e.show(q)
case q: Polymorphic => po.show(q)
}
}
implicit def showSimpleType(implicit pr: Show[Projection], pa: Show[ParameterRef], si: Show[Singleton], et: Show[EmptyType], p: Show[Parameterized]): Show[SimpleType] =
new Show[SimpleType] {
def show(t: SimpleType) =
t match
{
case q: Projection => pr.show(q)
case q: ParameterRef => pa.show(q)
case q: Singleton => si.show(q)
case q: EmptyType => et.show(q)
case q: Parameterized => p.show(q)
}
}
}
trait ShowBasicTypes
{
implicit def showSingleton(implicit p: Show[Path]): Show[Singleton] =
new Show[Singleton] { def show(s: Singleton) = p.show(s.path) }
implicit def showEmptyType: Show[EmptyType] =
new Show[EmptyType] { def show(e: EmptyType) = "<empty>" }
implicit def showParameterRef: Show[ParameterRef] =
new Show[ParameterRef] { def show(p: ParameterRef) = "<" + p.id + ">" }
}
trait ShowTypes
{
implicit def showStructure(implicit t: Show[Type], d: Show[Definition]): Show[Structure] =
new Show[Structure] {
def show(s: Structure) =
concat(s.parents, t, " with ") + "\n{\n\tDeclared:" + lines(s.declared, d) + "\n\tInherited:" + lines(s.inherited, d) + "\n}"
}
implicit def showAnnotated(implicit as: Show[Annotation], t: Show[SimpleType]): Show[Annotated] =
new Show[Annotated] { def show(a: Annotated) = spaced(a.annotations, as) + " " + t.show(a.baseType) }
implicit def showProjection(implicit t: Show[SimpleType]): Show[Projection] =
new Show[Projection] { def show(p: Projection) = t.show(p.prefix) + "#" + p.id }
implicit def showParameterized(implicit t: Show[Type]): Show[Parameterized] =
new Show[Parameterized] { def show(p: Parameterized) = t.show(p.baseType) + mapSeq(p.typeArguments, t).mkString("[", ", ", "]") }
implicit def showExistential(implicit t: Show[Type], tp: Show[TypeParameter]): Show[Existential] =
new Show[Existential] {
def show(e: Existential) =
t.show(e.baseType) + e.clause.map(t => "type " + tp.show(t)).mkString(" forSome { ", "; ", "}")
}
implicit def showPolymorphic(implicit t: Show[Type], tps: Show[Seq[TypeParameter]]): Show[Polymorphic] =
new Show[Polymorphic] { def show(p: Polymorphic) = t.show(p.baseType) + tps.show(p.parameters) }
}
trait ShowPath
{
implicit def showPath(implicit pc: Show[PathComponent]): Show[Path] =
new Show[Path] { def show(p: Path) = mapSeq(p.components, pc).mkString(".") }
implicit def showPathComponent(implicit sp: Show[Path]): Show[PathComponent] =
new Show[PathComponent] {
def show(p: PathComponent) =
p match
{
case s: Super => "super[" + sp.show(s.qualifier) + "]"
case _: This => "this"
case i: Id => i.id
}
}
}
trait ShowValueParameters
{
implicit def showParameterLists(implicit pl: Show[ParameterList]): Show[Seq[ParameterList]] =
new Show[Seq[ParameterList]] { def show(p: Seq[ParameterList]) = concat(p,pl, "") }
implicit def showParameterList(implicit mp: Show[MethodParameter]): Show[ParameterList] =
new Show[ParameterList] { def show(pl: ParameterList) = "(" + (if(pl.isImplicit) "implicit " else "") + commas(pl.parameters, mp) + ")" }
implicit def showMethodParameter(implicit t: Show[Type]): Show[MethodParameter] =
new Show[MethodParameter] {
def show(mp: MethodParameter) =
mp.name + ": " + parameterModifier(t.show(mp.tpe), mp.modifier) + (if(mp.hasDefault) "= ..." else "")
}
}
trait ShowTypeParameters
{
implicit def showTypeParameters(implicit as: Show[TypeParameter]): Show[Seq[TypeParameter]] =
new Show[Seq[TypeParameter]] { def show(tps: Seq[TypeParameter]) = if(tps.isEmpty) "" else mapSeq(tps, as).mkString("[", ",", "]") }
implicit def showTypeParameter(implicit as: Show[Annotation], tp: Show[Seq[TypeParameter]], t: Show[Type], v: Show[Variance]): Show[TypeParameter] =
new Show[TypeParameter] {
def show(tps: TypeParameter) =
spaced(tps.annotations, as) + " " + v.show(tps.variance) + tps.id + tp.show(tps.typeParameters) + " " + bounds(tps.lowerBound, tps.upperBound)
}
}
object DefaultShowAPI extends ShowBase with ShowBasicTypes with ShowValueParameters
{
def apply(d: Definition) = ShowAPI.show(d)
def apply(d: Type) = ShowAPI.show(d)
implicit lazy val showVal: Show[Val] = Cyclic.showVal
implicit lazy val showVar: Show[Var] = Cyclic.showVar
implicit lazy val showClassLike: Show[ClassLike] = Cyclic.showClassLike
implicit lazy val showTypeDeclaration: Show[TypeDeclaration] = Cyclic.showTypeDeclaration
implicit lazy val showTypeAlias: Show[TypeAlias] = Cyclic.showTypeAlias
implicit lazy val showDef: Show[Def] = Cyclic.showDef
implicit lazy val showProj: Show[Projection] = Cyclic.showProjection
implicit lazy val showPoly: Show[Polymorphic] = Cyclic.showPolymorphic
implicit lazy val showSimple: Show[SimpleType] = new ShowLazy(Cyclic.showSimpleType)
implicit lazy val showAnnotated: Show[Annotated] = Cyclic.showAnnotated
implicit lazy val showExistential: Show[Existential] = Cyclic.showExistential
implicit lazy val showParameterized: Show[Parameterized] = Cyclic.showParameterized
implicit lazy val showTypeParameters: Show[Seq[TypeParameter]] = new ShowLazy(Cyclic.showTypeParameters)
implicit lazy val showTypeParameter: Show[TypeParameter] = Cyclic.showTypeParameter
implicit lazy val showDefinition: Show[Definition] = new ShowLazy(Cyclic.showDefinition)
implicit lazy val showType: Show[Type] = new ShowLazy(Cyclic.showType)
implicit lazy val showStructure: Show[Structure] = new ShowLazy(Cyclic.showStructure)
implicit lazy val showPath: Show[Path] = new ShowLazy(Cyclic.showPath)
implicit lazy val showPathComponent: Show[PathComponent] = Cyclic.showPathComponent
private object Cyclic extends ShowTypes with ShowType with ShowPath with ShowDefinition with ShowDefinitions with ShowTypeParameters
}

View File

@ -0,0 +1,137 @@
package xsbt.api
import xsbti.api._
object TagTypeVariables
{
def apply(s: Source): scala.collection.Map[Int, (Int, Int)] = (new TagTypeVariables).tag(s)
}
private class TagTypeVariables extends NotNull
{
private val tags = new scala.collection.mutable.HashMap[Int, (Int, Int)]
private var level = 0
private var index = 0
def tag(s: Source): scala.collection.Map[Int, (Int, Int)] =
{
s.definitions.foreach(tagDefinition)
tags
}
def tagDefinitions(ds: Seq[Definition]) = ds.foreach(tagDefinition)
def tagDefinition(d: Definition)
{
d match
{
case c: ClassLike => tagClass(c)
case f: FieldLike => tagField(f)
case d: Def => tagDef(d)
case t: TypeDeclaration => tagTypeDeclaration(t)
case t: TypeAlias => tagTypeAlias(t)
}
}
def tagClass(c: ClassLike): Unit =
tagParameterizedDefinition(c) {
tagType(c.selfType)
tagStructure(c.structure)
}
def tagField(f: FieldLike)
{
tagType(f.tpe)
tagAnnotations(f.annotations)
}
def tagDef(d: Def): Unit =
tagParameterizedDefinition(d) {
tagValueParameters(d.valueParameters)
tagType(d.returnType)
}
def tagValueParameters(valueParameters: Seq[ParameterList]) = valueParameters.foreach(tagValueParameterList)
def tagValueParameterList(list: ParameterList) = list.parameters.foreach(tagValueParameter)
def tagValueParameter(parameter: MethodParameter) = tagType(parameter.tpe)
def tagParameterizedDefinition[T <: ParameterizedDefinition](d: T)(tagExtra: => Unit)
{
tagAnnotations(d.annotations)
scope {
tagTypeParameters(d.typeParameters)
tagExtra
}
}
def tagTypeDeclaration(d: TypeDeclaration): Unit =
tagParameterizedDefinition(d) {
tagType(d.lowerBound)
tagType(d.upperBound)
}
def tagTypeAlias(d: TypeAlias): Unit =
tagParameterizedDefinition(d) {
tagType(d.tpe)
}
def tagTypeParameters(parameters: Seq[TypeParameter]) = parameters.foreach(tagTypeParameter)
def tagTypeParameter(parameter: TypeParameter)
{
recordTypeParameter(parameter.id)
scope {
tagTypeParameters(parameter.typeParameters)
tagType(parameter.lowerBound)
tagType(parameter.upperBound)
}
}
def tagAnnotations(annotations: Seq[Annotation]) = tagTypes(annotations.map(_.base))
def tagTypes(ts: Seq[Type]) = ts.foreach(tagType)
def tagType(t: Type)
{
t match
{
case s: Structure => tagStructure(s)
case e: Existential => tagExistential(e)
case p: Polymorphic => tagPolymorphic(p)
case a: Annotated => tagAnnotated(a)
case p: Parameterized => tagParameterized(p)
case p: Projection => tagProjection(p)
case _: EmptyType | _: Singleton | _: ParameterRef => ()
}
}
def tagExistential(e: Existential) = tagParameters(e.clause, e.baseType)
def tagPolymorphic(p: Polymorphic) = tagParameters(p.parameters, p.baseType)
def tagProjection(p: Projection) = tagType(p.prefix)
def tagParameterized(p: Parameterized)
{
tagType(p.baseType)
tagTypes(p.typeArguments)
}
def tagAnnotated(a: Annotated)
{
tagType(a.baseType)
tagAnnotations(a.annotations)
}
def tagStructure(structure: Structure)
{
tagTypes(structure.parents)
tagDefinitions(structure.declared)
tagDefinitions(structure.inherited)
}
def tagParameters(parameters: Seq[TypeParameter], base: Type): Unit =
scope {
tagTypeParameters(parameters)
tagType(base)
}
def scope(action: => Unit)
{
val saveIndex = index
index = 0
level += 1
action
level -= 1
index = saveIndex
}
def recordTypeParameter(id: Int)
{
tags(id) = (level, index)
index += 1
}
}

View File

@ -65,7 +65,7 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
if(pre == NoPrefix)
{
if(sym.isLocalClass) Constants.emptyType
else if(sym.isType) new xsbti.api.ParameterRef(sym.id)
else if(sym.isTypeParameterOrSkolem || sym.isExistential) new xsbti.api.ParameterRef(sym.id)
else error("Unknown prefixless type: " + sym)
}
else if(sym.isRoot || sym.isRootPackage) Constants.emptyType
@ -121,6 +121,7 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
(tpe, Plain)
new xsbti.api.MethodParameter(name, processType(t), hasDefault(s), special)
}
build(s.info, Array(), Nil)
}
private def hasDefault(s: Symbol) =
@ -168,7 +169,7 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
structure(info.baseClasses.map(_.tpe), declared, inherited) // linearization instead of parents
}
private def structure(parents: List[Type], declared: List[Symbol], inherited: List[Symbol]): xsbti.api.Structure =
new xsbti.api.Structure(types(parents), processDefinitions(declared), processDefinitions(inherited))
new xsbti.api.Structure(types(parents), processDefinitions(declared), Array())//processDefinitions(inherited))
private def processDefinitions(defs: List[Symbol]): Array[xsbti.api.Definition] = defs.toArray.map(definition)
private def definition(sym: Symbol): xsbti.api.Definition =
{
@ -226,11 +227,12 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
{
val varianceInt = s.variance
import xsbti.api.Variance._
val annots = annotations(s)
val variance = if(varianceInt < 0) Contravariant else if(varianceInt > 0) Covariant else Invariant
s.info match
{
case TypeBounds(low, high) => new xsbti.api.TypeParameter( s.id, typeParameters(s), variance, processType(low), processType(high) )
case PolyType(typeParams, base) => new xsbti.api.TypeParameter( s.id, typeParameters(typeParams), variance, processType(base.bounds.lo), processType(base.bounds.hi))
case TypeBounds(low, high) => new xsbti.api.TypeParameter( s.id, annots, typeParameters(s), variance, processType(low), processType(high) )
case PolyType(typeParams, base) => new xsbti.api.TypeParameter( s.id, annots, typeParameters(typeParams), variance, processType(base.bounds.lo), processType(base.bounds.hi))
case x => error("Unknown type parameter info: " + x.getClass)
}
}

View File

@ -1,10 +1,3 @@
Source
packages : Package*
definitions: Definition*
Package
name: String
Definition
name: String
access: Access
@ -29,91 +22,3 @@ Definition
TypeDeclaration
lowerBound: Type
upperBound: Type
Type
SimpleType
Projection
prefix : SimpleType
id : String
ParameterRef
id: Int
Singleton
path: Path
EmptyType
Parameterized
baseType : SimpleType
typeArguments: Type*
Annotated
baseType : SimpleType
annotations : Annotation*
Structure
parents : Type*
declarations: Definition*
inherited: Definition*
Existential
baseType : Type
clause: TypeParameter*
Polymorphic
baseType: Type
parameters: TypeParameter*
Access
Public
Qualified
qualifier: Qualifier
Protected
Private
Pkg
Qualifier
Unqualified
ThisQualifier
IdQualifier
value: String
Modifiers
isAbstract: Boolean
isDeferred: Boolean
isOverride: Boolean
isFinal: Boolean
isSealed: Boolean
isImplicit: Boolean
isLazy: Boolean
isSynthetic: Boolean
ParameterList
parameters: MethodParameter*
isImplicit: Boolean
MethodParameter
name: String
tpe: Type
hasDefault: Boolean
modifier: ParameterModifier
TypeParameter
id: Int
typeParameters : TypeParameter*
variance: Variance
lowerBound: Type
upperBound: Type
Annotation
base: SimpleType
arguments: AnnotationArgument*
AnnotationArgument
name: String
value: String
enum Variance : Contravariant, Covariant, Invariant
enum ParameterModifier : Repeated, Plain, ByName
enum DefinitionType : Trait, ClassDef, Module, PackageModule
Path
components: PathComponent*
PathComponent
Super
qualifier: Path
This
Id
id: String

68
interface/other Normal file
View File

@ -0,0 +1,68 @@
Source
packages : Package*
definitions: Definition*
Package
name: String
Access
Public
Qualified
qualifier: Qualifier
Protected
Private
Pkg
Qualifier
Unqualified
ThisQualifier
IdQualifier
value: String
Modifiers
isAbstract: Boolean
isDeferred: Boolean
isOverride: Boolean
isFinal: Boolean
isSealed: Boolean
isImplicit: Boolean
isLazy: Boolean
isSynthetic: Boolean
ParameterList
parameters: MethodParameter*
isImplicit: Boolean
MethodParameter
name: String
tpe: Type
hasDefault: Boolean
modifier: ParameterModifier
TypeParameter
id: Int
annotations: Annotation*
typeParameters : TypeParameter*
variance: Variance
lowerBound: Type
upperBound: Type
Annotation
base: SimpleType
arguments: AnnotationArgument*
AnnotationArgument
name: String
value: String
enum Variance : Contravariant, Covariant, Invariant
enum ParameterModifier : Repeated, Plain, ByName
enum DefinitionType : Trait, ClassDef, Module, PackageModule
Path
components: PathComponent*
PathComponent
Super
qualifier: Path
This
Id
id: String

27
interface/type Normal file
View File

@ -0,0 +1,27 @@
Type
SimpleType
Projection
prefix : SimpleType
id : String
ParameterRef
id: Int
Singleton
path: Path
EmptyType
Parameterized
baseType : SimpleType
typeArguments: Type*
Annotated
baseType : SimpleType
annotations : Annotation*
Structure
parents : Type*
declared: Definition*
inherited: Definition*
Existential
baseType : Type
clause: TypeParameter*
Polymorphic
baseType: Type
parameters: TypeParameter*

View File

@ -34,6 +34,6 @@
project.version: quick=set(1.0), new=prompt(Version)[1.0], fill=prompt(Version)[1.0]
def.scala.version: quick=set(2.7.7), new=set(2.7.7), fill=set(2.7.7)
build.scala.versions: quick=set(2.7.7), new=prompt(Scala version)[2.7.7], fill=prompt(Scala version)[2.7.7]
sbt.version: quick=set(0.6.9), new=prompt(sbt version)[0.6.9], fill=prompt(sbt version)[0.6.9]
sbt.version: quick=set(0.6.11), new=prompt(sbt version)[0.6.11], fill=prompt(sbt version)[0.6.11]
project.scratch: quick=set(true)
project.initialize: quick=set(true), new=set(true)

View File

@ -28,8 +28,8 @@ class AggressiveCompiler extends xsbti.AppMain
val classpath = outputDirectory map { _ ++ (cwd ** "*.jar") }
val cacheDirectory = cwd / "target" / "cache"
val options = Task(args.tail.toSeq)
val log = new ConsoleLogger with CompileLogger with IvyLogger { def verbose(msg: => String) = debug(msg) }
val componentManager = new ComponentManager(launcher.globalLock, app.components, log)
val log = new ConsoleLogger with CompileLogger with sbt.IvyLogger { def verbose(msg: => String) = debug(msg) }
val componentManager = new sbt.ComponentManager(launcher.globalLock, app.components, log)
val compiler = Task(new AnalyzingCompiler(ScalaInstance(args.head, launcher), componentManager))
val compileTask = AggressiveCompile(sources, classpath, outputDirectory, options, cacheDirectory, compiler, log)

View File

@ -4,7 +4,7 @@
[app]
org: org.scala-tools.sbt
name: alternate-compiler-test
version: 0.6.10-SNAPSHOT
version: 0.6.12-SNAPSHOT
class: xsbt.AggressiveCompiler
components: xsbti
cross-versioned: true

8
notes
View File

@ -12,9 +12,9 @@ As usual:
- Robust, flexible API
ISSUES
- Scala issue #2265: cannot use structural types or libraries that use structural types, or OOME: PermGen will occur. Running/testing user code with structural types might also cause OOME, but is unlikely unless the Scala standard library or compiler uses structural types.
- API: changing package name in access qualifier does not work: must be API phase that doesn't correctly extract the ID
- webapp: Jetty 7 + jsp
- java sources are not recompiled when jars change: possibly binary dependencies are not recorded?
TODO
- launcher interface versioning
- allow comments in datatype definition file
@ -22,8 +22,6 @@ TODO
- have background triggered commands (~sync)
update 0_6_Summary to mention different output directories.
look at permgen in Josh's ScalaTest
TrapExit handles System.exit(0) as nonzero exit code
Task engine
- method tasks will be normal tasks that pull the command line from a CommandLine task

View File

@ -67,6 +67,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
//run in parallel
override def parallelExecution = false
def jlineRev = "0.9.94"
def jlineDep = "jline" % "jline" % jlineRev intransitive()
override def managedStyle = ManagedStyle.Ivy
val publishTo = Resolver.file("test-repo", new File("/var/dbwww/repo/"))
@ -74,7 +75,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
/* Subproject configurations*/
class LaunchProject(info: ProjectInfo) extends Base(info) with TestWithIO with TestDependencies with ProguardLaunch
{
val jline = "jline" % "jline" % jlineRev intransitive()
val jline = jlineDep
val ivy = "org.apache.ivy" % "ivy" % "2.0.0"
def rawJarPath = jarPath
override final def crossScalaVersions = Set.empty // don't need to cross-build, since the distributed jar is standalone (proguard)
@ -146,13 +147,21 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
def generatedBasePath = srcManagedPath / "main" / "java"
/** Files that define the datatypes.*/
def apiDefinitionPaths: PathFinder = "definition"
def apiDefinitions = apiDefinitionPaths.get.toList.map(_.absolutePath)
/** Delete up the generated sources*/
lazy val cleanManagedSrc = cleanTask(srcManagedPath)
override def cleanAction = super.cleanAction dependsOn(cleanManagedSrc)
/** Runs the generator compiled by 'compile', putting the classes in src_managed and processing the definitions 'apiDefinitions'. */
lazy val generateSource = generateSourceAction dependsOn(cleanManagedSrc, datatypeSub.compile)
def generateSourceAction = runTask(datatypeSub.getMainClass(true), datatypeSub.runClasspath, "xsbti.api" :: generatedBasePath.absolutePath :: apiDefinitions)
def generateSourceTask(immutable: Boolean, pkg: String, apiDefinitions: PathFinder): Task =
{
val m = if(immutable) "immutable" else "mutable"
generateSourceTask(m :: pkg :: generatedBasePath.absolutePath :: apiDefinitions.get.toList.map(_.absolutePath))
}
def generateSourceTask(args: List[String]): Task =
runTask(datatypeSub.getMainClass(true), datatypeSub.runClasspath, args)
def generateSourceAction =
//generateSourceTask(false, "xsbti.api", "definition" +++ "type") &&
generateSourceTask(true, "xsbti.api", "other" +++ "definition" +++ "type")
/** compiles the generated sources */
override def compileAction = super.compileAction dependsOn(generateSource)
}
@ -169,6 +178,8 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
def xTestClasspath = projectClasspath(Configurations.Test)
def cID = "compiler-interface-src"
override def componentID = Some(cID)
// necessary because jline is not distributed with 2.8 and we will get a compile error
//val jline = jlineDep artifacts(Artifact("jline", Map("e:component" -> cID)))
override def ivyXML =
<dependencies>
<dependency org="jline" name="jline" rev={jlineRev} transitive="false">

View File

@ -66,16 +66,26 @@ class AggressiveCompile(val cacheDirectory: File, val compilerTask: Task[Analyzi
val classpath = classpathChanges.checked
val readTracker = tracker.read
// directories that are no longer on the classpath, not necessarily removed from the filesystem
val removedDirectories = classpathChanges.removed.filter(_.isDirectory)
def uptodate(time: Long, files: Iterable[File]) = files.forall(_.lastModified <= time)
def isProductOutofdate(product: File) = !product.exists || !uptodate(product.lastModified, readTracker.sources(product))
val outofdateProducts = readTracker.allProducts.filter(isProductOutofdate)
def isOutofdate(file: File, related: => Iterable[File]) = !file.exists || !uptodate(file.lastModified, related)
def isProductOutofdate(product: File) = isOutofdate(product, readTracker.sources(product))
def inRemovedDirectory(file: File) = removedDirectories.exists(dir => FileUtilities.relativize(dir, file).isDefined)
def isUsedOutofdate(file: File) = classpathChanges.modified(file) || inRemovedDirectory(file) || isOutofdate(file, readTracker.usedBy(file))
val rawInvalidatedSources =
classpathChanges.modified.flatMap(readTracker.usedBy) ++
sourceChanges.removed.flatMap(readTracker.dependsOn) ++
sourceChanges.modified ++
outofdateProducts.flatMap(readTracker.sources)
// these are products that no longer exist or are older than the sources that produced them
val outofdateProducts = readTracker.allProducts.filter(isProductOutofdate)
// used classes and jars that a) no longer exist b) are no longer on the classpath or c) are newer than the sources that use them
val outofdateUses = readTracker.allUsed.filter(isUsedOutofdate)
val modifiedSources = sourceChanges.modified
val invalidatedByClasspath = outofdateUses.flatMap(readTracker.usedBy)
val invalidatedByRemovedSrc = sourceChanges.removed.flatMap(readTracker.dependsOn)
val productsOutofdate = outofdateProducts.flatMap(readTracker.sources)
val rawInvalidatedSources = modifiedSources ++ invalidatedByClasspath ++ invalidatedByRemovedSrc ++ productsOutofdate
val invalidatedSources = scc(readTracker, rawInvalidatedSources)
val sources = invalidatedSources.filter(_.exists)
val previousAPIMap = Map() ++ sources.map { src => (src, APIFormat.read(readTracker.tag(src))) }

View File

@ -5,29 +5,31 @@ import java.io.File
/** Generates a datatype hierarchy from a definition file.*/
object GenerateDatatypes
{
/** Arguments: <base package name> <base directory> <input file>+*/
/** Arguments: ('mutable' | 'immutable') <base package name> <base directory> <input file>+*/
def main(args: Array[String])
{
if(args.length < 3)
if(args.length < 4)
{
System.err.println("Invalid number of arguments, expected package, base directory, and files to process")
System.err.println("Invalid number of arguments, expected 'mutable' or 'immutable', package, base directory, and files to process")
System.exit(1)
}
else
{
val packageName = args(0).trim
val immutable = args(0).trim.toLowerCase == "immutable"
val packageName = args(1).trim
require(!packageName.isEmpty)
val baseDirectory = new File(args(1))
val baseDirectory = new File(args(2))
baseDirectory.mkdirs
val files = args.drop(2).map(new File(_))
val files = args.drop(3).map(new File(_))
// parse the files, getting all interface and enums
val parser = new DatatypeParser
val definitions = files.flatMap(parser.parseFile)
// create the interfaces, enums, and class implementations
val generator = new Generator(packageName, baseDirectory)
val generator = if(immutable) new ImmutableGenerator(packageName, baseDirectory) else new MutableGenerator(packageName, baseDirectory)
generator.writeDefinitions(definitions)
}
}

View File

@ -3,6 +3,32 @@ package xsbt.api
import java.io.File
import xsbt.FileUtilities
import Generator._
abstract class GeneratorBase(val basePkgName: String, val baseDirectory: File) extends NotNull
{
def writeDefinitions(ds: Iterable[Definition]) = Generator.writeDefinitions(ds)(writeDefinition)
def writeDefinition(d: Definition) = d match { case e: EnumDef => writeEnum(e); case c: ClassDef => writeClass(c) }
def writeEnum(e: EnumDef)
{
val content =
"public enum " + e.name + " {" +
e.members.mkString("\n\t", ",\n\t", "\n") +
"}"
writeSource(e.name, basePkgName, content)
}
def writeClass(c: ClassDef): Unit
def writeSource(name: String, pkgName: String, content: String)
{
import Paths._
val file =baseDirectory / packagePath(pkgName) / (name+ ".java")
file.getParentFile.mkdirs()
FileUtilities.write(file, "package " + pkgName + ";\n\n" + content)
}
private def packagePath(pkgName: String) = pkgName.replace('.', File.separatorChar)
}
/** Creates immutable datatype classes in Java from the intermediate Definition representation.
*
* A ClassDef is written as a class with an optional parent class. The class has a single constructor with
@ -15,30 +41,9 @@ import xsbt.FileUtilities
*
*.@param baseDirectory output directory for sources
* @param pkgName package that classes will be defined in*/
class Generator(pkgName: String, baseDirectory: File)
class ImmutableGenerator(pkgName: String, baseDir: File) extends GeneratorBase(pkgName, baseDir)
{
def writeDefinitions(ds: Iterable[Definition]) =
{
val (_, duplicates) =
( (Set[String](), Set[String]()) /: ds.map(_.name)) {
case ((nameSet, duplicates), name) =>
if(nameSet.contains(name)) (nameSet, duplicates + name) else (nameSet + name, duplicates)
}
if(duplicates.isEmpty)
ds.foreach(writeDefinition)
else
error("Duplicate names:\n\t" + duplicates.mkString("\n\t"))
}
def writeDefinition(d: Definition) = d match { case e: EnumDef => write(e); case c: ClassDef => write(c) }
def write(e: EnumDef)
{
val content =
"public enum " + e.name + " {" +
e.members.mkString("\n\t", ",\n\t", "\n") +
"}"
writeSource(e.name, content)
}
def write(c: ClassDef): Unit = writeSource(c.name, classContent(c))
def writeClass(c: ClassDef): Unit = writeSource(c.name, basePkgName, classContent(c))
def classContent(c: ClassDef): String =
{
val hasParent = c.parent.isDefined
@ -53,8 +58,6 @@ class Generator(pkgName: String, baseDirectory: File)
val inherited = c.inheritedMembers
if(inherited.isEmpty) "" else "super(" + inherited.map(_.name).mkString(", ") + ");\n\t\t"
}
val parametersString = if(allMembers.isEmpty) "\"\"" else allMembers.map(m => fieldToString(m.name, m.single)).mkString(" + \", \" + ")
val toStringMethod = method("public", "String", "toString", "", "\"" + c.name + "(\" + " + parametersString + "+ \")\"")
val constructor = "public " + c.name + "(" + parameters.mkString(", ") + ")\n\t" +
"{\n\t\t" +
@ -67,23 +70,82 @@ class Generator(pkgName: String, baseDirectory: File)
"{\n\t" +
constructor + "\n\t" +
(fields ++ accessors).mkString("\n\t") + "\n\t" +
toStringMethod + "\n\t" +
"}"
toStringMethod(c) + "\n" +
"}\n"
}
}
class MutableGenerator(pkgName: String, baseDir: File) extends GeneratorBase(pkgName, baseDir)
{
def writeClass(c: ClassDef): Unit =
{
writeSource(c.name, basePkgName, interfaceContent(c))
writeSource(implName(c.name), basePkgName + ".mutable", implContent(c))
}
def interfaceContent(c: ClassDef): String =
{
val normalizedMembers = c.members.map(normalize)
val getters = normalizedMembers.map(m => "public " + m.asJavaDeclaration + "();")
val setters = normalizedMembers.map(m => "public void " + m.name + "(" + m.javaType + " newValue);")
val extendsPhrase = c.parent.map(_.name).map(" extends " + _).getOrElse("")
("public interface " + c.name + extendsPhrase + "\n" +
"{\n\t" +
(setters ++ getters).mkString("\n\t") + "\n" +
"}\n")
}
def implContent(c: ClassDef): String =
{
val normalizedMembers = c.members.map(normalize)
val fields = normalizedMembers.map(m => "private " + m.asJavaDeclaration + ";")
val getters = normalizedMembers.map(m => "public final " + m.asJavaDeclaration + "()\n\t{\n\t\treturn " + m.name + ";\n\t}")
val setters = normalizedMembers.map(m => "public final void " + m.name + "(" + m.javaType + " newValue)\n\t{\n\t\t" + m.name + " = newValue;\n\t}")
val extendsClass = c.parent.map(p => implName(p.name))
val serializable = if(c.parent.isDefined) Nil else "java.io.Serializable" :: Nil
val implements = c.name :: serializable
val extendsPhrase = extendsClass.map(" extends " + _).getOrElse("") + " implements " + implements.mkString(", ")
("import java.util.Arrays;\n" +
"import java.util.List;\n" +
"import " + pkgName + ".*;\n\n" +
"public class " + implName(c.name) + extendsPhrase + "\n" +
"{\n\t" +
(fields ++ getters ++ setters).mkString("\n\t") + "\n\t" +
toStringMethod(c) + "\n" +
"}\n")
}
}
object Generator
{
def methodSignature(modifiers: String, returnType: String, name: String, parameters: String) =
modifiers + " " + returnType + " " + name + "(" + parameters + ")"
def method(modifiers: String, returnType: String, name: String, parameters: String, content: String) =
modifiers + " " + returnType + " " + name + "(" + parameters + ")\n\t{\n\t\treturn " + content + ";\n\t}"
methodSignature(modifiers, returnType, name, parameters) + "\n\t{\n\t\treturn " + content + ";\n\t}"
def fieldToString(name: String, single: Boolean) = "\"" + name + ": \" + " + fieldString(name + "()", single)
def fieldString(arg: String, single: Boolean) = if(single) arg else "Arrays.toString(" + arg + ")"
def normalize(m: MemberDef): MemberDef =
m.mapType(tpe => if(primitives(tpe.toLowerCase)) tpe.toLowerCase else tpe)
def writeSource(name: String, content: String)
{
import Paths._
val file =baseDirectory / packagePath / (name+ ".java")
file.getParentFile.mkdirs()
FileUtilities.write(file, "package " + pkgName + ";\n\n" + content)
}
private def packagePath = pkgName.replace('.', File.separatorChar)
private val primitives = Set("int", "boolean", "float", "long", "short", "byte", "char", "double")
}
def toStringMethod(c: ClassDef): String =
{
val allMembers = c.allMembers.map(normalize)
val parametersString = if(allMembers.isEmpty) "\"\"" else allMembers.map(m => fieldToString(m.name, m.single)).mkString(" + \", \" + ")
method("public", "String", "toString", "", "\"" + c.name + "(\" + " + parametersString + "+ \")\"")
}
def writeDefinitions(ds: Iterable[Definition])(writeDefinition: Definition => Unit)
{
val (_, duplicates) =
( (Set[String](), Set[String]()) /: ds.map(_.name)) {
case ((nameSet, duplicates), name) =>
if(nameSet.contains(name)) (nameSet, duplicates + name) else (nameSet + name, duplicates)
}
if(duplicates.isEmpty)
ds.foreach(writeDefinition)
else
error("Duplicate names:\n\t" + duplicates.mkString("\n\t"))
}
def implName(name: String) = name + "0"
}