All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.specs2.analysis.CompilerDependencyFinder.scala Maven / Gradle / Ivy

The newest version!
package org.specs2
package analysis

import org.specs2.io.fs
import scala.collection.mutable.{Map, HashMap}
import scala.tools.nsc._
import interactive._
import scala.reflect.io._
import java.net.URLClassLoader
import java.net.URLDecoder
import reflect.PackageName._

/**
 * Implementation of the DependencyFinder trait using the compiler dependency analysis
 */
trait CompilerDependencyFinder extends DependencyFinder {

  /**
   * @return the class depending on the classes of a given package
   */
  override def getPackageDependents(packageName: String, sourceDir: String, targetDir: String): Seq[Dependency] = {
    // load all dependencies for this source directory (compiles all files)
    val dependencies = sourceDependencies(sourceDir)
    // for each file in the package directory, get its dependencies
    packageDirectory(packageName, sourceDir).iterator.filter(_.path.endsWith(".scala")).toSeq flatMap { (file: AbstractFile) =>
      dependencies.dependentFiles(depth = 1, Set(file)).map { dependent =>
        Dependency(file, dependent, sourceDir)
      }
    }
  }

  /**
   * @return a seq of all scala files in the source directory
   */
  def selectFiles(sourceDir: String) = fs.filePaths(sourceDir, "**/*.scala")

  /** @return all the dependencies of source files in a given source directory by compiling them */
  private def sourceDependencies(sourceDir: String) =
    buildManager(sourceDir).compiler.dependencyAnalysis.dependencies
  
  private lazy val managers: Map[String, SimpleBuildManager] = new HashMap[String, SimpleBuildManager].withDefault { sourceDir =>
    val manager = new SimpleBuildManager(newSettings)
    manager.addSourceFiles(selectFiles(sourceDir).map(f => new PlainFile(f)).toSet)
    managers.put(sourceDir, manager)
    manager
  }
  /**
   * @return a new `BuildManager` for scala files in a source directory
   * the managers are memoized per sourceDir
   */
  private def buildManager(sourceDir: String) = managers(sourceDir)

  /**
   * @return a new Settings object for doing dependency analysis. It adds the current classpath and set-up a new directory for
   * adding the generated classes
   */
  private lazy val newSettings = {
    val settings = new Settings

    // the analysis output directory needs to be built before we run the analysis
    settings.classpath.value      = classPath.mkString(java.io.File.pathSeparator)
    settings.make.value           = "transitive"
    settings.stopAfter.value      = List("dependencyAnalysis")
    settings.skip.value           = List("flatten", "liftcode", "jvm")
    settings.dependencyfile.value = "xxx/the dependency file must not be saved"
    settings
  }

  /**
   * @return the current classPath as used by the current application
   */
  private lazy val classPath = {
    val cl = Thread.currentThread.getContextClassLoader
    if (cl.isInstanceOf[URLClassLoader]) {
      val ucl = cl.asInstanceOf[URLClassLoader]
      ucl.getURLs.foldLeft(Seq[String]()) { (res, url) =>
        if (url.getProtocol.equals("file")) res :+ new java.io.File(URLDecoder.decode(url.getPath, "UTF-8")).getCanonicalFile.getAbsolutePath
        else                                res
      }
    } else Seq[String]()
  }
  /**
   * @return a new package directory object from a source directory and a package name
   */
  private def packageDirectory(packageName: String, sourceDir: String) =
    new PlainDirectory(new Directory(new java.io.File(sourceDir+packageName.toPath)))
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy