async def render_classpath(classpath: Classpath) -> RenderedClasspath: rendered_classpaths = await MultiGet( Get(RenderedClasspath, ClasspathEntry, cpe) for cpe in ClasspathEntry.closure(classpath.entries)) return RenderedClasspath( {k: v for rc in rendered_classpaths for k, v in rc.content.items()})
def immutable_inputs(self, *, prefix: str = "") -> Iterator[tuple[str, Digest]]: """Returns (relpath, Digest) tuples for use with `Process.immutable_input_digests`.""" return ClasspathEntry.immutable_inputs(ClasspathEntry.closure( self.entries), prefix=prefix)
async def fetch_kotlinc_plugins( request: KotlincPluginsRequest) -> KotlincPlugins: # Fetch all the artifacts coarsened_targets = await Get( CoarsenedTargets, Addresses(target.address for target in request.artifacts)) fallible_artifacts = await MultiGet( Get( FallibleClasspathEntry, CoursierFetchRequest(ct, resolve=request.resolve), ) for ct in coarsened_targets) artifacts = FallibleClasspathEntry.if_all_succeeded(fallible_artifacts) if artifacts is None: failed = [i for i in fallible_artifacts if i.exit_code != 0] raise Exception(f"Fetching local kotlinc plugins failed: {failed}") entries = list(ClasspathEntry.closure(artifacts)) merged_classpath_digest = await Get( Digest, MergeDigests(entry.digest for entry in entries)) merged = ClasspathEntry.merge(merged_classpath_digest, entries) ids = tuple(_plugin_id(target) for target in request.plugins) plugin_args = FrozenDict({ _plugin_id(plugin): tuple(plugin[KotlincPluginArgsField].value or []) for plugin in request.plugins }) return KotlincPlugins(ids=ids, classpath=merged, plugin_args=plugin_args)
def immutable_inputs_args(self, *, prefix: str = "") -> Iterator[str]: """Returns relative filenames for the given entries to be used as immutable_inputs.""" return ClasspathEntry.immutable_inputs_args(ClasspathEntry.closure( self.entries), prefix=prefix)
def digests(self) -> Iterator[Digest]: """All transitive Digests for this Classpath.""" return (entry.digest for entry in ClasspathEntry.closure(self.entries))
def args(self, *, prefix: str = "") -> Iterator[str]: """All transitive filenames for this Classpath.""" return ClasspathEntry.args(ClasspathEntry.closure(self.entries), prefix=prefix)
def test_compile_with_multiple_scala_versions(rule_runner: RuleRunner) -> None: scala_library_coord_2_12 = Coordinate(group="org.scala-lang", artifact="scala-library", version="2.12.15") scala_library_coord_2_13 = Coordinate(group="org.scala-lang", artifact="scala-library", version="2.13.8") rule_runner.write_files({ "BUILD": dedent("""\ scala_sources( name = 'main_2.12', resolve = "scala2.12", ) scala_sources( name = 'main_2.13', resolve = "scala2.13", ) jvm_artifact( name="org.scala-lang_scala-library_2.12.15", group="org.scala-lang", artifact="scala-library", version="2.12.15", resolve="scala2.12", ) jvm_artifact( name="org.scala-lang_scala-library_2.13.8", group="org.scala-lang", artifact="scala-library", version="2.13.8", resolve="scala2.13", ) """), "Example.scala": SCALA_LIB_SOURCE, "3rdparty/jvm/scala2.12.lock": TestCoursierWrapper.new(entries=(CoursierLockfileEntry( coord=scala_library_coord_2_12, file_name="org.scala-lang_scala-library_2.12.15.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( "e518bb640e2175de5cb1f8e326679b8d975376221f1b547757de429bbf4563f0", 5443542, ), ), ), ).serialize([ArtifactRequirement(scala_library_coord_2_12)]), "3rdparty/jvm/scala2.13.lock": TestCoursierWrapper.new(entries=(CoursierLockfileEntry( coord=scala_library_coord_2_13, file_name="org.scala-lang_scala-library_2.13.8.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( "a0882b82514190c2bac7d1a459872a75f005fc0f3e88b2bc0390367146e35db7", 6003601, ), ), ), ).serialize([ArtifactRequirement(scala_library_coord_2_13)]), }) rule_runner.set_options( [ '--scala-version-for-resolve={"scala2.12":"2.12.15","scala2.13":"2.13.8"}', '--jvm-resolves={"scala2.12":"3rdparty/jvm/scala2.12.lock","scala2.13":"3rdparty/jvm/scala2.13.lock"}', ], env_inherit=PYTHON_BOOTSTRAP_ENV, ) classpath_2_12 = rule_runner.request( ClasspathEntry, [ CompileScalaSourceRequest( component=expect_single_expanded_coarsened_target( rule_runner, Address(spec_path="", target_name="main_2.12")), resolve=make_resolve(rule_runner, "scala2.12", "3rdparty/jvm/scala2.12.lock"), ) ], ) entries_2_12 = list(ClasspathEntry.closure([classpath_2_12])) filenames_2_12 = sorted( itertools.chain.from_iterable(entry.filenames for entry in entries_2_12)) assert filenames_2_12 == [ ".Example.scala.main_2.12.scalac.jar", "org.scala-lang_scala-library_2.12.15.jar", ] classpath_2_13 = rule_runner.request( ClasspathEntry, [ CompileScalaSourceRequest( component=expect_single_expanded_coarsened_target( rule_runner, Address(spec_path="", target_name="main_2.13")), resolve=make_resolve(rule_runner, "scala2.13", "3rdparty/jvm/scala2.13.lock"), ) ], ) entries_2_13 = list(ClasspathEntry.closure([classpath_2_13])) filenames_2_13 = sorted( itertools.chain.from_iterable(entry.filenames for entry in entries_2_13)) assert filenames_2_13 == [ ".Example.scala.main_2.13.scalac.jar", "org.scala-lang_scala-library_2.13.8.jar", ]
async def compile_scala_source( bash: BashBinary, coursier: Coursier, jdk_setup: JdkSetup, scala: ScalaSubsystem, union_membership: UnionMembership, request: CompileScalaSourceRequest, ) -> FallibleClasspathEntry: # Request classpath entries for our direct dependencies. direct_dependency_classpath_entries = FallibleClasspathEntry.if_all_succeeded( await MultiGet( Get( FallibleClasspathEntry, ClasspathEntryRequest, ClasspathEntryRequest.for_targets(union_membership, component=coarsened_dep, resolve=request.resolve), ) for coarsened_dep in request.component.dependencies)) if direct_dependency_classpath_entries is None: return FallibleClasspathEntry( description=str(request.component), result=CompileResult.DEPENDENCY_FAILED, output=None, exit_code=1, ) component_members_with_sources = tuple(t for t in request.component.members if t.has_field(SourcesField)) component_members_and_source_files = zip( component_members_with_sources, await MultiGet( Get( SourceFiles, SourceFilesRequest( (t.get(SourcesField), ), for_sources_types=(ScalaSourceField, ), enable_codegen=True, ), ) for t in component_members_with_sources), ) component_members_and_scala_source_files = [ (target, sources) for target, sources in component_members_and_source_files if sources.snapshot.digest != EMPTY_DIGEST ] if not component_members_and_scala_source_files: # Is a generator, and so exports all of its direct deps. exported_digest = await Get( Digest, MergeDigests(cpe.digest for cpe in direct_dependency_classpath_entries)) classpath_entry = ClasspathEntry.merge( exported_digest, direct_dependency_classpath_entries) return FallibleClasspathEntry( description=str(request.component), result=CompileResult.SUCCEEDED, output=classpath_entry, exit_code=0, ) ( tool_classpath, merged_transitive_dependency_classpath_entries_digest, ) = await MultiGet( Get( MaterializedClasspath, MaterializedClasspathRequest( prefix="__toolcp", artifact_requirements=(ArtifactRequirements([ Coordinate( group="org.scala-lang", artifact="scala-compiler", version=scala.version, ), Coordinate( group="org.scala-lang", artifact="scala-library", version=scala.version, ), ]), ), ), ), Get( Digest, # Flatten the entire transitive classpath. MergeDigests(classfiles.digest for classfiles in ClasspathEntry.closure( direct_dependency_classpath_entries)), ), ) usercp = "__cp" prefixed_transitive_dependency_classpath_digest = await Get( Digest, AddPrefix(merged_transitive_dependency_classpath_entries_digest, usercp)) merged_digest = await Get( Digest, MergeDigests(( prefixed_transitive_dependency_classpath_digest, tool_classpath.digest, jdk_setup.digest, *(sources.snapshot.digest for _, sources in component_members_and_scala_source_files), )), ) classpath_arg = ClasspathEntry.arg( ClasspathEntry.closure(direct_dependency_classpath_entries), prefix=usercp) output_file = f"{request.component.representative.address.path_safe_spec}.jar" process_result = await Get( FallibleProcessResult, Process( argv=[ *jdk_setup.args(bash, tool_classpath.classpath_entries()), "scala.tools.nsc.Main", "-bootclasspath", ":".join(tool_classpath.classpath_entries()), *(("-classpath", classpath_arg) if classpath_arg else ()), "-d", output_file, *sorted( chain.from_iterable( sources.snapshot.files for _, sources in component_members_and_scala_source_files)), ], input_digest=merged_digest, use_nailgun=jdk_setup.digest, output_files=(output_file, ), description=f"Compile {request.component} with scalac", level=LogLevel.DEBUG, append_only_caches=jdk_setup.append_only_caches, env=jdk_setup.env, ), ) output: ClasspathEntry | None = None if process_result.exit_code == 0: output = ClasspathEntry(process_result.output_digest, (output_file, ), direct_dependency_classpath_entries) return FallibleClasspathEntry.from_fallible_process_result( str(request.component), process_result, output, )