• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 19529437518

20 Nov 2025 07:44AM UTC coverage: 78.884% (-1.4%) from 80.302%
19529437518

push

github

web-flow
nfpm.native_libs: Add RPM package depends from packaged pex_binaries (#22899)

## PR Series Overview

This is the second in a series of PRs that introduces a new backend:
`pants.backend.npm.native_libs`
Initially, the backend will be available as:
`pants.backend.experimental.nfpm.native_libs`

I proposed this new backend (originally named `bindeps`) in discussion
#22396.

This backend will inspect ELF bin/lib files (like `lib*.so`) in packaged
contents (for this PR series, only in `pex_binary` targets) to identify
package dependency metadata and inject that metadata on the relevant
`nfpm_deb_package` or `nfpm_rpm_package` targets. Effectively, it will
provide an approximation of these native packager features:
- `rpm`: `rpmdeps` + `elfdeps`
- `deb`: `dh_shlibdeps` + `dpkg-shlibdeps` (These substitute
`${shlibs:Depends}` in debian control files have)

### Goal: Host-agnostic package builds

This pants backend is designed to be host-agnostic, like
[nFPM](https://nfpm.goreleaser.com/).

Native packaging tools are often restricted to a single release of a
single distro. Unlike native package builders, this new pants backend
does not use any of those distro-specific or distro-release-specific
utilities or local package databases. This new backend should be able to
run (help with building deb and rpm packages) anywhere that pants can
run (MacOS, rpm linux distros, deb linux distros, other linux distros,
docker, ...).

### Previous PRs in series

- #22873

## PR Overview

This PR adds rules in `nfpm.native_libs` to add package dependency
metadata to `nfpm_rpm_package`. The 2 new rules are:

- `inject_native_libs_dependencies_in_package_fields`:

    - An implementation of the polymorphic rule `inject_nfpm_package_fields`.
      This rule is low priority (`priority = 2`) so that in-repo plugins can
      override/augment what it injects. (See #22864)

    - Rule logic overview:
        - find any pex_binaries that will be packaged in an `nfpm_rpm_package`
   ... (continued)

96 of 118 new or added lines in 3 files covered. (81.36%)

910 existing lines in 53 files now uncovered.

73897 of 93678 relevant lines covered (78.88%)

3.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/python/pants/jvm/bsp/spec.py
1
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
UNCOV
3
from __future__ import annotations
×
4

UNCOV
5
from dataclasses import dataclass
×
UNCOV
6
from typing import Any
×
7

UNCOV
8
from pants.bsp.spec.base import BSPData, Uri
×
9

10

UNCOV
11
@dataclass(frozen=True)
×
UNCOV
12
class JvmBuildTarget:
×
13
    # Uri representing absolute path to jdk
14
    # For example: file:///usr/lib/jvm/java-8-openjdk-amd64
UNCOV
15
    java_home: Uri | None = None
×
16

17
    # The java version this target is supposed to use.
18
    # For example: 1.8
UNCOV
19
    java_version: str | None = None
×
20

UNCOV
21
    @classmethod
×
UNCOV
22
    def from_json_dict(cls, d: dict[str, Any]) -> Any:
×
23
        return cls(
×
24
            java_home=d.get("javaHome"),
25
            java_version=d.get("javaVersion"),
26
        )
27

UNCOV
28
    def to_json_dict(self) -> dict[str, Any]:
×
UNCOV
29
        result = {}
×
UNCOV
30
        if self.java_home is not None:
×
UNCOV
31
            result["javaHome"] = self.java_home
×
UNCOV
32
        if self.java_version is not None:
×
UNCOV
33
            result["javaVersion"] = self.java_version
×
UNCOV
34
        return result
×
35

36

UNCOV
37
@dataclass(frozen=True)
×
UNCOV
38
class MavenDependencyModuleArtifact:
×
UNCOV
39
    uri: Uri
×
UNCOV
40
    classifier: str | None = None
×
41

UNCOV
42
    @classmethod
×
UNCOV
43
    def from_json_dict(cls, d: dict[str, Any]) -> Any:
×
44
        return cls(uri=d["uri"], classifier=d.get("classifier"))
×
45

UNCOV
46
    def to_json_dict(self) -> Any:
×
47
        result = {"uri": self.uri}
×
48
        if self.classifier is not None:
×
49
            result["classifier"] = self.classifier
×
50
        return result
×
51

52

UNCOV
53
@dataclass(frozen=True)
×
UNCOV
54
class MavenDependencyModule(BSPData):
×
55
    """Maven-related module metadata."""
56

UNCOV
57
    organization: str
×
UNCOV
58
    name: str
×
UNCOV
59
    version: str
×
UNCOV
60
    scope: str | None
×
UNCOV
61
    artifacts: tuple[MavenDependencyModuleArtifact, ...]
×
62

UNCOV
63
    DATA_KIND = "maven"
×
64

UNCOV
65
    @classmethod
×
UNCOV
66
    def from_json_dict(cls, d: dict[str, Any]) -> Any:
×
67
        return cls(
×
68
            organization=d["organization"],
69
            name=d["name"],
70
            version=d["version"],
71
            scope=d.get("scope"),
72
            artifacts=tuple(
73
                MavenDependencyModuleArtifact.from_json_dict(ma) for ma in d.get("artifacts", [])
74
            ),
75
        )
76

UNCOV
77
    def to_json_dict(self) -> Any:
×
78
        result = {
×
79
            "organization": self.organization,
80
            "name": self.name,
81
            "version": self.version,
82
            "artifacts": [ma.to_json_dict() for ma in self.artifacts],
83
        }
84
        if self.scope is not None:
×
85
            result["scope"] = self.scope
×
86
        return result
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc