Repository: pantsbuild/pants

Issue:
Scalac fails when using plugins that emit additional compilation results
**Describe the bug**
Some Scala compiler plugins emit additional compilation results from the typical output that comes out of `scalac`. Some of those plugins are `semanticdb`, `scalajs`, `scalanative` and others.

The scenario workspace is as follows:

`3rdparty/jvm/BUILD`:
```python
jvm_artifact(
  name="scala-library",
  group="org.scala-lang",
  artifact="scala-library",
  version="2.13.12",
)

scala_artifact(
  name="semanticdb-jar",
  group="org.scalameta",
  artifact="semanticdb-scalac",
  version="4.8.15",
  crossversion="full"
)

scalac_plugin(
  name="semanticdb",
  artifact=":semanticdb-jar"
)
```

`pants.toml`:
```toml
[GLOBAL]
pants_version = "2.18.1"
backend_packages = [
  "pants.backend.experimental.scala",
]

[source]
root_patterns = [
  '/src/scala',
]

[jvm]
jdk = "temurin:1.11"
tool_jdk = "temurin:1.11"

[scala.version_for_resolve]
jvm-default = "2.13.12"

[scalac]
args = ["-Yrangepos"]

[scalac.plugins_for_resolve]
jvm-default = "semanticdb"
```

`src/scala/example/BUILD`:
```python
scala_sources()
```

`src/scala/example/SimpleApp.scala`:
```scala
package example

object SimpleApp {
  def main(args: Array[String]) {
    println("Hello World")
  }
}
```

When running `pants check ::` the following error is shown:
```
2:34:01.06 [ERROR] Completed: pants.backend.scala.compile.scalac.compile_scala_source - src/scala/example/SimpleApp.scala failed (exit code 1).
error: src.scala.example.SimpleApp.scala.scalac.jar (Is a directory)
1 error

12:34:01.06 [ERROR] Completed: Check compilation for Scala - scalac - scalac failed (exit code 1).
```

**Pants version**
2.18.1

**OS**
Both

**Additional info**
The error comes from the process running `scalac` as Pants expect it to output a JAR file but the additional output from the `scalac` plugin changes its behavior as it now produces a directory.



Discussion:

Resolving patch:
diff --git a/src/python/pants/backend/scala/compile/scalac.py b/src/python/pants/backend/scala/compile/scalac.py
--- a/src/python/pants/backend/scala/compile/scalac.py
+++ b/src/python/pants/backend/scala/compile/scalac.py
@@ -25,9 +25,11 @@
     ScalaArtifactsForVersionResult,
     ScalaVersion,
 )
-from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
-from pants.engine.fs import EMPTY_DIGEST, Digest, MergeDigests
-from pants.engine.process import FallibleProcessResult
+from pants.core.util_rules.source_files import SourceFilesRequest
+from pants.core.util_rules.stripped_source_files import StrippedSourceFiles
+from pants.core.util_rules.system_binaries import BashBinary, ZipBinary
+from pants.engine.fs import EMPTY_DIGEST, CreateDigest, Digest, Directory, MergeDigests
+from pants.engine.process import FallibleProcessResult, Process, ProcessResult
 from pants.engine.rules import Get, MultiGet, collect_rules, rule
 from pants.engine.target import CoarsenedTarget, SourcesField
 from pants.engine.unions import UnionRule
@@ -44,9 +46,9 @@
 from pants.jvm.jdk_rules import JdkEnvironment, JdkRequest, JvmProcess
 from pants.jvm.resolve.common import ArtifactRequirements
 from pants.jvm.resolve.coursier_fetch import ToolClasspath, ToolClasspathRequest
+from pants.jvm.strip_jar import strip_jar
 from pants.jvm.strip_jar.strip_jar import StripJarRequest
 from pants.jvm.subsystems import JvmSubsystem
-from pants.jvm.target_types import NO_MAIN_CLASS
 from pants.util.logging import LogLevel
 
 logger = logging.getLogger(__name__)
