Skip to content

Commit

Permalink
bugfix: Copy resources to client directory on successful compilation
Browse files Browse the repository at this point in the history
  • Loading branch information
tgodzik committed Jan 7, 2025
1 parent c1ff013 commit 62c3487
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 21 deletions.
44 changes: 43 additions & 1 deletion backend/src/main/scala/bloop/BloopClassFileManager.scala
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,38 @@ final class BloopClassFileManager(

}

private def copyResources(
resources: List[AbsolutePath],
copyTo: AbsolutePath,
config: ParallelOps.CopyConfiguration
): Task[Unit] = {
val (singleFiles, classpathEntries) =
resources.partition(path => path.exists && path.isFile)

val singleFilesCopy = Task {
for (file <- singleFiles) {
Files.copy(
file.underlying,
copyTo.underlying.resolve(file.underlying.toFile().getName()),
StandardCopyOption.REPLACE_EXISTING
)
}
}
val classpathEntriesCopy =
for (entry <- classpathEntries) yield {
ParallelOps
.copyDirectories(config)(
entry.underlying,
copyTo.underlying,
inputs.ioScheduler,
enableCancellation = false,
inputs.logger
)
}

Task.gatherUnordered(singleFilesCopy :: classpathEntriesCopy).map(_ => ())
}

def complete(success: Boolean): Unit = {

val deleteAfterCompilation = Task { BloopPaths.delete(AbsolutePath(backupDir)) }
Expand All @@ -220,7 +252,7 @@ final class BloopClassFileManager(
val config =
ParallelOps.CopyConfiguration(5, CopyMode.ReplaceExisting, Set.empty, Set.empty)

ParallelOps
val copyClassFiles = ParallelOps
.copyDirectories(config)(
newClassesDir,
clientExternalClassesDir.underlying,
Expand All @@ -233,6 +265,16 @@ final class BloopClassFileManager(
()
}
.flatMap(_ => deleteAfterCompilation)

Task
.gatherUnordered(
List(
copyClassFiles,
copyResources(inputs.resources, clientExternalClassesDir, config)
)
)
.map(_ => ())

}
}
)
Expand Down
3 changes: 2 additions & 1 deletion backend/src/main/scala/bloop/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ case class CompileInputs(
ioScheduler: Scheduler,
ioExecutor: Executor,
invalidatedClassFilesInDependentProjects: Set[File],
generatedClassFilePathsInDependentProjects: Map[String, File]
generatedClassFilePathsInDependentProjects: Map[String, File],
resources: List[AbsolutePath]
)

