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

bugfix: Don't add single files to classpath #2553

Merged
merged 1 commit into from
Jan 9, 2025
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
40 changes: 39 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,34 @@ 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)
adpi2 marked this conversation as resolved.
Show resolved Hide resolved

val singleFilesToCopy =
for (file <- singleFiles)
yield file.underlying -> copyTo.underlying.resolve(file.underlying.toFile().getName())

val classpathEntriesCopy =
for (entry <- classpathEntries) yield {
ParallelOps
.copyDirectories(config)(
entry.underlying,
copyTo.underlying,
inputs.ioScheduler,
enableCancellation = false,
inputs.logger,
singleFilesToCopy
)
}

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

def complete(success: Boolean): Unit = {

val deleteAfterCompilation = Task { BloopPaths.delete(AbsolutePath(backupDir)) }
Expand All @@ -220,7 +248,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 +261,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
16 changes: 14 additions & 2 deletions backend/src/main/scala/bloop/io/ParallelOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ object ParallelOps {
target: Path,
scheduler: Scheduler,
enableCancellation: Boolean,
logger: Logger
logger: Logger,
additionalFiles: Seq[(Path, Path)] = Nil
): Task[FileWalk] = Task.defer {
val isCancelled = AtomicBoolean(false)

Expand All @@ -82,6 +83,14 @@ object ParallelOps {
MulticastStrategy.publish
)(scheduler)

val copyAdditionalFiles = Task {
additionalFiles.foreach {
case (from, to) =>
val attributes = Files.readAttributes(from, classOf[BasicFileAttributes])
observer.onNext(((from, attributes), to))
}
}

val discovery = new FileVisitor[Path] {
var firstVisit: Boolean = true
var currentTargetDirectory: Path = target
Expand Down Expand Up @@ -274,7 +283,10 @@ object ParallelOps {
}
}

val orderlyDiscovery = Task.fromFuture(subscribed.future).flatMap(_ => discoverFileTree)
val orderlyDiscovery = Task
.fromFuture(subscribed.future)
.flatMap(_ => copyAdditionalFiles)
.flatMap(_ => discoverFileTree)
val aggregatedCopyTask = Task {
Task.mapBoth(orderlyDiscovery, copyFilesInParallel) { case (fileWalk, _) => fileWalk }
}.flatten.executeOn(scheduler)
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
34 changes: 34 additions & 0 deletions frontend/src/test/scala/bloop/RunSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bloop
import java.io.ByteArrayInputStream
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.StandardOpenOption
import java.util.concurrent.TimeUnit

import scala.concurrent.Await
Expand Down Expand Up @@ -474,6 +475,39 @@ 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 runState = state.run(`A`)
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
Loading