pants-backend-cc 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_cc-0.1.0/LICENSE-MIT.txt +21 -0
- pants_backend_cc-0.1.0/PKG-INFO +221 -0
- pants_backend_cc-0.1.0/README.md +181 -0
- pants_backend_cc-0.1.0/pants_backend_cc/__init__.py +0 -0
- pants_backend_cc-0.1.0/pants_backend_cc/coverage/__init__.py +0 -0
- pants_backend_cc-0.1.0/pants_backend_cc/coverage/data.py +99 -0
- pants_backend_cc-0.1.0/pants_backend_cc/coverage/gcov.py +213 -0
- pants_backend_cc-0.1.0/pants_backend_cc/coverage/gcovr.lock +1162 -0
- pants_backend_cc-0.1.0/pants_backend_cc/coverage/gcovr.lock.metadata +17 -0
- pants_backend_cc-0.1.0/pants_backend_cc/coverage/register.py +12 -0
- pants_backend_cc-0.1.0/pants_backend_cc/coverage/rules.py +288 -0
- pants_backend_cc-0.1.0/pants_backend_cc/coverage/subsystem.py +83 -0
- pants_backend_cc-0.1.0/pants_backend_cc/generate/__init__.py +0 -0
- pants_backend_cc-0.1.0/pants_backend_cc/generate/clang_format.py +58 -0
- pants_backend_cc-0.1.0/pants_backend_cc/generate/outputs.py +28 -0
- pants_backend_cc-0.1.0/pants_backend_cc/generate/register.py +33 -0
- pants_backend_cc-0.1.0/pants_backend_cc/goals/__init__.py +0 -0
- pants_backend_cc-0.1.0/pants_backend_cc/goals/package.py +54 -0
- pants_backend_cc-0.1.0/pants_backend_cc/goals/test.py +200 -0
- pants_backend_cc-0.1.0/pants_backend_cc/goals/wrapper.py +73 -0
- pants_backend_cc-0.1.0/pants_backend_cc/lint/__init__.py +0 -0
- pants_backend_cc-0.1.0/pants_backend_cc/lint/clang_tidy/__init__.py +0 -0
- pants_backend_cc-0.1.0/pants_backend_cc/lint/clang_tidy/clang_tidy.lock +82 -0
- pants_backend_cc-0.1.0/pants_backend_cc/lint/clang_tidy/clang_tidy.lock.metadata +17 -0
- pants_backend_cc-0.1.0/pants_backend_cc/lint/clang_tidy/register.py +12 -0
- pants_backend_cc-0.1.0/pants_backend_cc/lint/clang_tidy/rules.py +168 -0
- pants_backend_cc-0.1.0/pants_backend_cc/lint/clang_tidy/subsystem.py +60 -0
- pants_backend_cc-0.1.0/pants_backend_cc/lint/clang_tidy/target_types.py +20 -0
- pants_backend_cc-0.1.0/pants_backend_cc/lint/steps.py +60 -0
- pants_backend_cc-0.1.0/pants_backend_cc/register.py +30 -0
- pants_backend_cc-0.1.0/pants_backend_cc/subsystems.py +21 -0
- pants_backend_cc-0.1.0/pants_backend_cc/target_types.py +261 -0
- pants_backend_cc-0.1.0/pants_backend_cc/util_rules/__init__.py +0 -0
- pants_backend_cc-0.1.0/pants_backend_cc/util_rules/compile.py +81 -0
- pants_backend_cc-0.1.0/pants_backend_cc/util_rules/executable.py +559 -0
- pants_backend_cc-0.1.0/pants_backend_cc/util_rules/fields.py +32 -0
- pants_backend_cc-0.1.0/pants_backend_cc/util_rules/generator.py +65 -0
- pants_backend_cc-0.1.0/pants_backend_cc/util_rules/library.py +129 -0
- pants_backend_cc-0.1.0/pants_backend_cc/util_rules/outputs.py +36 -0
- pants_backend_cc-0.1.0/pants_backend_cc/util_rules/scan.py +130 -0
- pants_backend_cc-0.1.0/pants_backend_cc/util_rules/toolchain.py +199 -0
- pants_backend_cc-0.1.0/pyproject.toml +63 -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,221 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pants_backend_cc
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: C and C++ for Pants backends: cc_toolchain, cc_test, compile-step and library interfaces for providers, clang-tidy, gcovr coverage and cpp_generate.
|
|
5
|
+
Keywords: pantsbuild,pants,backend,c,c++,toolchain,testing,clang-tidy,coverage
|
|
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/cc
|
|
34
|
+
Project-URL: Changelog, https://github.com/tgolsson/pants-backends/tree/main/pants-plugins/cc/CHANGELOG.md
|
|
35
|
+
Requires-Python: >=3.11
|
|
36
|
+
Requires-Dist: pants_backend_platforms>=0.1.0
|
|
37
|
+
Provides-Extra: generate
|
|
38
|
+
Requires-Dist: pants_backend_generate>=0.1.0; extra == "generate"
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# pants-backend-cc
|
|
42
|
+
|
|
43
|
+
A [Pants](https://www.pantsbuild.org) backend for the C and C++ pieces that build-system backends
|
|
44
|
+
share: toolchains, interfaces for their compiles and libraries, and the tools built on them.
|
|
45
|
+
|
|
46
|
+
This release targets Pants **2.32.x**. Install it with `pants-backend-cc==0.1.0` in
|
|
47
|
+
`[GLOBAL].plugins` in `pants.toml`, and enable `pants_backend_cc` in
|
|
48
|
+
`[GLOBAL].backend_packages`.
|
|
49
|
+
|
|
50
|
+
**Status: alpha.**
|
|
51
|
+
|
|
52
|
+
[`examples/cc`](https://github.com/tgolsson/pants-backends/tree/main/examples/cc) declares a platform and its `cc_toolchain`, and builds a `cc_binary` and a
|
|
53
|
+
`cc_test` against a static library from a `cmake_project`.
|
|
54
|
+
|
|
55
|
+
## Toolchains
|
|
56
|
+
|
|
57
|
+
```toml
|
|
58
|
+
backend_packages = ["pants_backend_platforms", "pants_backend_cc"]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
platform(name="linux-x86_64", os="linux", arch="x86_64")
|
|
63
|
+
platform(name="windows-x86_64", os="windows", arch="x86_64")
|
|
64
|
+
|
|
65
|
+
cc_toolchain(name="linux", host="linux-x86_64", target="linux-x86_64", cc="gcc", cxx="g++", ar="ar")
|
|
66
|
+
cc_toolchain(
|
|
67
|
+
name="linux-to-windows",
|
|
68
|
+
host="linux-x86_64",
|
|
69
|
+
target="windows-x86_64",
|
|
70
|
+
cc="x86_64-w64-mingw32-gcc",
|
|
71
|
+
cxx="x86_64-w64-mingw32-g++",
|
|
72
|
+
ar="x86_64-w64-mingw32-ar",
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`host` and `target` are [pants-backend-platforms](../platforms) `platform` targets. The compilers'
|
|
77
|
+
`--version` output and the contents of `ar` and of the linker the compiler drives are part of the
|
|
78
|
+
cache key; headers and libraries are not. A native pair without a `cc_toolchain` uses `gcc`, `g++`
|
|
79
|
+
and `ar`, which `[cc-toolchains].strict` forbids.
|
|
80
|
+
|
|
81
|
+
## Compile steps, for backend authors
|
|
82
|
+
|
|
83
|
+
- `resolve_cc_toolchain(CcToolchainRequest(platform))` returns the toolchain for the host and that
|
|
84
|
+
platform. Put `CcToolchain.env` in the process environment.
|
|
85
|
+
- A backend exposes its compiles by making a field set a `CompileStepsFieldSet` member and
|
|
86
|
+
registering `UnionRule(CompileStepsFieldSet, MyFieldSet)`. Its rule takes the field set and a
|
|
87
|
+
`TargetPlatform` and returns `CompileSteps`, one `CompileStep` per translation unit: the
|
|
88
|
+
source and object (relative to the sandbox root), the language, the complete command
|
|
89
|
+
(compiler first) with the directory it runs in, and its environment.
|
|
90
|
+
- A step's `inputs` is a member of the `CompileInputsRequest` union whose rule returns
|
|
91
|
+
`CompileInputs`, the sandbox the command runs in. Listing steps builds no sandbox; a consumer
|
|
92
|
+
calls `compile_inputs` for the steps it runs.
|
|
93
|
+
- `pants_backend_cc.util_rules.scan` runs a compile with `-M -MG` to find the headers a
|
|
94
|
+
translation unit reads, against a `HeaderUniverse` the backend provides.
|
|
95
|
+
- A library backend makes a field set a `CcLibraryFieldSet` member and implements its three
|
|
96
|
+
rules, `library_cwd`, `compile_interface` and `link_interface`; `util_rules/library.py`
|
|
97
|
+
documents them.
|
|
98
|
+
|
|
99
|
+
## Tests
|
|
100
|
+
|
|
101
|
+
`cc_test` compiles `sources` into an executable linked against the libraries in
|
|
102
|
+
`dependencies`, from any backend that provides them, and `pants test` runs it with `args`;
|
|
103
|
+
`cc_tests` generates one per directory of translation units. `pants help cc_test` describes the
|
|
104
|
+
fields. A test that fails to build is a failed test.
|
|
105
|
+
|
|
106
|
+
- A library may require the directory its consumers compile and link in; a test whose
|
|
107
|
+
libraries require different ones fails.
|
|
108
|
+
- Every library's flags and link arguments are concatenated in declared order, without
|
|
109
|
+
deduplication, so objects or whole-archive spans that several libraries bring are repeated
|
|
110
|
+
and may define a symbol twice.
|
|
111
|
+
- Shared libraries are found at run time through an `$ORIGIN`-relative run path
|
|
112
|
+
(`@loader_path` on macOS).
|
|
113
|
+
- Test executables are built under their address without parameters, so parametrizing how a
|
|
114
|
+
test runs shares its build.
|
|
115
|
+
- A compile whose flags ask for coverage (`--coverage`) keeps its `.gcno` notes, in
|
|
116
|
+
`BuiltExecutable.coverage_notes`; see Coverage.
|
|
117
|
+
- Another backend can run tests under a wrapper (an emulator, a tracer) through the
|
|
118
|
+
`CcTestWrapperRequest` union; `goals/wrapper.py` documents the contract.
|
|
119
|
+
- `[cc-test-runner].skip` skips the tests.
|
|
120
|
+
|
|
121
|
+
## Binaries
|
|
122
|
+
|
|
123
|
+
`cc_binary` builds an executable for the host the way `cc_test` builds a test, from the same
|
|
124
|
+
fields less the ones for running it, and `pants package` writes it to `output_path`. A library's
|
|
125
|
+
working directory and the declared link order apply as for tests. Only the executable is
|
|
126
|
+
written, not the shared libraries it links against, and its run path (`$ORIGIN`-relative, or
|
|
127
|
+
`@loader_path` on macOS) points at where they sit in the build sandbox, so from `dist/` it will
|
|
128
|
+
not find them: link static libraries into a binary you package.
|
|
129
|
+
|
|
130
|
+
## Coverage
|
|
131
|
+
|
|
132
|
+
The optional sub-backend `pants_backend_cc.coverage` makes `pants test --use-coverage` report
|
|
133
|
+
`cc_test` coverage with [gcovr](https://gcovr.com), from its PyPI package (a pex, hence
|
|
134
|
+
`pants.backend.python`). Without it, `--use-coverage` changes nothing for `cc_test`.
|
|
135
|
+
|
|
136
|
+
```toml
|
|
137
|
+
backend_packages = [
|
|
138
|
+
"pants.backend.python",
|
|
139
|
+
"pants_backend_platforms",
|
|
140
|
+
"pants_backend_cc",
|
|
141
|
+
"pants_backend_cc.coverage",
|
|
142
|
+
"pants_backend_cmake",
|
|
143
|
+
]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- A test builds as a coverage consumer (`CcConsumer.coverage`): each library serves the build its
|
|
147
|
+
backend instruments, such as a `cmake_project` with `coverage_cmake_args`, and the files gcov
|
|
148
|
+
reads (`CcLinkInterface.coverage`). A library without one serves its usual build and is not
|
|
149
|
+
in the report. The test links with `--coverage`; its own sources are instrumented only when
|
|
150
|
+
its compile flags ask for it.
|
|
151
|
+
- gcc names a `.gcda` file after its object's absolute path at compile time, inside a sandbox
|
|
152
|
+
that is gone when the test runs. The test runs with `GCOV_PREFIX` in a directory it outputs,
|
|
153
|
+
and each `.gcda` it writes is moved next to the `.gcno` whose path is the longest suffix of
|
|
154
|
+
its own. A cached test result keeps them, so it still counts.
|
|
155
|
+
- gcov and gcov-tool are the ones next to the host toolchain's gcc with the same suffix
|
|
156
|
+
(`gcc-13`, `gcov-13`, `gcov-tool-13`), else those on the search path; their major version must
|
|
157
|
+
be the compiler's. They are checked before any test builds.
|
|
158
|
+
- gcov resolves an object's sources from the directory its compile ran in, which the notes
|
|
159
|
+
record as a sandbox that is gone. Whoever ran the compile names that directory, relative to
|
|
160
|
+
the sandbox root, in a file next to the notes (`x.c.gcno.cwd`; `coverage/data.py` has the
|
|
161
|
+
helper every producer of a link interface's `coverage` uses), and the gcov gcovr runs is a
|
|
162
|
+
wrapper that runs gcov there, once per object, in the text format on its stdout, and writes
|
|
163
|
+
the `.gcov` files where gcovr looks for them. gcovr asks gcov 14 and newer for their JSON
|
|
164
|
+
format, which names sources from the recorded directory; the wrapper drops the request and
|
|
165
|
+
gcovr reads the text it gets. gcovr runs gcov for one object at a time per directory it runs
|
|
166
|
+
it in, so it is told to run it in each object's own directory (`--gcov-object-directory=.`).
|
|
167
|
+
- gcov-tool sums the tests' `.gcda` files, two at a time, and one gcovr run reads the totals into a
|
|
168
|
+
JSON tracefile; a last run writes the reports under `dist/coverage/cc/`. Tests whose notes or
|
|
169
|
+
compile inputs differ at a path (two configures sharing a build directory) get a tracefile each,
|
|
170
|
+
added up in that last run.
|
|
171
|
+
- `[gcovr].report`: `console` (the default), `html`, `json`, `json-summary`, `cobertura`, `lcov`,
|
|
172
|
+
`markdown`. `filter` and `exclude` are gcovr's regular expressions over paths relative to the
|
|
173
|
+
build root; `fail_under_line` fails the goal below that line coverage, as does any other
|
|
174
|
+
`--fail-under-*` threshold in `args`; `args` go to every gcovr run.
|
|
175
|
+
- `pants test --debug` runs a test without coverage.
|
|
176
|
+
- A compile must name its source relative to the directory it runs in, not by its absolute
|
|
177
|
+
sandbox path, for gcov to find it again.
|
|
178
|
+
|
|
179
|
+
## clang-tidy
|
|
180
|
+
|
|
181
|
+
The optional sub-backend `pants_backend_cc.lint.clang_tidy` adds `[clang-tidy]` to `pants lint`,
|
|
182
|
+
from the `clang-tidy` PyPI wheel (a pex, hence `pants.backend.python`). A target with
|
|
183
|
+
`clang_tidy_paths` (a field the providing backend registers) has its compile steps listed for the
|
|
184
|
+
host, and clang-tidy runs once per step whose source is under those directories, in that step's
|
|
185
|
+
sandbox and with its command.
|
|
186
|
+
|
|
187
|
+
- A provider's sandbox may hold only the headers a gcc dependency scan found, so an include
|
|
188
|
+
that only clang takes (behind `__clang__` or `__has_include`) is missing.
|
|
189
|
+
- Configuration comes from `.clang-tidy` files in the directories above each translation unit; a
|
|
190
|
+
`--config-file` in `args` is not in the sandbox.
|
|
191
|
+
- Only errors fail the lint; `WarningsAsErrors` in `.clang-tidy` makes warnings fail it too.
|
|
192
|
+
Warnings are reported either way.
|
|
193
|
+
- The default lockfile has no linux-aarch64 wheel.
|
|
194
|
+
|
|
195
|
+
```toml
|
|
196
|
+
backend_packages = [
|
|
197
|
+
"pants.backend.python",
|
|
198
|
+
"pants_backend_platforms",
|
|
199
|
+
"pants_backend_cc",
|
|
200
|
+
"pants_backend_cc.lint.clang_tidy",
|
|
201
|
+
"pants_backend_cmake",
|
|
202
|
+
]
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## `cpp_generate`
|
|
206
|
+
|
|
207
|
+
The optional sub-backend `pants_backend_cc.generate` provides `cpp_generate`, the C/C++
|
|
208
|
+
specialization of `pants-backend-generate`: C/C++ sources written by a generator, formatted with
|
|
209
|
+
Pants' `pants.backend.cc` `[clang-format]` (a pex, hence `pants.backend.python`;
|
|
210
|
+
`[clang-format].skip` is not read). `pants generate` writes them, `pants lint` checks the
|
|
211
|
+
committed copies, and a target depending on one compiles them. Install
|
|
212
|
+
`pants_backend_cc[generate]` (it pulls in `pants-backend-generate`) and enable:
|
|
213
|
+
|
|
214
|
+
```toml
|
|
215
|
+
backend_packages = [
|
|
216
|
+
"pants.backend.python",
|
|
217
|
+
"pants_backend_generate",
|
|
218
|
+
"pants_backend_cc",
|
|
219
|
+
"pants_backend_cc.generate",
|
|
220
|
+
]
|
|
221
|
+
```
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# pants-backend-cc
|
|
2
|
+
|
|
3
|
+
A [Pants](https://www.pantsbuild.org) backend for the C and C++ pieces that build-system backends
|
|
4
|
+
share: toolchains, interfaces for their compiles and libraries, and the tools built on them.
|
|
5
|
+
|
|
6
|
+
This release targets Pants **2.32.x**. Install it with `pants-backend-cc==0.1.0` in
|
|
7
|
+
`[GLOBAL].plugins` in `pants.toml`, and enable `pants_backend_cc` in
|
|
8
|
+
`[GLOBAL].backend_packages`.
|
|
9
|
+
|
|
10
|
+
**Status: alpha.**
|
|
11
|
+
|
|
12
|
+
[`examples/cc`](https://github.com/tgolsson/pants-backends/tree/main/examples/cc) declares a platform and its `cc_toolchain`, and builds a `cc_binary` and a
|
|
13
|
+
`cc_test` against a static library from a `cmake_project`.
|
|
14
|
+
|
|
15
|
+
## Toolchains
|
|
16
|
+
|
|
17
|
+
```toml
|
|
18
|
+
backend_packages = ["pants_backend_platforms", "pants_backend_cc"]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
platform(name="linux-x86_64", os="linux", arch="x86_64")
|
|
23
|
+
platform(name="windows-x86_64", os="windows", arch="x86_64")
|
|
24
|
+
|
|
25
|
+
cc_toolchain(name="linux", host="linux-x86_64", target="linux-x86_64", cc="gcc", cxx="g++", ar="ar")
|
|
26
|
+
cc_toolchain(
|
|
27
|
+
name="linux-to-windows",
|
|
28
|
+
host="linux-x86_64",
|
|
29
|
+
target="windows-x86_64",
|
|
30
|
+
cc="x86_64-w64-mingw32-gcc",
|
|
31
|
+
cxx="x86_64-w64-mingw32-g++",
|
|
32
|
+
ar="x86_64-w64-mingw32-ar",
|
|
33
|
+
)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`host` and `target` are [pants-backend-platforms](../platforms) `platform` targets. The compilers'
|
|
37
|
+
`--version` output and the contents of `ar` and of the linker the compiler drives are part of the
|
|
38
|
+
cache key; headers and libraries are not. A native pair without a `cc_toolchain` uses `gcc`, `g++`
|
|
39
|
+
and `ar`, which `[cc-toolchains].strict` forbids.
|
|
40
|
+
|
|
41
|
+
## Compile steps, for backend authors
|
|
42
|
+
|
|
43
|
+
- `resolve_cc_toolchain(CcToolchainRequest(platform))` returns the toolchain for the host and that
|
|
44
|
+
platform. Put `CcToolchain.env` in the process environment.
|
|
45
|
+
- A backend exposes its compiles by making a field set a `CompileStepsFieldSet` member and
|
|
46
|
+
registering `UnionRule(CompileStepsFieldSet, MyFieldSet)`. Its rule takes the field set and a
|
|
47
|
+
`TargetPlatform` and returns `CompileSteps`, one `CompileStep` per translation unit: the
|
|
48
|
+
source and object (relative to the sandbox root), the language, the complete command
|
|
49
|
+
(compiler first) with the directory it runs in, and its environment.
|
|
50
|
+
- A step's `inputs` is a member of the `CompileInputsRequest` union whose rule returns
|
|
51
|
+
`CompileInputs`, the sandbox the command runs in. Listing steps builds no sandbox; a consumer
|
|
52
|
+
calls `compile_inputs` for the steps it runs.
|
|
53
|
+
- `pants_backend_cc.util_rules.scan` runs a compile with `-M -MG` to find the headers a
|
|
54
|
+
translation unit reads, against a `HeaderUniverse` the backend provides.
|
|
55
|
+
- A library backend makes a field set a `CcLibraryFieldSet` member and implements its three
|
|
56
|
+
rules, `library_cwd`, `compile_interface` and `link_interface`; `util_rules/library.py`
|
|
57
|
+
documents them.
|
|
58
|
+
|
|
59
|
+
## Tests
|
|
60
|
+
|
|
61
|
+
`cc_test` compiles `sources` into an executable linked against the libraries in
|
|
62
|
+
`dependencies`, from any backend that provides them, and `pants test` runs it with `args`;
|
|
63
|
+
`cc_tests` generates one per directory of translation units. `pants help cc_test` describes the
|
|
64
|
+
fields. A test that fails to build is a failed test.
|
|
65
|
+
|
|
66
|
+
- A library may require the directory its consumers compile and link in; a test whose
|
|
67
|
+
libraries require different ones fails.
|
|
68
|
+
- Every library's flags and link arguments are concatenated in declared order, without
|
|
69
|
+
deduplication, so objects or whole-archive spans that several libraries bring are repeated
|
|
70
|
+
and may define a symbol twice.
|
|
71
|
+
- Shared libraries are found at run time through an `$ORIGIN`-relative run path
|
|
72
|
+
(`@loader_path` on macOS).
|
|
73
|
+
- Test executables are built under their address without parameters, so parametrizing how a
|
|
74
|
+
test runs shares its build.
|
|
75
|
+
- A compile whose flags ask for coverage (`--coverage`) keeps its `.gcno` notes, in
|
|
76
|
+
`BuiltExecutable.coverage_notes`; see Coverage.
|
|
77
|
+
- Another backend can run tests under a wrapper (an emulator, a tracer) through the
|
|
78
|
+
`CcTestWrapperRequest` union; `goals/wrapper.py` documents the contract.
|
|
79
|
+
- `[cc-test-runner].skip` skips the tests.
|
|
80
|
+
|
|
81
|
+
## Binaries
|
|
82
|
+
|
|
83
|
+
`cc_binary` builds an executable for the host the way `cc_test` builds a test, from the same
|
|
84
|
+
fields less the ones for running it, and `pants package` writes it to `output_path`. A library's
|
|
85
|
+
working directory and the declared link order apply as for tests. Only the executable is
|
|
86
|
+
written, not the shared libraries it links against, and its run path (`$ORIGIN`-relative, or
|
|
87
|
+
`@loader_path` on macOS) points at where they sit in the build sandbox, so from `dist/` it will
|
|
88
|
+
not find them: link static libraries into a binary you package.
|
|
89
|
+
|
|
90
|
+
## Coverage
|
|
91
|
+
|
|
92
|
+
The optional sub-backend `pants_backend_cc.coverage` makes `pants test --use-coverage` report
|
|
93
|
+
`cc_test` coverage with [gcovr](https://gcovr.com), from its PyPI package (a pex, hence
|
|
94
|
+
`pants.backend.python`). Without it, `--use-coverage` changes nothing for `cc_test`.
|
|
95
|
+
|
|
96
|
+
```toml
|
|
97
|
+
backend_packages = [
|
|
98
|
+
"pants.backend.python",
|
|
99
|
+
"pants_backend_platforms",
|
|
100
|
+
"pants_backend_cc",
|
|
101
|
+
"pants_backend_cc.coverage",
|
|
102
|
+
"pants_backend_cmake",
|
|
103
|
+
]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- A test builds as a coverage consumer (`CcConsumer.coverage`): each library serves the build its
|
|
107
|
+
backend instruments, such as a `cmake_project` with `coverage_cmake_args`, and the files gcov
|
|
108
|
+
reads (`CcLinkInterface.coverage`). A library without one serves its usual build and is not
|
|
109
|
+
in the report. The test links with `--coverage`; its own sources are instrumented only when
|
|
110
|
+
its compile flags ask for it.
|
|
111
|
+
- gcc names a `.gcda` file after its object's absolute path at compile time, inside a sandbox
|
|
112
|
+
that is gone when the test runs. The test runs with `GCOV_PREFIX` in a directory it outputs,
|
|
113
|
+
and each `.gcda` it writes is moved next to the `.gcno` whose path is the longest suffix of
|
|
114
|
+
its own. A cached test result keeps them, so it still counts.
|
|
115
|
+
- gcov and gcov-tool are the ones next to the host toolchain's gcc with the same suffix
|
|
116
|
+
(`gcc-13`, `gcov-13`, `gcov-tool-13`), else those on the search path; their major version must
|
|
117
|
+
be the compiler's. They are checked before any test builds.
|
|
118
|
+
- gcov resolves an object's sources from the directory its compile ran in, which the notes
|
|
119
|
+
record as a sandbox that is gone. Whoever ran the compile names that directory, relative to
|
|
120
|
+
the sandbox root, in a file next to the notes (`x.c.gcno.cwd`; `coverage/data.py` has the
|
|
121
|
+
helper every producer of a link interface's `coverage` uses), and the gcov gcovr runs is a
|
|
122
|
+
wrapper that runs gcov there, once per object, in the text format on its stdout, and writes
|
|
123
|
+
the `.gcov` files where gcovr looks for them. gcovr asks gcov 14 and newer for their JSON
|
|
124
|
+
format, which names sources from the recorded directory; the wrapper drops the request and
|
|
125
|
+
gcovr reads the text it gets. gcovr runs gcov for one object at a time per directory it runs
|
|
126
|
+
it in, so it is told to run it in each object's own directory (`--gcov-object-directory=.`).
|
|
127
|
+
- gcov-tool sums the tests' `.gcda` files, two at a time, and one gcovr run reads the totals into a
|
|
128
|
+
JSON tracefile; a last run writes the reports under `dist/coverage/cc/`. Tests whose notes or
|
|
129
|
+
compile inputs differ at a path (two configures sharing a build directory) get a tracefile each,
|
|
130
|
+
added up in that last run.
|
|
131
|
+
- `[gcovr].report`: `console` (the default), `html`, `json`, `json-summary`, `cobertura`, `lcov`,
|
|
132
|
+
`markdown`. `filter` and `exclude` are gcovr's regular expressions over paths relative to the
|
|
133
|
+
build root; `fail_under_line` fails the goal below that line coverage, as does any other
|
|
134
|
+
`--fail-under-*` threshold in `args`; `args` go to every gcovr run.
|
|
135
|
+
- `pants test --debug` runs a test without coverage.
|
|
136
|
+
- A compile must name its source relative to the directory it runs in, not by its absolute
|
|
137
|
+
sandbox path, for gcov to find it again.
|
|
138
|
+
|
|
139
|
+
## clang-tidy
|
|
140
|
+
|
|
141
|
+
The optional sub-backend `pants_backend_cc.lint.clang_tidy` adds `[clang-tidy]` to `pants lint`,
|
|
142
|
+
from the `clang-tidy` PyPI wheel (a pex, hence `pants.backend.python`). A target with
|
|
143
|
+
`clang_tidy_paths` (a field the providing backend registers) has its compile steps listed for the
|
|
144
|
+
host, and clang-tidy runs once per step whose source is under those directories, in that step's
|
|
145
|
+
sandbox and with its command.
|
|
146
|
+
|
|
147
|
+
- A provider's sandbox may hold only the headers a gcc dependency scan found, so an include
|
|
148
|
+
that only clang takes (behind `__clang__` or `__has_include`) is missing.
|
|
149
|
+
- Configuration comes from `.clang-tidy` files in the directories above each translation unit; a
|
|
150
|
+
`--config-file` in `args` is not in the sandbox.
|
|
151
|
+
- Only errors fail the lint; `WarningsAsErrors` in `.clang-tidy` makes warnings fail it too.
|
|
152
|
+
Warnings are reported either way.
|
|
153
|
+
- The default lockfile has no linux-aarch64 wheel.
|
|
154
|
+
|
|
155
|
+
```toml
|
|
156
|
+
backend_packages = [
|
|
157
|
+
"pants.backend.python",
|
|
158
|
+
"pants_backend_platforms",
|
|
159
|
+
"pants_backend_cc",
|
|
160
|
+
"pants_backend_cc.lint.clang_tidy",
|
|
161
|
+
"pants_backend_cmake",
|
|
162
|
+
]
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## `cpp_generate`
|
|
166
|
+
|
|
167
|
+
The optional sub-backend `pants_backend_cc.generate` provides `cpp_generate`, the C/C++
|
|
168
|
+
specialization of `pants-backend-generate`: C/C++ sources written by a generator, formatted with
|
|
169
|
+
Pants' `pants.backend.cc` `[clang-format]` (a pex, hence `pants.backend.python`;
|
|
170
|
+
`[clang-format].skip` is not read). `pants generate` writes them, `pants lint` checks the
|
|
171
|
+
committed copies, and a target depending on one compiles them. Install
|
|
172
|
+
`pants_backend_cc[generate]` (it pulls in `pants-backend-generate`) and enable:
|
|
173
|
+
|
|
174
|
+
```toml
|
|
175
|
+
backend_packages = [
|
|
176
|
+
"pants.backend.python",
|
|
177
|
+
"pants_backend_generate",
|
|
178
|
+
"pants_backend_cc",
|
|
179
|
+
"pants_backend_cc.generate",
|
|
180
|
+
]
|
|
181
|
+
```
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""What a `cc_test` run under `--use-coverage` contributes to a report, how the `.gcda` files it
|
|
2
|
+
writes are matched to their notes, and what the compiles that wrote the notes leave for gcov.
|
|
3
|
+
|
|
4
|
+
gcc names an object's `.gcda` after the object's absolute path when it was compiled, inside that
|
|
5
|
+
compile's sandbox. `-fprofile-prefix-path` could strip the sandbox, but only from a command that
|
|
6
|
+
names it, which every library backend would then have to inject into its compiles. Instead the
|
|
7
|
+
test runs with `GCOV_PREFIX` in a directory it outputs, where each `.gcda` lands under its whole
|
|
8
|
+
compile-time path, and moves next to the `.gcno` whose path is the longest suffix of that path.
|
|
9
|
+
|
|
10
|
+
gcov resolves an object's sources from the directory its compile ran in, which the notes record
|
|
11
|
+
as a path in that sandbox. Whoever ran the compile knows that directory relative to the sandbox
|
|
12
|
+
root and writes it next to the notes (`compile_directories`), for the gcov gcovr runs (`gcov.py`).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
from collections.abc import Iterable
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
|
|
21
|
+
from pants.core.goals.test import CoverageData, CoverageDataCollection
|
|
22
|
+
from pants.engine.addresses import Address
|
|
23
|
+
from pants.engine.fs import CreateDigest, Digest, FileContent, FileEntry, MergeDigests
|
|
24
|
+
from pants.engine.intrinsics import create_digest, get_digest_entries, merge_digests
|
|
25
|
+
from pants.engine.rules import concurrently
|
|
26
|
+
|
|
27
|
+
GCDA_DIR = "__pants_gcda"
|
|
28
|
+
"""Where a test writes its `.gcda` files, relative to its working directory."""
|
|
29
|
+
|
|
30
|
+
COMPILE_DIRECTORY = ".gcno.cwd"
|
|
31
|
+
"""The suffix of the file next to an object's notes (`x.c.gcno`, `x.c.gcno.cwd`) naming the
|
|
32
|
+
directory its compile ran in, relative to the sandbox root; "" is the root."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def compile_directory_file(note: str) -> str:
|
|
36
|
+
return f"{note[: -len('.gcno')]}{COMPILE_DIRECTORY}"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def compile_directories(notes: Digest, directory: str) -> Digest:
|
|
40
|
+
"""Next to each `.gcno` in `notes`, the file naming `directory`, where all of their compiles
|
|
41
|
+
ran. Every producer of a coverage digest (`CcLinkInterface.coverage`) merges this in."""
|
|
42
|
+
entries = await get_digest_entries(notes)
|
|
43
|
+
return await create_digest(
|
|
44
|
+
CreateDigest(
|
|
45
|
+
FileContent(compile_directory_file(e.path), directory.encode())
|
|
46
|
+
for e in entries
|
|
47
|
+
if isinstance(e, FileEntry) and e.path.endswith(".gcno")
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass(frozen=True)
|
|
53
|
+
class CcCoverageData(CoverageData):
|
|
54
|
+
address: Address
|
|
55
|
+
digest: Digest
|
|
56
|
+
"""The `.gcno` notes, the `.gcda` data the run wrote next to them, and the files the compiles
|
|
57
|
+
read, relative to the sandbox root."""
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class CcCoverageDataCollection(CoverageDataCollection[CcCoverageData]):
|
|
61
|
+
element_type = CcCoverageData
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def gcda_destinations(written: Iterable[str], notes: Iterable[str]) -> dict[str, str]:
|
|
65
|
+
"""For each `.gcda` path under `GCDA_DIR`, the path next to its `.gcno`; one without notes
|
|
66
|
+
(an object no library described) is left out."""
|
|
67
|
+
known = set(notes)
|
|
68
|
+
destinations = {}
|
|
69
|
+
for path in written:
|
|
70
|
+
parts = path.split("/")[1:]
|
|
71
|
+
for start in range(len(parts)):
|
|
72
|
+
candidate = "/".join(parts[start:])
|
|
73
|
+
if f"{candidate[: -len('.gcda')]}.gcno" in known:
|
|
74
|
+
destinations[path] = candidate
|
|
75
|
+
break
|
|
76
|
+
return destinations
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
async def _entries(digest: Digest) -> list[FileEntry]:
|
|
80
|
+
return [e for e in await get_digest_entries(digest) if isinstance(e, FileEntry)]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
async def coverage_data(address: Address, coverage: Digest, run_outputs: Digest) -> CcCoverageData:
|
|
84
|
+
"""`coverage` is `BuiltExecutable.coverage`; `run_outputs` the test process's outputs, `GCDA_DIR`
|
|
85
|
+
relative to its working directory."""
|
|
86
|
+
written, files = await concurrently(_entries(run_outputs), _entries(coverage))
|
|
87
|
+
notes = [e.path for e in files if e.path.endswith(".gcno")]
|
|
88
|
+
destinations = gcda_destinations((e.path for e in written if e.path.endswith(".gcda")), notes)
|
|
89
|
+
moved = await create_digest(
|
|
90
|
+
CreateDigest(
|
|
91
|
+
FileEntry(destinations[e.path], e.file_digest) for e in written if e.path in destinations
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
return CcCoverageData(address, await merge_digests(MergeDigests([coverage, moved])))
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def gcov_prefix(cwd: str) -> str:
|
|
98
|
+
"""`GCOV_PREFIX` for a test running in `cwd`, relative to the sandbox root."""
|
|
99
|
+
return "{chroot}/" + os.path.join(cwd, GCDA_DIR)
|