case class CompileOutPaths(
Expand Down
21 changes: 5 additions & 16 deletions frontend/src/main/scala/bloop/data/Project.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import java.nio.charset.StandardCharsets

import scala.collection.mutable
import scala.util.Properties
import scala.util.Success
import scala.util.Try
import scala.util.control.NonFatal

Expand All @@ -28,7 +29,6 @@ import bloop.util.JavaRuntime
import scalaz.Cord
import xsbti.compile.ClasspathOptions
import xsbti.compile.CompileOrder
import scala.util.Success

final case class Project(
name: String,
Expand Down Expand Up @@ -117,35 +117,25 @@ final case class Project(
private def fullClasspath(
dag: Dag[Project],
client: ClientInfo,
rawClasspath: List[AbsolutePath],
pickValidResources: Project => Array[AbsolutePath]
rawClasspath: List[AbsolutePath]
): Array[AbsolutePath] = {
val addedResources = new mutable.HashSet[AbsolutePath]()
val cp = (this.genericClassesDir :: rawClasspath).toBuffer

// Add the resources right before the classes directory if found in the classpath
Dag.dfs(dag, mode = Dag.PreOrder).foreach { p =>
val genericClassesDir = p.genericClassesDir
val uniqueClassesDir = client.getUniqueClassesDirFor(p, forceGeneration = true)
val index = cp.indexOf(genericClassesDir)
val newResources = pickValidResources(p).filterNot(r => addedResources.contains(r))
newResources.foreach(r => addedResources.add(r))
if (index == -1) {
// Not found? Weird. Let's add resources to end just in case
cp.appendAll(newResources)
} else {
// Replace in-place for the classes directory unique to the client
if (index != -1) {
cp(index) = uniqueClassesDir
// Prepend resources to classes directories
cp.insertAll(index, newResources)
}
}

cp.toArray
}

def fullClasspath(dag: Dag[Project], client: ClientInfo): Array[AbsolutePath] = {
fullClasspath(dag, client, rawClasspath, p => Project.pickValidResources(p.resources))
fullClasspath(dag, client, rawClasspath)
}

def fullRuntimeClasspath(dag: Dag[Project], client: ClientInfo): Array[AbsolutePath] = {
Expand All @@ -156,8 +146,7 @@ final case class Project(
fullClasspath(
dag,
client,
rawRuntimeClasspath,
p => Project.pickValidResources(p.runtimeResources)
rawRuntimeClasspath
)
}

Expand Down
3 changes: 2 additions & 1 deletion frontend/src/main/scala/bloop/engine/tasks/CompileTask.scala
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ object CompileTask {
ExecutionContext.ioScheduler,
ExecutionContext.ioExecutor,
bundle.dependenciesData.allInvalidatedClassFiles,
bundle.dependenciesData.allGeneratedClassFilePaths
bundle.dependenciesData.allGeneratedClassFilePaths,
project.runtimeResources
)
}

Expand Down
37 changes: 37 additions & 0 deletions frontend/src/test/scala/bloop/RunSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.experimental.categories.Category
import java.nio.file.StandardOpenOption

@Category(Array(classOf[bloop.FastTests]))
class RunSpec extends BloopHelpers {
Expand Down Expand Up @@ -474,6 +475,42 @@ class RunSpec extends BloopHelpers {
}
}

@Test
def runSeesSingleFileResources(): Unit = {
TestUtil.withinWorkspace { workspace =>
object Sources {
val `a/A.scala` =
"""/a/A.scala
|object A {
| def main(args: Array[String]): Unit = {
| val res = getClass.getClassLoader.getResourceAsStream("resource.txt")
| val content = scala.io.Source.fromInputStream(res).mkString
| //assert("goodbye" == content)
| }
|}""".stripMargin
}

val tmpDir = Files.createTempDirectory("runtime-single")
val resource = tmpDir.resolve("resource.txt")
Files.write(resource, "goodbye".getBytes(), StandardOpenOption.CREATE)
val logger = new RecordingLogger(ansiCodesSupported = false)
val `A` = TestProject(
workspace,
"a",
List(Sources.`a/A.scala`),
additionalResources = List(resource)
)

val projects = List(`A`)
val state = loadState(workspace, projects, logger)
val external = state.getClientExternalDir(A)
println(external)
val runState = state.run(`A`)
logger.dump()
assertEquals(ExitStatus.Ok, runState.status)
}
}

@Test
def runUsesRuntimeEnvironment(): Unit = {
TestUtil.withinWorkspace { workspace =>
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/test/scala/bloop/util/TestProject.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ abstract class BaseTestProject {
order: Config.CompileOrder = Config.Mixed,
jars: Array[AbsolutePath] = Array(),
sourcesGlobs: List[Config.SourcesGlobs] = Nil,
sourceGenerators: List[Config.SourceGenerator] = Nil
sourceGenerators: List[Config.SourceGenerator] = Nil,
additionalResources: List[Path] = Nil
): TestProject = {
val projectBaseDir = Files.createDirectories(baseDir.underlying.resolve(name))
val ProjectArchetype(sourceDir, outDir, resourceDir, classes, runtimeResourceDir) =
Expand Down Expand Up @@ -146,7 +147,7 @@ abstract class BaseTestProject {
if (strictDependencies) (directClasspath, transitiveClasspath)
else (transitiveClasspath, transitiveClasspath)
}
val compileResourcesList = Some(List(resourceDir.underlying))
val compileResourcesList = Some(List(resourceDir.underlying) ::: additionalResources)
val runtimeResourcesList = runtimeResources match {
case None => compileResourcesList
case Some(_) => Some(List(runtimeResourceDir.underlying))
Expand Down

0 comments on commit 62c3487

Please sign in to comment.