From 4add1d1b68509c2b6dc8a3e8e8c074a94655615b Mon Sep 17 00:00:00 2001 From: jvican Date: Thu, 22 Nov 2018 13:45:21 +0100 Subject: [PATCH 1/2] Add resources to the configuration file Resources are files that are used at runtime, either when testing or running an application. These files can be generated by the build tool or present in a resource directory of a project. Resource files have been supported in bloop since 1.0, where in sbt and other build tools we were relying on the resources to be copied by the build tool to the classes directory. This approach had a problem: whenever a change is done in an unmanaged resource file (a file that is controlled by the user, like a `log4j2.properties` file), bloop would not pick it up until there's a new `bloopInstall` invocation. This is a known limitation that this commit fixes. The approach to support resources has changed: 1. A new `resources` field has been added to the JSON file. 2. Resources are not copied to the class file, they are instead added to the test and run classpaths. The second item makes a subtle but important point. The fact that we don't copy the resources means two things: 1. We don't care about the relative path with regards to the root resource directory where a resource lives. 2. We rely on the contents of the `resources` field, which should contain only those directories which are either considered roots (e.g. files from where the application can use relative paths to look for resources) or single files. We support this treatment of resources in all build tools. Sbt requires a special case in the plugin because the source of truth is `resources`, which contains all the contained files in the resource directories + any other file the user may have added. As a consequence, we filter the paths until we get either root resource directories or single source files. Note that removing directories that are already contained in a root resource directory is really important because it dictates the way the resource will be accessible at runtime (where the lookup relative paths play an important role). Fixes https://github.com/scalacenter/bloop/issues/586 --- .../bloop/scalanative/NativeBridge.scala | 4 +- .../main/scala/bloop/scalajs/JsBridge.scala | 2 +- .../main/scala/bloop/scalajs/JsBridge.scala | 2 +- .../src/main/scala/bloop/config/Config.scala | 6 +- .../scala/bloop/config/util/ConfigUtil.scala | 26 +++++++ .../scala/bloop/bsp/BloopBspServices.scala | 2 +- .../src/main/scala/bloop/data/Project.scala | 21 +++++- .../bloop/engine/tasks/CompilationTask.scala | 2 +- .../main/scala/bloop/engine/tasks/Tasks.scala | 4 +- .../scala/bloop/engine/tasks/TestTask.scala | 5 +- .../tasks/compilation/CompileGraph.scala | 2 +- .../scala/bloop/bsp/BspProtocolSpec.scala | 2 +- .../src/test/scala/bloop/engine/DagSpec.scala | 4 +- .../test/scala/bloop/exec/ForkerSpec.scala | 2 +- .../bloop/tasks/IntegrationTestSuite.scala | 1 + .../test/scala/bloop/tasks/JsTestSpec.scala | 3 +- .../test/scala/bloop/tasks/JvmTestSpec.scala | 3 +- .../scala/bloop/tasks/TestResourcesSpec.scala | 2 - .../src/test/scala/bloop/tasks/TestUtil.scala | 1 + .../gradle/model/BloopConverter.scala | 4 ++ .../maven/MojoImplementation.scala | 54 +++++++++----- .../bloop/integrations/mill/MillBloop.scala | 8 +-- .../bloop/integrations/sbt/SbtBloop.scala | 71 +++++-------------- project/BuildPlugin.scala | 2 +- 24 files changed, 136 insertions(+), 97 deletions(-) create mode 100644 config/src/main/scala/bloop/config/util/ConfigUtil.scala diff --git a/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala b/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala index 0059241d50..b83e055196 100644 --- a/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala +++ b/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala @@ -15,7 +15,7 @@ object NativeBridge { if (workdir.isDirectory) Paths.delete(workdir) Files.createDirectories(workdir.underlying) - val classpath = project.classpath.map(_.underlying) + val classpath = project.compilationClasspath.map(_.underlying) val nativeLogger = NativeLogger(logger.debug _, logger.info _, logger.warn _, logger.error _) val config = setUpNativeConfig(project, config0) val nativeMode = config.mode match { @@ -66,7 +66,7 @@ object NativeBridge { if (config.nativelib.toString.nonEmpty) config.nativelib else { Discover - .nativelib(project.classpath.map(_.underlying)) + .nativelib(project.compilationClasspath.map(_.underlying)) .getOrElse(sys.error("Fatal: nativelib is missing and could not be found.")) } } diff --git a/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala b/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala index fb0580e3be..628ac3dbb2 100644 --- a/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala +++ b/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala @@ -57,7 +57,7 @@ object JsBridge { target: Path, logger: BloopLogger ): Unit = { - val classpath = project.classpath.map(_.underlying) + val classpath = project.compilationClasspath.map(_.underlying) val classpathIrFiles = classpath .filter(Files.isDirectory(_)) .flatMap(findIrFiles) diff --git a/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala b/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala index c8a6ada55f..9216a86489 100644 --- a/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala +++ b/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala @@ -59,7 +59,7 @@ object JsBridge { } val cache = new IRFileCache().newCache - val irClasspath = FileScalaJSIRContainer.fromClasspath(project.classpath.map(_.toFile)) + val irClasspath = FileScalaJSIRContainer.fromClasspath(project.compilationClasspath.map(_.toFile)) val irFiles = cache.cached(irClasspath) val moduleInitializers = mainClass match { diff --git a/config/src/main/scala/bloop/config/Config.scala b/config/src/main/scala/bloop/config/Config.scala index abf717e963..2a7ebc5487 100644 --- a/config/src/main/scala/bloop/config/Config.scala +++ b/config/src/main/scala/bloop/config/Config.scala @@ -183,6 +183,7 @@ object Config { classpath: List[Path], out: Path, classesDir: Path, + resources: Option[List[Path]], `scala`: Option[Scala], java: Option[Java], sbt: Option[Sbt], @@ -193,7 +194,7 @@ object Config { object Project { // FORMAT: OFF - private[bloop] val empty: Project = Project("", emptyPath, List(), List(), List(), emptyPath, emptyPath, None, None, None, None, None, None) + private[bloop] val empty: Project = Project("", emptyPath, List(), List(), List(), emptyPath, emptyPath, None, None, None, None, None, None, None) // FORMAT: ON def analysisFileName(projectName: String) = s"$projectName-analysis.bin" @@ -201,7 +202,7 @@ object Config { case class File(version: String, project: Project) object File { - final val LatestVersion = "1.1.0-M1" + final val LatestVersion = "1.1.0-M2" private[bloop] val empty = File(LatestVersion, Project.empty) @@ -237,6 +238,7 @@ object Config { List(scalaLibraryJar), outDir, classesDir, + Some(List(outDir.resolve("resource1.xml"))), Some( Scala( "org.scala-lang", diff --git a/config/src/main/scala/bloop/config/util/ConfigUtil.scala b/config/src/main/scala/bloop/config/util/ConfigUtil.scala new file mode 100644 index 0000000000..56487f742d --- /dev/null +++ b/config/src/main/scala/bloop/config/util/ConfigUtil.scala @@ -0,0 +1,26 @@ +package bloop.config.util + +import java.nio.file.{Path, Files} + +object ConfigUtil { + def pathsOutsideRoots(roots: Seq[Path], paths: Seq[Path]): Seq[Path] = { + paths.filterNot { path => + roots.exists { root => + var found: Boolean = false + val rootDirSize = root.toString.size + var currentTarget = (if (Files.isRegularFile(path)) path.getParent else path).toAbsolutePath + while (!found && + currentTarget != null && + // Use a heuristic to know if we should short-circuit and return false + currentTarget.toString.size >= rootDirSize) { + if (currentTarget == root) { + found = true + } + + currentTarget = currentTarget.getParent + } + found + } + } + } +} diff --git a/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala b/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala index 79febd1d8f..c0c06f9588 100644 --- a/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala +++ b/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala @@ -498,7 +498,7 @@ final class BloopBspServices( bsp.ScalacOptionsItem( target = target, options = project.scalacOptions.toList, - classpath = project.classpath.map(e => bsp.Uri(e.toBspUri)).toList, + classpath = project.compilationClasspath.map(e => bsp.Uri(e.toBspUri)).toList, classDirectory = bsp.Uri(project.classesDir.toBspUri) ) }.toList diff --git a/frontend/src/main/scala/bloop/data/Project.scala b/frontend/src/main/scala/bloop/data/Project.scala index ff69aee506..dfa96b05a4 100644 --- a/frontend/src/main/scala/bloop/data/Project.scala +++ b/frontend/src/main/scala/bloop/data/Project.scala @@ -10,6 +10,7 @@ import xsbti.compile.{ClasspathOptions, CompileOrder} import bloop.ScalaInstance import bloop.bsp.ProjectUris import bloop.config.{Config, ConfigEncoderDecoders} +import bloop.engine.Dag import bloop.engine.tasks.toolchains.{JvmToolchain, ScalaJsToolchain, ScalaNativeToolchain} import ch.epfl.scala.{bsp => Bsp} @@ -19,6 +20,7 @@ final case class Project( dependencies: List[String], scalaInstance: Option[ScalaInstance], rawClasspath: List[AbsolutePath], + resources: List[AbsolutePath], compileSetup: Config.CompileSetup, classesDir: AbsolutePath, scalacOptions: List[String], @@ -33,11 +35,12 @@ final case class Project( resolution: Option[Config.Resolution], origin: Origin ) { + /** 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 classpath: Array[AbsolutePath] = (classesDir :: rawClasspath).toArray + val compilationClasspath: Array[AbsolutePath] = (classesDir :: rawClasspath).toArray val classpathOptions: ClasspathOptions = { ClasspathOptions.of( @@ -63,6 +66,20 @@ final case class Project( case _ => false } } + + def fullClasspathFor(dag: Dag[Project]): Array[AbsolutePath] = { + val cp = compilationClasspath.toBuffer + // Add the resources right after 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 + if (index == -1) { + p.resources.foreach(r => cp.append(r)) + } + else cp.insertAll(index + 1, p.resources) + } + cp.toArray + } } object Project { @@ -113,6 +130,7 @@ object Project { val analysisOut = scala .flatMap(_.analysis.map(AbsolutePath.apply)) .getOrElse(out.resolve(Config.Project.analysisFileName(project.name))) + val resources = project.resources.toList.flatten.map(AbsolutePath.apply) Project( project.name, @@ -120,6 +138,7 @@ object Project { project.dependencies, instance, project.classpath.map(AbsolutePath.apply), + resources, setup, AbsolutePath(project.classesDir), scala.map(_.options).getOrElse(Nil), diff --git a/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala b/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala index eef2d508f4..d45659c5c8 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala @@ -85,7 +85,7 @@ object CompilationTask { instance, compilerCache, sources.toArray, - project.classpath, + project.compilationClasspath, graphInputs.store, project.classesDir, project.out, diff --git a/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala b/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala index 14a0d09ba1..c73f31b160 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala @@ -54,7 +54,7 @@ object Tasks { import state.logger project.scalaInstance match { case Some(instance) => - val classpath = project.classpath + val classpath = project.fullClasspathFor(state.build.getDagFor(project)) val entries = classpath.map(_.underlying.toFile).toSeq logger.debug(s"Setting up the console classpath with ${entries.mkString(", ")}")( DebugFilter.All) @@ -182,7 +182,7 @@ object Tasks { fqn: String, args: Array[String] ): Task[State] = { - val classpath = project.classpath + val classpath = project.fullClasspathFor(state.build.getDagFor(project)) val processConfig = Forker(javaEnv, classpath) val runTask = processConfig.runMain(cwd, fqn, args, state.logger, state.commonOptions) runTask.map { exitCode => diff --git a/frontend/src/main/scala/bloop/engine/tasks/TestTask.scala b/frontend/src/main/scala/bloop/engine/tasks/TestTask.scala index 85aaf2fc49..92d5aedb45 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/TestTask.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/TestTask.scala @@ -3,7 +3,7 @@ package bloop.engine.tasks import bloop.cli.ExitStatus import bloop.config.Config import bloop.data.{Platform, Project} -import bloop.engine.{Feedback, State} +import bloop.engine.{Dag, Feedback, State} import bloop.engine.tasks.toolchains.ScalaJsToolchain import bloop.exec.Forker import bloop.io.AbsolutePath @@ -129,7 +129,8 @@ object TestTask { implicit val logContext: DebugFilter = DebugFilter.Test project.platform match { case Platform.Jvm(env, _, _) => - val forker = Forker(env, project.classpath) + val classpath = project.fullClasspathFor(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)) diff --git a/frontend/src/main/scala/bloop/engine/tasks/compilation/CompileGraph.scala b/frontend/src/main/scala/bloop/engine/tasks/compilation/CompileGraph.scala index cc1c01c6d0..563779d418 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/compilation/CompileGraph.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/compilation/CompileGraph.scala @@ -214,7 +214,7 @@ object CompileGraph { // Let's order the IRs exactly in the same order as provided in the classpath! // Required for symbol clashes in dependencies (`AppLoader` in guardian/frontend) - val indexDirs = project.classpath.iterator.filter(_.isDirectory).zipWithIndex.toMap + val indexDirs = project.compilationClasspath.iterator.filter(_.isDirectory).zipWithIndex.toMap val dependentStore = { val transitiveStores = results.flatMap(r => indexDirs.get(r.bundle.project.classesDir).iterator.map(i => i -> r.store)) diff --git a/frontend/src/test/scala/bloop/bsp/BspProtocolSpec.scala b/frontend/src/test/scala/bloop/bsp/BspProtocolSpec.scala index 0b8a49b793..75e786f3c3 100644 --- a/frontend/src/test/scala/bloop/bsp/BspProtocolSpec.scala +++ b/frontend/src/test/scala/bloop/bsp/BspProtocolSpec.scala @@ -317,7 +317,7 @@ class BspProtocolSpec { Assert.assertEquals(obtainedUri, expectedUri) val obtainedOptions = stringifyOptions(opts.options, opts.classpath, opts.classDirectory) - val classpath = p.classpath.iterator.map(i => bsp.Uri(i.toBspUri)).toList + val classpath = p.compilationClasspath.iterator.map(i => bsp.Uri(i.toBspUri)).toList val classesDir = bsp.Uri(p.classesDir.toBspUri) val expectedOptions = stringifyOptions(p.scalacOptions.toList, classpath, classesDir) diff --git a/frontend/src/test/scala/bloop/engine/DagSpec.scala b/frontend/src/test/scala/bloop/engine/DagSpec.scala index 3d7c82d6a5..033d6ba59e 100644 --- a/frontend/src/test/scala/bloop/engine/DagSpec.scala +++ b/frontend/src/test/scala/bloop/engine/DagSpec.scala @@ -21,8 +21,8 @@ class DagSpec { // format: OFF def dummyOrigin = TestUtil.syntheticOriginFor(dummyPath) def dummyProject(name: String, dependencies: List[String]): Project = - Project(name, dummyPath, dependencies, Some(dummyInstance), Nil, compileOptions, dummyPath, Nil, - Nil, Nil, Nil, Config.TestOptions.empty, dummyPath, dummyPath, + Project(name, dummyPath, dependencies, Some(dummyInstance), Nil, Nil, compileOptions, + dummyPath, Nil, Nil, Nil, Nil, Config.TestOptions.empty, dummyPath, dummyPath, Project.defaultPlatform(logger), None, None, dummyOrigin) // format: ON diff --git a/frontend/src/test/scala/bloop/exec/ForkerSpec.scala b/frontend/src/test/scala/bloop/exec/ForkerSpec.scala index 11c7730cac..32f4ea4887 100644 --- a/frontend/src/test/scala/bloop/exec/ForkerSpec.scala +++ b/frontend/src/test/scala/bloop/exec/ForkerSpec.scala @@ -43,7 +43,7 @@ class ForkerSpec { val cwdPath = AbsolutePath(cwd) val project = TestUtil.getProject(TestUtil.RootProject, state) val env = JavaEnv.default - val classpath = project.classpath + val classpath = project.fullClasspathFor(state.build.getDagFor(project)) val config = Forker(env, classpath) val logger = new RecordingLogger val opts = state.commonOptions.copy(env = TestUtil.runAndTestProperties) diff --git a/frontend/src/test/scala/bloop/tasks/IntegrationTestSuite.scala b/frontend/src/test/scala/bloop/tasks/IntegrationTestSuite.scala index 945bfa63f4..89b1258e70 100644 --- a/frontend/src/test/scala/bloop/tasks/IntegrationTestSuite.scala +++ b/frontend/src/test/scala/bloop/tasks/IntegrationTestSuite.scala @@ -79,6 +79,7 @@ class IntegrationTestSuite(testDirectory: Path) { dependencies = previousProjects.map(_.name), scalaInstance = previousProjects.head.scalaInstance, rawClasspath = Nil, + resources = Nil, compileSetup = Config.CompileSetup.empty, classesDir = classesDir, scalacOptions = Nil, diff --git a/frontend/src/test/scala/bloop/tasks/JsTestSpec.scala b/frontend/src/test/scala/bloop/tasks/JsTestSpec.scala index ee0fa31b2b..4149131267 100644 --- a/frontend/src/test/scala/bloop/tasks/JsTestSpec.scala +++ b/frontend/src/test/scala/bloop/tasks/JsTestSpec.scala @@ -181,7 +181,8 @@ class JsTestSpec( @Test def testsAreDetected(): Unit = { // Load the project's classpath by filtering out unwanted FQNs to create the test loader - val classpathEntries = testProject.classpath.map(_.underlying.toUri.toURL) + val classpath = testProject.fullClasspathFor(testState.build.getDagFor(testProject)) + val classpathEntries = classpath.map(_.underlying.toUri.toURL) val testLoader = new URLClassLoader(classpathEntries, Some(TestInternals.filteredLoader).orNull) def frameworks(classLoader: ClassLoader): List[Framework] = { testProject.testFrameworks.flatMap(f => diff --git a/frontend/src/test/scala/bloop/tasks/JvmTestSpec.scala b/frontend/src/test/scala/bloop/tasks/JvmTestSpec.scala index 0472ff18fa..21a8bfc59c 100644 --- a/frontend/src/test/scala/bloop/tasks/JvmTestSpec.scala +++ b/frontend/src/test/scala/bloop/tasks/JvmTestSpec.scala @@ -82,7 +82,8 @@ class JvmTestSpec( private val processRunnerConfig: Forker = { val javaEnv = JavaEnv.default - val classpath = testProject.classpath + val classpath = testProject.fullClasspathFor(testState.build.getDagFor(testProject)) + val classpathEntries = classpath.map(_.underlying.toUri.toURL) Forker(javaEnv, classpath) } diff --git a/frontend/src/test/scala/bloop/tasks/TestResourcesSpec.scala b/frontend/src/test/scala/bloop/tasks/TestResourcesSpec.scala index b23e6d503e..0619fd810c 100644 --- a/frontend/src/test/scala/bloop/tasks/TestResourcesSpec.scala +++ b/frontend/src/test/scala/bloop/tasks/TestResourcesSpec.scala @@ -1,13 +1,11 @@ package bloop.tasks import org.junit.Test -import org.junit.Assert.assertEquals import org.junit.experimental.categories.Category import sbt.internal.util.EscHelpers.removeEscapeSequences import bloop.cli.Commands -import bloop.exec.JavaEnv import bloop.tasks.TestUtil.{loadTestProject, runAndCheck} @Category(Array(classOf[bloop.FastTests])) diff --git a/frontend/src/test/scala/bloop/tasks/TestUtil.scala b/frontend/src/test/scala/bloop/tasks/TestUtil.scala index 76ce8c160e..3c2f036ae3 100644 --- a/frontend/src/test/scala/bloop/tasks/TestUtil.scala +++ b/frontend/src/test/scala/bloop/tasks/TestUtil.scala @@ -289,6 +289,7 @@ object TestUtil { dependencies = dependencies.toList, scalaInstance = scalaInstance, rawClasspath = classpath, + resources = Nil, compileSetup = Config.CompileSetup.empty.copy(order = compileOrder), classesDir = AbsolutePath(target), scalacOptions = Nil, diff --git a/integrations/gradle-bloop/src/main/scala/bloop/integrations/gradle/model/BloopConverter.scala b/integrations/gradle-bloop/src/main/scala/bloop/integrations/gradle/model/BloopConverter.scala index 5ebe9857a4..ac926223cf 100644 --- a/integrations/gradle-bloop/src/main/scala/bloop/integrations/gradle/model/BloopConverter.scala +++ b/integrations/gradle-bloop/src/main/scala/bloop/integrations/gradle/model/BloopConverter.scala @@ -94,6 +94,7 @@ final class BloopConverter(parameters: BloopParameters) { classpath = classpath, out = project.getBuildDir.toPath, classesDir = classesDir, + resources = Some(getResources(sourceSet)), `scala` = scalaConfig, java = getJavaConfig(project, sourceSet), sbt = None, @@ -118,6 +119,9 @@ final class BloopConverter(parameters: BloopParameters) { private def getSources(sourceSet: SourceSet): List[Path] = sourceSet.getAllSource.getSrcDirs.asScala.map(_.toPath).toList + private def getResources(sourceSet: SourceSet): List[Path] = + sourceSet.getResources.getSrcDirs.asScala.map(_.toPath).toList + private def isProjectDependency( projectDependencies: List[ProjectDependency], resolvedArtifact: ResolvedArtifact diff --git a/integrations/maven-bloop/src/main/scala/bloop/integrations/maven/MojoImplementation.scala b/integrations/maven-bloop/src/main/scala/bloop/integrations/maven/MojoImplementation.scala index 864957247a..be86e6c886 100644 --- a/integrations/maven-bloop/src/main/scala/bloop/integrations/maven/MojoImplementation.scala +++ b/integrations/maven-bloop/src/main/scala/bloop/integrations/maven/MojoImplementation.scala @@ -2,15 +2,19 @@ package bloop.integrations.maven import java.io.File import java.nio.file.{Files, Path} +import java.util import bloop.config.Config import org.apache.maven.execution.MavenSession +import org.apache.maven.model.Resource import org.apache.maven.plugin.logging.Log import org.apache.maven.plugin.{MavenPluginManager, Mojo, MojoExecution} import org.apache.maven.project.MavenProject import org.codehaus.plexus.util.xml.Xpp3Dom import scala_maven.AppLauncher +import scala.collection.mutable + object MojoImplementation { private val ScalaMavenGroupArtifact = "net.alchim31.maven:scala-maven-plugin" @@ -68,6 +72,13 @@ object MojoImplementation { private val DefaultTestOptions = Config.TestOptions(Nil, List(Config.TestArgument(List("-v", "-a"), Some(JUnitFramework)))) + def writeConfig( + asScala: mutable.Buffer[File], + getTestOutputDir: File, + strings: util.List[String], + launcher: AppLauncher, + str: String): Int = ??? + def writeCompileAndTestConfiguration(mojo: BloopMojo, session: MavenSession, log: Log): Unit = { import scala.collection.JavaConverters._ def abs(file: File): Path = file.toPath().toRealPath().toAbsolutePath() @@ -97,11 +108,13 @@ object MojoImplementation { val allScalaJars = mojo.getAllScalaJars().map(abs).toList val scalacArgs = mojo.getScalacArgs().asScala.toList - def writeConfig(sourceDirs0: Seq[File], - classesDir0: File, - classpath0: java.util.List[_], - launcher: AppLauncher, - configuration: String): Unit = { + def writeConfig( + sourceDirs0: Seq[File], + classesDir0: File, + classpath0: java.util.List[_], + resources0: java.util.List[Resource], + launcher: AppLauncher, + configuration: String): Unit = { val suffix = if (configuration == "compile") "" else s"-$configuration" val name = project.getArtifactId() + suffix val build = project.getBuild() @@ -131,7 +144,9 @@ object MojoImplementation { val mainClass = if (launcher.getMainClass().isEmpty) None else Some(launcher.getMainClass()) val platform = Some(Config.Platform.Jvm(Config.JvmConfig(javaHome, launcher.getJvmArgs().toList), mainClass)) val resolution = None - val project = Config.Project(name, baseDirectory, sourceDirs, dependencyNames, classpath, out, classesDir, `scala`, java, sbt, test, platform, resolution) + // Resources in Maven require + val resources = Some(resources0.asScala.toList.map(r => classesDir.resolve(r.getTargetPath))) + val project = Config.Project(name, baseDirectory, sourceDirs, dependencyNames, classpath, out, classesDir, resources, `scala`, java, sbt, test, platform, resolution) Config.File(Config.File.LatestVersion, project) } // FORMAT: ON @@ -142,16 +157,23 @@ object MojoImplementation { bloop.config.write(config, configTarget.toPath) } - writeConfig(mojo.getCompileSourceDirectories.asScala, - mojo.getCompileOutputDir, - project.getCompileClasspathElements(), - launcher, - "compile") - writeConfig(mojo.getTestSourceDirectories.asScala, - mojo.getTestOutputDir, - project.getTestClasspathElements(), - launcher, - "test") + writeConfig( + mojo.getCompileSourceDirectories.asScala, + mojo.getCompileOutputDir, + project.getCompileClasspathElements, + project.getResources, + launcher, + "compile" + ) + + writeConfig( + mojo.getTestSourceDirectories.asScala, + mojo.getTestOutputDir, + project.getTestClasspathElements, + project.getTestResources, + launcher, + "test" + ) } private def relativize(base: File, file: File): Option[String] = { diff --git a/integrations/mill-bloop/src/main/scala/bloop/integrations/mill/MillBloop.scala b/integrations/mill-bloop/src/main/scala/bloop/integrations/mill/MillBloop.scala index d92a8373ef..93b60a564b 100644 --- a/integrations/mill-bloop/src/main/scala/bloop/integrations/mill/MillBloop.scala +++ b/integrations/mill-bloop/src/main/scala/bloop/integrations/mill/MillBloop.scala @@ -4,8 +4,8 @@ import _root_.mill._ import _root_.mill.define._ import _root_.mill.scalalib._ import _root_.mill.eval.Evaluator - import ammonite.ops._ +import bloop.config.util.ConfigUtil object Bloop extends ExternalModule { @@ -33,8 +33,7 @@ object Bloop extends ExternalModule { val scalaConfig = module match { case s: ScalaModule => T.task { - val pluginOptions = s.scalacPluginClasspath().map { pathRef => - s"-Xplugin:${pathRef.path}" + val pluginOptions = s.scalacPluginClasspath().map { pathRef => s"-Xplugin:${pathRef.path}" } Some( @@ -88,12 +87,12 @@ object Bloop extends ExternalModule { def transitiveClasspath(m: JavaModule): Task[Seq[Path]] = T.task { m.moduleDeps.map(classes) ++ - m.resources().map(_.path) ++ m.unmanagedClasspath().map(_.path) ++ Task.traverse(m.moduleDeps)(transitiveClasspath)().flatten } val classpath = T.task(transitiveClasspath(module)() ++ ivyDepsClasspath()) + val resources = T.task(module.resources().map(_.path.toNIO).toList) val project = T.task { Config.Project( @@ -104,6 +103,7 @@ object Bloop extends ExternalModule { classpath = classpath().map(_.toNIO).toList, out = out(module).toNIO, classesDir = classes(module).toNIO, + resources = Some(resources()), `scala` = scalaConfig(), java = javaConfig(), sbt = None, diff --git a/integrations/sbt-bloop/src/main/scala/bloop/integrations/sbt/SbtBloop.scala b/integrations/sbt-bloop/src/main/scala/bloop/integrations/sbt/SbtBloop.scala index c3a9443f3a..0d94709864 100644 --- a/integrations/sbt-bloop/src/main/scala/bloop/integrations/sbt/SbtBloop.scala +++ b/integrations/sbt-bloop/src/main/scala/bloop/integrations/sbt/SbtBloop.scala @@ -4,26 +4,9 @@ import java.nio.charset.StandardCharsets import java.nio.file.{Files, Path} import bloop.config.Config +import bloop.config.util.ConfigUtil import bloop.integration.sbt.Feedback -import sbt.{ - AutoPlugin, - ClasspathDep, - ClasspathDependency, - Compile, - ConfigKey, - Configuration, - Def, - File, - Global, - Keys, - LocalRootProject, - Logger, - ProjectRef, - ResolvedProject, - Test, - ThisBuild, - ThisProject -} +import sbt.{AutoPlugin, ClasspathDep, ClasspathDependency, Compile, ConfigKey, Configuration, Def, File, Global, Keys, LocalRootProject, Logger, ProjectRef, ResolvedProject, Test, ThisBuild, ThisProject} import xsbti.compile.CompileOrder object BloopPlugin extends AutoPlugin { @@ -51,14 +34,10 @@ object BloopKeys { "The classifiers that will be exported with `updateClassifiers`") val bloopProductDirectories: TaskKey[Seq[File]] = taskKey[Seq[File]]("Bloop product directories") - val bloopManagedResourceDirectories: SettingKey[Seq[File]] = - settingKey[Seq[File]]("Managed resource directories for bloop") val bloopClassDirectory: SettingKey[File] = settingKey[File]("Directory where to write the class files") val bloopTargetDir: SettingKey[File] = settingKey[File]("Target directory for the pertinent project and configuration") - val bloopResourceManaged: SettingKey[File] = - settingKey[File]("Resource managed for bloop") val bloopInternalClasspath: TaskKey[Seq[(File, File)]] = taskKey[Seq[(File, File)]]("Directory where to write the class files") val bloopInstall: TaskKey[Unit] = @@ -135,10 +114,8 @@ object BloopDefaults { lazy val configSettings: Seq[Def.Setting[_]] = List( BloopKeys.bloopProductDirectories := List(BloopKeys.bloopClassDirectory.value), - BloopKeys.bloopManagedResourceDirectories := managedResourceDirs.value, BloopKeys.bloopClassDirectory := generateBloopProductDirectories.value, BloopKeys.bloopInternalClasspath := bloopInternalDependencyClasspath.value, - BloopKeys.bloopResourceManaged := BloopKeys.bloopTargetDir.value / "resource_managed", BloopKeys.bloopGenerate := bloopGenerate.value, BloopKeys.bloopAnalysisOut := None ) ++ discoveredSbtPluginsSettings @@ -744,8 +721,6 @@ object BloopDefaults { // Force source generators on this task manually Keys.managedSources.value - // Copy the resources, so that they're available when running and testing - bloopCopyResourcesTask.value // format: OFF val config = { @@ -754,10 +729,11 @@ object BloopDefaults { val analysisOut = None val compileSetup = Config.CompileSetup(compileOrder, c.bootLibrary, c.compiler, c.extra, c.autoBoot, c.filterLibrary) val `scala` = Config.Scala(scalaOrg, scalaName, scalaVersion, scalacOptions, allScalaJars, analysisOut, Some(compileSetup)) + val resources = Some(bloopResourcesTask.value) val sbt = computeSbtMetadata.value.map(_.config) val project = Config.Project(projectName, baseDirectory, sources, dependenciesAndAggregates, - classpath, out, classesDir, Some(`scala`), Some(java), sbt, Some(testOptions), Some(platform), resolution) + classpath, out, classesDir, resources, Some(`scala`), Some(java), sbt, Some(testOptions), Some(platform), resolution) Config.File(Config.File.LatestVersion, project) } // format: ON @@ -866,37 +842,24 @@ object BloopDefaults { internalClasspath ++ externalClasspath } - def bloopCopyResourcesTask = Def.taskDyn { + def bloopResourcesTask = Def.taskDyn { val configKey = sbt.ConfigKey(Keys.configuration.value.name) Def.task { import sbt._ - val t = BloopKeys.bloopClassDirectory.value - val dirs = - Classpaths - .concatSettings( - Keys.unmanagedResourceDirectories.in(configKey), - BloopKeys.bloopManagedResourceDirectories.in(configKey)) - .value val s = Keys.streams.value - val cacheStore = bloop.integrations.sbt.Compat.generateCacheFile(s, "copy-resources-bloop") - val mappings = (sbt.PathFinder(Keys.resources.value) --- dirs) pair (sbt.Path - .rebase(dirs, t) | sbt.Path.flat(t)) - s.log.debug("Copy resource mappings: " + mappings.mkString("\n\t", "\n\t", "")) - sbt.Sync(cacheStore)(mappings) - mappings - } - } + val bloopClassDir = BloopKeys.bloopClassDirectory.value + val resourceDirs = Classpaths + .concatSettings( + Keys.unmanagedResourceDirectories.in(configKey), + Keys.managedResourceDirectories.in(configKey) + ) + .value + .map(_.toPath) - def managedResourceDirs: Def.Initialize[Seq[File]] = Def.settingDyn { - val configName = Keys.configuration.value.name - val configKey = sbt.ConfigKey(configName) - Def.setting { - val oldUnmanagedResourceDirs = Keys.managedResourceDirectories.in(configKey).value - val oldResourceDir = Keys.resourceManaged.in(configKey).value - val newResourceDir = - BloopKeys.bloopResourceManaged.in(configKey).value / Defaults.nameForSrc(configName) - oldUnmanagedResourceDirs.map { dir => if (dir == oldResourceDir) newResourceDir else dir - } + val allResourceFiles = Keys.resources.in(configKey).value + val additionalResources = + ConfigUtil.pathsOutsideRoots(resourceDirs, allResourceFiles.map(_.toPath)) + (resourceDirs ++ additionalResources).toList } } diff --git a/project/BuildPlugin.scala b/project/BuildPlugin.scala index 6bfc49843a..5e74b8e1a8 100644 --- a/project/BuildPlugin.scala +++ b/project/BuildPlugin.scala @@ -267,7 +267,7 @@ object BuildImplementation { final val globalSettings: Seq[Def.Setting[_]] = Seq( Keys.cancelable := true, - BuildKeys.schemaVersion := "4.0", + BuildKeys.schemaVersion := "4.2", Keys.testOptions in Test += sbt.Tests.Argument("-oD"), Keys.onLoadMessage := Header.intro, Keys.onLoad := BuildDefaults.bloopOnLoad.value, From 1f4604ca98cbeaa97a3fcf7b734ff7e201bfc211 Mon Sep 17 00:00:00 2001 From: jvican Date: Thu, 22 Nov 2018 14:02:47 +0100 Subject: [PATCH 2/2] Add resources before the classes directory The resources should always be in front of the classes directory of the same project to ensure that if the build tool adds the resources to the classes directory we override them with the source of the resources. --- frontend/src/main/scala/bloop/data/Project.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/main/scala/bloop/data/Project.scala b/frontend/src/main/scala/bloop/data/Project.scala index dfa96b05a4..71e65e426d 100644 --- a/frontend/src/main/scala/bloop/data/Project.scala +++ b/frontend/src/main/scala/bloop/data/Project.scala @@ -76,7 +76,7 @@ final case class Project( if (index == -1) { p.resources.foreach(r => cp.append(r)) } - else cp.insertAll(index + 1, p.resources) + else cp.insertAll(index, p.resources) } cp.toArray }