diff --git a/main/Build.scala b/main/Build.scala index 4bd733720..a06f04d20 100644 --- a/main/Build.scala +++ b/main/Build.scala @@ -516,7 +516,7 @@ object Load def build(classpath: Seq[File], sources: Seq[File], target: File, compilers: Compilers, log: Logger): (Inputs, inc.Analysis) = { - val inputs = Compiler.inputs(classpath, sources, target, Nil, Nil, Nil, Compiler.DefaultMaxErrors)(compilers, log) + val inputs = Compiler.inputs(classpath, sources, target, Nil, Nil, Compiler.DefaultMaxErrors)(compilers, log) val analysis = Compiler(inputs, log) (inputs, analysis) } diff --git a/main/Compiler.scala b/main/Compiler.scala index 609294e3b..61c57e795 100644 --- a/main/Compiler.scala +++ b/main/Compiler.scala @@ -24,22 +24,22 @@ object Compiler final class Inputs(val compilers: Compilers, val config: Options, val incSetup: IncSetup) final class Options(val classpath: Seq[File], val sources: Seq[File], val classesDirectory: File, val options: Seq[String], val javacOptions: Seq[String], val maxErrors: Int) - final class IncSetup(val srcBases: Seq[File], val analysisMap: Map[File, Analysis], val cacheDirectory: File) + final class IncSetup(val analysisMap: Map[File, Analysis], val cacheDirectory: File) final class Compilers(val scalac: AnalyzingCompiler, val javac: JavaCompiler) - def inputs(classpath: Seq[File], sources: Seq[File], outputDirectory: File, options: Seq[String], javacOptions: Seq[String], srcBases: Seq[File], maxErrors: Int)(implicit compilers: Compilers, log: Logger): Inputs = + def inputs(classpath: Seq[File], sources: Seq[File], outputDirectory: File, options: Seq[String], javacOptions: Seq[String], maxErrors: Int)(implicit compilers: Compilers, log: Logger): Inputs = { import Path._ val classesDirectory = outputDirectory / "classes" val cacheDirectory = outputDirectory / "cache" val augClasspath = classesDirectory.asFile +: classpath - inputs(augClasspath, sources, classesDirectory, options, javacOptions, srcBases, Map.empty, cacheDirectory, maxErrors) + inputs(augClasspath, sources, classesDirectory, options, javacOptions, Map.empty, cacheDirectory, maxErrors) } - def inputs(classpath: Seq[File], sources: Seq[File], classesDirectory: File, options: Seq[String], javacOptions: Seq[String], srcBases: Seq[File], analysisMap: Map[File, Analysis], cacheDirectory: File, maxErrors: Int)(implicit compilers: Compilers, log: Logger): Inputs = + def inputs(classpath: Seq[File], sources: Seq[File], classesDirectory: File, options: Seq[String], javacOptions: Seq[String], analysisMap: Map[File, Analysis], cacheDirectory: File, maxErrors: Int)(implicit compilers: Compilers, log: Logger): Inputs = new Inputs( compilers, new Options(classpath, sources, classesDirectory, options, javacOptions, maxErrors), - new IncSetup(srcBases, analysisMap, cacheDirectory) + new IncSetup(analysisMap, cacheDirectory) ) def compilers(implicit app: AppConfiguration, log: Logger): Compilers = @@ -49,14 +49,14 @@ object Compiler } def compilers(instance: ScalaInstance)(implicit app: AppConfiguration, log: Logger): Compilers = - compilers(instance, ClasspathOptions.auto) + compilers(instance, ClasspathOptions.auto, None) def compilers(instance: ScalaInstance, javac: (Seq[String], Logger) => Int)(implicit app: AppConfiguration, log: Logger): Compilers = compilers(instance, ClasspathOptions.auto, javac) - def compilers(instance: ScalaInstance, cpOptions: ClasspathOptions)(implicit app: AppConfiguration, log: Logger): Compilers = + def compilers(instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File])(implicit app: AppConfiguration, log: Logger): Compilers = { - val javac = JavaCompiler.directOrFork(cpOptions, instance)( (args: Seq[String], log: Logger) => Process("javac", args) ! log ) + val javac = directOrFork(instance, cpOptions, javaHome) compilers(instance, cpOptions, javac) } def compilers(instance: ScalaInstance, cpOptions: ClasspathOptions, javac: (Seq[String], Logger) => Int)(implicit app: AppConfiguration, log: Logger): Compilers = @@ -75,6 +75,21 @@ object Compiler val componentManager = new ComponentManager(launcher.globalLock, app.provider.components, log) new AnalyzingCompiler(instance, componentManager, cpOptions, log) } + def directOrFork(instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File]): JavaCompiler = + if(javaHome.isDefined) + JavaCompiler.fork(cpOptions, instance)(forkJavac(javaHome)) + else + JavaCompiler.directOrFork(cpOptions, instance)( forkJavac(None) ) + + def forkJavac(javaHome: Option[File]): (Seq[String], Logger) => Int = + { + import Path._ + val exec = javaHome match { case None => "javac"; case Some(jh) => (jh / "bin" / "javac").absolutePath } + (args: Seq[String], log: Logger) => { + log.debug("Forking javac: " + exec + " " + args.mkString(" ")) + Process(exec, args) ! log + } + } def apply(in: Inputs, log: Logger): Analysis = { @@ -83,6 +98,6 @@ object Compiler import in.incSetup._ val agg = new build.AggressiveCompile(cacheDirectory) - agg(scalac, javac, sources, classpath, classesDirectory, srcBases, options, javacOptions, analysisMap, maxErrors)(log) + agg(scalac, javac, sources, classpath, classesDirectory, options, javacOptions, analysisMap, maxErrors)(log) } } \ No newline at end of file diff --git a/main/Defaults.scala b/main/Defaults.scala index c1839dbc7..e87b32014 100755 --- a/main/Defaults.scala +++ b/main/Defaults.scala @@ -55,7 +55,8 @@ object Defaults (cp map extractAnalysis).toMap def buildCore: Seq[Setting[_]] = inScope(GlobalScope)(Seq( - pollInterval :== 1, + pollInterval :== 500, + scalaHome :== None, javaHome :== None, outputStrategy :== None, fork :== false, @@ -94,9 +95,8 @@ object Defaults sources <<= (sourceDirectories, sourceFilter, defaultExcludes) map { (d,f,excl) => d.descendentsExcept(f,excl).getFiles.toSeq }, scalaSource <<= sourceDirectory / "scala", javaSource <<= sourceDirectory / "java", - javaSourceRoots <<= toSeq(javaSource), resourceDirectory <<= sourceDirectory / "resources", - sourceDirectories <<= (scalaSource, javaSourceRoots) { _ +: _ }, + sourceDirectories <<= (scalaSource, javaSource) { _ :: _ :: Nil }, resourceDirectories <<= toSeq(resourceDirectory), resources <<= (resourceDirectories, defaultExcludes) map { (dirs, excl) => dirs.descendentsExcept("*",excl).getFiles.toSeq } ) @@ -110,10 +110,10 @@ object Defaults def compileBase = Seq( classpathOptions in GlobalScope :== ClasspathOptions.auto, - compilers <<= (scalaInstance, appConfiguration, streams, classpathOptions) map { (si, app, s, co) => Compiler.compilers(si, co)(app, s.log) }, + compilers <<= (scalaInstance, appConfiguration, streams, classpathOptions, javaHome) map { (si, app, s, co, jh) => Compiler.compilers(si, co, jh)(app, s.log) }, javacOptions in GlobalScope :== Nil, scalacOptions in GlobalScope :== Nil, - scalaInstance <<= (appConfiguration, scalaVersion){ (app, version) => ScalaInstance(version, app.provider.scalaProvider.launcher) }, + scalaInstance <<= scalaInstanceSetting, scalaVersion <<= appConfiguration( _.provider.scalaProvider.version), target <<= (target, scalaInstance, crossPaths)( (t,si,cross) => if(cross) t / ("scala-" + si.actualVersion) else t ) ) @@ -165,6 +165,13 @@ object Defaults override def watchPaths(s: State) = EvaluateTask.evaluateTask(Project structure s, key, s, base) match { case Some(Value(ps)) => ps; case _ => Nil } } } + def scalaInstanceSetting = (appConfiguration, scalaVersion, scalaHome){ (app, version, home) => + val launcher = app.provider.scalaProvider.launcher + home match { + case None => ScalaInstance(version, launcher) + case Some(h) => ScalaInstance(h, launcher) + } + } lazy val testTasks = Seq( testLoader <<= (fullClasspath, scalaInstance) map { (cp, si) => TestFramework.createTestLoader(data(cp), si) }, @@ -296,12 +303,12 @@ object Defaults def compileTask = (compileInputs, streams) map { (i,s) => Compiler(i,s.log) } def compileInputsTask = - (dependencyClasspath, sources, javaSourceRoots, compilers, javacOptions, scalacOptions, cacheDirectory, classDirectory, streams) map { - (cp, srcs, javaRoots, cs, javacOpts, scalacOpts, cacheDir, classes, s) => + (dependencyClasspath, sources, compilers, javacOptions, scalacOptions, cacheDirectory, classDirectory, streams) map { + (cp, srcs, cs, javacOpts, scalacOpts, cacheDir, classes, s) => val classpath = classes +: data(cp) val analysis = analysisMap(cp) val cache = cacheDir / "compile" - Compiler.inputs(classpath, srcs, classes, scalacOpts, javacOpts, javaRoots, analysis, cache, 100)(cs, s.log) + Compiler.inputs(classpath, srcs, classes, scalacOpts, javacOpts, analysis, cache, 100)(cs, s.log) } def copyResourcesTask = @@ -416,7 +423,7 @@ object Classpaths normalizedName <<= name(StringUtilities.normalize), organization :== normalizedName, classpathFilter in GlobalScope :== "*.jar", - resolvers <<= (projectResolver,baseResolvers).map( (pr,rs) => pr +: Resolver.withDefaultResolvers(rs)), + fullResolvers <<= (projectResolver,resolvers).map( (pr,rs) => pr +: Resolver.withDefaultResolvers(rs)), offline in GlobalScope :== false, moduleID :== normalizedName, defaultConfiguration in GlobalScope :== Some(Configurations.Compile), @@ -440,12 +447,12 @@ object Classpaths pomArtifact <<= (publishMavenStyle, moduleID)( (mavenStyle, name) => if(mavenStyle) Artifact(name, "pom", "pom") :: Nil else Nil), artifacts <<= (pomArtifact,moduleID)( (pom,name) => Artifact(name) +: pom), projectID <<= (organization,moduleID,version,artifacts){ (org,module,version,as) => ModuleID(org, module, version).cross(true).artifacts(as : _*) }, - baseResolvers in GlobalScope :== Nil, + resolvers in GlobalScope :== Nil, projectDescriptors <<= depMap, retrievePattern :== "[type]/[organisation]/[module]/[artifact](-[revision])(-[classifier]).[ext]", updateConfiguration <<= (retrieveConfiguration, ivyLoggingLevel)((conf,level) => new UpdateConfiguration(conf, level) ), retrieveConfiguration :== None, //Some(new RetrieveConfiguration(managedDependencyPath asFile, retrievePattern, true)) - ivyConfiguration <<= (resolvers, ivyPaths, otherResolvers, moduleConfigurations, offline, appConfiguration) map { (rs, paths, other, moduleConfs, off, app) => + ivyConfiguration <<= (fullResolvers, ivyPaths, otherResolvers, moduleConfigurations, offline, appConfiguration) map { (rs, paths, other, moduleConfs, off, app) => // todo: pass logger from streams directly to IvyActions val lock = app.provider.scalaProvider.launcher.globalLock new InlineIvyConfiguration(paths, rs, other, moduleConfs, off, Some(lock), ConsoleLogger()) diff --git a/main/Keys.scala b/main/Keys.scala index f527279df..3a889eb13 100644 --- a/main/Keys.scala +++ b/main/Keys.scala @@ -50,7 +50,6 @@ object Keys val sourceManaged = SettingKey[File]("source-managed") val scalaSource = SettingKey[File]("scala-source") val javaSource = SettingKey[File]("java-source") - val javaSourceRoots = SettingKey[Seq[File]]("java-source-roots") val resourceDirectory = SettingKey[File]("resource-directory") val sourceDirectories = SettingKey[Seq[File]]("source-directories") val resourceDirectories = SettingKey[Seq[File]]("resource-directories") @@ -70,6 +69,7 @@ object Keys val javacOptions = SettingKey[Seq[String]]("javac-options") val initialCommands = SettingKey[String]("initial-commands") val compileInputs = TaskKey[Compiler.Inputs]("compile-inputs") + val scalaHome = SettingKey[Option[File]]("scala-home") val scalaInstance = SettingKey[ScalaInstance]("scala-instance") val scalaVersion = SettingKey[String]("scala-version") val classpathOptions = SettingKey[ClasspathOptions]("classpath-options") @@ -164,9 +164,9 @@ object Keys val moduleID = SettingKey[String]("module-id") val version = SettingKey[String]("version") val projectID = SettingKey[ModuleID]("project-id") - val baseResolvers = SettingKey[Seq[Resolver]]("base-resolvers") + val resolvers = SettingKey[Seq[Resolver]]("resolvers") val projectResolver = TaskKey[Resolver]("project-resolver") - val resolvers = TaskKey[Seq[Resolver]]("resolvers") + val fullResolvers = TaskKey[Seq[Resolver]]("full-resolvers") val otherResolvers = SettingKey[Seq[Resolver]]("other-resolvers") val moduleConfigurations = SettingKey[Seq[ModuleConfiguration]]("module-configurations") val retrievePattern = SettingKey[String]("retrieve-pattern") diff --git a/main/build/AggressiveCompile.scala b/main/build/AggressiveCompile.scala index 450d17707..9627bbb11 100644 --- a/main/build/AggressiveCompile.scala +++ b/main/build/AggressiveCompile.scala @@ -15,25 +15,25 @@ import inc._ import CompileSetup._ import sbinary.DefaultProtocol.{ immutableMapFormat, immutableSetFormat, StringFormat } -final class CompileConfiguration(val sources: Seq[File], val classpath: Seq[File], val javaSrcBases: Seq[File], +final class CompileConfiguration(val sources: Seq[File], val classpath: Seq[File], val previousAnalysis: Analysis, val previousSetup: Option[CompileSetup], val currentSetup: CompileSetup, val getAnalysis: File => Option[Analysis], val maxErrors: Int, val compiler: AnalyzingCompiler, val javac: JavaCompiler) class AggressiveCompile(cacheDirectory: File) { - def apply(compiler: AnalyzingCompiler, javac: JavaCompiler, sources: Seq[File], classpath: Seq[File], outputDirectory: File, javaSrcBases: Seq[File] = Nil, options: Seq[String] = Nil, javacOptions: Seq[String] = Nil, analysisMap: Map[File, Analysis] = Map.empty, maxErrors: Int = 100)(implicit log: Logger): Analysis = + def apply(compiler: AnalyzingCompiler, javac: JavaCompiler, sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String] = Nil, javacOptions: Seq[String] = Nil, analysisMap: Map[File, Analysis] = Map.empty, maxErrors: Int = 100)(implicit log: Logger): Analysis = { val setup = new CompileSetup(outputDirectory, new CompileOptions(options, javacOptions), compiler.scalaInstance.actualVersion, CompileOrder.Mixed) - compile1(sources, classpath, javaSrcBases, setup, store, analysisMap, compiler, javac, maxErrors) + compile1(sources, classpath, setup, store, analysisMap, compiler, javac, maxErrors) } def withBootclasspath(args: CompilerArguments, classpath: Seq[File]): Seq[File] = args.bootClasspath ++ args.finishClasspath(classpath) - def compile1(sources: Seq[File], classpath: Seq[File], javaSrcBases: Seq[File], setup: CompileSetup, store: AnalysisStore, analysis: Map[File, Analysis], compiler: AnalyzingCompiler, javac: JavaCompiler, maxErrors: Int)(implicit log: Logger): Analysis = + def compile1(sources: Seq[File], classpath: Seq[File], setup: CompileSetup, store: AnalysisStore, analysis: Map[File, Analysis], compiler: AnalyzingCompiler, javac: JavaCompiler, maxErrors: Int)(implicit log: Logger): Analysis = { val (previousAnalysis, previousSetup) = extract(store.get()) - val config = new CompileConfiguration(sources, classpath, javaSrcBases, previousAnalysis, previousSetup, setup, analysis.get _, maxErrors, compiler, javac) + val config = new CompileConfiguration(sources, classpath, previousAnalysis, previousSetup, setup, analysis.get _, maxErrors, compiler, javac) val (modified, result) = compile2(config) if(modified) store.set(result, setup) @@ -68,7 +68,7 @@ class AggressiveCompile(cacheDirectory: File) import Path._ val loader = ClasspathUtilities.toLoader(absClasspath, compiler.scalaInstance.loader) def readAPI(source: File, classes: Seq[Class[_]]) { callback.api(source, ClassToAPI(classes)) } - Analyze(outputDirectory, javaSrcs, javaSrcBases, log)(callback, loader, readAPI) { + Analyze(outputDirectory, javaSrcs, log)(callback, loader, readAPI) { javac(javaSrcs, absClasspath, outputDirectory, options.javacOptions) } } diff --git a/util/classfile/Analyze.scala b/util/classfile/Analyze.scala index dcf2f14bf..f44b7f817 100644 --- a/util/classfile/Analyze.scala +++ b/util/classfile/Analyze.scala @@ -13,9 +13,9 @@ import java.lang.reflect.Modifier.{STATIC, PUBLIC, ABSTRACT} private[sbt] object Analyze { - def apply[T](outputDirectory: Path, sources: Seq[Path], roots: Seq[Path], log: Logger)(analysis: xsbti.AnalysisCallback, loader: ClassLoader, readAPI: (File,Seq[Class[_]]) => Unit)(compile: => Unit) + def apply[T](outputDirectory: Path, sources: Seq[File], log: Logger)(analysis: xsbti.AnalysisCallback, loader: ClassLoader, readAPI: (File,Seq[Class[_]]) => Unit)(compile: => Unit) { - val sourceSet = Set(sources.toSeq : _*) + val sourceMap = sources.groupBy(_.getName) val classesFinder = outputDirectory ** GlobFilter("*.class") val existingClasses = classesFinder.get @@ -38,7 +38,7 @@ private[sbt] object Analyze path <- Path.relativize(outputDirectory, newClass); classFile = Parser(newClass.asFile); sourceFile <- classFile.sourceFile orElse guessSourceName(newClass.asFile.getName); - source <- guessSourcePath(sourceSet, roots, classFile, log)) + source <- guessSourcePath(sourceMap, classFile, log)) { analysis.beginSource(source) analysis.generatedClass(source, path) @@ -102,23 +102,54 @@ private[sbt] object Analyze private final val ClassExt = ".class" private def trimClassExt(name: String) = if(name.endsWith(ClassExt)) name.substring(0, name.length - ClassExt.length) else name private def resolveClassFile(file: File, className: String): File = (file /: (className.replace('.','/') + ClassExt).split("/"))(new File(_, _)) - private def guessSourcePath(sources: scala.collection.Set[Path], roots: Iterable[Path], classFile: ClassFile, log: Logger) = + private def guessSourcePath(sourceNameMap: Map[String, Iterable[File]], classFile: ClassFile, log: Logger) = { val classNameParts = classFile.className.split("""\.""") - val lastIndex = classNameParts.length - 1 - val pkg = classNameParts.take(lastIndex) - val simpleClassName = classNameParts(lastIndex) + val pkg = classNameParts.init + val simpleClassName = classNameParts.last val sourceFileName = classFile.sourceFile.getOrElse(simpleClassName.takeWhile(_ != '$').mkString("", "", ".java")) - val relativeSourceFile = (pkg ++ (sourceFileName :: Nil)).mkString("/") - val candidates = roots.map(root => Path.fromString(root, relativeSourceFile)).filter(sources.contains).toList + val candidates = findSource(sourceNameMap, pkg.toList, sourceFileName) candidates match { case Nil => log.warn("Could not determine source for class " + classFile.className) case head :: Nil => () - case _ =>log.warn("Multiple sources matched for class " + classFile.className + ": " + candidates.mkString(", ")) + case _ => log.warn("Multiple sources matched for class " + classFile.className + ": " + candidates.mkString(", ")) } candidates } + private def findSource(sourceNameMap: Map[String, Iterable[File]], pkg: List[String], sourceFileName: String): List[File] = + refine( (sourceNameMap get sourceFileName).toList.flatten map { x => (x,x) }, pkg.reverse) + + private def refine(sources: List[(File, File)], pkgRev: List[String]): List[File] = + { + def make = sources.map(_._1) + if(sources.isEmpty || sources.tail.isEmpty) + make + else + pkgRev match + { + case Nil => shortest(make) + case x :: xs => + val retain = sources flatMap { case (src, pre) => + if(pre != null && pre.getName == x) + (src, pre.getParentFile) :: Nil + else + Nil + } + refine(retain, xs) + } + } + private def shortest(files: List[File]): List[File] = + if(files.isEmpty) files + else + { + val fs = files.groupBy(distanceToRoot(0)) + fs(fs.keys.min) + } + + private def distanceToRoot(acc: Int)(file: File): Int = + if(file == null) acc else distanceToRoot(acc + 1)(file.getParentFile) + private def isTopLevel(classFile: ClassFile) = classFile.className.indexOf('$') < 0 private lazy val unit = classOf[Unit] private lazy val strArray = List(classOf[Array[String]])