Example #1
0
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)
Example #2
0
    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)
Example #3
0
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,
    )
Example #4
0
    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,
        )
Example #5
0
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,
    )
Example #6
0
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,
    )