async def gather_coordinates_for_jvm_lockfile( request: GatherJvmCoordinatesRequest, ) -> ArtifactRequirements: # Separate `artifact_inputs` by whether the strings parse as an `Address` or not. requirements: set[ArtifactRequirement] = set() candidate_address_inputs: set[AddressInput] = set() bad_artifact_inputs = [] for artifact_input in request.artifact_inputs: # Try parsing as a `Coordinate` first since otherwise `AddressInput.parse` will try to see if the # group name is a file on disk. if 2 <= artifact_input.count(":") <= 3: try: maybe_coord = Coordinate.from_coord_str( artifact_input).as_requirement() requirements.add(maybe_coord) continue except Exception: pass try: address_input = AddressInput.parse( artifact_input, description_of_origin=f"the option `{request.option_name}`") candidate_address_inputs.add(address_input) except Exception: bad_artifact_inputs.append(artifact_input) if bad_artifact_inputs: raise ValueError( "The following values could not be parsed as an address nor as a JVM coordinate string. " f"The problematic inputs supplied to the `{request.option_name}` option were: " f"{', '.join(bad_artifact_inputs)}.") # Gather coordinates from the provided addresses. addresses = await MultiGet( Get(Address, AddressInput, ai) for ai in candidate_address_inputs) all_supplied_targets = await Get(Targets, Addresses(addresses)) other_targets = [] for tgt in all_supplied_targets: if JvmArtifactFieldSet.is_applicable(tgt): requirements.add(ArtifactRequirement.from_jvm_artifact_target(tgt)) else: other_targets.append(tgt) if other_targets: raise ValueError( softwrap(f""" The following addresses reference targets that are not `jvm_artifact` targets. Please only supply the addresses of `jvm_artifact` for the `{request.option_name}` option. The problematic addresses are: {', '.join(str(tgt.address) for tgt in other_targets)}. """)) return ArtifactRequirements(requirements)
def from_jvm_artifact_target(target: Target) -> Coordinate: if not JvmArtifactFieldSet.is_applicable(target): raise CoursierError( "`Coordinate.from_jvm_artifact_target()` only works on targets with " "`JvmArtifactFieldSet` fields present.") group = target[JvmArtifactGroupField].value artifact = target[JvmArtifactArtifactField].value version = target[JvmArtifactVersionField].value # These are all required, but mypy doesn't think so. assert group is not None and artifact is not None and version is not None return Coordinate(group=group, artifact=artifact, version=version)
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, )
def from_jvm_artifact_target(cls, target: Target) -> ArtifactRequirement: if not JvmArtifactFieldSet.is_applicable(target): raise AssertionError( "`ArtifactRequirement.from_jvm_artifact_target()` only works on targets with " "`JvmArtifactFieldSet` fields present.") return ArtifactRequirement( coordinate=Coordinate( group=target[JvmArtifactGroupField].value, artifact=target[JvmArtifactArtifactField].value, version=target[JvmArtifactVersionField].value, ), url=target[JvmArtifactUrlField].value, jar=(target[JvmArtifactJarSourceField] if target[JvmArtifactJarSourceField].value else None), excludes=frozenset( target[JvmArtifactExcludeDependenciesField].value or []) or None, )
async def coursier_generate_lockfile( request: CoursierGenerateLockfileRequest, jvm: JvmSubsystem, targets_by_resolve_name: JvmTargetsByResolveName, ) -> CoursierGenerateLockfileResult: # `targets_by_resolve_name` supplies all of the targets that depend on each JVM resolve, so # no need to find transitive deps? # This task finds all of the sources that depend on this lockfile, and then resolves # a lockfile that satisfies all of their `jvm_artifact` dependencies. targets = targets_by_resolve_name.targets_by_resolve_name[request.resolve] # Find JVM artifacts in the dependency tree of the targets that depend on this lockfile. # These artifacts constitute the requirements that will be resolved for this lockfile. dependee_targets = await Get( TransitiveTargets, TransitiveTargetsRequest(tgt.address for tgt in targets) ) resolvable_dependencies = [ tgt for tgt in dependee_targets.closure if JvmArtifactFieldSet.is_applicable(tgt) ] artifact_requirements = ArtifactRequirements( [_coordinate_from_target(tgt) for tgt in resolvable_dependencies] ) resolved_lockfile = await Get( CoursierResolvedLockfile, ArtifactRequirements, artifact_requirements, ) resolved_lockfile_json = resolved_lockfile.to_json() lockfile_path = jvm.options.resolves[request.resolve] # Materialise the existing lockfile, and check for changes. We don't want to re-write # identical lockfiles existing_lockfile_source = PathGlobs( [lockfile_path], glob_match_error_behavior=GlobMatchErrorBehavior.ignore, ) existing_lockfile_digest_contents = await Get( DigestContents, PathGlobs, existing_lockfile_source ) if not existing_lockfile_digest_contents: # The user defined the target and resolved it, but hasn't created a lockfile yet. # For convenience, create the initial lockfile for them in the specified path. return CoursierGenerateLockfileResult( digest=await Get( Digest, CreateDigest( ( FileContent( path=lockfile_path, content=resolved_lockfile_json, ), ) ), ) ) existing_lockfile_json = existing_lockfile_digest_contents[0].content if resolved_lockfile_json != existing_lockfile_json: # The generated lockfile differs from the existing one, so return the digest of the generated one. return CoursierGenerateLockfileResult( digest=await Get( Digest, CreateDigest((FileContent(path=lockfile_path, content=resolved_lockfile_json),)), ) ) # The generated lockfile didn't change, so return an empty digest. return CoursierGenerateLockfileResult( digest=EMPTY_DIGEST, )
async def scala_bsp_dependency_modules( request: ScalaBSPDependencyModulesRequest, build_root: BuildRoot, ) -> BSPDependencyModulesResult: coarsened_targets = await Get( CoarsenedTargets, Addresses([fs.address for fs in request.field_sets])) resolve = await Get(CoursierResolveKey, CoarsenedTargets, coarsened_targets) lockfile = await Get(CoursierResolvedLockfile, CoursierResolveKey, resolve) # TODO: Can this use ClasspathEntryRequest? transitive_targets = await Get( TransitiveTargets, TransitiveTargetsRequest(roots=[ tgt.address for coarsened_target in coarsened_targets for tgt in coarsened_target.members ]), ) artifact_requirements = [ ArtifactRequirement.from_jvm_artifact_target(tgt) for tgt in transitive_targets.closure if JvmArtifactFieldSet.is_applicable(tgt) ] applicable_lockfile_entries: set[CoursierLockfileEntry] = set() for artifact_requirement in artifact_requirements: 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.add(entry) resolve_digest = await Get( Digest, CreateDigest([ FileEntry(entry.file_name, entry.file_digest) for entry in applicable_lockfile_entries ]), ) resolve_digest = await Get( Digest, AddPrefix(resolve_digest, f"jvm/resolves/{resolve.name}/lib")) modules = [ DependencyModule( name=f"{entry.coord.group}:{entry.coord.artifact}", version=entry.coord.version, data=MavenDependencyModule( organization=entry.coord.group, name=entry.coord.artifact, version=entry.coord.version, scope=None, artifacts=(MavenDependencyModuleArtifact( uri=build_root.pathlib_path.joinpath( f".pants.d/bsp/jvm/resolves/{resolve.name}/lib/{entry.file_name}" ).as_uri()), ), ), ) for entry in applicable_lockfile_entries ] return BSPDependencyModulesResult( modules=tuple(modules), digest=resolve_digest, )