Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include resources in dependency classpath #796

Merged
merged 4 commits into from
Jan 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@ import scala.scalanative.build.{Build, Config, Discover, GC, Mode, Logger => Nat

object NativeBridge {
private implicit val ctx: DebugFilter = DebugFilter.Link
def nativeLink(config0: NativeConfig, project: Project, entry: String, target: Path, logger: Logger): Path = {
def nativeLink(
config0: NativeConfig,
project: Project,
classpath: Array[Path],
entry: String,
target: Path,
logger: Logger
): Path = {
val workdir = project.out.resolve("native")
if (workdir.isDirectory) Paths.delete(workdir)
Files.createDirectories(workdir.underlying)

val classpath = project.compilationClasspath.map(_.underlying)
val nativeLogger = NativeLogger(logger.debug _, logger.info _, logger.warn _, logger.error _)
val config = setUpNativeConfig(project, config0)
val config = setUpNativeConfig(project, classpath, config0)
val nativeMode = config.mode match {
case LinkerMode.Debug => Mode.debug
case LinkerMode.Release => Mode.release
Expand All @@ -44,6 +50,7 @@ object NativeBridge {

private[scalanative] def setUpNativeConfig(
project: Project,
classpath: Array[Path],
config: NativeConfig
): NativeConfig = {
val mode = config.mode
Expand All @@ -66,7 +73,7 @@ object NativeBridge {
if (config.nativelib.toString.nonEmpty) config.nativelib
else {
Discover
.nativelib(project.compilationClasspath.map(_.underlying))
.nativelib(classpath)
.getOrElse(sys.error("Fatal: nativelib is missing and could not be found."))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ object JsBridge {
def link(
config: JsConfig,
project: Project,
classpath: Array[Path],
runMain: java.lang.Boolean,
mainClass: Option[String],
target: Path,
logger: BloopLogger
): Unit = {
val classpath = project.compilationClasspath.map(_.underlying)
val classpathIrFiles = classpath
.filter(Files.isDirectory(_))
.flatMap(findIrFiles)
Expand Down Expand Up @@ -137,7 +137,8 @@ object JsBridge {
} else {
new CustomDomNodeEnv(
JSDOMNodeJSEnv.Config().withEnv(fullEnv).withExecutable(nodePath),
customScripts)
customScripts
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ object JsBridge {
def link(
config: JsConfig,
project: Project,
classpath: Array[Path],
runMain: java.lang.Boolean,
mainClass: Option[String],
target: Path,
Expand All @@ -61,8 +62,7 @@ object JsBridge {
}

val cache = new IRFileCache().newCache
val irClasspath =
FileScalaJSIRContainer.fromClasspath(project.compilationClasspath.map(_.toFile))
val irClasspath = FileScalaJSIRContainer.fromClasspath(classpath.map(_.toFile))
val irFiles = cache.cached(irClasspath)

val moduleInitializers = mainClass match {
Expand Down
11 changes: 8 additions & 3 deletions frontend/src/main/scala/bloop/bsp/BloopBspServices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ final class BloopBspServices(
* @return An async computation that returns the response to the client.
*/
def initialize(
params: bsp.InitializeBuildParams): BspEndpointResponse[bsp.InitializeBuildResult] = {
params: bsp.InitializeBuildParams
): BspEndpointResponse[bsp.InitializeBuildResult] = {
val uri = new java.net.URI(params.rootUri.value)
val configDir = AbsolutePath(uri).resolve(relativeConfigPath)
reloadState(configDir).map { state =>
Expand Down Expand Up @@ -561,18 +562,22 @@ final class BloopBspServices(
}

def scalacOptions(
request: bsp.ScalacOptionsParams): BspEndpointResponse[bsp.ScalacOptionsResult] = {
request: bsp.ScalacOptionsParams
): BspEndpointResponse[bsp.ScalacOptionsResult] = {
def scalacOptions(
projects: Seq[ProjectMapping],
state: State
): BspResult[bsp.ScalacOptionsResult] = {
val response = bsp.ScalacOptionsResult(
projects.iterator.map {
case (target, project) =>
val dag = state.build.getDagFor(project)
val fullClasspath = project.dependencyClasspath(dag)
val classpath = fullClasspath.map(e => bsp.Uri(e.toBspUri)).toList
bsp.ScalacOptionsItem(
target = target,
options = project.scalacOptions.toList,
classpath = project.compilationClasspath.map(e => bsp.Uri(e.toBspUri)).toList,
classpath = classpath,
classDirectory = bsp.Uri(project.classesDir.toBspUri)
)
}.toList
Expand Down
15 changes: 7 additions & 8 deletions frontend/src/main/scala/bloop/data/Project.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ final case class Project(
/** The bsp uri associated with this project. */
val bspUri: Bsp.Uri = Bsp.Uri(ProjectUris.toURI(baseDirectory, name))

/** This project's full classpath (classes directory and raw classpath) */
val compilationClasspath: Array[AbsolutePath] = (classesDir :: rawClasspath).toArray

val classpathOptions: ClasspathOptions = {
ClasspathOptions.of(
compileSetup.addLibraryToBootClasspath,
Expand Down Expand Up @@ -70,16 +67,18 @@ final case class Project(
}
}

def fullClasspathFor(dag: Dag[Project]): Array[AbsolutePath] = {
val cp = compilationClasspath.toBuffer
def dependencyClasspath(dag: Dag[Project]): Array[AbsolutePath] = {
val cp = (classesDir :: rawClasspath).toBuffer
// Add the resources right before the classes directory if found in the classpath
Dag.dfs(dag).foreach { p =>
val index = cp.indexOf(p.classesDir)
// If there is an anomaly and the classes dir of a dependency is missing, add resource at end
// Only add those resources that exist at the moment of creating the classpath
val resources = p.resources.filter(_.exists)
if (index == -1) {
cp.appendAll(p.resources)
// If anomaly and classes dir of a dependency is missing, add resources at end
cp.appendAll(resources)
} else {
cp.insertAll(index, p.resources)
cp.insertAll(index, resources)
}
}
cp.toArray
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ object CompilationTask {
import state.{logger, compilerCache}
def compile(graphInputs: CompileGraph.Inputs): Task[Compiler.Result] = {
val project = graphInputs.bundle.project
val classpath = graphInputs.bundle.classpath
graphInputs.bundle.toSourcesAndInstance match {
case Left(earlyResult) =>
val complete = CompileExceptions.CompletePromise(graphInputs.store)
Expand Down Expand Up @@ -96,7 +97,7 @@ object CompilationTask {
instance,
compilerCache,
sources.toArray,
project.compilationClasspath,
classpath,
graphInputs.store,
project.classesDir,
project.out,
Expand Down Expand Up @@ -144,8 +145,8 @@ object CompilationTask {
}
}

def setup(project: Project): CompileBundle = CompileBundle(project)
CompileGraph.traverse(dag, setup(_), compile(_), pipeline, logger).flatMap { partialDag =>
def setup(project: Project, dag: Dag[Project]): CompileBundle = CompileBundle(project, dag)
CompileGraph.traverse(dag, setup(_, _), compile(_), pipeline, logger).flatMap { partialDag =>
val partialResults = Dag.dfs(partialDag)
val finalResults = partialResults.map(r => PartialCompileResult.toFinalResult(r))
Task.gatherUnordered(finalResults).map(_.flatten).map { results =>
Expand Down
24 changes: 15 additions & 9 deletions frontend/src/main/scala/bloop/engine/tasks/LinkTask.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,26 @@ object LinkTask {
state: State,
mainClass: String,
target: AbsolutePath,
platform: Platform.Js,
platform: Platform.Js
): Task[State] = {
val config0 = platform.config
platform.toolchain match {
case Some(toolchain) =>
config0.output.flatMap(Tasks.reasonOfInvalidPath(_, ".js")) match {
case Some(msg) => Task.now(state.withError(msg, ExitStatus.LinkingError))
case None =>
val fullClasspath =
project.dependencyClasspath(state.build.getDagFor(project)).map(_.underlying)
val config = config0.copy(mode = getOptimizerMode(cmd.optimize, config0.mode))
toolchain.link(config, project, true, Some(mainClass), target, state.logger).map {
case scala.util.Success(_) =>
state.withInfo(s"Generated JavaScript file '${target.syntax}'")
case scala.util.Failure(t) =>
val msg = Feedback.failedToLink(project, ScalaJsToolchain.name, t)
state.withError(msg, ExitStatus.LinkingError).withTrace(t)
}
toolchain
.link(config, project, fullClasspath, true, Some(mainClass), target, state.logger)
.map {
case scala.util.Success(_) =>
state.withInfo(s"Generated JavaScript file '${target.syntax}'")
case scala.util.Failure(t) =>
val msg = Feedback.failedToLink(project, ScalaJsToolchain.name, t)
state.withError(msg, ExitStatus.LinkingError).withTrace(t)
}
}
case None =>
val artifactName = ScalaJsToolchain.artifactNameFrom(config0.version)
Expand All @@ -54,8 +58,10 @@ object LinkTask {
config0.output.flatMap(Tasks.reasonOfInvalidPath(_)) match {
case Some(msg) => Task.now(state.withError(msg, ExitStatus.LinkingError))
case None =>
val fullClasspath =
project.dependencyClasspath(state.build.getDagFor(project)).map(_.underlying)
val config = config0.copy(mode = getOptimizerMode(cmd.optimize, config0.mode))
toolchain.link(config, project, mainClass, target, state.logger) map {
toolchain.link(config, project, fullClasspath, mainClass, target, state.logger) map {
case scala.util.Success(_) =>
state.withInfo(s"Generated native binary '${target.syntax}'")
case scala.util.Failure(t) =>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/main/scala/bloop/engine/tasks/Tasks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ object Tasks {
import state.logger
project.scalaInstance match {
case Some(instance) =>
val classpath = project.fullClasspathFor(state.build.getDagFor(project))
val classpath = project.dependencyClasspath(state.build.getDagFor(project))
val entries = classpath.map(_.underlying.toFile).toSeq
logger.debug(s"Setting up the console classpath with ${entries.mkString(", ")}")(
DebugFilter.All)
Expand Down Expand Up @@ -180,7 +180,7 @@ object Tasks {
args: Array[String],
skipJargs: Boolean
): Task[State] = {
val classpath = project.fullClasspathFor(state.build.getDagFor(project))
val classpath = project.dependencyClasspath(state.build.getDagFor(project))
val processConfig = Forker(javaEnv, classpath)
val runTask =
processConfig.runMain(cwd, fqn, args, skipJargs, state.logger, state.commonOptions)
Expand Down
45 changes: 26 additions & 19 deletions frontend/src/main/scala/bloop/engine/tasks/TestTask.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ object TestTask {

val lastCompileResult = state.results.lastSuccessfulResultOrEmpty(project)
val analysis = lastCompileResult.analysis().toOption.getOrElse {
logger.warn(
s"Test execution was triggered, but no compilation detected for ${project.name}")
logger
.warn(s"Test execution was triggered, but no compilation detected for ${project.name}")
Analysis.empty
}

Expand Down Expand Up @@ -141,32 +141,39 @@ object TestTask {
implicit val logContext: DebugFilter = DebugFilter.Test
project.platform match {
case Platform.Jvm(env, _, _) =>
val classpath = project.fullClasspathFor(state.build.getDagFor(project))
val classpath = project.dependencyClasspath(state.build.getDagFor(project))
val forker = Forker(env, classpath)
val testLoader = forker.newClassLoader(Some(TestInternals.filteredLoader))
val frameworks = project.testFrameworks.flatMap(f =>
TestInternals.loadFramework(testLoader, f.names, logger))
val frameworks = project.testFrameworks.flatMap(
f => TestInternals.loadFramework(testLoader, f.names, logger)
)
Task.now(Some(DiscoveredTestFrameworks.Jvm(frameworks, forker, testLoader)))

case Platform.Js(config, toolchain, userMainClass) =>
val target = ScalaJsToolchain.linkTargetFrom(project, config)
toolchain match {
case Some(toolchain) =>
toolchain.link(config, project, false, userMainClass, target, state.logger).map {
case Success(_) =>
logger.info(s"Generated JavaScript file '${target.syntax}'")
val fnames = project.testFrameworks.map(_.names)
logger.debug(s"Resolving test frameworks: $fnames")
val baseDir = project.baseDirectory
val env = state.commonOptions.env.toMap
Some(toolchain.discoverTestFrameworks(project, fnames, target, logger, config, env))
val fullClasspath =
project.dependencyClasspath(state.build.getDagFor(project)).map(_.underlying)
toolchain
.link(config, project, fullClasspath, false, userMainClass, target, state.logger)
.map {
case Success(_) =>
logger.info(s"Generated JavaScript file '${target.syntax}'")
val fnames = project.testFrameworks.map(_.names)
logger.debug(s"Resolving test frameworks: $fnames")
val baseDir = project.baseDirectory
val env = state.commonOptions.env.toMap
Some(
toolchain.discoverTestFrameworks(project, fnames, target, logger, config, env)
)

case Failure(ex) =>
ex.printStackTrace()
logger.trace(ex)
logger.error(s"JavaScript linking failed with '${ex.getMessage}'")
None
}
case Failure(ex) =>
ex.printStackTrace()
logger.trace(ex)
logger.error(s"JavaScript linking failed with '${ex.getMessage}'")
None
}

case None =>
val artifactName = ScalaJsToolchain.artifactNameFrom(config.version)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bloop.engine.tasks.compilation

import bloop.data.Project
import bloop.engine.Feedback
import bloop.engine.Dag
import bloop.io.{AbsolutePath, Paths}
import bloop.{Compiler, ScalaInstance}

Expand All @@ -27,8 +28,9 @@ import bloop.{Compiler, ScalaInstance}
*/
final case class CompileBundle(
project: Project,
classpath: Array[AbsolutePath],
javaSources: List[AbsolutePath],
scalaSources: List[AbsolutePath],
scalaSources: List[AbsolutePath]
) {
val isJavaOnly: Boolean = scalaSources.isEmpty && !javaSources.isEmpty

Expand Down Expand Up @@ -70,10 +72,11 @@ case class CompileSourcesAndInstance(
)

object CompileBundle {
def apply(project: Project): CompileBundle = {
def apply(project: Project, dag: Dag[Project]): CompileBundle = {
val sources = project.sources.distinct
val classpath = project.dependencyClasspath(dag)
val javaSources = sources.flatMap(src => Paths.pathFilesUnder(src, "glob:**.java")).distinct
val scalaSources = sources.flatMap(src => Paths.pathFilesUnder(src, "glob:**.scala")).distinct
new CompileBundle(project, javaSources, scalaSources)
new CompileBundle(project, classpath, javaSources, scalaSources)
}
}
Loading