@@ -72,6 +74,8 @@ async def compile_scala_source(
     scala: ScalaSubsystem,
     jvm: JvmSubsystem,
     scalac: Scalac,
+    bash: BashBinary,
+    zip_binary: ZipBinary,
     request: CompileScalaSourceRequest,
 ) -> FallibleClasspathEntry:
     # Request classpath entries for our direct dependencies.
@@ -98,7 +102,9 @@ async def compile_scala_source(
         component_members_with_sources,
         await MultiGet(
             Get(
-                SourceFiles,
+                # Some Scalac plugins (i.e. SemanticDB) require us to use stripped source files so the plugin
+                # would emit compilation output that correlates with the appropiate paths in the input files.
+                StrippedSourceFiles,
                 SourceFilesRequest(
                     (t.get(SourcesField),),
                     for_sources_types=(ScalaSourceField, JavaSourceField),
@@ -175,7 +181,11 @@ async def compile_scala_source(
     classpath_arg = ":".join(user_classpath.immutable_inputs_args(prefix=usercp))
 
     output_file = compute_output_jar_filename(request.component)
-    process_result = await Get(
+    compilation_output_dir = "__out"
+    compilation_empty_dir = await Get(Digest, CreateDigest([Directory(compilation_output_dir)]))
+    merged_digest = await Get(Digest, MergeDigests([sources_digest, compilation_empty_dir]))
+
+    compile_result = await Get(
         FallibleProcessResult,
         JvmProcess(
             jdk=jdk,
@@ -187,12 +197,8 @@ async def compile_scala_source(
                 *local_plugins.args(local_scalac_plugins_relpath),
                 *(("-classpath", classpath_arg) if classpath_arg else ()),
                 *scalac.args,
-                # NB: We set a non-existent main-class so that using `-d` produces a `jar` manifest
-                # with stable content.
-                "-Xmain-class",
-                NO_MAIN_CLASS,
                 "-d",
-                output_file,
+                compilation_output_dir,
                 *sorted(
                     chain.from_iterable(
                         sources.snapshot.files
@@ -200,17 +206,44 @@ async def compile_scala_source(
                     )
                 ),
             ],
-            input_digest=sources_digest,
+            input_digest=merged_digest,
             extra_immutable_input_digests=extra_immutable_input_digests,
             extra_nailgun_keys=extra_nailgun_keys,
-            output_files=(output_file,),
+            output_directories=(compilation_output_dir,),
             description=f"Compile {request.component} with scalac",
             level=LogLevel.DEBUG,
         ),
     )
     output: ClasspathEntry | None = None
-    if process_result.exit_code == 0:
-        output_digest = process_result.output_digest
+    if compile_result.exit_code == 0:
+        # We package the outputs into a JAR file in a similar way as how it's
+        # done in the `javac.py` implementation
+        jar_result = await Get(
+            ProcessResult,
+            Process(
+                argv=[
+                    bash.path,
+                    "-c",
+                    " ".join(
+                        [
+                            "cd",
+                            compilation_output_dir,
+                            ";",
+                            zip_binary.path,
+                            "-r",
+                            f"../{output_file}",
+                            ".",
+                        ]
+                    ),
+                ],
+                input_digest=compile_result.output_digest,
+                output_files=(output_file,),
+                description=f"Capture outputs of {request.component} for scalac",
+                level=LogLevel.TRACE,
+            ),
+        )
+        output_digest = jar_result.output_digest
+
         if jvm.reproducible_jars:
             output_digest = await Get(
                 Digest, StripJarRequest(digest=output_digest, filenames=(output_file,))
@@ -220,7 +253,7 @@ async def compile_scala_source(
 
     return FallibleClasspathEntry.from_fallible_process_result(
         str(request.component),
-        process_result,
+        compile_result,
         output,
     )
 
@@ -251,5 +284,6 @@ def rules():
         *scala_artifact_rules(),
         *scalac_plugins_rules(),
         *versions.rules(),
+        *strip_jar.rules(),
         UnionRule(ClasspathEntryRequest, CompileScalaSourceRequest),
     ]
diff --git a/src/python/pants/jvm/jar_tool/jar_tool.py b/src/python/pants/jvm/jar_tool/jar_tool.py
--- a/src/python/pants/jvm/jar_tool/jar_tool.py
+++ b/src/python/pants/jvm/jar_tool/jar_tool.py
@@ -72,19 +72,22 @@ def __init__(
         manifest: str | None = None,
         jars: Iterable[str] | None = None,
         file_mappings: Mapping[str, str] | None = None,
+        files: Iterable[str] | None = None,
         default_action: JarDuplicateAction | None = None,
         policies: Iterable[tuple[str, str | JarDuplicateAction]] | None = None,
         skip: Iterable[str] | None = None,
         compress: bool = False,
         update: bool = False,
     ) -> None:
+        _file_mappings = {**(file_mappings or {}), **({f: f for f in (files or [])})}
+
         object.__setattr__(self, "jar_name", jar_name)
         object.__setattr__(self, "digest", digest)
         object.__setattr__(self, "main_class", main_class)
         object.__setattr__(self, "manifest", manifest)
         object.__setattr__(self, "classpath_entries", tuple(classpath_entries or ()))
         object.__setattr__(self, "jars", tuple(jars or ()))
-        object.__setattr__(self, "file_mappings", FrozenDict(file_mappings or {}))
+        object.__setattr__(self, "file_mappings", FrozenDict(_file_mappings))
         object.__setattr__(self, "default_action", default_action)
         object.__setattr__(self, "policies", tuple(JarToolRequest.__parse_policies(policies or ())))
         object.__setattr__(self, "skip", tuple(skip or ()))


Test patch:
diff --git a/src/python/pants/backend/codegen/protobuf/scala/rules_integration_test.py b/src/python/pants/backend/codegen/protobuf/scala/rules_integration_test.py
--- a/src/python/pants/backend/codegen/protobuf/scala/rules_integration_test.py
+++ b/src/python/pants/backend/codegen/protobuf/scala/rules_integration_test.py
@@ -234,7 +234,7 @@ def assert_gen(addr: Address, expected: Iterable[str]) -> None:
         assert_files_generated(
             rule_runner,
             addr,
-            source_roots=["src/python", "/src/protobuf", "/tests/protobuf"],
+            source_roots=["src/python", "/src/protobuf", "/tests/protobuf", "src/jvm"],
             expected_files=list(expected),
         )
 
diff --git a/src/python/pants/backend/codegen/thrift/scrooge/scala/rules_integration_test.py b/src/python/pants/backend/codegen/thrift/scrooge/scala/rules_integration_test.py
--- a/src/python/pants/backend/codegen/thrift/scrooge/scala/rules_integration_test.py
+++ b/src/python/pants/backend/codegen/thrift/scrooge/scala/rules_integration_test.py
@@ -200,7 +200,7 @@ def assert_gen(addr: Address, expected: list[str]) -> None:
         assert_files_generated(
             rule_runner,
             addr,
-            source_roots=["src/python", "/src/thrift", "/tests/thrift"],
+            source_roots=["src/python", "/src/thrift", "/tests/thrift", "src/jvm"],
             expected_files=expected,
         )
 
diff --git a/src/python/pants/backend/scala/compile/acyclic.test.lock b/src/python/pants/backend/scala/compile/acyclic.test.lock
--- a/src/python/pants/backend/scala/compile/acyclic.test.lock
+++ b/src/python/pants/backend/scala/compile/acyclic.test.lock
@@ -1,6 +1,6 @@
 # This lockfile was autogenerated by Pants. To regenerate, run:
 #
-#    ./pants internal-generate-test-lockfile-fixtures ::
+#    pants internal-generate-test-lockfile-fixtures ::
 #
 # --- BEGIN PANTS LOCKFILE METADATA: DO NOT EDIT OR REMOVE ---
 # {
diff --git a/src/python/pants/backend/scala/compile/joda-time.test.lock b/src/python/pants/backend/scala/compile/joda-time.test.lock
--- a/src/python/pants/backend/scala/compile/joda-time.test.lock
+++ b/src/python/pants/backend/scala/compile/joda-time.test.lock
@@ -1,6 +1,6 @@
 # This lockfile was autogenerated by Pants. To regenerate, run:
 #
-#    ./pants internal-generate-test-lockfile-fixtures ::
+#    pants internal-generate-test-lockfile-fixtures ::
 #
 # --- BEGIN PANTS LOCKFILE METADATA: DO NOT EDIT OR REMOVE ---
 # {
diff --git a/src/python/pants/backend/scala/compile/multiple-scalac-plugins.test.lock b/src/python/pants/backend/scala/compile/multiple-scalac-plugins.test.lock
--- a/src/python/pants/backend/scala/compile/multiple-scalac-plugins.test.lock
+++ b/src/python/pants/backend/scala/compile/multiple-scalac-plugins.test.lock
@@ -1,6 +1,6 @@
 # This lockfile was autogenerated by Pants. To regenerate, run:
 #
-#    ./pants internal-generate-test-lockfile-fixtures ::
+#    pants internal-generate-test-lockfile-fixtures ::
 #
 # --- BEGIN PANTS LOCKFILE METADATA: DO NOT EDIT OR REMOVE ---
 # {
diff --git a/src/python/pants/backend/scala/compile/scala-library-2.12.test.lock b/src/python/pants/backend/scala/compile/scala-library-2.12.test.lock
--- a/src/python/pants/backend/scala/compile/scala-library-2.12.test.lock
+++ b/src/python/pants/backend/scala/compile/scala-library-2.12.test.lock
@@ -1,6 +1,6 @@
 # This lockfile was autogenerated by Pants. To regenerate, run:
 #
-#    ./pants internal-generate-test-lockfile-fixtures ::
+#    pants internal-generate-test-lockfile-fixtures ::
 #
 # --- BEGIN PANTS LOCKFILE METADATA: DO NOT EDIT OR REMOVE ---
 # {
diff --git a/src/python/pants/backend/scala/compile/scala-library-2.13.test.lock b/src/python/pants/backend/scala/compile/scala-library-2.13.test.lock
--- a/src/python/pants/backend/scala/compile/scala-library-2.13.test.lock
+++ b/src/python/pants/backend/scala/compile/scala-library-2.13.test.lock
@@ -1,6 +1,6 @@
 # This lockfile was autogenerated by Pants. To regenerate, run:
 #
-#    ./pants internal-generate-test-lockfile-fixtures ::
+#    pants internal-generate-test-lockfile-fixtures ::
 #
 # --- BEGIN PANTS LOCKFILE METADATA: DO NOT EDIT OR REMOVE ---
 # {
diff --git a/src/python/pants/backend/scala/compile/scala-library-3.test.lock b/src/python/pants/backend/scala/compile/scala-library-3.test.lock
--- a/src/python/pants/backend/scala/compile/scala-library-3.test.lock
+++ b/src/python/pants/backend/scala/compile/scala-library-3.test.lock
@@ -1,6 +1,6 @@
 # This lockfile was autogenerated by Pants. To regenerate, run:
 #
-#    ./pants internal-generate-test-lockfile-fixtures ::
+#    pants internal-generate-test-lockfile-fixtures ::
 #
 # --- BEGIN PANTS LOCKFILE METADATA: DO NOT EDIT OR REMOVE ---
 # {
diff --git a/src/python/pants/backend/scala/compile/scalac_test.py b/src/python/pants/backend/scala/compile/scalac_test.py
--- a/src/python/pants/backend/scala/compile/scalac_test.py
+++ b/src/python/pants/backend/scala/compile/scalac_test.py
@@ -26,7 +26,7 @@
 from pants.backend.scala.target_types import rules as target_types_rules
 from pants.build_graph.address import Address
 from pants.core.goals.check import CheckResults
-from pants.core.util_rules import source_files
+from pants.core.util_rules import source_files, stripped_source_files, system_binaries
 from pants.engine.addresses import Addresses
 from pants.engine.internals.parametrize import Parametrize
 from pants.engine.internals.scheduler import ExecutionError
@@ -56,11 +56,13 @@ def rule_runner() -> RuleRunner:
             *strip_jar.rules(),
             *scalac_rules(),
             *source_files.rules(),
+            *stripped_source_files.rules(),
             *target_types_rules(),
             *testutil.rules(),
             *util_rules(),
             *scala_dep_inf_rules(),
             *scala_artifact_rules(),
+            *system_binaries.rules(),
             QueryRule(CheckResults, (ScalacCheckRequest,)),
             QueryRule(CoarsenedTargets, (Addresses,)),
             QueryRule(FallibleClasspathEntry, (CompileScalaSourceRequest,)),
@@ -164,7 +166,6 @@ def test_compile_no_deps(
     )
     assert classpath.content == {
         ".ExampleLib.scala.lib.scalac.jar": {
-            "META-INF/MANIFEST.MF",
             "org/pantsbuild/example/lib/C.class",
         }
     }
@@ -290,7 +291,6 @@ def test_compile_with_deps(
     )
     assert classpath.content == {
         ".Example.scala.main.scalac.jar": {
-            "META-INF/MANIFEST.MF",
             "org/pantsbuild/example/Main$.class",
             "org/pantsbuild/example/Main.class",
         }
@@ -392,7 +392,6 @@ def main(args: Array[String]): Unit = {
     )
     assert classpath.content == {
         ".Example.scala.main.scalac.jar": {
-            "META-INF/MANIFEST.MF",
             "org/pantsbuild/example/Main$.class",
             "org/pantsbuild/example/Main.class",
         }
@@ -869,7 +868,6 @@ def test_compile_no_deps_scala3(
     )
     assert classpath.content == {
         ".ExampleLib.scala.lib.scalac.jar": {
-            "META-INF/MANIFEST.MF",
             "org/pantsbuild/example/lib/C.class",
             "org/pantsbuild/example/lib/C.tasty",
         }
@@ -967,7 +965,6 @@ def test_compile_dep_on_scala_artifact(
         ".ExampleLib.scala.lib.scalac.jar": {
             "ExampleLib$.class",
             "ExampleLib.class",
-            "META-INF/MANIFEST.MF",
         }
     }
 
@@ -1103,3 +1100,66 @@ class B {
 
     assert classpath_2_13.result == CompileResult.FAILED and classpath_2_13.stderr
     assert "error: Unwanted cyclic dependency" in classpath_2_13.stderr
+
+
+@pytest.fixture
+def scala2_semanticdb_lockfile_def() -> JVMLockfileFixtureDefinition:
+    return JVMLockfileFixtureDefinition(
+        "semanticdb-scalac-2.13.test.lock",
+        ["org.scala-lang:scala-library:2.13.12", "org.scalameta:semanticdb-scalac_2.13.12:4.8.14"],
+    )
+
+
+@pytest.fixture
+def scala2_semanticdb_lockfile(
+    scala2_semanticdb_lockfile_def: JVMLockfileFixtureDefinition, request
+) -> JVMLockfileFixture:
+    return scala2_semanticdb_lockfile_def.load(request)
+
+
+@maybe_skip_jdk_test
+def test_scalac_plugin_extra_output(
+    rule_runner: RuleRunner, scala2_semanticdb_lockfile: JVMLockfileFixture
+) -> None:
+    rule_runner.write_files(
+        {
+            "3rdparty/jvm/default.lock": scala2_semanticdb_lockfile.serialized_lockfile,
+            "3rdparty/jvm/BUILD": scala2_semanticdb_lockfile.requirements_as_jvm_artifact_targets(),
+            "src/jvm/Foo.scala": dedent(
+                """\
+                import scala.collection.immutable
+                object Foo { immutable.Seq.empty[Int] }
+                """
+            ),
+            "src/jvm/BUILD": dedent(
+                """\
+                scalac_plugin(
+                    name="semanticdb",
+                    artifact="//3rdparty/jvm:org.scalameta_semanticdb-scalac_2.13.12",
+                )
+
+                scala_sources(scalac_plugins=["semanticdb"])
+                """
+            ),
+        }
+    )
+
+    rule_runner.set_options(
+        [
+            f"--source-root-patterns={repr(['src/jvm'])}",
+            f"--scala-version-for-resolve={repr({'jvm-default': '2.13.12'})}",
+        ],
+        env_inherit=PYTHON_BOOTSTRAP_ENV,
+    )
+
+    request = CompileScalaSourceRequest(
+        component=expect_single_expanded_coarsened_target(
+            rule_runner, Address(spec_path="src/jvm")
+        ),
+        resolve=make_resolve(rule_runner),
+    )
+    rendered_classpath = rule_runner.request(RenderedClasspath, [request])
+    assert (
+        "META-INF/semanticdb/Foo.scala.semanticdb"
+        in rendered_classpath.content["src.jvm.Foo.scala.scalac.jar"]
+    )
diff --git a/src/python/pants/backend/scala/compile/semanticdb-scalac-2.13.test.lock b/src/python/pants/backend/scala/compile/semanticdb-scalac-2.13.test.lock
new file mode 100644
--- /dev/null
+++ b/src/python/pants/backend/scala/compile/semanticdb-scalac-2.13.test.lock
@@ -0,0 +1,50 @@
+# This lockfile was autogenerated by Pants. To regenerate, run:
+#
+#    pants internal-generate-test-lockfile-fixtures ::
+#
+# --- BEGIN PANTS LOCKFILE METADATA: DO NOT EDIT OR REMOVE ---
+# {
+#   "version": 1,
+#   "generated_with_requirements": [
+#     "org.scala-lang:scala-library:2.13.12,url=not_provided,jar=not_provided",
+#     "org.scalameta:semanticdb-scalac_2.13.12:4.8.14,url=not_provided,jar=not_provided"
+#   ]
+# }
+# --- END PANTS LOCKFILE METADATA ---
+
+[[entries]]
+directDependencies = []
+dependencies = []
+file_name = "org.scala-lang_scala-library_2.13.12.jar"
+
+[entries.coord]
+group = "org.scala-lang"
+artifact = "scala-library"
+version = "2.13.12"
+packaging = "jar"
+[entries.file_digest]
+fingerprint = "c6a879e4973a60f6162668542a33eaccc2bb565d1c934fb061c5844259131dd1"
+serialized_bytes_length = 5917034
+[[entries]]
+file_name = "org.scalameta_semanticdb-scalac_2.13.12_4.8.14.jar"
+[[entries.directDependencies]]
+group = "org.scala-lang"
+artifact = "scala-library"
+version = "2.13.12"
+packaging = "jar"
+
+[[entries.dependencies]]
+group = "org.scala-lang"
+artifact = "scala-library"
+version = "2.13.12"
+packaging = "jar"
+
+
+[entries.coord]
+group = "org.scalameta"
+artifact = "semanticdb-scalac_2.13.12"
+version = "4.8.14"
+packaging = "jar"
+[entries.file_digest]
+fingerprint = "516da9260db08ec221f712eaf3c454a693121ebd64da501200bbf29dd28fcfc4"
+serialized_bytes_length = 17218706
diff --git a/src/python/pants/backend/scala/lint/scalafmt/rules_integration_test.py b/src/python/pants/backend/scala/lint/scalafmt/rules_integration_test.py
--- a/src/python/pants/backend/scala/lint/scalafmt/rules_integration_test.py
+++ b/src/python/pants/backend/scala/lint/scalafmt/rules_integration_test.py
@@ -15,7 +15,7 @@
 from pants.backend.scala.target_types import ScalaSourcesGeneratorTarget, ScalaSourceTarget
 from pants.build_graph.address import Address
 from pants.core.goals.fmt import FmtResult, Partitions
-from pants.core.util_rules import config_files, source_files
+from pants.core.util_rules import config_files, source_files, stripped_source_files
 from pants.core.util_rules.external_tool import rules as external_tool_rules
 from pants.engine.fs import PathGlobs, Snapshot
 from pants.engine.rules import QueryRule
@@ -39,6 +39,7 @@ def rule_runner() -> RuleRunner:
             *coursier_setup_rules(),
             *external_tool_rules(),
             *source_files.rules(),
+            *stripped_source_files.rules(),
             *strip_jar.rules(),
             *scalac_rules(),
             *util_rules(),
diff --git a/src/python/pants/backend/scala/test/scalatest_test.py b/src/python/pants/backend/scala/test/scalatest_test.py
--- a/src/python/pants/backend/scala/test/scalatest_test.py
+++ b/src/python/pants/backend/scala/test/scalatest_test.py
@@ -25,7 +25,7 @@
 from pants.build_graph.address import Address
 from pants.core.goals.test import TestResult, get_filtered_environment
 from pants.core.target_types import FilesGeneratorTarget, FileTarget, RelocatedFiles
-from pants.core.util_rules import config_files, source_files, system_binaries
+from pants.core.util_rules import config_files, source_files, stripped_source_files, system_binaries
 from pants.engine.addresses import Addresses
 from pants.engine.target import CoarsenedTargets
 from pants.jvm import classpath
@@ -59,6 +59,7 @@ def rule_runner() -> RuleRunner:
             *scala_target_types_rules(),
             *scalac_rules(),
             *source_files.rules(),
+            *stripped_source_files.rules(),
             *system_binaries.rules(),
             *target_types_rules(),
             *util_rules(),
diff --git a/src/python/pants/jvm/compile_test.py b/src/python/pants/jvm/compile_test.py
--- a/src/python/pants/jvm/compile_test.py
+++ b/src/python/pants/jvm/compile_test.py
@@ -311,7 +311,6 @@ def test_compile_mixed(
     )
 
     assert rendered_classpath.content[".Example.scala.main.scalac.jar"] == {
-        "META-INF/MANIFEST.MF",
         "org/pantsbuild/example/Main$.class",
         "org/pantsbuild/example/Main.class",
     }
diff --git a/src/python/pants/jvm/test/junit_test.py b/src/python/pants/jvm/test/junit_test.py
--- a/src/python/pants/jvm/test/junit_test.py
+++ b/src/python/pants/jvm/test/junit_test.py
@@ -21,7 +21,7 @@
 from pants.backend.scala.target_types import rules as scala_target_types_rules
 from pants.core.goals.test import TestResult, get_filtered_environment
 from pants.core.target_types import FilesGeneratorTarget, FileTarget, RelocatedFiles
-from pants.core.util_rules import config_files, source_files
+from pants.core.util_rules import config_files, source_files, stripped_source_files
 from pants.core.util_rules.external_tool import rules as external_tool_rules
 from pants.engine.addresses import Addresses
 from pants.engine.target import CoarsenedTargets
@@ -59,6 +59,7 @@ def rule_runner() -> RuleRunner:
             *scala_target_types_rules(),
             *scalac_rules(),
             *source_files.rules(),
+            *stripped_source_files.rules(),
             *target_types_rules(),
             *util_rules(),
             *non_jvm_dependencies_rules(),