benchset 0.0.2__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 (50) hide show
  1. benchset-0.0.2/Cargo.lock +7 -0
  2. benchset-0.0.2/Cargo.toml +25 -0
  3. benchset-0.0.2/LICENSE +21 -0
  4. benchset-0.0.2/MANIFEST.in +12 -0
  5. benchset-0.0.2/PKG-INFO +437 -0
  6. benchset-0.0.2/Package.swift +17 -0
  7. benchset-0.0.2/README.md +403 -0
  8. benchset-0.0.2/Sources/Benchset/Benchset.swift +149 -0
  9. benchset-0.0.2/benchset/__init__.py +16 -0
  10. benchset-0.0.2/benchset/__main__.py +3 -0
  11. benchset-0.0.2/benchset/_native.py +203 -0
  12. benchset-0.0.2/benchset/api.py +134 -0
  13. benchset-0.0.2/benchset/cli.py +168 -0
  14. benchset-0.0.2/benchset/models.py +52 -0
  15. benchset-0.0.2/benchset/py.typed +0 -0
  16. benchset-0.0.2/benchset.egg-info/PKG-INFO +437 -0
  17. benchset-0.0.2/benchset.egg-info/SOURCES.txt +48 -0
  18. benchset-0.0.2/benchset.egg-info/dependency_links.txt +1 -0
  19. benchset-0.0.2/benchset.egg-info/entry_points.txt +2 -0
  20. benchset-0.0.2/benchset.egg-info/requires.txt +1 -0
  21. benchset-0.0.2/benchset.egg-info/top_level.txt +1 -0
  22. benchset-0.0.2/examples/native/c.c +26 -0
  23. benchset-0.0.2/examples/native/rust.rs +15 -0
  24. benchset-0.0.2/examples/native/swift.swift +5 -0
  25. benchset-0.0.2/include/benchset.h +64 -0
  26. benchset-0.0.2/include/module.modulemap +4 -0
  27. benchset-0.0.2/native/src/ffi.rs +302 -0
  28. benchset-0.0.2/native/src/ffi_tests.rs +411 -0
  29. benchset-0.0.2/native/src/kernel.rs +113 -0
  30. benchset-0.0.2/native/src/kernel_tests.rs +87 -0
  31. benchset-0.0.2/native/src/lib.rs +555 -0
  32. benchset-0.0.2/native/src/runtime_tests.rs +638 -0
  33. benchset-0.0.2/pyproject.toml +80 -0
  34. benchset-0.0.2/rust-toolchain.toml +3 -0
  35. benchset-0.0.2/scripts/__init__.py +1 -0
  36. benchset-0.0.2/scripts/build_backend.py +107 -0
  37. benchset-0.0.2/scripts/build_native.py +17 -0
  38. benchset-0.0.2/scripts/check_native_coverage.py +126 -0
  39. benchset-0.0.2/scripts/check_source_install.py +44 -0
  40. benchset-0.0.2/scripts/package_native.py +78 -0
  41. benchset-0.0.2/scripts/release.py +369 -0
  42. benchset-0.0.2/setup.cfg +4 -0
  43. benchset-0.0.2/setup.py +83 -0
  44. benchset-0.0.2/tests/swift/BenchsetTests.swift +413 -0
  45. benchset-0.0.2/tests/test_benchset.py +189 -0
  46. benchset-0.0.2/tests/test_build.py +223 -0
  47. benchset-0.0.2/tests/test_errors.py +195 -0
  48. benchset-0.0.2/tests/test_release.py +237 -0
  49. benchset-0.0.2/tests/test_resources.py +114 -0
  50. benchset-0.0.2/tests/test_semantics.py +552 -0
