From 24c9d2b8bb8bcd678dc40a3dd16e7dd90dfdd9e1 Mon Sep 17 00:00:00 2001 From: James Judd Date: Thu, 25 Jul 2024 12:58:30 -0600 Subject: [PATCH] Handle rules_jvm_external's jar stamping in more places If you were using rules_jvm_external's stamping for the jars you create an AnnexScalaInstance with, then the jars would be filtered out of a collection they shouldn't have been and you'd get a null error when looking for the compiler version. --- .../workers/common/AnnexScalaInstance.scala | 49 +++++++++++-------- .../rules_scala/workers/common/FileUtil.scala | 16 +++++- .../workers/zinc/compile/ZincRunner.scala | 9 ++-- 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/main/scala/higherkindness/rules_scala/workers/common/AnnexScalaInstance.scala b/src/main/scala/higherkindness/rules_scala/workers/common/AnnexScalaInstance.scala index 149ab4ab..dffb7cc3 100644 --- a/src/main/scala/higherkindness/rules_scala/workers/common/AnnexScalaInstance.scala +++ b/src/main/scala/higherkindness/rules_scala/workers/common/AnnexScalaInstance.scala @@ -97,16 +97,19 @@ object AnnexScalaInstance { // These are not the most robust checks for these jars, but it is more or // less what Zinc and Bloop are doing. They're also fast, so if it works it // works. - private final def isScala2CompilerJar(jarName: String): Boolean = { + private final def isScala2CompilerJar(jar: File): Boolean = { + val jarName = FileUtil.getNameWithoutRulesJvmExternalStampPrefix(jar) jarName.startsWith("scala-compiler") || jarName.startsWith("scala-reflect") } - private final def isScala3CompilerJar(jarName: String): Boolean = { + private final def isScala3CompilerJar(jar: File): Boolean = { + val jarName = FileUtil.getNameWithoutRulesJvmExternalStampPrefix(jar) jarName.startsWith("scala3-compiler") || jarName.startsWith("scala3-interfaces") || jarName.startsWith("tasty-core_3") || jarName.startsWith("scala-asm") } - private final def isScalaLibraryJar(jarName: String): Boolean = { + private final def isScalaLibraryJar(jar: File): Boolean = { + val jarName = FileUtil.getNameWithoutRulesJvmExternalStampPrefix(jar) jarName.startsWith("scala-library") || jarName.startsWith("scala3-library") } } @@ -125,25 +128,40 @@ private[common] class AnnexScalaInstance(override val allJars: Array[File]) exte // We need to include the full classpath for the Scala 2 or Scala 3 compilers. // Thankfully that classpath doesn't seem to change very often. override val compilerJars: Array[File] = allJars.filter { jar => - val jarName = jar.getName - AnnexScalaInstance.isScala2CompilerJar(jarName) || - AnnexScalaInstance.isScala3CompilerJar(jarName) || - AnnexScalaInstance.isScalaLibraryJar(jarName) + AnnexScalaInstance.isScala2CompilerJar(jar) || + AnnexScalaInstance.isScala3CompilerJar(jar) || + AnnexScalaInstance.isScalaLibraryJar(jar) } // Jars for the Scala library classes - override val libraryJars: Array[File] = - allJars.filter(jar => AnnexScalaInstance.isScalaLibraryJar(jar.getName)) + override val libraryJars: Array[File] = allJars.filter(AnnexScalaInstance.isScalaLibraryJar) // All the jars that are not compiler or library jars override val otherJars: Array[File] = allJars.diff(compilerJars ++ libraryJars) + // Loader for only the classes and resources in the library jars of this Scala instance + override val loaderLibraryOnly: ClassLoader = AnnexScalaInstance.getClassLoader(libraryJars) + + // Loader for all the classes and resources in all the jars of this Scala instance + override val loader: ClassLoader = AnnexScalaInstance.getClassLoader(allJars) + + // Loader for all the classes and resources in all the compiler jars of this + // Scala instance + override val loaderCompilerOnly: ClassLoader = AnnexScalaInstance.getClassLoader(compilerJars) + // Version for this Scala instance override val actualVersion: String = { - val stream = AnnexScalaInstance - .getClassLoader(compilerJars) + val stream = loaderCompilerOnly .getResourceAsStream("compiler.properties") + if (stream == null) { + throw new Exception( + "The resource stream for the compiler.properties file in the compiler jar is null." + + " Something went wrong getting the version in that file in the compiler jar. The jars" + + s" which were searched are as follows: ${compilerJars.mkString}", + ) + } + try { val props = new Properties props.load(stream) @@ -152,15 +170,6 @@ private[common] class AnnexScalaInstance(override val allJars: Array[File]) exte } override val version: String = actualVersion - // Loader for only the classes and resources in the library jars of this Scala instance - override val loaderLibraryOnly: ClassLoader = AnnexScalaInstance.getClassLoader(libraryJars) - - // Loader for all the classes and resources in all the jars of this Scala instance - override val loader: ClassLoader = AnnexScalaInstance.getClassLoader(allJars) - - // Loader for all the classes and resources in all the compiler jars of this - // Scala instance - override val loaderCompilerOnly: ClassLoader = AnnexScalaInstance.getClassLoader(compilerJars) } /** diff --git a/src/main/scala/higherkindness/rules_scala/workers/common/FileUtil.scala b/src/main/scala/higherkindness/rules_scala/workers/common/FileUtil.scala index f6358a32..f4cf2c2e 100644 --- a/src/main/scala/higherkindness/rules_scala/workers/common/FileUtil.scala +++ b/src/main/scala/higherkindness/rules_scala/workers/common/FileUtil.scala @@ -2,7 +2,7 @@ package higherkindness.rules_scala package workers.common import scala.annotation.tailrec -import java.io.IOException +import java.io.{File, IOException} import java.nio.channels.FileChannel import java.nio.file.{FileAlreadyExistsException, FileVisitResult, Files, Path, SimpleFileVisitor, StandardCopyOption, StandardOpenOption} import java.nio.file.attribute.BasicFileAttributes @@ -45,6 +45,20 @@ class ZipFileVisitor(root: Path, zip: ZipOutputStream) extends SimpleFileVisitor object FileUtil { + /** + * Given a jar, return the file name without the rules_jvm_external prefix added to it when stamping. + * + * rules_jvm_external adds a prefix to jars when it stamps them. We sometimes need to look at jar names for various + * things. The rules_jvm_external prefix often breaks that string comparison. + */ + def getNameWithoutRulesJvmExternalStampPrefix(file: File): String = { + file.getName.stripPrefix("header_").stripPrefix("processed_") + } + + def getNameWithoutRulesJvmExternalStampPrefix(path: Path): String = { + getNameWithoutRulesJvmExternalStampPrefix(path.toFile) + } + def copy(source: Path, target: Path) = Files.walkFileTree(source, new CopyFileVisitor(source, target)) def delete(path: Path) = Files.walkFileTree(path, new DeleteFileVisitor) diff --git a/src/main/scala/higherkindness/rules_scala/workers/zinc/compile/ZincRunner.scala b/src/main/scala/higherkindness/rules_scala/workers/zinc/compile/ZincRunner.scala index f59aa878..bc4dc7d7 100644 --- a/src/main/scala/higherkindness/rules_scala/workers/zinc/compile/ZincRunner.scala +++ b/src/main/scala/higherkindness/rules_scala/workers/zinc/compile/ZincRunner.scala @@ -302,12 +302,11 @@ object ZincRunner extends WorkerMain[Namespace] { // Filter out the Scala standard library as that should just always be // implicitly available and not something we should be book keeping. deps.filter(Dep.used(deps, resultAnalysis.relations, lookup)).filterNot { dep => - // rules_jvm_external adds header_ or processed_ to a lot of jars - // rules_jvm_external is frequently used in bazel projects, so we - // handle when jars like that show up - val depFileName = dep.file.toFile.getName.stripPrefix("header_").stripPrefix("processed_") + val filteredDepFileName = FileUtil.getNameWithoutRulesJvmExternalStampPrefix(dep.file) - scalaInstance.libraryJars.map(_.getName).contains(depFileName) + scalaInstance.libraryJars + .map(FileUtil.getNameWithoutRulesJvmExternalStampPrefix) + .contains(filteredDepFileName) } Files.write(namespace.get[File]("output_used").toPath, usedDeps.map(_.file.toString).sorted.asJava)