mirror of https://github.com/sbt/sbt.git
153 lines
4.6 KiB
Scala
153 lines
4.6 KiB
Scala
/* sbt -- Simple Build Tool
|
|
* Copyright 2008, 2009 Mark Harrah
|
|
*/
|
|
package xsbt
|
|
|
|
import scala.tools.nsc.{io, plugins, symtab, Global, Phase}
|
|
import io.{AbstractFile, PlainFile, ZipArchive}
|
|
import plugins.{Plugin, PluginComponent}
|
|
import symtab.Flags
|
|
import scala.collection.mutable.{HashMap, HashSet, Map, Set}
|
|
|
|
import java.io.File
|
|
import java.util.zip.ZipFile
|
|
import xsbti.AnalysisCallback
|
|
|
|
object Analyzer
|
|
{
|
|
def name = "xsbt-analyzer"
|
|
}
|
|
final class Analyzer(val global: CallbackGlobal) extends Compat
|
|
{
|
|
import global._
|
|
|
|
def newPhase(prev: Phase): Phase = new AnalyzerPhase(prev)
|
|
private class AnalyzerPhase(prev: Phase) extends Phase(prev)
|
|
{
|
|
override def description = "Extracts dependency information, finds concrete instances of provided superclasses, and application entry points."
|
|
def name = Analyzer.name
|
|
def run
|
|
{
|
|
for(unit <- currentRun.units if !unit.isJava)
|
|
{
|
|
// build dependencies structure
|
|
val sourceFile = unit.source.file.file
|
|
callback.beginSource(sourceFile)
|
|
for(on <- unit.depends)
|
|
{
|
|
def binaryDependency(file: File, className: String) = callback.binaryDependency(file, className, sourceFile)
|
|
val onSource = on.sourceFile
|
|
if(onSource == null)
|
|
{
|
|
classFile(on) match
|
|
{
|
|
case Some((f,className,inOutDir)) =>
|
|
if(inOutDir && on.isJavaDefined) registerTopLevelSym(on)
|
|
f match
|
|
{
|
|
case ze: ZipArchive#Entry => for(zip <- ze.underlyingSource; zipFile <- Option(zip.file) ) binaryDependency(zipFile, className)
|
|
case pf: PlainFile => binaryDependency(pf.file, className)
|
|
case _ => ()
|
|
}
|
|
case None => ()
|
|
}
|
|
}
|
|
else
|
|
callback.sourceDependency(onSource.file, sourceFile)
|
|
}
|
|
|
|
// build list of generated classes
|
|
for(iclass <- unit.icode)
|
|
{
|
|
val sym = iclass.symbol
|
|
def addGenerated(separatorRequired: Boolean)
|
|
{
|
|
for(classFile <- outputDirs map (fileForClass(_, sym, separatorRequired)) find (_.exists))
|
|
callback.generatedClass(sourceFile, classFile, className(sym, '.', separatorRequired))
|
|
}
|
|
if(sym.isModuleClass && !sym.isImplClass)
|
|
{
|
|
if(isTopLevelModule(sym) && sym.companionClass == NoSymbol)
|
|
addGenerated(false)
|
|
addGenerated(true)
|
|
}
|
|
else
|
|
addGenerated(false)
|
|
}
|
|
callback.endSource(sourceFile)
|
|
}
|
|
}
|
|
}
|
|
|
|
private[this] final val classSeparator = '.'
|
|
private[this] def classFile(sym: Symbol): Option[(AbstractFile, String, Boolean)] =
|
|
{
|
|
import scala.tools.nsc.symtab.Flags
|
|
val name = flatname(sym, classSeparator) + moduleSuffix(sym)
|
|
findClass(name).map { case (file,inOut) => (file, name,inOut) } orElse {
|
|
if(isTopLevelModule(sym))
|
|
{
|
|
val linked = sym.companionClass
|
|
if(linked == NoSymbol)
|
|
None
|
|
else
|
|
classFile(linked)
|
|
}
|
|
else
|
|
None
|
|
}
|
|
}
|
|
// doesn't seem to be in 2.7.7, so copied from GenJVM to here
|
|
private def moduleSuffix(sym: Symbol) =
|
|
if (sym.hasFlag(Flags.MODULE) && !sym.isMethod && !sym.isImplClass && !sym.hasFlag(Flags.JAVA)) "$" else "";
|
|
private def flatname(s: Symbol, separator: Char) =
|
|
atPhase(currentRun.flattenPhase.next) { s fullName separator }
|
|
|
|
private def isTopLevelModule(sym: Symbol): Boolean =
|
|
atPhase (currentRun.picklerPhase.next) {
|
|
sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass
|
|
}
|
|
private def className(s: Symbol, sep: Char, dollarRequired: Boolean): String =
|
|
flatname(s, sep) + (if(dollarRequired) "$" else "")
|
|
private def fileForClass(outputDirectory: File, s: Symbol, separatorRequired: Boolean): File =
|
|
new File(outputDirectory, className(s, File.separatorChar, separatorRequired) + ".class")
|
|
}
|
|
abstract class Compat
|
|
{
|
|
val global: Global
|
|
import global._
|
|
val LocalChild = global.tpnme.LOCAL_CHILD
|
|
val Nullary = global.NullaryMethodType
|
|
val ScalaObjectClass = definitions.ScalaObjectClass
|
|
|
|
private[this] final class MiscCompat
|
|
{
|
|
// in 2.9, nme.LOCALCHILD was renamed to tpnme.LOCAL_CHILD
|
|
def tpnme = nme
|
|
def LOCAL_CHILD = nme.LOCALCHILD
|
|
def LOCALCHILD = sourceCompatibilityOnly
|
|
|
|
// in 2.10, ScalaObject was removed
|
|
def ScalaObjectClass = definitions.ObjectClass
|
|
|
|
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
|
|
}
|