@@ -0,0 +1,7 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 3
4
+
5
+ [[package]]
6
+ name = "benchset"
7
+ version = "0.0.2"
@@ -0,0 +1,25 @@
1
+ [package]
2
+ name = "benchset"
3
+ version = "0.0.2"
4
+ edition = "2021"
5
+ rust-version = "1.74"
6
+ description = "Embeddable sustained scalar CPU benchmarks with a C ABI"
7
+ license = "MIT"
8
+ repository = "https://github.com/mutating/benchset"
9
+ readme = "README.md"
10
+ keywords = ["benchmark", "cpu", "ffi", "performance"]
11
+ categories = ["development-tools::profiling"]
12
+ include = ["/Cargo.toml", "/Cargo.lock", "/README.md", "/LICENSE", "/native/**", "/include/**", "/examples/native/**"]
13
+
14
+ [lib]
15
+ path = "native/src/lib.rs"
16
+ crate-type = ["rlib", "staticlib", "cdylib"]
17
+
18
+ [profile.release]
19
+ opt-level = 3
20
+ lto = "thin"
21
+ codegen-units = 1
22
+
23
+ [[example]]
24
+ name = "rust"
25
+ path = "examples/native/rust.rs"
benchset-0.0.2/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 The Mutating Company
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,12 @@
1
+ include setup.py
2
+ include Cargo.toml Cargo.lock rust-toolchain.toml LICENSE README.md
3
+ recursive-include native *.rs
4
+ recursive-include include *.h *.modulemap
5
+ recursive-include scripts *.py
6
+ recursive-include examples *.c *.rs *.swift
7
+ include Package.swift
8
+ recursive-include Sources *.swift
9
+ recursive-include tests *.swift
10
+ prune target
11
+ prune Artifacts
12
+ prune benchset/_native
@@ -0,0 +1,437 @@
1
+ Metadata-Version: 2.4
2
+ Name: benchset
3
+ Version: 0.0.2
4
+ Summary: Sustained scalar CPU benchmarks with an embeddable Rust core
5
+ Author-email: Evgeniy Blinov <zheni-b@yandex.ru>
6
+ Project-URL: Source, https://github.com/mutating/benchset
7
+ Project-URL: Tracker, https://github.com/mutating/benchset/issues
8
+ Keywords: benchmarks
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Operating System :: MacOS :: MacOS X
11
+ Classifier: Operating System :: Microsoft :: Windows
12
+ Classifier: Operating System :: POSIX
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Programming Language :: Python :: 3.15
23
+ Classifier: Programming Language :: Python :: Free Threading
24
+ Classifier: Programming Language :: Python :: Free Threading :: 3 - Stable
25
+ Classifier: License :: OSI Approved :: MIT License
26
+ Classifier: Intended Audience :: Developers
27
+ Classifier: Topic :: Software Development :: Libraries
28
+ Classifier: Typing :: Typed
29
+ Requires-Python: >=3.8
30
+ Description-Content-Type: text/markdown
31
+ License-File: LICENSE
32
+ Requires-Dist: skelet>=0.0.21
33
+ Dynamic: license-file
34
+
35
+ ![logo](https://raw.githubusercontent.com/mutating/benchset/develop/docs/assets/logo_1.svg)
36
+
37
+ Sustained scalar CPU throughput, with an embeddable Rust core, a public C ABI,
38
+ a Python library, a CLI, and a macOS Swift package. No Python interpreter is
39
+ needed by applications embedding the native library.
40
+
41
+ The initial suite contains **`single_core`** (one computational worker) and
42
+ **`multi_core`** (all available computational workers). Both execute the same
43
+ fixed mixed workload and return a score in **completed iterations per second**.
44
+ Higher is faster. There is no reference machine or score normalization.
45
+
46
+ ## Install and run
47
+
48
+ ```sh
49
+ pip install benchset
50
+ benchset
51
+ benchset --bench single_core
52
+ benchset --bench multi_core --warmup 120 --duration 60
53
+ python -m benchset --warmup 0 --duration 1
54
+ ```
55
+
56
+ The last command is a smoke test, not a sustained performance measurement.
57
+ Supported wheels include the native library: Rust is not required at runtime
58
+ or to install a wheel. `pip install .` and source distributions build the Rust
59
+ core automatically. If Cargo is not on PATH, the build downloads the pinned Rust
60
+ version with [rustup](https://rust-lang.github.io/rustup/installation/other.html)
61
+ into a private user cache and reuses it on later builds. No shell profile or
62
+ system PATH changes are made, and no manual environment setup is required.
63
+ The first such build needs internet access. Source compilation still requires
64
+ the platform linker/SDK: macOS Command Line Tools, a Linux C toolchain, or
65
+ Windows MSVC Build Tools with the Windows SDK. Prebuilt wheels need none of
66
+ these build tools.
67
+
68
+ | CLI option | Default | Meaning |
69
+ |---|---:|---|
70
+ | `--bench` | all | `single_core` or `multi_core`; omitted runs both in this order |
71
+ | `--warmup` | 120 | Seconds of continuous workload excluded from the score; zero is allowed |
72
+ | `--duration` | 60 | Seconds of measurement after warmup; must be positive |
73
+ | `--help` | | Show syntax |
74
+ | `--version` | | Show the library/benchmark version |
75
+
76
+ `--bench` is the public spelling of the selector. CLI settings are read through
77
+ `skelet`; configuration files and environment settings are not used.
78
+
79
+ A default full suite takes approximately six minutes. Each benchmark has its
80
+ own warmup. Warmup transitions directly into measurement without stopping the
81
+ workload. Terminal progress is shown on stdout only when attached to a TTY.
82
+ Completed result fields, including all measurement samples, are printed to
83
+ stdout. Failed/cancelled results go to stderr. Exit codes: 0 completed, 1
84
+ execution failure, 2 invalid CLI arguments, 130 interrupted/cancelled. The
85
+ suite stops after a failed or cancelled benchmark.
86
+
87
+ ## Python API
88
+
89
+ Python 3.8–3.15, including free-threaded CPython, is supported. The wrapper uses
90
+ standard-library `ctypes` and loads the version-matched bundled native library.
91
+
92
+ ```python
93
+ from benchset import run, run_all
94
+
95
+ result = run('single_core', warmup_seconds=120, measurement_seconds=60)
96
+ if result.success:
97
+ print(result.score, result.unit)
98
+ else:
99
+ print(result.status, result.reason)
100
+
101
+ results = run_all(warmup_seconds=120, measurement_seconds=60)
102
+ ```
103
+
104
+ For a GUI or cancellable task, run a reusable runner on a background thread:
105
+
106
+ ```python
107
+ from concurrent.futures import ThreadPoolExecutor
108
+ from benchset import BenchmarkRunner
109
+
110
+ runner = BenchmarkRunner()
111
+ with ThreadPoolExecutor(max_workers=1) as pool:
112
+ future = pool.submit(
113
+ runner.run, 'multi_core', warmup_seconds=120, measurement_seconds=60,
114
+ on_progress=lambda p: print(p.phase, p.fraction),
115
+ )
116
+ # A UI handler on another thread may call runner.cancel().
117
+ result = future.result()
118
+ ```
119
+
120
+ Callbacks execute on the thread calling `run`, approximately ten times per
121
+ second and on observed phase transitions. A very short phase can finish before
122
+ the coordinator reports it, so callback sequences may omit that phase.
123
+ Callbacks receive the benchmark name, phase,
124
+ worker count, overall fraction, phase elapsed/target time and completed
125
+ iterations in that phase. Dispatch UI updates to the UI thread yourself and
126
+ return promptly. A Python callback exception cancels the workload and produces
127
+ a failed result; it does not propagate through C.
128
+
129
+ `BenchmarkRunner.cancel()` is thread-safe while a call is active. After it
130
+ returns, all native workers are joined and the runner can be reused. Concurrent
131
+ runs are rejected. The native core enforces one active run per loaded library
132
+ instance, including calls from separate Python runners. Separate OS processes
133
+ or separately linked copies of the library can still compete for CPU time.
134
+
135
+ ## What one iteration does
136
+
137
+ This is a fixed scalar integer workload, not a prediction of arbitrary
138
+ application execution time. Every complete iteration consists of:
139
+
140
+ 1. **Integer arithmetic:** 256 dependent rounds of wrapping 64-bit addition,
141
+ multiplication, XOR, shifts and rotation.
142
+ 2. **Stateful parsing:** a pass over 4,096 bytes of deterministic structured
143
+ input, using data-dependent transitions, digit accumulation, punctuation,
144
+ signs, exponents and invalid-token handling.
145
+ 3. **Indexed data access:** 1,024 dependent indexed reads and modifications of
146
+ a 32,768-element `u64` table (256 KiB). The next index depends on the current
147
+ data and accumulator.
148
+
149
+ The table and parser input belong to each worker. Initialization and allocation
150
+ occur before warmup; initialization time is excluded. The initial worker seeds
151
+ are fixed and distinct. State continues evolving during warmup and measurement.
152
+ Checksums make the computed values observable. The implementation uses explicit
153
+ wrapping operations so overflow has identical semantics across builds.
154
+
155
+ There is one fixed package of each kind per iteration. These are **not equal
156
+ percentages of elapsed time or CPU instructions**. The recipe and data sizes
157
+ remain identical across machines. Single-core uses one worker; multi-core sums
158
+ complete iterations from independent workers without shared data locks in the
159
+ measured kernel. Multicore throughput is measured, not extrapolated from the
160
+ single-core score.
161
+
162
+ Normal release compiler optimization is enabled. There is no `target-cpu=native`,
163
+ inline assembly, manual SIMD, hardware cryptography or CPU-specific dispatch.
164
+ The dependent operations prevent the main loops from becoming independent
165
+ vector workloads. Floating-point, SIMD, GPU, disk and network benchmarks are
166
+ outside this initial suite. Memory capacity is not measured.
167
+
168
+ ## Timing and result interpretation
169
+
170
+ The default is 120 seconds of continuous warmup followed by 60 seconds of
171
+ measurement. Both durations are configurable in Rust, C, Swift, Python and CLI.
172
+ This is a starting point for sustained measurements; it cannot guarantee
173
+ thermal equilibrium on every device. Stable turbo operation is part of the
174
+ measured performance. The library does not change power settings, priorities,
175
+ CPU affinity, background activity or cooling conditions.
176
+
177
+ `multi_core` queries Rust's [`available_parallelism`](https://doc.rust-lang.org/std/thread/fn.available_parallelism.html).
178
+ The returned count is recorded in the result. This portable API is an estimate
179
+ of process-available parallelism; its documented limitations include Windows
180
+ processor groups above 64 logical CPUs and some affinity/quota configurations.
181
+ The library does not reserve cores or model sharing between isolates.
182
+
183
+ The primary score is `iterations / elapsed_seconds`, using monotonic wall time
184
+ after warmup. Workers publish counters in batches of eight iterations; a final
185
+ batch can slightly extend the requested interval. The actual measured duration
186
+ is included and used as the denominator. Worker setup and teardown are excluded.
187
+ Cancellation is checked between batches; completion waits for active workers.
188
+
189
+ The immutable Python result contains:
190
+
191
+ | Field | Meaning |
192
+ |---|---|
193
+ | `benchmark`, `version` | Name and benchmark/library version |
194
+ | `status`, `success`, `reason` | `completed`, `cancelled` or `failed`; explanation on failure |
195
+ | `score`, `unit` | Iterations/s; `score=None` unless completed |
196
+ | `workers` | Number of computational workers |
197
+ | `warmup_seconds`, `measurement_seconds` | Requested windows |
198
+ | `elapsed_seconds`, `iterations` | Actual measured wall time and completed work |
199
+ | `checksum` | XOR of final per-worker checksums; diagnostic, not a score |
200
+ | `samples` | Approximately one-second intervals plus the final partial interval |
201
+ | `variation_percent`, `unstable` | Time-weighted coefficient of variation, and whether it exceeds 10% |
202
+
203
+ Each sample stores cumulative measured elapsed time, interval duration,
204
+ interval iterations and iterations/s. Variation is `None` with fewer than two
205
+ samples. It reports within-run variability, not repeat-to-repeat accuracy.
206
+ An unstable run retains its score and completed status; the consumer decides
207
+ whether to use it. Cancellation/failure can retain partial diagnostics but
208
+ never produces a usable score.
209
+
210
+ The Python package version is defined in `pyproject.toml`. `benchset.__version__`
211
+ and result objects read the installed package metadata through
212
+ `importlib.metadata`; no Python source version constant needs updating.
213
+ The native library shares this version; builds check its Cargo metadata for
214
+ consistency. Consumers decide which versions and measurement settings are comparable.
215
+
216
+ ## Embed in Rust
217
+
218
+ ```toml
219
+ [dependencies]
220
+ benchset = "0.0.1"
221
+ # Before registry publication, use:
222
+ # benchset = { git = "https://github.com/mutating/benchset.git" }
223
+ ```
224
+
225
+ ```rust
226
+ use benchset::{run, Benchmark, Cancellation, Options};
227
+ let cancellation = Cancellation::default();
228
+ let result = run(Benchmark::MultiCore, Options::default(), &cancellation,
229
+ |progress| println!("{:?}: {}", progress.phase, progress.fraction));
230
+ println!("{:?}", result.score);
231
+ ```
232
+
233
+ The cancellation token can be cloned and cancelled from another thread. It is
234
+ sticky: create a new token for a new call. A short runnable example is in
235
+ `examples/native/rust.rs`.
236
+
237
+ ## Embed through C ABI
238
+
239
+ Download the archive for the target OS/architecture from GitHub Releases, or
240
+ build locally:
241
+
242
+ ```sh
243
+ cargo build --release --locked
244
+ ```
245
+
246
+ Artifacts include `.a`/`.lib` for static linking and `.dylib`/`.so`/`.dll` for
247
+ dynamic loading. `include/benchset.h` documents structures, status codes and
248
+ lifetime rules. `examples/native/c.c` demonstrates configuration and progress.
249
+
250
+ ```c
251
+ BenchsetConfig config;
252
+ benchset_default_config(&config);
253
+ config.benchmark = BENCHSET_MULTI_CORE;
254
+ BenchsetHandle *handle = benchset_create();
255
+ if (handle) {
256
+ benchset_run(handle, &config, callback, context);
257
+ BenchsetResult result;
258
+ if (benchset_result(handle, &result) && result.status == BENCHSET_COMPLETED) {
259
+ /* result.score is iterations/s */
260
+ }
261
+ benchset_free(handle);
262
+ }
263
+ ```
264
+
265
+ Call `benchset_cancel(handle)` from another thread or a progress callback.
266
+ Handles are single-use. Getters are used after `benchset_run` returns. Strings
267
+ are borrowed until the handle is freed; progress pointers are borrowed only
268
+ during the callback. Do not free a handle while it is in use or throw/unwind
269
+ through a C callback. Invalid configuration produces a failed result.
270
+
271
+ When statically linking Rust into a foreign application, system libraries may
272
+ also be required. Obtain their list with
273
+ `cargo rustc --release -- --print=native-static-libs`. Dynamic consumers must
274
+ ship the matching platform library with their application. Source consumers
275
+ control their compiler/toolchain settings; use our release artifacts for a
276
+ consistent build policy.
277
+
278
+ ## Embed in Swift on macOS
279
+
280
+ In Xcode, add `https://github.com/mutating/benchset.git` as a package dependency
281
+ and select a released version. Release tags contain a `Package.swift` that
282
+ downloads the universal ARM64/Intel static XCFramework from that version's
283
+ GitHub Release and verifies its SHA-256 checksum. The Swift wrapper lives in
284
+ the same repository. Rust is not required by the consumer.
285
+
286
+ For a Swift manifest, add:
287
+
288
+ ```swift
289
+ .package(url: "https://github.com/mutating/benchset.git", .exact("0.0.1"))
290
+ ```
291
+
292
+ Add `.product(name: "Benchset", package: "benchset")` to your target's
293
+ dependencies. Pinning a version also pins the benchmark implementation.
294
+
295
+ For offline use, download and extract **`Benchset-Swift.zip`** from the release
296
+ and add the extracted directory as a local package. It contains the wrapper,
297
+ local manifest and XCFramework. Development branches use the local artifact
298
+ path and need the build step below; release tags use the remote binary target.
299
+
300
+ ```swift
301
+ import Benchset
302
+ let runner = BenchmarkRunner()
303
+ // Execute from a background queue; dispatch progress updates to the UI queue.
304
+ let result = try runner.run(.multiCore, warmupSeconds: 120, measurementSeconds: 60) {
305
+ progress in print(progress.fraction)
306
+ }
307
+ // Another thread may call runner.cancel() while run is active.
308
+ ```
309
+
310
+ To build the Apple package from source:
311
+
312
+ ```sh
313
+ rustup target add aarch64-apple-darwin x86_64-apple-darwin
314
+ python scripts/package_native.py --target aarch64-apple-darwin --apple
315
+ swift build
316
+ ```
317
+
318
+ This requires a macOS SDK and `lipo`. The packager generates the XCFramework
319
+ with standard bundle metadata and C headers; no Xcode project is required.
320
+ The Swift test suite uses Swift Testing (Swift 6 or newer). Run it with
321
+ `python scripts/check_native_coverage.py swift`; it also works with current
322
+ Command Line Tools, without full Xcode. The test executable targets macOS 13
323
+ while the library keeps the deployment target declared in `Package.swift`.
324
+
325
+ ## Platforms and development
326
+
327
+ Release builds target macOS ARM64/Intel, Linux x86-64/ARM64 with glibc, and
328
+ Windows x86-64 MSVC. Native baseline targets are macOS 11 ARM64 / 10.12 Intel,
329
+ Linux glibc 2.17 and Windows 10, following ordinary Rust target requirements.
330
+ Older-OS execution still needs platform testing; the installed Python version
331
+ or a host application may impose a newer minimum. Linux musl and Windows ARM64
332
+ are not part of the initial prebuilt matrix.
333
+
334
+ ```sh
335
+ python -m venv .venv
336
+ # Activate the virtual environment using your shell's normal command.
337
+ pip install -r requirements_dev.txt 'skelet>=0.0.21' wheel setuptools
338
+ pip install .
339
+ python scripts/build_native.py
340
+ python -m pytest
341
+ cargo test -- --test-threads=1
342
+ python -m build
343
+ ```
344
+
345
+ `scripts/build_native.py` makes the source Python package usable without an
346
+ installation. Release wheels are platform-specific but interpreter-independent
347
+ (`py3-none-<platform>`). The sdist includes the native source. Tests validate
348
+ correctness, result accounting, progress, cancellation and API/CLI behavior;
349
+ they do not enforce speed thresholds in CI.
350
+
351
+ ## Test coverage
352
+
353
+ The [test matrix](tests/coverage-plan.md) combines five independent reviews and
354
+ covers measurement accounting, statistical boundaries, cancellation, reentry,
355
+ callback failures, C ABI validation, ownership and CLI behavior. Tests use
356
+ controlled initialization failures instead of exhausting machine resources.
357
+
358
+ Python coverage includes statements and branches, including CLI subprocesses;
359
+ CI installs a coverage startup hook for those subprocesses. Rust and Swift each
360
+ have independent 100% line/function/region/instantiation gates. Their current
361
+ LLVM reports do not provide branch counters, so no separate native branch
362
+ percentage is claimed. Only benchmark runtime and wrappers are counted;
363
+ build/release utilities and tests are outside those coverage percentages.
364
+
365
+ ```bash
366
+ rustup component add llvm-tools-preview
367
+ python scripts/check_native_coverage.py rust
368
+ python scripts/check_native_coverage.py swift
369
+ ```
370
+
371
+ Build the local XCFramework first as described above before the Swift command.
372
+ The checker uses the pinned Rust toolchain's LLVM tools; `--llvm-tools DIR`
373
+ can point to those tools when they are unpacked separately. Reports are written
374
+ to `target/coverage/{rust,swift}`. CI runs Rust coverage on Linux, Windows and
375
+ both Mac architectures, and Swift coverage on both Mac architectures.
376
+
377
+ ## Publishing
378
+
379
+ All distributions are released from this repository by
380
+ `.github/workflows/release.yml`. A push to `main` checks the common package
381
+ version. An unreleased version is built and published automatically; a version
382
+ whose managed GitHub Release is already published is skipped. To release a new
383
+ version, update `Cargo.toml` and `pyproject.toml` together,
384
+ run `cargo generate-lockfile` to update `Cargo.lock`, commit the changes and
385
+ push to `main`. Use an `X.Y.Z` version. Do not create or move release tags
386
+ manually.
387
+
388
+ The workflow performs these steps:
389
+
390
+ 1. Test the Python API/CLI and release tooling on Linux, macOS and Windows;
391
+ test Rust and build/install wheels across all five platform targets.
392
+ 2. Build native archives, the offline Swift package and the universal
393
+ XCFramework. Release compilation uses Rust **1.94.1** throughout, including
394
+ the Linux containers; update the pin and package version together when
395
+ changing the release toolchain.
396
+ 3. Compute the XCFramework archive checksum and create a detached release
397
+ commit that changes only `Package.swift`. Its parent is the tested source
398
+ commit. `main` keeps its local development manifest.
399
+ 4. Build the sdist and crate from that exact release commit, then retain the
400
+ complete, checksummed release bundle as a GitHub Actions artifact.
401
+ 5. Push `v<version>` at the prepared commit and populate a draft GitHub Release
402
+ with the wheels, sources, native archives and checksum manifest.
403
+ 6. Publish to PyPI and crates.io, then publish the populated GitHub Release.
404
+ The Swift dependency URL is this repository and the same version tag.
405
+
406
+ The tag and publications happen in the same workflow run: tags pushed using
407
+ `GITHUB_TOKEN` do not trigger another release run. Only the publication job
408
+ uses the `release` GitHub environment, OIDC `id-token: write` and
409
+ `contents: write`; no additional Swift repository or personal token is needed.
410
+ Repository tag rules must allow this workflow to create the version tag.
411
+ The Git repository and release assets must be accessible to Swift consumers.
412
+
413
+ One-time publisher setup:
414
+
415
+ * **PyPI:** configure the Trusted Publisher for owner `mutating`, repository
416
+ `benchset`, workflow filename `release.yml` and environment `release`.
417
+ An already configured publisher with these values continues to work.
418
+ * **crates.io:** sign in with GitHub, verify the account email, and create an
419
+ API token with publishing permission. Store it as the **repository secret**
420
+ `CARGO_REGISTRY_TOKEN`. Missing credentials stop publication before a tag is
421
+ created. Rust consumers do not need an account. See the
422
+ [Cargo publishing guide](https://doc.rust-lang.org/cargo/reference/publishing.html).
423
+
424
+ To validate packaging without publishing, run the **Release** workflow
425
+ manually with `publish` left unchecked. To retry an incomplete release, run it
426
+ from `main` with `publish` checked, or rerun the failed publication job. If its
427
+ managed tag already exists, the workflow retrieves the original frozen bundle
428
+ using the workflow run ID recorded in the release commit; it does not rebuild
429
+ or move the tag. Uploaded PyPI/crates.io files are skipped only after their
430
+ checksums match. GitHub asset replacement is limited to the draft release.
431
+ Once all channels succeed, another push of the same version performs no uploads.
432
+
433
+ Frozen Actions bundles are retained for 90 days (subject to repository retention
434
+ settings). Resume an incomplete publication while its original bundle is
435
+ available. Code changes after a frozen release require a new version; a retry
436
+ continues to publish the original source snapshot. Concurrent release runs are
437
+ serialized. Local builds and build-only workflow runs do not publish anything.
@@ -0,0 +1,17 @@
1
+ // swift-tools-version:5.3
2
+ import PackageDescription
3
+
4
+ // Development checkouts and the offline ZIP use a locally built XCFramework.
5
+ // Release tags replace this binary target with an immutable URL and checksum.
6
+ let package = Package(
7
+ name: "Benchset",
8
+ platforms: [.macOS(.v10_12)],
9
+ products: [.library(name: "Benchset", targets: ["Benchset"])],
10
+ targets: [
11
+ .binaryTarget(name: "CBenchset",
12
+ url: "https://github.com/mutating/benchset/releases/download/v0.0.2/Benchset.xcframework.zip",
13
+ checksum: "29af959b4b1604abb0365f197cfceb73b7c54dc1713e24541b82b04c1956268f"),
14
+ .target(name: "Benchset", dependencies: ["CBenchset"]),
15
+ .testTarget(name: "BenchsetTests", dependencies: ["Benchset"], path: "tests/swift")
16
+ ]
17
+ )