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.
- pants_backend_cargo-0.1.0/LICENSE-MIT.txt +21 -0
- pants_backend_cargo-0.1.0/PKG-INFO +179 -0
- pants_backend_cargo-0.1.0/README.md +140 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/__init__.py +1 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/backends/clippy/__init__.py +1 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/backends/clippy/goals/__init__.py +1 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/backends/clippy/goals/lint.py +94 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/backends/clippy/register.py +6 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/backends/clippy/subsystem.py +15 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/goals/__init__.py +1 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/goals/fmt.py +122 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/goals/generate_lockfiles.py +118 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/goals/package.py +78 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/goals/run.py +40 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/goals/tailor.py +64 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/goals/test.py +233 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/internal/build.py +99 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/register.py +41 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/subsystems.py +144 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/target_generator.py +160 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/target_types.py +197 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/util_rules/cargo.py +214 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/util_rules/dependency_inference.py +100 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/util_rules/platform.py +153 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/util_rules/rustup.py +226 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/util_rules/sandbox.py +214 -0
- pants_backend_cargo-0.1.0/pants_backend_cargo/util_rules/workspace.py +146 -0
- 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 @@
|
|
|
1
|
+
""" """
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
""" """
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
""" """
|
|
@@ -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,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 @@
|
|
|
1
|
+
""" """
|
|
@@ -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()]
|