pants-backend-cargo 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. pants_backend_cargo-0.1.0/LICENSE-MIT.txt +21 -0
  2. pants_backend_cargo-0.1.0/PKG-INFO +179 -0
  3. pants_backend_cargo-0.1.0/README.md +140 -0
  4. pants_backend_cargo-0.1.0/pants_backend_cargo/__init__.py +1 -0
  5. pants_backend_cargo-0.1.0/pants_backend_cargo/backends/clippy/__init__.py +1 -0
  6. pants_backend_cargo-0.1.0/pants_backend_cargo/backends/clippy/goals/__init__.py +1 -0
  7. pants_backend_cargo-0.1.0/pants_backend_cargo/backends/clippy/goals/lint.py +94 -0
  8. pants_backend_cargo-0.1.0/pants_backend_cargo/backends/clippy/register.py +6 -0
  9. pants_backend_cargo-0.1.0/pants_backend_cargo/backends/clippy/subsystem.py +15 -0
  10. pants_backend_cargo-0.1.0/pants_backend_cargo/goals/__init__.py +1 -0
  11. pants_backend_cargo-0.1.0/pants_backend_cargo/goals/fmt.py +122 -0
  12. pants_backend_cargo-0.1.0/pants_backend_cargo/goals/generate_lockfiles.py +118 -0
  13. pants_backend_cargo-0.1.0/pants_backend_cargo/goals/package.py +78 -0
  14. pants_backend_cargo-0.1.0/pants_backend_cargo/goals/run.py +40 -0
  15. pants_backend_cargo-0.1.0/pants_backend_cargo/goals/tailor.py +64 -0
  16. pants_backend_cargo-0.1.0/pants_backend_cargo/goals/test.py +233 -0
  17. pants_backend_cargo-0.1.0/pants_backend_cargo/internal/build.py +99 -0
  18. pants_backend_cargo-0.1.0/pants_backend_cargo/register.py +41 -0
  19. pants_backend_cargo-0.1.0/pants_backend_cargo/subsystems.py +144 -0
  20. pants_backend_cargo-0.1.0/pants_backend_cargo/target_generator.py +160 -0
  21. pants_backend_cargo-0.1.0/pants_backend_cargo/target_types.py +197 -0
  22. pants_backend_cargo-0.1.0/pants_backend_cargo/util_rules/cargo.py +214 -0
  23. pants_backend_cargo-0.1.0/pants_backend_cargo/util_rules/dependency_inference.py +100 -0
  24. pants_backend_cargo-0.1.0/pants_backend_cargo/util_rules/platform.py +153 -0
  25. pants_backend_cargo-0.1.0/pants_backend_cargo/util_rules/rustup.py +226 -0
  26. pants_backend_cargo-0.1.0/pants_backend_cargo/util_rules/sandbox.py +214 -0
  27. pants_backend_cargo-0.1.0/pants_backend_cargo/util_rules/workspace.py +146 -0
  28. pants_backend_cargo-0.1.0/pyproject.toml +52 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Tom Solberg
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,179 @@
1
+ Metadata-Version: 2.1
2
+ Name: pants_backend_cargo
3
+ Version: 0.1.0
4
+ Summary: A Pants backend for Rust that wraps Cargo: cargo_workspace and cargo_package targets, a hermetic toolchain, kache-cached builds, and package, run, test, fmt and lint through cargo.
5
+ Keywords: pantsbuild,pants,backend,rust,cargo
6
+ Author-Email: Tom Solberg <me@sbg.dev>
7
+ License: MIT License
8
+
9
+ Copyright (c) 2022 Tom Solberg
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+
29
+ Classifier: Development Status :: 3 - Alpha
30
+ Classifier: Programming Language :: Python
31
+ Classifier: Topic :: Software Development :: Build Tools
32
+ Project-URL: Repository, https://github.com/tgolsson/pants-backends
33
+ Project-URL: Code, https://github.com/tgolsson/pants-backends/tree/main/pants-plugins/cargo
34
+ Project-URL: Changelog, https://github.com/tgolsson/pants-backends/tree/main/pants-plugins/cargo/CHANGELOG.md
35
+ Requires-Python: >=3.11
36
+ Requires-Dist: pants_backend_cc>=0.1.0
37
+ Requires-Dist: pants_backend_platforms>=0.1.0
38
+ Description-Content-Type: text/markdown
39
+
40
+ # pants-backend-cargo
41
+
42
+ A [Pants](https://www.pantsbuild.org) backend for Rust that wraps Cargo rather than replacing
43
+ it: Cargo keeps its package model, its lockfiles and its workspaces, and Pants adds target
44
+ addresses, caching, a hermetic toolchain and the usual goals on top.
45
+
46
+ This release targets Pants **2.32.x**. Install it with `pants-backend-cargo==0.1.0` in
47
+ `[GLOBAL].plugins` in `pants.toml`, and enable `pants_backend_cargo` in
48
+ `[GLOBAL].backend_packages`.
49
+
50
+ **Status: alpha.** No stability guarantees until it nears feature completeness.
51
+
52
+ ## Targets
53
+
54
+ - `cargo_workspace`: the root of a Cargo workspace: `Cargo.toml`, `Cargo.lock`,
55
+ `rust-toolchain.toml` and `.cargo/config.toml`. Members are found through the manifest's
56
+ `members` (globs one path segment at a time, less `exclude`) and depend on it. Test
57
+ executables and clippy run once per workspace. Members cannot inherit from the workspace
58
+ (`version.workspace = true`, `b = { workspace = true }`): a package's targets are generated
59
+ from its own manifest alone.
60
+ - `cargo_package`: a package. `cargo metadata` on its manifest generates
61
+ - `path:name#package` (`cargo_package_impl`): the sources and cargo dependencies, what
62
+ `fmt` and `lint` act on;
63
+ - `path:name#library` (`cargo_library`), one `cargo_binary` per `[[bin]]` named after it,
64
+ one `cargo_test` per `[[test]]`: what `test` runs, and for binaries what `package` and
65
+ `run` build.
66
+
67
+ `dependencies` adds `files`/`resources` targets the build reads (`include_str!`, build
68
+ script inputs) and packageable targets of other backends whose `package` output the build
69
+ needs: they are built and staged in the sandbox at their package path for `package`,
70
+ `run`, `test` and `lint` (not for `fmt` and `generate-lockfiles`). Their own dependencies
71
+ are their backend's inputs and are not staged. `env` sets `KEY=VALUE` entries for every
72
+ cargo process whose sandbox holds the package (its own, those of packages depending on it,
73
+ and the per-workspace `test` build, `fmt` and `lint`), and for the test executables and
74
+ `run` binaries those build; `{chroot}` is the sandbox root, so
75
+ `env=["MYLIB_DIR={chroot}/build"]` points a build script at a staged library. `PATH`,
76
+ `CARGO`, `CARGO_HOME`, `CARGO_TARGET_DIR`, `CARGO_INCREMENTAL`, `RUSTUP_HOME`, `RUSTUP_TOOLCHAIN`,
77
+ `RUSTC_WRAPPER`, `RUSTFLAGS`, `KACHE_*` and the toolchain's `CARGO_TARGET_<TRIPLE>_LINKER`,
78
+ `CC_<triple>`, `CXX_<triple>` and `AR_<triple>` are the backend's and rejected. `skip_tests` as
79
+ usual; `output_path` is the directory under `dist/` (default `path.to.target/`), the file
80
+ keeps the binary's name. A target with `test = false` in
81
+ `Cargo.toml` gets `skip_tests`. A `[[bin]]` or `[[test]]` named `package` or `library`, or a
82
+ bin and a test of the same name, is an error: rename it.
83
+
84
+ `path` dependencies between packages are inferred. `pants tailor` adds both target types.
85
+ Pants reads nothing `.gitignore` ignores, so a workspace's `.cargo/config.toml` must not be
86
+ (`!/.cargo/` after a `.cargo` rule).
87
+
88
+ ## Toolchain
89
+
90
+ Nothing from the host's `~/.rustup` or `~/.cargo` is used. `[rustup]` pins rustup-init, which
91
+ installs the toolchain into a named cache shared by every sandbox. The version is
92
+ `[rustup].rust_version` or, unset, the `channel` of the `rust-toolchain.toml` at the build
93
+ root, and must be exact: it is part of every cache key. The components the file lists are
94
+ installed with it; `fmt` and `lint` add `rustfmt` and `clippy`.
95
+
96
+ Every cargo process runs in a fresh sandbox with its own `target/`, so cargo's own incremental
97
+ state never carries over. Instead, `[kache]` puts [kache](https://github.com/kunobi-ninja/kache)
98
+ in front of rustc: compiled crates are keyed by their inputs and kept in a named cache, and
99
+ unchanged crates are restored rather than rebuilt, across sandboxes and across packages of a
100
+ workspace. Local only, no daemon. `--remap-path-prefix` maps the sandbox root to a fixed
101
+ prefix so rustc's output does not depend on the sandbox path; this sets `RUSTFLAGS`, so a
102
+ `build.rustflags` in `.cargo/config.toml` is not read. Its `[target.<triple>] linker` is only
103
+ for plain `cargo`: under Pants the platform's `cc_toolchain` links (see Platforms).
104
+
105
+ ## Platforms
106
+
107
+ The backend builds for the `platform` targets of
108
+ [pants-backend-platforms](https://github.com/tgolsson/pants-backends). A `cargo_package`'s
109
+ `platforms` field lists what its binaries can be built for, the host alone when unset; `pants
110
+ package` builds those that are the host or in `[platforms].enabled`, the host's binary at its
111
+ usual path and every other under `dist/<platform>/`, with `.exe` for Windows.
112
+
113
+ ```python
114
+ platform(name="windows-x86_64", os="windows", arch="x86_64") # builds as x86_64-pc-windows-gnu
115
+ cargo_package(platforms=["linux-x86_64", "windows-x86_64"])
116
+ ```
117
+
118
+ - A `platform` builds as its `rust_target`, by default the triple of its `os` and `arch`
119
+ (`x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`, `x86_64-pc-windows-gnu`,
120
+ `aarch64-apple-darwin`, `x86_64-apple-darwin`). Every enabled platform's target is installed
121
+ with the toolchain. Another platform's build passes `--target`; the host's does not.
122
+ - The platform's `cc_toolchain` ([pants-backend-cc](https://github.com/tgolsson/pants-backends))
123
+ links the binary and is cc-rs's compiler for that triple, host included; a cross build also
124
+ gets the host's for its build scripts and proc macros.
125
+ - A packageable dependency of another backend that builds for platforms (a `cmake_target`)
126
+ is built for the same platform, and must list it in its own `platforms`. Cargo packages
127
+ that a binary depends on need no `platforms` of their own.
128
+ - `run`, `test`, `fmt`, `lint` and `generate-lockfiles` build for the host only; `test` and
129
+ `lint` do not pass `--target`, even for a host whose `rust_target` is not the machine's.
130
+
131
+ ## Goals
132
+
133
+ - `package`: `cargo build --locked --bin <name>` (`--release` with `[rust].release`), the
134
+ binary at `dist/<path.to.target>/<name>` or under `output_path`.
135
+ - `run`: the packaged binary, with the `files`, `resources` and staged packages of its closure,
136
+ but not its cargo sources.
137
+ - `test`: one `cargo test --no-run --workspace` per workspace (or per package outside any),
138
+ then one process per test executable, so each `cargo_library`, `cargo_binary` and
139
+ `cargo_test` is its own cached test result. Doctests are not run. The executables run from
140
+ the package directory with the sources at hand and `CARGO_MANIFEST_DIR` set to it, as
141
+ `cargo test` does, but a path compiled in through `env!("CARGO_MANIFEST_DIR")` or
142
+ `env!("CARGO_BIN_EXE_<name>")` names the build sandbox, which is gone when the test runs:
143
+ read `CARGO_MANIFEST_DIR` with `std::env::var` instead. A target cargo builds no executable
144
+ for (its `required-features` are off) is skipped with a warning.
145
+ - `fmt` / `lint`: `cargo fmt` (`[rust].skip`) and, with
146
+ `pants_backend_cargo.backends.clippy`, `cargo clippy` (`[clippy].args`, `[clippy].skip`),
147
+ once per workspace.
148
+ - `generate-lockfiles`: `cargo update`, never cached across runs; the resolve names are the workspace and package
149
+ addresses (`pants generate-lockfiles --resolve=path:name#package`), and a member's name
150
+ updates its workspace's `Cargo.lock`.
151
+
152
+ ## Setup
153
+
154
+ Install `pants-backend-cargo` in `[GLOBAL].plugins` in `pants.toml`, and enable it:
155
+
156
+ ```toml
157
+ [GLOBAL]
158
+ backend_packages = [
159
+ "pants_backend_cargo",
160
+ "pants_backend_cargo.backends.clippy",
161
+ ]
162
+
163
+ [rustup]
164
+ rust_version = "1.97.0" # or a rust-toolchain.toml at the build root
165
+
166
+ [clippy]
167
+ args = ["--", "-Dwarnings"]
168
+
169
+ [kache]
170
+ enabled = true # the default; false runs rustc bare
171
+ ```
172
+
173
+ Linux and macOS. With no `cc_toolchain` targets, the host's `gcc`, `g++` and `ar` link.
174
+ Declaring `platform` and `cc_toolchain` targets needs `pants_backend_platforms` and
175
+ `pants_backend_cc` in `backend_packages` as well; `pants-backend-cargo` installs both.
176
+
177
+ ## License
178
+
179
+ MIT ([LICENSE-MIT.txt](LICENSE-MIT.txt)).
@@ -0,0 +1,140 @@
1
+ # pants-backend-cargo
2
+
3
+ A [Pants](https://www.pantsbuild.org) backend for Rust that wraps Cargo rather than replacing
4
+ it: Cargo keeps its package model, its lockfiles and its workspaces, and Pants adds target
5
+ addresses, caching, a hermetic toolchain and the usual goals on top.
6
+
7
+ This release targets Pants **2.32.x**. Install it with `pants-backend-cargo==0.1.0` in
8
+ `[GLOBAL].plugins` in `pants.toml`, and enable `pants_backend_cargo` in
9
+ `[GLOBAL].backend_packages`.
10
+
11
+ **Status: alpha.** No stability guarantees until it nears feature completeness.
12
+
13
+ ## Targets
14
+
15
+ - `cargo_workspace`: the root of a Cargo workspace: `Cargo.toml`, `Cargo.lock`,
16
+ `rust-toolchain.toml` and `.cargo/config.toml`. Members are found through the manifest's
17
+ `members` (globs one path segment at a time, less `exclude`) and depend on it. Test
18
+ executables and clippy run once per workspace. Members cannot inherit from the workspace
19
+ (`version.workspace = true`, `b = { workspace = true }`): a package's targets are generated
20
+ from its own manifest alone.
21
+ - `cargo_package`: a package. `cargo metadata` on its manifest generates
22
+ - `path:name#package` (`cargo_package_impl`): the sources and cargo dependencies, what
23
+ `fmt` and `lint` act on;
24
+ - `path:name#library` (`cargo_library`), one `cargo_binary` per `[[bin]]` named after it,
25
+ one `cargo_test` per `[[test]]`: what `test` runs, and for binaries what `package` and
26
+ `run` build.
27
+
28
+ `dependencies` adds `files`/`resources` targets the build reads (`include_str!`, build
29
+ script inputs) and packageable targets of other backends whose `package` output the build
30
+ needs: they are built and staged in the sandbox at their package path for `package`,
31
+ `run`, `test` and `lint` (not for `fmt` and `generate-lockfiles`). Their own dependencies
32
+ are their backend's inputs and are not staged. `env` sets `KEY=VALUE` entries for every
33
+ cargo process whose sandbox holds the package (its own, those of packages depending on it,
34
+ and the per-workspace `test` build, `fmt` and `lint`), and for the test executables and
35
+ `run` binaries those build; `{chroot}` is the sandbox root, so
36
+ `env=["MYLIB_DIR={chroot}/build"]` points a build script at a staged library. `PATH`,
37
+ `CARGO`, `CARGO_HOME`, `CARGO_TARGET_DIR`, `CARGO_INCREMENTAL`, `RUSTUP_HOME`, `RUSTUP_TOOLCHAIN`,
38
+ `RUSTC_WRAPPER`, `RUSTFLAGS`, `KACHE_*` and the toolchain's `CARGO_TARGET_<TRIPLE>_LINKER`,
39
+ `CC_<triple>`, `CXX_<triple>` and `AR_<triple>` are the backend's and rejected. `skip_tests` as
40
+ usual; `output_path` is the directory under `dist/` (default `path.to.target/`), the file
41
+ keeps the binary's name. A target with `test = false` in
42
+ `Cargo.toml` gets `skip_tests`. A `[[bin]]` or `[[test]]` named `package` or `library`, or a
43
+ bin and a test of the same name, is an error: rename it.
44
+
45
+ `path` dependencies between packages are inferred. `pants tailor` adds both target types.
46
+ Pants reads nothing `.gitignore` ignores, so a workspace's `.cargo/config.toml` must not be
47
+ (`!/.cargo/` after a `.cargo` rule).
48
+
49
+ ## Toolchain
50
+
51
+ Nothing from the host's `~/.rustup` or `~/.cargo` is used. `[rustup]` pins rustup-init, which
52
+ installs the toolchain into a named cache shared by every sandbox. The version is
53
+ `[rustup].rust_version` or, unset, the `channel` of the `rust-toolchain.toml` at the build
54
+ root, and must be exact: it is part of every cache key. The components the file lists are
55
+ installed with it; `fmt` and `lint` add `rustfmt` and `clippy`.
56
+
57
+ Every cargo process runs in a fresh sandbox with its own `target/`, so cargo's own incremental
58
+ state never carries over. Instead, `[kache]` puts [kache](https://github.com/kunobi-ninja/kache)
59
+ in front of rustc: compiled crates are keyed by their inputs and kept in a named cache, and
60
+ unchanged crates are restored rather than rebuilt, across sandboxes and across packages of a
61
+ workspace. Local only, no daemon. `--remap-path-prefix` maps the sandbox root to a fixed
62
+ prefix so rustc's output does not depend on the sandbox path; this sets `RUSTFLAGS`, so a
63
+ `build.rustflags` in `.cargo/config.toml` is not read. Its `[target.<triple>] linker` is only
64
+ for plain `cargo`: under Pants the platform's `cc_toolchain` links (see Platforms).
65
+
66
+ ## Platforms
67
+
68
+ The backend builds for the `platform` targets of
69
+ [pants-backend-platforms](https://github.com/tgolsson/pants-backends). A `cargo_package`'s
70
+ `platforms` field lists what its binaries can be built for, the host alone when unset; `pants
71
+ package` builds those that are the host or in `[platforms].enabled`, the host's binary at its
72
+ usual path and every other under `dist/<platform>/`, with `.exe` for Windows.
73
+
74
+ ```python
75
+ platform(name="windows-x86_64", os="windows", arch="x86_64") # builds as x86_64-pc-windows-gnu
76
+ cargo_package(platforms=["linux-x86_64", "windows-x86_64"])
77
+ ```
78
+
79
+ - A `platform` builds as its `rust_target`, by default the triple of its `os` and `arch`
80
+ (`x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`, `x86_64-pc-windows-gnu`,
81
+ `aarch64-apple-darwin`, `x86_64-apple-darwin`). Every enabled platform's target is installed
82
+ with the toolchain. Another platform's build passes `--target`; the host's does not.
83
+ - The platform's `cc_toolchain` ([pants-backend-cc](https://github.com/tgolsson/pants-backends))
84
+ links the binary and is cc-rs's compiler for that triple, host included; a cross build also
85
+ gets the host's for its build scripts and proc macros.
86
+ - A packageable dependency of another backend that builds for platforms (a `cmake_target`)
87
+ is built for the same platform, and must list it in its own `platforms`. Cargo packages
88
+ that a binary depends on need no `platforms` of their own.
89
+ - `run`, `test`, `fmt`, `lint` and `generate-lockfiles` build for the host only; `test` and
90
+ `lint` do not pass `--target`, even for a host whose `rust_target` is not the machine's.
91
+
92
+ ## Goals
93
+
94
+ - `package`: `cargo build --locked --bin <name>` (`--release` with `[rust].release`), the
95
+ binary at `dist/<path.to.target>/<name>` or under `output_path`.
96
+ - `run`: the packaged binary, with the `files`, `resources` and staged packages of its closure,
97
+ but not its cargo sources.
98
+ - `test`: one `cargo test --no-run --workspace` per workspace (or per package outside any),
99
+ then one process per test executable, so each `cargo_library`, `cargo_binary` and
100
+ `cargo_test` is its own cached test result. Doctests are not run. The executables run from
101
+ the package directory with the sources at hand and `CARGO_MANIFEST_DIR` set to it, as
102
+ `cargo test` does, but a path compiled in through `env!("CARGO_MANIFEST_DIR")` or
103
+ `env!("CARGO_BIN_EXE_<name>")` names the build sandbox, which is gone when the test runs:
104
+ read `CARGO_MANIFEST_DIR` with `std::env::var` instead. A target cargo builds no executable
105
+ for (its `required-features` are off) is skipped with a warning.
106
+ - `fmt` / `lint`: `cargo fmt` (`[rust].skip`) and, with
107
+ `pants_backend_cargo.backends.clippy`, `cargo clippy` (`[clippy].args`, `[clippy].skip`),
108
+ once per workspace.
109
+ - `generate-lockfiles`: `cargo update`, never cached across runs; the resolve names are the workspace and package
110
+ addresses (`pants generate-lockfiles --resolve=path:name#package`), and a member's name
111
+ updates its workspace's `Cargo.lock`.
112
+
113
+ ## Setup
114
+
115
+ Install `pants-backend-cargo` in `[GLOBAL].plugins` in `pants.toml`, and enable it:
116
+
117
+ ```toml
118
+ [GLOBAL]
119
+ backend_packages = [
120
+ "pants_backend_cargo",
121
+ "pants_backend_cargo.backends.clippy",
122
+ ]
123
+
124
+ [rustup]
125
+ rust_version = "1.97.0" # or a rust-toolchain.toml at the build root
126
+
127
+ [clippy]
128
+ args = ["--", "-Dwarnings"]
129
+
130
+ [kache]
131
+ enabled = true # the default; false runs rustc bare
132
+ ```
133
+
134
+ Linux and macOS. With no `cc_toolchain` targets, the host's `gcc`, `g++` and `ar` link.
135
+ Declaring `platform` and `cc_toolchain` targets needs `pants_backend_platforms` and
136
+ `pants_backend_cc` in `backend_packages` as well; `pants-backend-cargo` installs both.
137
+
138
+ ## License
139
+
140
+ MIT ([LICENSE-MIT.txt](LICENSE-MIT.txt)).
@@ -0,0 +1,94 @@
1
+ """`pants lint` with clippy: one `cargo clippy` per workspace (or loose package)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from pants.core.environments.target_types import EnvironmentField
8
+ from pants.core.goals.lint import LintResult, LintTargetsRequest
9
+ from pants.core.util_rules.partitions import Partition, Partitions
10
+ from pants.engine.intrinsics import execute_process
11
+ from pants.engine.rules import collect_rules, concurrently, implicitly, rule
12
+ from pants.engine.target import FieldSet
13
+ from pants.util.frozendict import FrozenDict
14
+ from pants.util.logging import LogLevel
15
+ from pants_backend_cargo.backends.clippy.subsystem import ClippySubsystem
16
+ from pants_backend_cargo.goals.fmt import RootMetadata, root_of
17
+ from pants_backend_cargo.target_types import CargoPackageNameField, _CargoPackageMarker
18
+ from pants_backend_cargo.util_rules.cargo import CargoProcessRequest, manifest_path
19
+ from pants_backend_cargo.util_rules.platform import CargoPlatformRequest, cargo_platform
20
+ from pants_backend_cargo.util_rules.rustup import install_rust_toolchain
21
+ from pants_backend_cargo.util_rules.sandbox import CargoSourcesRequest, cargo_sources
22
+ from pants_backend_cargo.util_rules.workspace import CargoPackageMapping
23
+
24
+ from pants_backend_platforms.util_rules.platforms import KnownPlatforms
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class CargoClippyFieldSet(FieldSet):
29
+ required_fields = (CargoPackageNameField, _CargoPackageMarker)
30
+
31
+ environment: EnvironmentField
32
+
33
+
34
+ class CargoClippyRequest(LintTargetsRequest):
35
+ field_set_type = CargoClippyFieldSet
36
+ tool_subsystem = ClippySubsystem
37
+
38
+
39
+ @rule
40
+ async def partition_clippy(
41
+ request: CargoClippyRequest.PartitionRequest[CargoClippyFieldSet],
42
+ clippy: ClippySubsystem,
43
+ mapping: CargoPackageMapping,
44
+ ) -> Partitions[CargoClippyFieldSet, RootMetadata]:
45
+ if clippy.skip:
46
+ return Partitions()
47
+ by_root: dict[RootMetadata, list[CargoClippyFieldSet]] = {}
48
+ for field_set in request.field_sets:
49
+ by_root.setdefault(root_of(mapping, field_set.address), []).append(field_set)
50
+ return Partitions(Partition(tuple(fss), root) for root, fss in by_root.items())
51
+
52
+
53
+ @rule(desc="Lint with clippy", level=LogLevel.DEBUG)
54
+ async def run_clippy(
55
+ request: CargoClippyRequest.Batch[CargoClippyFieldSet, RootMetadata],
56
+ clippy: ClippySubsystem,
57
+ known: KnownPlatforms,
58
+ ) -> LintResult:
59
+ root = request.partition_metadata
60
+ toolchain, cargo, sources = await concurrently(
61
+ install_rust_toolchain(**implicitly()),
62
+ cargo_platform(CargoPlatformRequest(known.host)),
63
+ cargo_sources(
64
+ CargoSourcesRequest(
65
+ frozenset([root.root]),
66
+ include_packages=True,
67
+ platform=known.host,
68
+ dependent=root.root,
69
+ )
70
+ ),
71
+ )
72
+ result = await execute_process(
73
+ **implicitly(
74
+ CargoProcessRequest(
75
+ toolchain,
76
+ (
77
+ "clippy",
78
+ "--locked",
79
+ f"--manifest-path={manifest_path(root.root.spec_path)}",
80
+ *(("--workspace",) if root.workspace else ()),
81
+ *clippy.args,
82
+ ),
83
+ description=f"cargo clippy {root.root}",
84
+ digest=sources.digest,
85
+ env=FrozenDict({**sources.env, **cargo.env}),
86
+ level=LogLevel.INFO,
87
+ )
88
+ )
89
+ )
90
+ return LintResult.create(request, result)
91
+
92
+
93
+ def rules():
94
+ return [*collect_rules(), *CargoClippyRequest.rules()]
@@ -0,0 +1,6 @@
1
+ from pants_backend_cargo.backends.clippy import subsystem
2
+ from pants_backend_cargo.backends.clippy.goals import lint
3
+
4
+
5
+ def rules():
6
+ return [*subsystem.rules(), *lint.rules()]
@@ -0,0 +1,15 @@
1
+ from pants.option.option_types import ArgsListOption, SkipOption
2
+ from pants.option.subsystem import Subsystem
3
+
4
+
5
+ class ClippySubsystem(Subsystem):
6
+ options_scope = "clippy"
7
+ name = "clippy"
8
+ help = "Settings for clippy."
9
+
10
+ skip = SkipOption("lint")
11
+ args = ArgsListOption(example="-- -D warnings")
12
+
13
+
14
+ def rules():
15
+ return ClippySubsystem.rules()
@@ -0,0 +1,122 @@
1
+ """`pants fmt` with rustfmt: one `cargo fmt` per workspace (or loose package)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import glob
6
+ from dataclasses import dataclass
7
+
8
+ from pants.core.environments.target_types import EnvironmentField
9
+ from pants.core.goals.fmt import FmtResult, FmtTargetsRequest
10
+ from pants.core.util_rules.partitions import Partition, PartitionerType, Partitions
11
+ from pants.core.util_rules.source_files import SourceFilesRequest, determine_source_files
12
+ from pants.engine.addresses import Address
13
+ from pants.engine.fs import CreateDigest, FileEntry, MergeDigests
14
+ from pants.engine.intrinsics import create_digest, get_digest_entries, merge_digests
15
+ from pants.engine.process import fallible_to_exec_result_or_raise
16
+ from pants.engine.rules import collect_rules, concurrently, implicitly, rule
17
+ from pants.engine.target import FieldSet
18
+ from pants.util.logging import LogLevel
19
+ from pants_backend_cargo.subsystems import RustSubsystem
20
+ from pants_backend_cargo.target_types import CargoPackageNameField, CargoPackageSourcesField
21
+ from pants_backend_cargo.util_rules.cargo import CargoProcessRequest, manifest_path
22
+ from pants_backend_cargo.util_rules.rustup import install_rust_toolchain
23
+ from pants_backend_cargo.util_rules.sandbox import CargoSourcesRequest, cargo_sources
24
+ from pants_backend_cargo.util_rules.workspace import CargoPackageMapping
25
+
26
+ from pants_backend_platforms.util_rules.platforms import KnownPlatforms
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class CargoFmtFieldSet(FieldSet):
31
+ required_fields = (CargoPackageSourcesField, CargoPackageNameField)
32
+
33
+ sources: CargoPackageSourcesField
34
+ environment: EnvironmentField
35
+
36
+
37
+ class CargoFmtRequest(FmtTargetsRequest):
38
+ field_set_type = CargoFmtFieldSet
39
+ partitioner_type = PartitionerType.CUSTOM
40
+ tool_subsystem = RustSubsystem
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class RootMetadata:
45
+ """A workspace (or a package outside any), the unit cargo fmt and clippy run on."""
46
+
47
+ root: Address
48
+ workspace: bool
49
+
50
+ @property
51
+ def description(self) -> str:
52
+ return str(self.root)
53
+
54
+
55
+ def root_of(mapping: CargoPackageMapping, package: Address) -> RootMetadata:
56
+ workspace = mapping.workspace_for(package)
57
+ return RootMetadata(workspace or package, workspace is not None)
58
+
59
+
60
+ @rule
61
+ async def partition_cargo_fmt(
62
+ request: CargoFmtRequest.PartitionRequest[CargoFmtFieldSet],
63
+ rust: RustSubsystem,
64
+ mapping: CargoPackageMapping,
65
+ ) -> Partitions[str, RootMetadata]:
66
+ if rust.skip:
67
+ return Partitions()
68
+ all_sources = await concurrently(
69
+ determine_source_files(SourceFilesRequest([fs.sources])) for fs in request.field_sets
70
+ )
71
+ by_root: dict[RootMetadata, set[str]] = {}
72
+ for field_set, sources in zip(request.field_sets, all_sources):
73
+ files = by_root.setdefault(root_of(mapping, field_set.address), set())
74
+ files.update(f for f in sources.files if f.endswith(".rs"))
75
+ return Partitions(Partition(tuple(sorted(files)), root) for root, files in by_root.items() if files)
76
+
77
+
78
+ @rule(desc="Format with cargo fmt", level=LogLevel.DEBUG)
79
+ async def cargo_fmt(request: CargoFmtRequest.Batch[str, RootMetadata], known: KnownPlatforms) -> FmtResult:
80
+ root = request.partition_metadata
81
+ toolchain, sources = await concurrently(
82
+ install_rust_toolchain(**implicitly()),
83
+ cargo_sources(
84
+ CargoSourcesRequest(
85
+ frozenset([root.root]),
86
+ include_packages=False,
87
+ platform=known.host,
88
+ dependent=root.root,
89
+ )
90
+ ),
91
+ )
92
+ # The batch's files come from the fmt chain (an earlier formatter may have changed them);
93
+ # everything else cargo needs comes from the sources.
94
+ # A file name may hold glob characters (`src/[gen].rs`): the batch's files leave the
95
+ # sources by exact path, and their outputs are captured escaped.
96
+ batch = set(request.files)
97
+ entries = await get_digest_entries(sources.digest)
98
+ others = await create_digest(
99
+ CreateDigest(e for e in entries if not (isinstance(e, FileEntry) and e.path in batch))
100
+ )
101
+ input_digest = await merge_digests(MergeDigests([others, request.snapshot.digest]))
102
+ result = await fallible_to_exec_result_or_raise(
103
+ **implicitly(
104
+ CargoProcessRequest(
105
+ toolchain,
106
+ (
107
+ "fmt",
108
+ f"--manifest-path={manifest_path(root.root.spec_path)}",
109
+ *(("--all",) if root.workspace else ()),
110
+ ),
111
+ description=f"cargo fmt {root.root}",
112
+ digest=input_digest,
113
+ output_files=tuple(glob.escape(f) for f in request.files),
114
+ env=sources.env,
115
+ )
116
+ )
117
+ )
118
+ return await FmtResult.create(request, result)
119
+
120
+
121
+ def rules():
122
+ return [*collect_rules(), *CargoFmtRequest.rules()]