def classify( targets: Sequence[Target], members: Sequence[type[ClasspathEntryRequest]], generators: FrozenDict[type[ClasspathEntryRequest], frozenset[type[SourcesField]]], ) -> tuple[type[ClasspathEntryRequest], type[ClasspathEntryRequest] | None]: factory = ClasspathEntryRequestFactory(tuple(members), generators) req = factory.for_targets( CoarsenedTarget(targets, ()), CoursierResolveKey("example", "path", EMPTY_DIGEST), ) return (type(req), type(req.prerequisite) if req.prerequisite else None)
async def kotlinc_check( request: KotlincCheckRequest, classpath_entry_request: ClasspathEntryRequestFactory, ) -> CheckResults: coarsened_targets = await Get( CoarsenedTargets, Addresses(field_set.address for field_set in request.field_sets) ) # NB: Each root can have an independent resolve, because there is no inherent relation # between them other than that they were on the commandline together. resolves = await MultiGet( Get(CoursierResolveKey, CoarsenedTargets([t])) for t in coarsened_targets ) results = await MultiGet( Get( FallibleClasspathEntry, ClasspathEntryRequest, classpath_entry_request.for_targets(component=target, resolve=resolve), ) for target, resolve in zip(coarsened_targets, resolves) ) # NB: We don't pass stdout/stderr as it will have already been rendered as streaming. exit_code = next((result.exit_code for result in results if result.exit_code != 0), 0) return CheckResults([CheckResult(exit_code, "", "")], checker_name=request.name)
async def bsp_scala_compile_request( request: ScalaBSPCompileFieldSet, classpath_entry_request: ClasspathEntryRequestFactory, ) -> BSPCompileResult: coarsened_targets = await Get(CoarsenedTargets, Addresses([request.source.address])) assert len(coarsened_targets) == 1 coarsened_target = coarsened_targets[0] resolve = await Get(CoursierResolveKey, CoarsenedTargets([coarsened_target])) result = await Get( FallibleClasspathEntry, ClasspathEntryRequest, classpath_entry_request.for_targets(component=coarsened_target, resolve=resolve), ) _logger.info(f"scala compile result = {result}") output_digest = EMPTY_DIGEST if result.exit_code == 0 and result.output: entries = await Get(DigestEntries, Digest, result.output.digest) new_entires = [ dataclasses.replace(entry, path=os.path.basename(entry.path)) for entry in entries ] flat_digest = await Get(Digest, CreateDigest(new_entires)) output_digest = await Get( Digest, AddPrefix(flat_digest, f"jvm/resolves/{resolve.name}/lib")) return BSPCompileResult( status=StatusCode.ERROR if result.exit_code != 0 else StatusCode.OK, output_digest=output_digest, )
async def _jvm_bsp_compile( request: BSPCompileRequest, classpath_entry_request: ClasspathEntryRequestFactory ) -> BSPCompileResult: """Generically handles a BSPCompileRequest (subclass). This is a `@rule_helper` rather than a `@rule`, because BSP backends like `java` and `scala` independently declare their `BSPCompileRequest` union members. We can't register a single shared `BSPCompileRequest` @union member for all JVM because their FieldSets are also declared via @unions, and we can't forward the implementation of a @union to another the way we might with an abstract class. """ coarsened_targets = await Get( CoarsenedTargets, Addresses([fs.address for fs in request.field_sets])) resolve = await Get(CoursierResolveKey, CoarsenedTargets, coarsened_targets) # TODO: We include the (non-3rdparty) transitive dependencies here, because each project # currently only has a single BuildTarget. This has the effect of including `resources` targets, # which are referenced by BuildTargets (via `buildTarget/resources`), rather than necessarily # being owned by any particular BuildTarget. # # To resolve #15051, this will no longer be transitive, and so `resources` will need to be # attached-to/referenced-by nearby BuildTarget(s) instead (most likely: direct dependent(s)). results = await MultiGet( Get( FallibleClasspathEntry, BSPClasspathEntryRequest( classpath_entry_request.for_targets(component=coarsened_target, resolve=resolve), task_id=request.task_id, ), ) for coarsened_target in coarsened_targets.coarsened_closure() if not any( JvmArtifactFieldSet.is_applicable(t) for t in coarsened_target.members)) entries = FallibleClasspathEntry.if_all_succeeded(results) if entries is None: return BSPCompileResult( status=StatusCode.ERROR, output_digest=EMPTY_DIGEST, ) loose_classfiles = await MultiGet( Get(LooseClassfiles, ClasspathEntry, entry) for entry in entries) merged_loose_classfiles = await Get( Digest, MergeDigests(lc.digest for lc in loose_classfiles)) output_digest = await Get( Digest, AddPrefix(merged_loose_classfiles, jvm_classes_directory(request.bsp_target.bsp_target_id)), ) return BSPCompileResult( status=StatusCode.OK, output_digest=output_digest, )
async def collect_thirdparty_modules( request: ThirdpartyModulesRequest, classpath_entry_request: ClasspathEntryRequestFactory, ) -> ThirdpartyModules: coarsened_targets = await Get(CoarsenedTargets, Addresses, request.addresses) resolve = await Get(CoursierResolveKey, CoarsenedTargets, coarsened_targets) lockfile = await Get(CoursierResolvedLockfile, CoursierResolveKey, resolve) applicable_lockfile_entries: dict[CoursierLockfileEntry, CoarsenedTarget] = {} for ct in coarsened_targets.coarsened_closure(): for tgt in ct.members: if not JvmArtifactFieldSet.is_applicable(tgt): continue artifact_requirement = ArtifactRequirement.from_jvm_artifact_target( tgt) entry = get_entry_for_coord(lockfile, artifact_requirement.coordinate) if not entry: _logger.warning( f"No lockfile entry for {artifact_requirement.coordinate} in resolve {resolve.name}." ) continue applicable_lockfile_entries[entry] = ct classpath_entries = await MultiGet( Get( ClasspathEntry, ClasspathEntryRequest, classpath_entry_request.for_targets(component=target, resolve=resolve), ) for target in applicable_lockfile_entries.values()) resolve_digest = await Get( Digest, MergeDigests(cpe.digest for cpe in classpath_entries)) return ThirdpartyModules( resolve, dict(zip(applicable_lockfile_entries, classpath_entries)), resolve_digest, )
async def classpath( coarsened_targets: CoarsenedTargets, classpath_entry_request: ClasspathEntryRequestFactory, ) -> Classpath: # Compute a single shared resolve for all of the roots, which will validate that they # are compatible with one another. resolve = await Get(CoursierResolveKey, CoarsenedTargets, coarsened_targets) # Then request classpath entries for each root. classpath_entries = await MultiGet( Get( ClasspathEntry, ClasspathEntryRequest, classpath_entry_request.for_targets( component=t, resolve=resolve, root=True), ) for t in coarsened_targets) return Classpath(classpath_entries, resolve)