Compare commits

...

145 Commits

Author SHA1 Message Date
Mayukha Vadari
537c520e32 Merge branch 'develop' into mvadari/test-debugging 2026-06-24 20:51:37 -04:00
Mayukha Vadari
310bfc7b94 fix: Improve test debuggability 2026-06-24 20:44:47 -04:00
Mayukha Vadari
afc0b7ab8c add build errors 2026-06-24 20:14:13 -04:00
Michael Legleux
556d62a0de build: Align xrpld RPM packaging with DEB package (#7529) 2026-06-24 23:53:46 +00:00
Ayaz Salikhov
eef8f4a4ff chore: Use clang-tidy v22 new features (#7427) 2026-06-24 17:23:29 +00:00
Ayaz Salikhov
4fec58251b build: Patch nix binaries in CMake (#7539)
Co-authored-by: Bart <bthomee@users.noreply.github.com>
2026-06-24 13:56:18 +00:00
Ayaz Salikhov
8bbbc2051e chore: Check more tools to be available (#7600) 2026-06-24 12:25:03 +00:00
Mayukha Vadari
6736ab39df test: Add test for Permissioned Domain sequence fix (#7591)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-24 12:24:27 +00:00
Ayaz Salikhov
b68e1f7170 fix: Add pragma once checker (#7580) 2026-06-24 12:24:04 +00:00
Timothy Banks
bb7c4d1c9f fix: Additional RPC validation checks on ammRpcInfo account and amm_account fields. (#7324) 2026-06-24 12:23:12 +00:00
Zhiyuan Wang
69d289a388 fix: AMM Quality Leak into Domain BookStep for Permissioned DEX (#6853)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-24 12:15:45 +00:00
Jingchen
6341e75200 refactor: Refactor TaggedCache.ipp to remove const_cast in canonicalize_replace_cache (#5638)
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-06-24 12:15:11 +00:00
yinyiqian1
5a2c82f699 fix: Reject delegate permission to pseudo accounts (#7597) 2026-06-23 19:55:23 +00:00
Jingchen
0b22050b5e ci: Update workflows and conan to use VS2026 and grpc 1.81.0 (#7550)
Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
2026-06-23 19:25:38 +00:00
Bart
ff02269c0d refactor: Use dispatch instead of post (#7438)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-06-22 22:35:28 +00:00
Mayukha Vadari
dd7401fde2 refactor: Clean up tec object deletion logic (#6588)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-06-22 18:44:42 +00:00
Zhiyuan Wang
19a9ed7767 fix: Move AMMInvariant weakInvariantCheck logic into the transaction (#7032) 2026-06-22 18:42:57 +00:00
Zhiyuan Wang
93eab33dc2 fix: Improve ValidAMM invariant (#7295) 2026-06-22 17:45:42 +00:00
yinyiqian1
997267f845 feat: Remove clear mutable flags for DynamicMPT XLS-94 (#7439) 2026-06-22 17:36:06 +00:00
Ayaz Salikhov
e29b523620 ci: Build and push docker images in forks too (#7588) 2026-06-22 17:00:40 +00:00
dependabot[bot]
b1f794f067 ci: [DEPENDABOT] bump actions/checkout from 6.0.3 to 7.0.0 (#7585)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-22 13:39:38 +00:00
Michael Legleux
b6a1ad5bb3 fix: Ensure xrpld service directories exist at startup (#7565) 2026-06-18 19:21:12 +00:00
yinyiqian1
772ea80a25 fix: Use template for granular delegation permissions (#6613)
Co-authored-by: Bart <bthomee@users.noreply.github.com>
2026-06-17 23:20:54 +00:00
solunolab
480676d0bf docs: Fix some comments to improve readability (#7405)
Signed-off-by: solunolab <solunolab@outlook.com>
Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
2026-06-17 13:55:00 +00:00
Michael Legleux
f07de6c454 ci: Disable assertions on Release builds (#7443) 2026-06-17 13:54:55 +00:00
Ayaz Salikhov
cb2642be05 build: Add graphviz to Nix images (#7566) 2026-06-17 13:54:46 +00:00
Pratik Mankawde
7e0ff536f5 refactor: Rerevert "Explicitly trim the heap after cache sweeps (#6022)"
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-17 13:31:43 +01:00
Pratik Mankawde
044ca7719d release: Bump version to 3.3.0-b0
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-17 12:58:01 +01:00
Pratik Mankawde
cccce1c32e Merge remote-tracking branch 'origin/release/3.2.x' into pratik/merge_3.2.x
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-17 12:53:02 +01:00
Ayaz Salikhov
5de434436e ci: Make clang-tidy workflow adjustments to stay in sync with Clio (#7563) 2026-06-17 10:02:17 +00:00
Ayaz Salikhov
45ddc1d868 build: Add git-lfs to Nix images (#7561) 2026-06-16 23:13:33 +00:00
Ayaz Salikhov
7b9d55326d build: Add zip to Nix images (#7551) 2026-06-16 17:35:33 +00:00
Ayaz Salikhov
0364e4dc41 docs: Rewrite build environment docs (#7533)
Co-authored-by: Ed Hennis <ed@ripple.com>
2026-06-16 13:24:12 +00:00
Ayaz Salikhov
3c43f4614f release: Bump version to 3.2.0 2026-06-15 19:46:56 -04:00
dependabot[bot]
6b63f0ff61 ci: [DEPENDABOT] bump codecov/codecov-action from 6.0.1 to 7.0.0 (#7426)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 19:46:49 -04:00
Bart
0ac8e6cf1e release: Bump version to 3.2.0-rc6 2026-06-15 22:24:03 +01:00
Vito Tumas
ed5f13481a fix: Disable transaction invariants 2026-06-15 22:24:03 +01:00
Vito Tumas
781ef175c9 perf: Dispatch "hasInvalidAmount()" on type tag instead of dynamic_cast 2026-06-15 22:24:03 +01:00
Ed Hennis
e5785c4fcb fix: Fix Number comparison operator 2026-06-15 22:24:02 +01:00
Michael Legleux
96d0563ea6 fix: Adjust xrpld systemd service 2026-06-15 22:24:02 +01:00
Bart
61dae6f792 release: Bump version to 3.2.0-rc5 2026-06-15 22:24:02 +01:00
yinyiqian1
fded06652a fix: Add zero NFT Offer ID check for NFTokenCancelOffer 2026-06-15 22:24:02 +01:00
Valentin Balaschenko
e833e8884d refactor: Revert "Explicitly trim the heap after cache sweeps (#6022)" 2026-06-15 22:24:02 +01:00
Michael Legleux
8e3eabc398 refactor: Remove auto-update script and update RPM version
* refactor: Update RPM version scheme; remove auto-update script; service hardening

- **RPM version scheme**: pre-releases now use `~` in the `Version` field instead of the `0.<release>.<suffix>` `Release`-field hack. Matches Debian's `~` convention, so RPM and DEB version strings are symmetric. Requires rpm ≥ 4.10 (RHEL 9 ships 4.17).

  Before/after for a pre-release build:
  ```
  # before
  xrpld-3.2.0-0.1.rc3+202606011647.d4cb68d5.el9.x86_64.rpm

  # after (symmetric with DEB)
  xrpld-3.2.0~rc2+202606010139.7679a310-1.el9.x86_64.rpm
  xrpld_3.2.0~rc2+202606010139.7679a310-1_amd64.deb
  ```
- **Auto-update removed**: `update-xrpld`, `update-xrpld.service`, and `update-xrpld.timer` deleted. The `50-xrpld.preset` `disable` line for the timer is dropped too.
- **Service hardening** (two new `[Service]` directives in `xrpld.service`):
  - `CapabilityBoundingSet=CAP_NET_BIND_SERVICE` — drops every Linux capability except `CAP_NET_BIND_SERVICE`, capping the privilege ceiling to least-privilege while still letting operators bind ports <1024 (e.g. WS/HTTPS on 443).
  - `SystemCallArchitectures=native` — restricts the service to the native syscall ABI, blocking alternate-ABI (32-bit/x32) syscalls used to evade seccomp filtering.

- [ ] Build RPM from a pre-release version (e.g. `3.2.0-b1`) and confirm `rpm -qi` shows `Version: 3.2.0~b1`, `Release: 1`
- [ ] Confirm `3.2.0~b1` sorts before `3.2.0` via `rpmvercmp`
- [ ] Install package and confirm no `update-xrpld*` units appear in `systemctl list-unit-files`
- [ ] Confirm `systemctl show xrpld` reflects the new `CapabilityBoundingSet` and `SystemCallArchitectures`

* fix: Track tmpfiles-created directories in RPM %files as %ghost
2026-06-15 22:24:02 +01:00
Sergey Kuznetsov
47b06ecd17 refactor: Use rocksdb includes only when it is available 2026-06-15 22:23:54 +01:00
Bart
5a25c9188b release: Bump version to 3.2.0-rc4 2026-06-15 22:23:53 +01:00
Bart
82ee5b7556 refactor: Handle int and uint API versions separately 2026-06-15 22:23:38 +01:00
Pratik Mankawde
f98c251011 refactor: Improve tracking of book (un)subscriptions 2026-06-15 22:23:38 +01:00
Sergey Kuznetsov
e29dc474b3 refactor: Improve payment channel closing and returned error codes 2026-06-15 22:23:28 +01:00
Pratik Mankawde
2728e11809 fix: Set request size limits and differential pricing for get-object-by-hash calls 2026-06-15 22:23:28 +01:00
Jingchen
9650fe8a6e refactor: Use explicit types to help compiler 2026-06-15 22:22:53 +01:00
Pratik Mankawde
2df96b1550 fix: Silence UBSan diagnostics in the ubsan build config (#7531)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 19:25:37 +00:00
Ayaz Salikhov
fe4c8ae82a build: Add ClangBuildAnalyzer to Nix (#7538)
Co-authored-by: Bart <bthomee@users.noreply.github.com>
2026-06-15 19:04:33 +00:00
Zhiyuan Wang
b34aa84e5a fix: Check Fee-Free Division by Zero in AMMWithdraw singleWithdrawEPrice (#6989) 2026-06-15 15:31:22 +00:00
Bart
f5985e73ec fix: Always charge peer on strand (#7422)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-06-15 14:55:56 +00:00
Sergey Kuznetsov
4387aac1a5 chore: Remove conan patch in nix (#7534) 2026-06-15 14:55:43 +00:00
Pratik Mankawde
df395d6851 test: Add null check unit test for Oracle::aggregatePrice (#7306)
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-11 18:05:36 +00:00
Ayaz Salikhov
8e618d68cd ci: Patch conan recipe for Nix to be able to use on macOS (#7532) 2026-06-11 17:36:33 +00:00
Ayaz Salikhov
cee157485e ci: Run sanitizers on release builds too (#7527) 2026-06-11 12:59:22 +00:00
Zhiyuan Wang
09c36d066e fix: Correct hybrid offer deletion on credential expiry (#6843)
Co-authored-by: Bart <bthomee@users.noreply.github.com>
2026-06-10 20:42:41 +00:00
Ayaz Salikhov
2f6b466feb ci: Make sanitizer flags lists in the profile, not a string (#7449) 2026-06-10 18:24:34 +00:00
Ayaz Salikhov
8000adfa79 ci: Make configurations launch on certain event types (#7447) 2026-06-10 18:08:34 +00:00
Shi Cheng
1f359f719c fix: Add [[maybe_unused]] to fix320Enabled for assert=OFF builds (#7446)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 17:24:44 +00:00
Ayaz Salikhov
dd0b6754d4 ci: Add gh and file to nix packages (#7444) 2026-06-10 14:45:51 +00:00
Vito Tumas
83cc5df72e fix: Disable transaction invariants (#7409) 2026-06-10 12:05:53 +00:00
Vito Tumas
97ca7d57bc perf: Dispatch "hasInvalidAmount()" on type tag instead of dynamic_cast (#7402) 2026-06-10 11:44:57 +00:00
Pratik Mankawde
8a4bf2dee6 refactor: Retire fixUniversalNumber amendment (#5962)
Signed-off-by: Pratik Mankawde <pmankawde@ripple.com>
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-10 10:16:03 +00:00
Bart
742aa0878b test: Do not create data directory for memory databases (#7323)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-06-10 09:16:53 +00:00
Ayaz Salikhov
8617eaeb26 ci: Launch upload-conan-deps on profile change (#7442) 2026-06-10 00:00:19 +00:00
Ed Hennis
2cbc3c139e fix: Fix Number comparison operator (#7406) 2026-06-09 17:46:56 +00:00
Ayaz Salikhov
fccb109e48 feat: Use C++ 23 standard (#7431) 2026-06-09 17:36:17 +00:00
Vito Tumas
0fb1aca461 refactor: Introduce XRPL_ASSERT_IF for amendment-gated assertions (#7378)
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-06-09 17:02:06 +00:00
Bart
c552eb333f refactor: Change config section and key string literals into constants (#7095)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-06-09 14:58:21 +00:00
Bart
c9769d1add refactor: Use std::move and std::string_view where possible (#7424)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-06-09 13:56:32 +00:00
Bart
ee9fbc4e08 refactor: Use const function arguments where possible (#7423)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-06-09 10:04:09 +00:00
Ayaz Salikhov
577d7457f1 ci: Use XRPLF/actions build-multiarch-image workflow (#7428) 2026-06-08 17:10:05 +00:00
Ayaz Salikhov
a389f922dd ci: Use new packaging images and don't cancel develop builds (#7417)
Co-authored-by: Bart <bthomee@users.noreply.github.com>
2026-06-08 13:41:08 +00:00
dependabot[bot]
79f4ddc4a6 ci: [DEPENDABOT] bump codecov/codecov-action from 6.0.1 to 7.0.0 (#7426)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-08 09:37:50 +00:00
Ayaz Salikhov
949887feb9 build: Create single test binary xrpl_tests (#7327) 2026-06-05 19:24:32 +00:00
dependabot[bot]
fc57dab78b ci: [DEPENDABOT] bump actions/checkout from 6.0.2 to 6.0.3 (#7414)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-05 17:17:47 +00:00
Ayaz Salikhov
63ffdc39dc ci: Refactor build-related nix / docker / workflows (#7408) 2026-06-05 17:05:19 +00:00
Ayaz Salikhov
6571f75d39 ci: Use multiple directories in dependabot config (#7413) 2026-06-05 14:36:05 +00:00
Ayaz Salikhov
2111bb4b95 ci: Update clang-tidy to nix-based v22 (#7412) 2026-06-05 14:11:47 +00:00
Ayaz Salikhov
8abe82eefa ci: Redesign matrix configuration based on Nix images (#7385)
Co-authored-by: semgrep-companion-app[bot] <218312740+semgrep-companion-app[bot]@users.noreply.github.com>
2026-06-04 20:02:59 +00:00
Ayaz Salikhov
5b8e6cd1dd test: Fix LCOV_EXCL_END -> LCOV_EXCL_STOP (#7407) 2026-06-04 19:35:36 +00:00
Ayaz Salikhov
12e81abef3 ci: Improve sanitizer-libs, add doxygen, dpkg, rpm in nix (#7403) 2026-06-04 14:52:42 +00:00
Ayaz Salikhov
6c543426c3 ci: Fix clang asan include dirs in nix images, add curl & gnupg (#7400) 2026-06-03 22:19:15 +00:00
yinyiqian1
e5cf1a0985 fix: Add zero NFT Offer ID check for NFTokenCancelOffer (#7391) 2026-06-03 19:30:20 +00:00
Ayaz Salikhov
023bdaeeed ci: Install gcov, nettools, cacert in nix images (#7398) 2026-06-03 19:14:17 +00:00
Bart
96b2c0964f refactor: Replace intr_ptr::SharedPtr<SHAMapTreeNode> by SHAMapTreeNodePtr (#7396)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-06-03 15:34:19 +00:00
Ayaz Salikhov
1441d4690d chore: Update flake.lock to allow conan with clang-22 support (#7390) 2026-06-03 00:16:02 +00:00
Vito Tumas
225ed204ad test: Suppress invariant-failure logs in Vault and LoanBroker bug-regression tests (#7379) 2026-06-02 17:12:09 +00:00
Ayaz Salikhov
ad111bcc22 ci: Patch binaries in nix-based images and test in every distro (#7376)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-02 13:51:20 +00:00
Ayaz Salikhov
d4cb68d5a1 ci: Check binaries separately from building them (#7355)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-01 16:47:01 +00:00
dependabot[bot]
e209ee5371 ci: [DEPENDABOT] bump eps1lon/actions-label-merge-conflict from 3.0.3 to 3.1.0 (#7375)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-01 15:29:12 +00:00
Vito Tumas
109b649106 refactor: Use STLedgerEntry type aliases instead of std::shared_ptr (#7282) 2026-06-01 15:27:13 +00:00
Michael Legleux
0fffe23abc fix: Adjust xrpld systemd service and update timer (#7374) 2026-06-01 03:33:19 +00:00
Bart
7e15621e7b release: Bump version to 3.2.0-rc3 (#7371)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-05-31 22:55:18 +00:00
Vito Tumas
99431d7833 fix: Pin overpayment principal reduction to exact on-grid value (#7360) 2026-05-31 22:54:23 +00:00
Ed Hennis
47365f4220 fix: Improve upward rounding edge cases for Number::operator/= (#7328)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com>
2026-05-31 00:23:29 +00:00
Bart
1599c1a672 refactor: Revert "perf: Remove unnecessary caches (#5439)" (#7359)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-05-30 18:48:59 +00:00
yinyiqian1
763dd503be fix: Add zero domainID check for permissionedDomain (#7362) 2026-05-30 00:16:25 +00:00
Bart
2f3558c610 ci: Run PR title and description checks on staging and release branches (#7331)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-05-28 14:57:29 +00:00
Ayaz Salikhov
f9551ac5ca style: Run shfmt on workflows, actions and markdown bash code (#7333) 2026-05-27 19:24:18 +00:00
Bart
1acc42313c release: Bump version to 3.2.0-rc2 (#7348) 2026-05-27 15:11:38 -04:00
Bart
396d772a15 refactor: Enable support for fixCleanup3_2_0 amendment (#7347) 2026-05-27 19:10:33 +00:00
Ayaz Salikhov
1438bf1c67 release: Bump version to 3.2.0-rc1 (#7335) 2026-05-27 13:20:57 -04:00
Ed Hennis
7da643d864 fix: Fix a rounding error at the Number::maxRep cusp (#7051)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com>
2026-05-27 15:19:20 +00:00
Ayaz Salikhov
1162371def ci: Only push docker images in XRPLF/rippled (#7330) 2026-05-26 20:03:04 +00:00
dependabot[bot]
2a0feca46b ci: [DEPENDABOT] bump docker/setup-buildx-action from 4.0.0 to 4.1.0 (#7322)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-26 19:36:32 +00:00
dependabot[bot]
108a4c8217 ci: [DEPENDABOT] bump codecov/codecov-action from 6.0.0 to 6.0.1 (#7321)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-26 19:36:21 +00:00
dependabot[bot]
4584b01bde ci: [DEPENDABOT] bump docker/build-push-action from 7.1.0 to 7.2.0 (#7320)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-26 19:36:13 +00:00
dependabot[bot]
7c59786565 ci: [DEPENDABOT] bump docker/metadata-action from 6.0.0 to 6.1.0 (#7319)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-26 19:36:00 +00:00
dependabot[bot]
9623e67b76 ci: [DEPENDABOT] bump docker/login-action from 4.1.0 to 4.2.0 (#7318)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-26 19:35:52 +00:00
Andrzej Budzanowski
85af406a0f fix: Update clang-tidy to include src/tests directory header check (#7307) 2026-05-26 19:35:32 +00:00
Ayaz Salikhov
ac33fb32a7 chore: Pin Python packages for codegen using uv (#7329) 2026-05-26 18:35:38 +00:00
Ayaz Salikhov
23d0812827 style: Use shfmt instead of bashate (#7326) 2026-05-26 18:28:23 +00:00
Vito Tumas
49567e7283 fix: Fix edge-case where vault-depositor may get stuck (#7139) 2026-05-26 18:18:40 +00:00
Vito Tumas
633ef4706f fix: Fix VaultInvariant and VaultDeposit precision bugs at IOU scale boundaries (#7272)
Co-authored-by: Bart <bthomee@users.noreply.github.com>
2026-05-26 16:32:44 +00:00
Ayaz Salikhov
49cb3f45a4 ci: Add clang to nix images (#7308)
Co-authored-by: semgrep-companion-app[bot] <218312740+semgrep-companion-app[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 15:45:33 +00:00
Vito Tumas
22a21b175e fix: Include management-fee delta in doOverpayment assertion (#7039) 2026-05-26 14:01:52 +00:00
Pratik Mankawde
e9d885bd9b fix: Fix clang-tidy pre-commit hook to locate compile_commands.json from repo root (#7325)
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-05-26 13:50:18 +00:00
Jingchen
a911f9089e fix: Use consistent scale for debtTotal (#7093)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-24 20:44:29 +00:00
Peter Chen
e34c2667d7 fix: Skip deleted book directories and non-root modifications in ValidBookDirectory invariant (#7312) 2026-05-24 20:37:16 +00:00
Valentin Balaschenko
30de556224 fix: Address review feedback on FD/handle guarding (#5823 follow-up) (#7310) 2026-05-23 14:48:48 +00:00
Gregory Tsipenyuk
dcd2ff0b5f fix: Fix non-canonical MPT amount (#7117)
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-23 06:40:26 +00:00
Bart
dfb9b8ed9a release: Bump version to 3.2.0-b7 (#7316)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-05-22 19:32:12 +00:00
Jingchen
179e73594a fix: Check if the MPT first loss cover can be sent to the broker before deleting the broker (#7125)
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-05-22 11:58:48 +00:00
Michael Legleux
15dd653e4b fix: Fix RPM prerelease ordering and start xrpld on DEB install (#7313) 2026-05-22 11:30:45 +00:00
Michael Legleux
a37afe13ff ci: Re-enable full nproc for Linux (#7315) 2026-05-22 11:30:37 +00:00
Gregory Tsipenyuk
3547a9335f fix: Add assorted MPT/DEX fixes (#7040)
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
Co-authored-by: Shawn Xie <35279399+shawnxie999@users.noreply.github.com>
2026-05-21 18:29:53 +00:00
Bart
1a98182e23 refactor: Remove dead fetchBatch code (#7309)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-05-21 17:52:41 +00:00
Bart
79308705c5 release: Bump version to 3.2.0-b6 (#7311)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-05-21 17:50:59 +00:00
Vito Tumas
e24de65f42 chore: Revert graceful peer disconnection and follow-up fix (#7296) 2026-05-21 16:13:41 +00:00
Vito Tumas
7fdaa0a5ef fix: Fix IOU precision issues in LoanBrokerCover transactions (#7274) 2026-05-21 14:51:58 +00:00
Vito Tumas
795dc5e364 fix: Avoid principal-zeroing in non-final loan payments at coarse scale (#7050)
Co-authored-by: Ed Hennis <ed@ripple.com>
2026-05-21 14:46:26 +00:00
Pratik Mankawde
f6fd5ddb0a fix: Add null check (#7305)
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-21 13:24:04 +00:00
Rithvik Reddygari
afcf6fbcdc docs: Add --parallel flag to cmake build commands in BUILD.md (#7302) 2026-05-21 06:33:19 +00:00
Shawn Xie
28cc20c816 fix: Fix wrong hybrid offer orderbook placement and update LedgerStateFix to amend ExchangeRate meta (#7087)
Co-authored-by: Peter Chen <ychen@ripple.com>
2026-05-21 06:19:04 +00:00
Alex Kremer
a830ab10ef style: More clang-tidy identifier renaming (#7290) 2026-05-20 21:31:15 +00:00
Shawn Xie
8c0080020f fix: Update pDEX invariant firing under a valid offer deletion (#7118)
Co-authored-by: Peter Chen <ychen@ripple.com>
2026-05-20 21:10:04 +00:00
yinyiqian1
9cb0740673 fix: Fix multisign and signfor to check for delegate (#7064) 2026-05-20 20:24:09 +00:00
Mayukha Vadari
242ce3e9e4 refactor: Fix sfGeneric and sfInvalid field names (#7300) 2026-05-20 19:47:59 +00:00
box4wangjing
a5d238e7d4 docs: Fix some comments to improve readability (#7122)
Signed-off-by: box4wangjing <box4wangjing@outlook.com>
Co-authored-by: Mayukha Vadari <mvadari@ripple.com>
2026-05-20 19:46:45 +00:00
Vito Tumas
9cb049276d feat: Propagate underlying MPT flags to vault shares (#7077)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Denis Angell <dangell@transia.co>
Co-authored-by: Fomo <508629+shortthefomo@users.noreply.github.com>
Co-authored-by: Bart <bthomee@users.noreply.github.com>
Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-20 19:44:09 +00:00
836 changed files with 23980 additions and 13528 deletions

View File

@@ -11,6 +11,7 @@ Checks: "-*,
bugprone-copy-constructor-init, bugprone-copy-constructor-init,
bugprone-crtp-constructor-accessibility, bugprone-crtp-constructor-accessibility,
bugprone-dangling-handle, bugprone-dangling-handle,
bugprone-derived-method-shadowing-base-method,
bugprone-dynamic-static-initializers, bugprone-dynamic-static-initializers,
bugprone-empty-catch, bugprone-empty-catch,
bugprone-fold-init-type, bugprone-fold-init-type,
@@ -21,6 +22,7 @@ Checks: "-*,
bugprone-incorrect-roundings, bugprone-incorrect-roundings,
bugprone-infinite-loop, bugprone-infinite-loop,
bugprone-integer-division, bugprone-integer-division,
bugprone-invalid-enum-default-initialization,
bugprone-lambda-function-name, bugprone-lambda-function-name,
bugprone-macro-parentheses, bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects, bugprone-macro-repeated-side-effects,
@@ -137,6 +139,7 @@ Checks: "-*,
readability-enum-initial-value, readability-enum-initial-value,
readability-identifier-naming, readability-identifier-naming,
readability-implicit-bool-conversion, readability-implicit-bool-conversion,
readability-inconsistent-ifelse-braces,
readability-make-member-function-const, readability-make-member-function-const,
readability-math-missing-parentheses, readability-math-missing-parentheses,
readability-misleading-indentation, readability-misleading-indentation,
@@ -145,7 +148,9 @@ Checks: "-*,
readability-redundant-declaration, readability-redundant-declaration,
readability-redundant-inline-specifier, readability-redundant-inline-specifier,
readability-redundant-member-init, readability-redundant-member-init,
readability-redundant-parentheses,
readability-redundant-string-init, readability-redundant-string-init,
readability-redundant-typename,
readability-reference-to-constructed-temporary, readability-reference-to-constructed-temporary,
readability-simplify-boolean-expr, readability-simplify-boolean-expr,
readability-static-definition-in-anonymous-namespace, readability-static-definition-in-anonymous-namespace,
@@ -153,8 +158,10 @@ Checks: "-*,
readability-use-std-min-max readability-use-std-min-max
" "
# --- # ---
# readability-inconsistent-declaration-parameter-name, # in this codebase this check will break a lot of arg names # bugprone-narrowing-conversions, # This will break a lot of code but we should enable it in the future because it can eliminate a lot of bugs
# readability-static-accessed-through-instance, # this check is probably unnecessary. it makes the code less readable # misc-override-with-different-visibility, # Will be addressed in a future PR, but for now it generates too many warnings
# readability-inconsistent-declaration-parameter-name, # In this codebase this check will break a lot of arg names
# readability-static-accessed-through-instance, # this check is probably unnecessary. It makes the code less readable
# --- # ---
CheckOptions: CheckOptions:
@@ -191,11 +198,14 @@ CheckOptions:
readability-identifier-naming.ParameterCase: camelBack readability-identifier-naming.ParameterCase: camelBack
readability-identifier-naming.FunctionCase: camelBack readability-identifier-naming.FunctionCase: camelBack
readability-identifier-naming.MemberCase: camelBack readability-identifier-naming.MemberCase: camelBack
readability-identifier-naming.PrivateMemberCase: camelBack
readability-identifier-naming.PrivateMemberSuffix: _ readability-identifier-naming.PrivateMemberSuffix: _
readability-identifier-naming.ProtectedMemberCase: camelBack
readability-identifier-naming.ProtectedMemberSuffix: _ readability-identifier-naming.ProtectedMemberSuffix: _
readability-identifier-naming.PublicMemberCase: camelBack
readability-identifier-naming.PublicMemberSuffix: "" readability-identifier-naming.PublicMemberSuffix: ""
readability-identifier-naming.GlobalFunctionIgnoredRegexp: "^(to_string|hash_append|tuple_hash)$" readability-identifier-naming.GlobalFunctionIgnoredRegexp: "^(to_string|hash_append|tuple_hash)$"
HeaderFilterRegex: '^.*/(test|xrpl|xrpld)/.*\.(h|hpp|ipp)$' HeaderFilterRegex: '^.*/(tests?|xrpl|xrpld)/.*\.(h|hpp|ipp)$'
ExcludeHeaderFilterRegex: '^.*/protocol_autogen/.*\.(h|hpp)$' ExcludeHeaderFilterRegex: '^.*/protocol_autogen/.*\.(h|hpp)$'
WarningsAsErrors: "*" WarningsAsErrors: "*"

View File

@@ -11,9 +11,6 @@ endfunction()
function(create_symbolic_link target link) function(create_symbolic_link target link)
endfunction() endfunction()
function(xrpl_add_test name)
endfunction()
macro(exclude_from_default target_) macro(exclude_from_default target_)
endmacro() endmacro()
@@ -99,3 +96,6 @@ function(verbose_find_path variable name)
${ARGN} ${ARGN}
) )
endfunction() endfunction()
function(patch_nix_binary target)
endfunction()

View File

@@ -35,14 +35,13 @@ runs:
LOG_VERBOSITY: ${{ inputs.log_verbosity }} LOG_VERBOSITY: ${{ inputs.log_verbosity }}
SANITIZERS: ${{ inputs.sanitizers }} SANITIZERS: ${{ inputs.sanitizers }}
run: | run: |
echo 'Installing dependencies.'
conan install \ conan install \
--profile ci \ --profile:all ci \
--build="${BUILD_OPTION}" \ --build="${BUILD_OPTION}" \
--options:host='&:tests=True' \ --options:host='&:tests=True' \
--options:host='&:xrpld=True' \ --options:host='&:xrpld=True' \
--settings:all build_type="${BUILD_TYPE}" \ --settings:all build_type="${BUILD_TYPE}" \
--conf:all tools.build:jobs=${BUILD_NPROC} \ --conf:all tools.build:jobs=${BUILD_NPROC} \
--conf:all tools.build:verbosity="${LOG_VERBOSITY}" \ --conf:all tools.build:verbosity="${LOG_VERBOSITY}" \
--conf:all tools.compilation:verbosity="${LOG_VERBOSITY}" \ --conf:all tools.compilation:verbosity="${LOG_VERBOSITY}" \
. .

View File

@@ -15,7 +15,7 @@ runs:
shell: bash shell: bash
env: env:
VERSION: ${{ github.ref_name }} VERSION: ${{ github.ref_name }}
run: echo "VERSION=${VERSION}" >> "${GITHUB_ENV}" run: echo "VERSION=${VERSION}" >>"${GITHUB_ENV}"
# When a tag is not pushed, then the version (e.g. 1.2.3-b0) is extracted # When a tag is not pushed, then the version (e.g. 1.2.3-b0) is extracted
# from the BuildInfo.cpp file and the shortened commit hash appended to it. # from the BuildInfo.cpp file and the shortened commit hash appended to it.
@@ -28,17 +28,17 @@ runs:
echo 'Extracting version from BuildInfo.cpp.' echo 'Extracting version from BuildInfo.cpp.'
VERSION="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')" VERSION="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')"
if [[ -z "${VERSION}" ]]; then if [[ -z "${VERSION}" ]]; then
echo 'Unable to extract version from BuildInfo.cpp.' echo 'Unable to extract version from BuildInfo.cpp.'
exit 1 exit 1
fi fi
echo 'Appending shortened commit hash to version.' echo 'Appending shortened commit hash to version.'
SHA='${{ github.sha }}' SHA='${{ github.sha }}'
VERSION="${VERSION}+${SHA:0:7}" VERSION="${VERSION}+${SHA:0:7}"
echo "VERSION=${VERSION}" >> "${GITHUB_ENV}" echo "VERSION=${VERSION}" >>"${GITHUB_ENV}"
- name: Output version - name: Output version
id: version id: version
shell: bash shell: bash
run: echo "version=${VERSION}" >> "${GITHUB_OUTPUT}" run: echo "version=${VERSION}" >>"${GITHUB_OUTPUT}"

View File

@@ -0,0 +1,34 @@
name: Set compiler environment
description: "Set CC and CXX environment variables for the given compiler."
inputs:
compiler:
description: 'The compiler to use ("gcc" or "clang").'
required: true
runs:
using: composite
steps:
- name: Set CC and CXX for gcc
if: ${{ inputs.compiler == 'gcc' }}
shell: bash
run: |
echo "CC=gcc" >>"${GITHUB_ENV}"
echo "CXX=g++" >>"${GITHUB_ENV}"
- name: Set CC and CXX for clang
if: ${{ inputs.compiler == 'clang' }}
shell: bash
run: |
echo "CC=clang" >>"${GITHUB_ENV}"
echo "CXX=clang++" >>"${GITHUB_ENV}"
- name: Fail on unknown compiler
if: ${{ inputs.compiler != 'gcc' && inputs.compiler != 'clang' }}
shell: bash
env:
COMPILER: ${{ inputs.compiler }}
run: |
echo "Unknown compiler: $COMPILER" >&2
exit 1

View File

@@ -15,32 +15,35 @@ runs:
using: composite using: composite
steps: steps:
- name: Set up Conan configuration - name: Apply custom configuration to global.conf
shell: bash shell: bash
run: | run: |
echo 'Installing configuration.'
cat conan/global.conf ${{ runner.os == 'Linux' && '>>' || '>' }} $(conan config home)/global.conf cat conan/global.conf ${{ runner.os == 'Linux' && '>>' || '>' }} $(conan config home)/global.conf
echo 'Conan configuration:' - name: Show global configuration
conan config show '*' shell: bash
run: |
- name: Set up Conan profile conan config show '*'
- name: Install profiles
shell: bash shell: bash
run: | run: |
echo 'Installing profile.'
conan config install conan/profiles/ -tf $(conan config home)/profiles/ conan config install conan/profiles/ -tf $(conan config home)/profiles/
echo 'Conan profile:' - name: Show CI profile
shell: bash
run: |
conan profile show --profile ci conan profile show --profile ci
- name: Set up Conan remote - name: Add a remote
shell: bash shell: bash
env: env:
REMOTE_NAME: ${{ inputs.remote_name }} REMOTE_NAME: ${{ inputs.remote_name }}
REMOTE_URL: ${{ inputs.remote_url }} REMOTE_URL: ${{ inputs.remote_url }}
run: | run: |
echo "Adding Conan remote '${REMOTE_NAME}' at '${REMOTE_URL}'."
conan remote add --index 0 --force "${REMOTE_NAME}" "${REMOTE_URL}" conan remote add --index 0 --force "${REMOTE_NAME}" "${REMOTE_URL}"
echo 'Listing Conan remotes.' - name: List remotes
shell: bash
run: |
conan remote list conan remote list

View File

@@ -1,40 +1,12 @@
version: 2 version: 2
updates: updates:
- package-ecosystem: github-actions - package-ecosystem: github-actions
directory: / directories:
schedule: - /
interval: weekly - .github/actions/build-deps/
day: monday - .github/actions/generate-version/
time: "04:00" - .github/actions/set-compiler-env/
timezone: Etc/GMT - .github/actions/setup-conan/
commit-message:
prefix: "ci: [DEPENDABOT] "
target-branch: develop
- package-ecosystem: github-actions
directory: .github/actions/build-deps/
schedule:
interval: weekly
day: monday
time: "04:00"
timezone: Etc/GMT
commit-message:
prefix: "ci: [DEPENDABOT] "
target-branch: develop
- package-ecosystem: github-actions
directory: .github/actions/generate-version/
schedule:
interval: weekly
day: monday
time: "04:00"
timezone: Etc/GMT
commit-message:
prefix: "ci: [DEPENDABOT] "
target-branch: develop
- package-ecosystem: github-actions
directory: .github/actions/setup-conan/
schedule: schedule:
interval: weekly interval: weekly
day: monday day: monday

403
.github/scripts/format-inline-bash.py vendored Executable file
View File

@@ -0,0 +1,403 @@
#!/usr/bin/env python3
"""
Format embedded shell snippets using the shfmt hook configured in
.pre-commit-config.yaml.
Two shapes are recognised:
* YAML workflow/action files: literal block-scalar runs (`run: |`) and
single-line runs (`run: some command`). A single-line run is upgraded to
a `run: |` block scalar if shfmt's output spans multiple lines.
* Markdown files: ``` ```bash ``` fenced code blocks.
Any block that shfmt cannot parse is skipped with a warning on stderr, so
the file is left untouched and surrounding blocks still get formatted.
For each occurrence the body is dedented, written to a temp .sh file,
formatted via `pre-commit run shfmt --files <temp>` (falling back to
`prek`), then re-indented and written back in place.
When invoked without arguments, every .yml/.yaml under .github/ plus every
.md file in the repo is scanned. When invoked with file arguments (the
pre-commit case), only those files are processed.
"""
from __future__ import annotations
import re
import shutil
import subprocess
import sys
import tempfile
from dataclasses import dataclass
from pathlib import Path
from typing import Union
REPO = Path(__file__).resolve().parents[2]
_HOOK_RUNNER = next((cmd for cmd in ("pre-commit", "prek") if shutil.which(cmd)), None)
if _HOOK_RUNNER is None:
sys.exit("error: neither `pre-commit` nor `prek` found on PATH")
RUN_BLOCK_RE = re.compile(r"^(?P<prefix>[ \t]*(?:- )?)run:[ \t]*\|[+-]?[ \t]*$")
RUN_INLINE_RE = re.compile(
r"^(?P<prefix>[ \t]*(?:- )?)run:[ \t]+" r"(?P<value>(?!\|[+-]?[ \t]*$)\S.*?)[ \t]*$"
)
MD_BASH_OPEN_RE = re.compile(r"^(?P<indent>[ ]{0,3})`{3}bash[ \t]*$")
MD_FENCE_CLOSE_RE = re.compile(r"^[ ]{0,3}`{3,}[ \t]*$")
@dataclass(frozen=True)
class BlockRun:
"""A `run: |` block scalar; `body_start:body_end` slices into `lines`."""
body_start: int
body_end: int
body_indent: int
@dataclass(frozen=True)
class InlineRun:
"""A single-line `run: value` at `line_idx`."""
line_idx: int
prefix: str
value: str
@dataclass(frozen=True)
class MdBashBlock:
"""A markdown ``` ```bash ``` fenced code block.
`body_start:body_end` slices into the file's lines; `open_line_idx`
points at the opening fence line.
"""
open_line_idx: int
body_start: int
body_end: int
body_indent: int
RunItem = Union[BlockRun, InlineRun]
def _scan_block_body(
lines: list[str], body_start: int, run_col: int
) -> tuple[int | None, int]:
"""Locate the body of a `run: |` block scalar starting at `body_start`.
Returns `(body_indent, scan_end)`. `scan_end` is the line index where the
outer scanner should resume. `body_indent` is `None` when no body is
present (the scalar is empty, or the next non-blank line has indent
`<= run_col`).
"""
body_indent: int | None = None
scan_end = len(lines)
for idx in range(body_start, len(lines)):
line = lines[idx]
if line.strip() == "":
continue
indent = len(line) - len(line.lstrip(" "))
if body_indent is None:
if indent > run_col:
body_indent = indent
else:
scan_end = idx
break
elif indent < body_indent:
scan_end = idx
break
if body_indent is not None:
while scan_end > body_start and lines[scan_end - 1].strip() == "":
scan_end -= 1
if scan_end <= body_start:
body_indent = None
return body_indent, scan_end
def find_run_blocks(lines: list[str]) -> list[RunItem]:
"""Return run items in document order."""
items: list[RunItem] = []
line_idx = 0
while line_idx < len(lines):
line = lines[line_idx]
if block_match := RUN_BLOCK_RE.match(line):
run_col = len(block_match.group("prefix"))
body_start = line_idx + 1
body_indent, scan_end = _scan_block_body(lines, body_start, run_col)
if body_indent is not None:
items.append(
BlockRun(
body_start=body_start,
body_end=scan_end,
body_indent=body_indent,
)
)
line_idx = scan_end
continue
if inline_match := RUN_INLINE_RE.match(line):
items.append(
InlineRun(
line_idx=line_idx,
prefix=inline_match.group("prefix"),
value=inline_match.group("value"),
)
)
line_idx += 1
return items
def find_md_bash_blocks(lines: list[str]) -> list[MdBashBlock]:
"""Return ``` ```bash ``` fenced code blocks in document order."""
blocks: list[MdBashBlock] = []
line_idx = 0
while line_idx < len(lines):
open_match = MD_BASH_OPEN_RE.match(lines[line_idx])
if not open_match:
line_idx += 1
continue
body_start = line_idx + 1
close_idx = next(
(
j
for j in range(body_start, len(lines))
if MD_FENCE_CLOSE_RE.match(lines[j])
),
None,
)
if close_idx is None:
line_idx = body_start
continue
body = lines[body_start:close_idx]
non_blank = [b for b in body if b.strip()]
body_indent = (
min(len(b) - len(b.lstrip(" ")) for b in non_blank)
if non_blank
else len(open_match.group("indent"))
)
blocks.append(
MdBashBlock(
open_line_idx=line_idx,
body_start=body_start,
body_end=close_idx,
body_indent=body_indent,
)
)
line_idx = close_idx + 1
return blocks
def dedent(lines: list[str], n: int) -> list[str]:
pad = " " * n
return [
(
""
if line.strip() == ""
else (line[n:] if line.startswith(pad) else line.lstrip(" "))
)
for line in lines
]
def reindent(lines: list[str], n: int) -> list[str]:
pad = " " * n
return [pad + line if line else "" for line in lines]
_SHFMT_ERR_RE = re.compile(r"\.sh:\d+:\d+:\s")
_GHA_EXPR_RE = re.compile(r"\$\{\{.*?\}\}", re.DOTALL)
_GHA_PLACEHOLDER_RE = re.compile(r"__GHA_EXPR_(\d+)__")
def _encode_gha_exprs(text: str) -> tuple[str, list[str]]:
"""Replace `${{ ... }}` expressions with bash-safe placeholder identifiers."""
exprs: list[str] = []
def repl(match: re.Match[str]) -> str:
exprs.append(match.group(0))
return f"__GHA_EXPR_{len(exprs) - 1}__"
return _GHA_EXPR_RE.sub(repl, text), exprs
def _decode_gha_exprs(text: str, exprs: list[str]) -> str:
"""Restore `${{ ... }}` expressions from placeholder identifiers."""
return _GHA_PLACEHOLDER_RE.sub(lambda m: exprs[int(m.group(1))], text)
def shfmt_via_hook(tmp_path: Path) -> tuple[bool, str]:
# `${{ ... }}` is not valid shell, so swap it for a placeholder identifier
# that shfmt can parse, then restore it after formatting.
encoded, exprs = _encode_gha_exprs(tmp_path.read_text())
if exprs:
tmp_path.write_text(encoded)
res = subprocess.run(
[_HOOK_RUNNER, "run", "shfmt", "--files", str(tmp_path)],
cwd=REPO,
capture_output=True,
text=True,
)
output = res.stdout + res.stderr
# shfmt emits parse errors as "<path>:<line>:<col>: <message>".
parse_err = bool(_SHFMT_ERR_RE.search(output))
# A non-zero exit that is neither a parse error nor pre-commit's "I had
# to modify files" signal means the hook itself failed to run (missing
# binary, install failure, bad config, ...). Surface that loudly rather
# than silently treating it as a no-op.
if (
res.returncode != 0
and not parse_err
and "files were modified by this hook" not in output
):
sys.exit(
f"error: `{_HOOK_RUNNER} run shfmt` failed with exit {res.returncode}:\n{output}"
)
if exprs and not parse_err:
tmp_path.write_text(_decode_gha_exprs(tmp_path.read_text(), exprs))
return not parse_err, output
def _skip(path: Path, where: int, kind: str, output: str) -> None:
print(
f" shfmt could not parse {kind} at {path}:{where + 1} — skipped",
file=sys.stderr,
)
print(f" {output.strip()}", file=sys.stderr)
def process_yaml_file(path: Path, tmp_path: Path) -> int:
text = path.read_text()
had_nl = text.endswith("\n")
lines = text.split("\n")
if had_nl:
lines = lines[:-1]
items = find_run_blocks(lines)
if not items:
return 0
changed = 0
# Process in reverse so earlier indices remain valid as we splice.
for item in reversed(items):
if isinstance(item, BlockRun):
body = lines[item.body_start : item.body_end]
tmp_path.write_text("\n".join(dedent(body, item.body_indent)) + "\n")
ok, output = shfmt_via_hook(tmp_path)
if not ok:
_skip(path, item.body_start, "block", output)
continue
formatted = tmp_path.read_text().rstrip("\n")
new_body = reindent(formatted.split("\n"), item.body_indent)
if new_body != body:
lines[item.body_start : item.body_end] = new_body
changed += 1
else:
tmp_path.write_text(item.value + "\n")
ok, output = shfmt_via_hook(tmp_path)
if not ok:
_skip(path, item.line_idx, "inline run", output)
continue
formatted = tmp_path.read_text().rstrip("\n")
if formatted == item.value:
continue
formatted_lines = formatted.split("\n")
if len(formatted_lines) == 1:
lines[item.line_idx] = f"{item.prefix}run: {formatted}"
else:
body_indent = len(item.prefix) + 2
lines[item.line_idx : item.line_idx + 1] = [
f"{item.prefix}run: |",
*reindent(formatted_lines, body_indent),
]
changed += 1
new_text = "\n".join(lines) + ("\n" if had_nl else "")
if new_text != text:
path.write_text(new_text)
return changed
def process_md_file(path: Path, tmp_path: Path) -> int:
text = path.read_text()
had_nl = text.endswith("\n")
lines = text.split("\n")
if had_nl:
lines = lines[:-1]
blocks = find_md_bash_blocks(lines)
if not blocks:
return 0
changed = 0
for block in reversed(blocks):
body = lines[block.body_start : block.body_end]
tmp_path.write_text("\n".join(dedent(body, block.body_indent)) + "\n")
ok, output = shfmt_via_hook(tmp_path)
if not ok:
_skip(path, block.open_line_idx, "```bash block", output)
continue
formatted = tmp_path.read_text().rstrip("\n")
formatted_lines = formatted.split("\n") if formatted else []
new_body = reindent(formatted_lines, block.body_indent)
if new_body != body:
lines[block.body_start : block.body_end] = new_body
changed += 1
new_text = "\n".join(lines) + ("\n" if had_nl else "")
if new_text != text:
path.write_text(new_text)
return changed
def process_file(path: Path, tmp_path: Path) -> int:
if path.suffix in (".yml", ".yaml"):
return process_yaml_file(path, tmp_path)
if path.suffix == ".md":
return process_md_file(path, tmp_path)
return 0
def gather_files(argv: list[str]) -> list[Path]:
"""Return YAML workflow/action files and markdown files that we should
process — either the paths in `argv` or, when `argv` is empty, every
such file in the repo (skipping `external/`)."""
if argv:
candidates: list[Path] = [
(REPO / a).resolve() if not Path(a).is_absolute() else Path(a) for a in argv
]
else:
gh = REPO / ".github"
candidates = [
*gh.rglob("*.yml"),
*gh.rglob("*.yaml"),
*(
p
for p in REPO.rglob("*.md")
if "external" not in p.relative_to(REPO).parts
),
]
return sorted(
p
for p in candidates
if p.exists()
and (
(p.suffix in (".yml", ".yaml") and ".github" in p.parts)
or p.suffix == ".md"
)
)
def main(argv: list[str]) -> int:
files = gather_files(argv)
if not files:
return 0
with tempfile.TemporaryDirectory(prefix="format-inline-bash-") as tmpdir:
tmp_path = Path(tmpdir) / "shfmt.sh"
total = 0
for f in files:
n = process_file(f, tmp_path)
if n:
print(f"{f.relative_to(REPO)}: reformatted {n} block(s)")
total += n
return 1 if total else 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))

0
.github/scripts/levelization/generate.py vendored Normal file → Executable file
View File

View File

@@ -1,6 +1,8 @@
libxrpl.basics > xrpl.basics libxrpl.basics > xrpl.basics
libxrpl.conditions > xrpl.basics libxrpl.conditions > xrpl.basics
libxrpl.conditions > xrpl.conditions libxrpl.conditions > xrpl.conditions
libxrpl.config > xrpl.basics
libxrpl.config > xrpl.config
libxrpl.core > xrpl.basics libxrpl.core > xrpl.basics
libxrpl.core > xrpl.core libxrpl.core > xrpl.core
libxrpl.core > xrpl.json libxrpl.core > xrpl.json
@@ -12,11 +14,11 @@ libxrpl.ledger > xrpl.json
libxrpl.ledger > xrpl.ledger libxrpl.ledger > xrpl.ledger
libxrpl.ledger > xrpl.nodestore libxrpl.ledger > xrpl.nodestore
libxrpl.ledger > xrpl.protocol libxrpl.ledger > xrpl.protocol
libxrpl.ledger > xrpl.server
libxrpl.ledger > xrpl.shamap libxrpl.ledger > xrpl.shamap
libxrpl.net > xrpl.basics libxrpl.net > xrpl.basics
libxrpl.net > xrpl.net libxrpl.net > xrpl.net
libxrpl.nodestore > xrpl.basics libxrpl.nodestore > xrpl.basics
libxrpl.nodestore > xrpl.config
libxrpl.nodestore > xrpl.json libxrpl.nodestore > xrpl.json
libxrpl.nodestore > xrpl.nodestore libxrpl.nodestore > xrpl.nodestore
libxrpl.nodestore > xrpl.protocol libxrpl.nodestore > xrpl.protocol
@@ -24,6 +26,7 @@ libxrpl.protocol > xrpl.basics
libxrpl.protocol > xrpl.json libxrpl.protocol > xrpl.json
libxrpl.protocol > xrpl.protocol libxrpl.protocol > xrpl.protocol
libxrpl.rdb > xrpl.basics libxrpl.rdb > xrpl.basics
libxrpl.rdb > xrpl.config
libxrpl.rdb > xrpl.core libxrpl.rdb > xrpl.core
libxrpl.rdb > xrpl.rdb libxrpl.rdb > xrpl.rdb
libxrpl.resource > xrpl.basics libxrpl.resource > xrpl.basics
@@ -31,6 +34,7 @@ libxrpl.resource > xrpl.json
libxrpl.resource > xrpl.protocol libxrpl.resource > xrpl.protocol
libxrpl.resource > xrpl.resource libxrpl.resource > xrpl.resource
libxrpl.server > xrpl.basics libxrpl.server > xrpl.basics
libxrpl.server > xrpl.config
libxrpl.server > xrpl.core libxrpl.server > xrpl.core
libxrpl.server > xrpl.json libxrpl.server > xrpl.json
libxrpl.server > xrpl.protocol libxrpl.server > xrpl.protocol
@@ -52,6 +56,7 @@ libxrpl.tx > xrpl.tx
test.app > test.jtx test.app > test.jtx
test.app > test.unit_test test.app > test.unit_test
test.app > xrpl.basics test.app > xrpl.basics
test.app > xrpl.config
test.app > xrpl.core test.app > xrpl.core
test.app > xrpld.app test.app > xrpld.app
test.app > xrpld.consensus test.app > xrpld.consensus
@@ -78,7 +83,6 @@ test.conditions > xrpl.basics
test.conditions > xrpl.conditions test.conditions > xrpl.conditions
test.consensus > test.csf test.consensus > test.csf
test.consensus > test.jtx test.consensus > test.jtx
test.consensus > test.toplevel
test.consensus > test.unit_test test.consensus > test.unit_test
test.consensus > xrpl.basics test.consensus > xrpl.basics
test.consensus > xrpld.app test.consensus > xrpld.app
@@ -90,6 +94,7 @@ test.consensus > xrpl.tx
test.core > test.jtx test.core > test.jtx
test.core > test.unit_test test.core > test.unit_test
test.core > xrpl.basics test.core > xrpl.basics
test.core > xrpl.config
test.core > xrpl.core test.core > xrpl.core
test.core > xrpld.core test.core > xrpld.core
test.core > xrpl.json test.core > xrpl.json
@@ -104,6 +109,7 @@ test.csf > xrpl.protocol
test.json > test.jtx test.json > test.jtx
test.json > xrpl.json test.json > xrpl.json
test.jtx > xrpl.basics test.jtx > xrpl.basics
test.jtx > xrpl.config
test.jtx > xrpl.core test.jtx > xrpl.core
test.jtx > xrpld.app test.jtx > xrpld.app
test.jtx > xrpld.core test.jtx > xrpld.core
@@ -126,6 +132,7 @@ test.ledger > xrpl.protocol
test.nodestore > test.jtx test.nodestore > test.jtx
test.nodestore > test.unit_test test.nodestore > test.unit_test
test.nodestore > xrpl.basics test.nodestore > xrpl.basics
test.nodestore > xrpl.config
test.nodestore > xrpld.core test.nodestore > xrpld.core
test.nodestore > xrpl.nodestore test.nodestore > xrpl.nodestore
test.nodestore > xrpl.protocol test.nodestore > xrpl.protocol
@@ -133,6 +140,7 @@ test.nodestore > xrpl.rdb
test.overlay > test.jtx test.overlay > test.jtx
test.overlay > test.unit_test test.overlay > test.unit_test
test.overlay > xrpl.basics test.overlay > xrpl.basics
test.overlay > xrpl.config
test.overlay > xrpld.app test.overlay > xrpld.app
test.overlay > xrpld.core test.overlay > xrpld.core
test.overlay > xrpld.overlay test.overlay > xrpld.overlay
@@ -159,6 +167,7 @@ test.resource > xrpl.basics
test.resource > xrpl.resource test.resource > xrpl.resource
test.rpc > test.jtx test.rpc > test.jtx
test.rpc > xrpl.basics test.rpc > xrpl.basics
test.rpc > xrpl.config
test.rpc > xrpl.core test.rpc > xrpl.core
test.rpc > xrpld.app test.rpc > xrpld.app
test.rpc > xrpld.core test.rpc > xrpld.core
@@ -173,6 +182,7 @@ test.rpc > xrpl.tx
test.server > test.jtx test.server > test.jtx
test.server > test.unit_test test.server > test.unit_test
test.server > xrpl.basics test.server > xrpl.basics
test.server > xrpl.config
test.server > xrpld.app test.server > xrpld.app
test.server > xrpld.core test.server > xrpld.core
test.server > xrpl.json test.server > xrpl.json
@@ -180,6 +190,7 @@ test.server > xrpl.protocol
test.server > xrpl.server test.server > xrpl.server
test.shamap > test.unit_test test.shamap > test.unit_test
test.shamap > xrpl.basics test.shamap > xrpl.basics
test.shamap > xrpl.config
test.shamap > xrpl.nodestore test.shamap > xrpl.nodestore
test.shamap > xrpl.protocol test.shamap > xrpl.protocol
test.shamap > xrpl.shamap test.shamap > xrpl.shamap
@@ -188,6 +199,7 @@ test.toplevel > xrpl.json
test.unit_test > xrpl.basics test.unit_test > xrpl.basics
test.unit_test > xrpl.protocol test.unit_test > xrpl.protocol
tests.libxrpl > xrpl.basics tests.libxrpl > xrpl.basics
tests.libxrpl > xrpl.config
tests.libxrpl > xrpl.core tests.libxrpl > xrpl.core
tests.libxrpl > xrpl.json tests.libxrpl > xrpl.json
tests.libxrpl > xrpl.ledger tests.libxrpl > xrpl.ledger
@@ -200,16 +212,17 @@ tests.libxrpl > xrpl.shamap
tests.libxrpl > xrpl.tx tests.libxrpl > xrpl.tx
xrpl.conditions > xrpl.basics xrpl.conditions > xrpl.basics
xrpl.conditions > xrpl.protocol xrpl.conditions > xrpl.protocol
xrpl.config > xrpl.basics
xrpl.core > xrpl.basics xrpl.core > xrpl.basics
xrpl.core > xrpl.json xrpl.core > xrpl.json
xrpl.core > xrpl.protocol xrpl.core > xrpl.protocol
xrpl.json > xrpl.basics xrpl.json > xrpl.basics
xrpl.ledger > xrpl.basics xrpl.ledger > xrpl.basics
xrpl.ledger > xrpl.protocol xrpl.ledger > xrpl.protocol
xrpl.ledger > xrpl.server
xrpl.ledger > xrpl.shamap xrpl.ledger > xrpl.shamap
xrpl.net > xrpl.basics xrpl.net > xrpl.basics
xrpl.nodestore > xrpl.basics xrpl.nodestore > xrpl.basics
xrpl.nodestore > xrpl.config
xrpl.nodestore > xrpl.protocol xrpl.nodestore > xrpl.protocol
xrpl.protocol > xrpl.basics xrpl.protocol > xrpl.basics
xrpl.protocol > xrpl.json xrpl.protocol > xrpl.json
@@ -237,6 +250,7 @@ xrpl.tx > xrpl.ledger
xrpl.tx > xrpl.protocol xrpl.tx > xrpl.protocol
xrpld.app > test.unit_test xrpld.app > test.unit_test
xrpld.app > xrpl.basics xrpld.app > xrpl.basics
xrpld.app > xrpl.config
xrpld.app > xrpl.core xrpld.app > xrpl.core
xrpld.app > xrpld.consensus xrpld.app > xrpld.consensus
xrpld.app > xrpld.core xrpld.app > xrpld.core
@@ -255,11 +269,13 @@ xrpld.consensus > xrpl.json
xrpld.consensus > xrpl.ledger xrpld.consensus > xrpl.ledger
xrpld.consensus > xrpl.protocol xrpld.consensus > xrpl.protocol
xrpld.core > xrpl.basics xrpld.core > xrpl.basics
xrpld.core > xrpl.config
xrpld.core > xrpl.core xrpld.core > xrpl.core
xrpld.core > xrpl.net xrpld.core > xrpl.net
xrpld.core > xrpl.protocol xrpld.core > xrpl.protocol
xrpld.core > xrpl.rdb xrpld.core > xrpl.rdb
xrpld.overlay > xrpl.basics xrpld.overlay > xrpl.basics
xrpld.overlay > xrpl.config
xrpld.overlay > xrpl.core xrpld.overlay > xrpl.core
xrpld.overlay > xrpld.consensus xrpld.overlay > xrpld.consensus
xrpld.overlay > xrpld.core xrpld.overlay > xrpld.core
@@ -272,15 +288,18 @@ xrpld.overlay > xrpl.server
xrpld.overlay > xrpl.shamap xrpld.overlay > xrpl.shamap
xrpld.overlay > xrpl.tx xrpld.overlay > xrpl.tx
xrpld.peerfinder > xrpl.basics xrpld.peerfinder > xrpl.basics
xrpld.peerfinder > xrpl.config
xrpld.peerfinder > xrpld.core xrpld.peerfinder > xrpld.core
xrpld.peerfinder > xrpl.protocol xrpld.peerfinder > xrpl.protocol
xrpld.peerfinder > xrpl.rdb xrpld.peerfinder > xrpl.rdb
xrpld.perflog > xrpl.basics xrpld.perflog > xrpl.basics
xrpld.perflog > xrpl.config
xrpld.perflog > xrpl.core xrpld.perflog > xrpl.core
xrpld.perflog > xrpld.rpc xrpld.perflog > xrpld.rpc
xrpld.perflog > xrpl.json xrpld.perflog > xrpl.json
xrpld.perflog > xrpl.protocol xrpld.perflog > xrpl.protocol
xrpld.rpc > xrpl.basics xrpld.rpc > xrpl.basics
xrpld.rpc > xrpl.config
xrpld.rpc > xrpl.core xrpld.rpc > xrpl.core
xrpld.rpc > xrpld.core xrpld.rpc > xrpld.core
xrpld.rpc > xrpl.json xrpld.rpc > xrpl.json

View File

@@ -6,7 +6,7 @@ set -e
# On MacOS, ensure that GNU sed is installed and available as `gsed`. # On MacOS, ensure that GNU sed is installed and available as `gsed`.
SED_COMMAND=sed SED_COMMAND=sed
if [[ "${OSTYPE}" == 'darwin'* ]]; then if [[ "${OSTYPE}" == 'darwin'* ]]; then
if ! command -v gsed &> /dev/null; then if ! command -v gsed &>/dev/null; then
echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'."
exit 1 exit 1
fi fi

View File

@@ -8,12 +8,12 @@ set -e
SED_COMMAND=sed SED_COMMAND=sed
HEAD_COMMAND=head HEAD_COMMAND=head
if [[ "${OSTYPE}" == 'darwin'* ]]; then if [[ "${OSTYPE}" == 'darwin'* ]]; then
if ! command -v gsed &> /dev/null; then if ! command -v gsed &>/dev/null; then
echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'."
exit 1 exit 1
fi fi
SED_COMMAND=gsed SED_COMMAND=gsed
if ! command -v ghead &> /dev/null; then if ! command -v ghead &>/dev/null; then
echo "Error: ghead is not installed. Please install it using 'brew install coreutils'." echo "Error: ghead is not installed. Please install it using 'brew install coreutils'."
exit 1 exit 1
fi fi
@@ -43,9 +43,6 @@ pushd "${DIRECTORY}"
# Rename the files. # Rename the files.
find cmake -type f -name 'Rippled*.cmake' -exec bash -c 'mv "${1}" "${1/Rippled/Xrpl}"' - {} \; find cmake -type f -name 'Rippled*.cmake' -exec bash -c 'mv "${1}" "${1/Rippled/Xrpl}"' - {} \;
find cmake -type f -name 'Ripple*.cmake' -exec bash -c 'mv "${1}" "${1/Ripple/Xrpl}"' - {} \; find cmake -type f -name 'Ripple*.cmake' -exec bash -c 'mv "${1}" "${1/Ripple/Xrpl}"' - {} \;
if [ -e cmake/xrpl_add_test.cmake ]; then
mv cmake/xrpl_add_test.cmake cmake/XrplAddTest.cmake
fi
if [ -e include/xrpl/proto/ripple.proto ]; then if [ -e include/xrpl/proto/ripple.proto ]; then
mv include/xrpl/proto/ripple.proto include/xrpl/proto/xrpl.proto mv include/xrpl/proto/ripple.proto include/xrpl/proto/xrpl.proto
fi fi
@@ -60,7 +57,6 @@ find cmake -type f -name '*.cmake' | while read -r FILE; do
done done
${SED_COMMAND} -i -E 's/Rippled?/Xrpl/g' CMakeLists.txt ${SED_COMMAND} -i -E 's/Rippled?/Xrpl/g' CMakeLists.txt
${SED_COMMAND} -i 's/ripple/xrpl/g' CMakeLists.txt ${SED_COMMAND} -i 's/ripple/xrpl/g' CMakeLists.txt
${SED_COMMAND} -i 's/include(xrpl_add_test)/include(XrplAddTest)/' src/tests/libxrpl/CMakeLists.txt
${SED_COMMAND} -i 's/ripple.pb.h/xrpl.pb.h/' include/xrpl/protocol/messages.h ${SED_COMMAND} -i 's/ripple.pb.h/xrpl.pb.h/' include/xrpl/protocol/messages.h
${SED_COMMAND} -i 's/ripple.pb.h/xrpl.pb.h/' BUILD.md ${SED_COMMAND} -i 's/ripple.pb.h/xrpl.pb.h/' BUILD.md
${SED_COMMAND} -i 's/ripple.pb.h/xrpl.pb.h/' BUILD.md ${SED_COMMAND} -i 's/ripple.pb.h/xrpl.pb.h/' BUILD.md
@@ -74,10 +70,10 @@ if grep -q '"xrpld"' cmake/XrplCore.cmake; then
# The script has been rerun, so just restore the name of the binary. # The script has been rerun, so just restore the name of the binary.
${SED_COMMAND} -i 's/"xrpld"/"rippled"/' cmake/XrplCore.cmake ${SED_COMMAND} -i 's/"xrpld"/"rippled"/' cmake/XrplCore.cmake
elif ! grep -q '"rippled"' cmake/XrplCore.cmake; then elif ! grep -q '"rippled"' cmake/XrplCore.cmake; then
${HEAD_COMMAND} -n -1 cmake/XrplCore.cmake > cmake.tmp ${HEAD_COMMAND} -n -1 cmake/XrplCore.cmake >cmake.tmp
echo ' # For the time being, we will keep the name of the binary as it was.' >> cmake.tmp echo ' # For the time being, we will keep the name of the binary as it was.' >>cmake.tmp
echo ' set_target_properties(xrpld PROPERTIES OUTPUT_NAME "rippled")' >> cmake.tmp echo ' set_target_properties(xrpld PROPERTIES OUTPUT_NAME "rippled")' >>cmake.tmp
tail -1 cmake/XrplCore.cmake >> cmake.tmp tail -1 cmake/XrplCore.cmake >>cmake.tmp
mv cmake.tmp cmake/XrplCore.cmake mv cmake.tmp cmake/XrplCore.cmake
fi fi

View File

@@ -6,7 +6,7 @@ set -e
# On MacOS, ensure that GNU sed is installed and available as `gsed`. # On MacOS, ensure that GNU sed is installed and available as `gsed`.
SED_COMMAND=sed SED_COMMAND=sed
if [[ "${OSTYPE}" == 'darwin'* ]]; then if [[ "${OSTYPE}" == 'darwin'* ]]; then
if ! command -v gsed &> /dev/null; then if ! command -v gsed &>/dev/null; then
echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'."
exit 1 exit 1
fi fi

View File

@@ -6,7 +6,7 @@ set -e
# On MacOS, ensure that GNU sed is installed and available as `gsed`. # On MacOS, ensure that GNU sed is installed and available as `gsed`.
SED_COMMAND=sed SED_COMMAND=sed
if [[ "${OSTYPE}" == 'darwin'* ]]; then if [[ "${OSTYPE}" == 'darwin'* ]]; then
if ! command -v gsed &> /dev/null; then if ! command -v gsed &>/dev/null; then
echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'."
exit 1 exit 1
fi fi
@@ -62,37 +62,37 @@ done
# restoring the verbiage that is already present in LICENSE.md. Ensure that if # restoring the verbiage that is already present in LICENSE.md. Ensure that if
# the script is run multiple times, duplicate notices are not added. # the script is run multiple times, duplicate notices are not added.
if ! grep -q 'Raw Material Software' include/xrpl/beast/core/CurrentThreadName.h; then if ! grep -q 'Raw Material Software' include/xrpl/beast/core/CurrentThreadName.h; then
echo -e "// Portions of this file are from JUCE (http://www.juce.com).\n// Copyright (c) 2013 - Raw Material Software Ltd.\n// Please visit http://www.juce.com\n\n$(cat include/xrpl/beast/core/CurrentThreadName.h)" > include/xrpl/beast/core/CurrentThreadName.h echo -e "// Portions of this file are from JUCE (http://www.juce.com).\n// Copyright (c) 2013 - Raw Material Software Ltd.\n// Please visit http://www.juce.com\n\n$(cat include/xrpl/beast/core/CurrentThreadName.h)" >include/xrpl/beast/core/CurrentThreadName.h
fi fi
if ! grep -q 'Dev Null' src/test/app/NetworkID_test.cpp; then if ! grep -q 'Dev Null' src/test/app/NetworkID_test.cpp; then
echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/NetworkID_test.cpp)" > src/test/app/NetworkID_test.cpp echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/NetworkID_test.cpp)" >src/test/app/NetworkID_test.cpp
fi fi
if ! grep -q 'Dev Null' src/test/app/tx/apply_test.cpp; then if ! grep -q 'Dev Null' src/test/app/tx/apply_test.cpp; then
echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/tx/apply_test.cpp)" > src/test/app/tx/apply_test.cpp echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/tx/apply_test.cpp)" >src/test/app/tx/apply_test.cpp
fi fi
if ! grep -q 'Dev Null' src/test/rpc/ManifestRPC_test.cpp; then if ! grep -q 'Dev Null' src/test/rpc/ManifestRPC_test.cpp; then
echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ManifestRPC_test.cpp)" > src/test/rpc/ManifestRPC_test.cpp echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ManifestRPC_test.cpp)" >src/test/rpc/ManifestRPC_test.cpp
fi fi
if ! grep -q 'Dev Null' src/test/rpc/ValidatorInfo_test.cpp; then if ! grep -q 'Dev Null' src/test/rpc/ValidatorInfo_test.cpp; then
echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ValidatorInfo_test.cpp)" > src/test/rpc/ValidatorInfo_test.cpp echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ValidatorInfo_test.cpp)" >src/test/rpc/ValidatorInfo_test.cpp
fi fi
if ! grep -q 'Dev Null' src/xrpld/rpc/handlers/server_info/Manifest.cpp; then if ! grep -q 'Dev Null' src/xrpld/rpc/handlers/server_info/Manifest.cpp; then
echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/server_info/Manifest.cpp)" > src/xrpld/rpc/handlers/server_info/Manifest.cpp echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/server_info/Manifest.cpp)" >src/xrpld/rpc/handlers/server_info/Manifest.cpp
fi fi
if ! grep -q 'Dev Null' src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp; then if ! grep -q 'Dev Null' src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp; then
echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp)" > src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp)" >src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp
fi fi
if ! grep -q 'Bougalis' include/xrpl/basics/SlabAllocator.h; then if ! grep -q 'Bougalis' include/xrpl/basics/SlabAllocator.h; then
echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis <nikb@bougalis.net>\n\n$(cat include/xrpl/basics/SlabAllocator.h)" > include/xrpl/basics/SlabAllocator.h # cspell: ignore Nikolaos Bougalis nikb echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis <nikb@bougalis.net>\n\n$(cat include/xrpl/basics/SlabAllocator.h)" >include/xrpl/basics/SlabAllocator.h # cspell: ignore Nikolaos Bougalis nikb
fi fi
if ! grep -q 'Bougalis' include/xrpl/basics/spinlock.h; then if ! grep -q 'Bougalis' include/xrpl/basics/spinlock.h; then
echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis <nikb@bougalis.net>\n\n$(cat include/xrpl/basics/spinlock.h)" > include/xrpl/basics/spinlock.h # cspell: ignore Nikolaos Bougalis nikb echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis <nikb@bougalis.net>\n\n$(cat include/xrpl/basics/spinlock.h)" >include/xrpl/basics/spinlock.h # cspell: ignore Nikolaos Bougalis nikb
fi fi
if ! grep -q 'Bougalis' include/xrpl/basics/tagged_integer.h; then if ! grep -q 'Bougalis' include/xrpl/basics/tagged_integer.h; then
echo -e "// Copyright (c) 2014, Nikolaos D. Bougalis <nikb@bougalis.net>\n\n$(cat include/xrpl/basics/tagged_integer.h)" > include/xrpl/basics/tagged_integer.h # cspell: ignore Nikolaos Bougalis nikb echo -e "// Copyright (c) 2014, Nikolaos D. Bougalis <nikb@bougalis.net>\n\n$(cat include/xrpl/basics/tagged_integer.h)" >include/xrpl/basics/tagged_integer.h # cspell: ignore Nikolaos Bougalis nikb
fi fi
if ! grep -q 'Ritchford' include/xrpl/beast/utility/Zero.h; then if ! grep -q 'Ritchford' include/xrpl/beast/utility/Zero.h; then
echo -e "// Copyright (c) 2014, Tom Ritchford <tom@swirly.com>\n\n$(cat include/xrpl/beast/utility/Zero.h)" > include/xrpl/beast/utility/Zero.h # cspell: ignore Ritchford echo -e "// Copyright (c) 2014, Tom Ritchford <tom@swirly.com>\n\n$(cat include/xrpl/beast/utility/Zero.h)" >include/xrpl/beast/utility/Zero.h # cspell: ignore Ritchford
fi fi
# Restore newlines and tabs in string literals in the affected file. # Restore newlines and tabs in string literals in the affected file.

View File

@@ -6,7 +6,7 @@ set -e
# On MacOS, ensure that GNU sed is installed and available as `gsed`. # On MacOS, ensure that GNU sed is installed and available as `gsed`.
SED_COMMAND=sed SED_COMMAND=sed
if [[ "${OSTYPE}" == 'darwin'* ]]; then if [[ "${OSTYPE}" == 'darwin'* ]]; then
if ! command -v gsed &> /dev/null; then if ! command -v gsed &>/dev/null; then
echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'."
exit 1 exit 1
fi fi

View File

@@ -6,7 +6,7 @@ set -e
# On MacOS, ensure that GNU sed is installed and available as `gsed`. # On MacOS, ensure that GNU sed is installed and available as `gsed`.
SED_COMMAND=sed SED_COMMAND=sed
if [[ "${OSTYPE}" == 'darwin'* ]]; then if [[ "${OSTYPE}" == 'darwin'* ]]; then
if ! command -v gsed &> /dev/null; then if ! command -v gsed &>/dev/null; then
echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'."
exit 1 exit 1
fi fi

View File

@@ -6,7 +6,7 @@ set -e
# On MacOS, ensure that GNU sed is installed and available as `gsed`. # On MacOS, ensure that GNU sed is installed and available as `gsed`.
SED_COMMAND=sed SED_COMMAND=sed
if [[ "${OSTYPE}" == 'darwin'* ]]; then if [[ "${OSTYPE}" == 'darwin'* ]]; then
if ! command -v gsed &> /dev/null; then if ! command -v gsed &>/dev/null; then
echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'."
exit 1 exit 1
fi fi

View File

@@ -1,384 +1,322 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import argparse
import dataclasses
import itertools import itertools
import json import json
from dataclasses import dataclass
from pathlib import Path from pathlib import Path
THIS_DIR = Path(__file__).parent.resolve() THIS_DIR = Path(__file__).parent.resolve()
_BASE_CMAKE_ARGS = ["-Dtests=ON", "-Dwerr=ON", "-Dxrpld=ON", "-Dwextra=ON"]
@dataclass # Maps sanitizer names (as used in cmake) to short config-name suffixes.
class Config: _SANITIZER_SUFFIX: dict[str, str] = {
architecture: list[dict] "address": "asan",
os: list[dict] "undefinedbehavior": "ubsan",
build_type: list[str] "thread": "tsan",
cmake_args: list[str] }
""" def get_cmake_args(build_type: str, extra_args: str) -> str:
Generate a strategy matrix for GitHub Actions CI. """Get the full list of CMake arguments for a config."""
args = _BASE_CMAKE_ARGS.copy()
On each PR commit we will build a selection of Debian, RHEL, Ubuntu, MacOS, and if extra_args:
Windows configurations, while upon merge into the develop or release branches, args.extend(extra_args.split())
we will build all configurations, and test most of them. return " ".join(args)
We will further set additional CMake arguments as follows:
- All builds will have the `tests`, `werr`, and `xrpld` options.
- All builds will have the `wextra` option except for GCC 12 and Clang 16.
- All release builds will have the `assert` option.
- Certain Debian Bookworm configurations will change the reference fee, enable
codecov, and enable voidstar in PRs.
"""
def build_config_name(os_entry: dict[str, str], platform: str, build_type: str) -> str: def runs_on_event(exclude_event_types: list[str], event: str | None) -> bool:
parts = [os_entry["distro_name"]] """Whether a config should run for the current event.
for key in ("distro_version", "compiler_name", "compiler_version"):
if value := os_entry[key]:
parts.append(value)
parts.append("arm64" if "arm64" in platform else "amd64")
parts.append(build_type.lower())
return "-".join(parts)
'exclude_event_types' is a list of GitHub event names (e.g.
def generate_packaging_matrix(config: Config) -> list[dict]: ["pull_request"]) on which the config should NOT run; an empty list means
"""Emit one entry per os entry with `package: true`. Architecture is the config runs on every event. When no event is given (event is None), no
hardcoded to linux/amd64 here (and the runner is hardcoded at the filtering is applied.
workflow level) until arm64 packaging is ready.
""" """
return [ if event is None:
{ return True
"artifact_name": f"xrpld-{build_config_name(os, 'linux/amd64', 'Release')}", return event not in exclude_event_types
"os": os,
}
for os in config.os
if os.get("package", False)
]
def generate_strategy_matrix(all: bool, config: Config) -> list[dict]: # ---------------------------------------------------------------------------
configurations = [] # Input types — shapes of the JSON config files
for architecture, os, build_type, cmake_args in itertools.product( # ---------------------------------------------------------------------------
config.architecture, config.os, config.build_type, config.cmake_args
):
# The default CMake target is 'all' for Linux and MacOS and 'install'
# for Windows, but it can get overridden for certain configurations.
cmake_target = "install" if os["distro_name"] == "windows" else "all"
# We build and test all configurations by default, except for Windows in
# Debug, because it is too slow, as well as when code coverage is
# enabled as that mode already runs the tests.
build_only = False
if os["distro_name"] == "windows" and build_type == "Debug":
build_only = True
# Only generate a subset of configurations in PRs. @dataclasses.dataclass
if not all: class LinuxConfig:
# Debian: """One entry in linux.json's 'configs' or 'package_configs' arrays."""
# - Bookworm using GCC 13: Debug on linux/amd64, set the reference
# fee to 500 and enable code coverage (which will be done below).
# - Bookworm using GCC 15: Debug on linux/amd64, enable Address and
# UB sanitizers (which will be done below).
# - Bookworm using Clang 16: Debug on linux/amd64, enable voidstar.
# - Bookworm using Clang 17: Release on linux/amd64, set the
# reference fee to 1000.
# - Bookworm using Clang 20: Debug on linux/amd64, enable Address
# and UB sanitizers (which will be done below).
if os["distro_name"] == "debian":
skip = True
if os["distro_version"] == "bookworm":
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-13"
and build_type == "Debug"
and architecture["platform"] == "linux/amd64"
):
cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=500 {cmake_args}"
skip = False
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15"
and build_type == "Release"
and architecture["platform"] == "linux/amd64"
):
skip = False
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-16"
and build_type == "Debug"
and architecture["platform"] == "linux/amd64"
):
cmake_args = f"-Dvoidstar=ON {cmake_args}"
skip = False
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-17"
and build_type == "Release"
and architecture["platform"] == "linux/amd64"
):
cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=1000 {cmake_args}"
skip = False
elif os["distro_version"] == "trixie":
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-22"
and build_type == "Debug"
and architecture["platform"] == "linux/amd64"
):
skip = False
if skip:
continue
# RHEL: compiler: list[str]
# - 9 using GCC 12: Debug and Release on linux/amd64 build_type: list[str]
# (Release is required for RPM packaging). arch: list[str]
# - 10 using Clang: Release on linux/amd64. sanitizers: list[str] = dataclasses.field(default_factory=list)
if os["distro_name"] == "rhel": suffix: str = ""
skip = True extra_cmake_args: str = ""
if os["distro_version"] == "9": image: str = "" # only used by package_configs entries
if ( # List of GitHub event names (e.g. "pull_request") on which this config
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12" # should NOT run. Empty means it runs on every event.
and build_type in ["Debug", "Release"] exclude_event_types: list[str] = dataclasses.field(default_factory=list)
and architecture["platform"] == "linux/amd64"
):
skip = False
elif os["distro_version"] == "10":
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-any"
and build_type == "Release"
and architecture["platform"] == "linux/amd64"
):
skip = False
if skip:
continue
# Ubuntu:
# - Jammy using GCC 12: Debug on linux/arm64, Release on
# linux/amd64 (Release is required for DEB packaging).
# - Noble using GCC 14: Release on linux/amd64.
# - Noble using Clang 18: Debug on linux/amd64.
# - Noble using Clang 19: Release on linux/arm64.
if os["distro_name"] == "ubuntu":
skip = True
if os["distro_version"] == "jammy":
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
and build_type == "Debug"
and architecture["platform"] == "linux/arm64"
):
skip = False
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
and build_type == "Release"
and architecture["platform"] == "linux/amd64"
):
skip = False
elif os["distro_version"] == "noble":
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-14"
and build_type == "Release"
and architecture["platform"] == "linux/amd64"
):
skip = False
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-18"
and build_type == "Debug"
and architecture["platform"] == "linux/amd64"
):
skip = False
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-19"
and build_type == "Release"
and architecture["platform"] == "linux/arm64"
):
skip = False
if skip:
continue
# MacOS: @dataclasses.dataclass
# - Debug on macos/arm64. class LinuxFile:
if os["distro_name"] == "macos" and not ( """Shape of linux.json."""
build_type == "Debug" and architecture["platform"] == "macos/arm64"
): image_tag: str
configs: dict[str, list[LinuxConfig]] # distro → configs
package_configs: dict[str, list[LinuxConfig]] # distro → packaging configs
@classmethod
def load(cls, path: Path) -> "LinuxFile":
data = json.loads(path.read_text())
def parse(section: dict) -> dict[str, list[LinuxConfig]]:
return {
distro: [LinuxConfig(**c) for c in cfgs]
for distro, cfgs in section.items()
}
return cls(
image_tag=data["image_tag"],
configs=parse(data["configs"]),
package_configs=parse(data.get("package_configs", {})),
)
@dataclasses.dataclass
class PlatformConfig:
"""One entry in macos.json's or windows.json's 'configs' array."""
build_type: list[str]
build_only: bool = False # if true, skip tests (e.g. macos/Windows Debug)
extra_cmake_args: str = ""
# List of GitHub event names (e.g. "pull_request") on which this config
# should NOT run. Empty means it runs on every event.
exclude_event_types: list[str] = dataclasses.field(default_factory=list)
def __post_init__(self) -> None:
if isinstance(self.build_type, str):
self.build_type = [self.build_type]
@dataclasses.dataclass
class PlatformFile:
"""Shape of macos.json and windows.json."""
platform: str # e.g. "macos/arm64" or "windows/amd64"
runner: list[str] # GitHub Actions runner labels
configs: list[PlatformConfig]
@classmethod
def load(cls, path: Path) -> "PlatformFile":
data = json.loads(path.read_text())
return cls(
platform=data["platform"],
runner=data["runner"],
configs=[PlatformConfig(**c) for c in data["configs"]],
)
# ---------------------------------------------------------------------------
# Output types — shapes of the generated GitHub Actions matrix entries
# ---------------------------------------------------------------------------
@dataclasses.dataclass
class Architecture:
platform: str
runner: list[str]
@dataclasses.dataclass
class MatrixEntry:
"""One entry in the generated build/test strategy matrix."""
config_name: str
cmake_args: str
cmake_target: str
build_only: bool
build_type: str
architecture: Architecture
sanitizers: str
image: str = "" # container image; empty for macOS/Windows (runs natively)
compiler: str = "" # compiler name ("gcc" or "clang"); empty for macOS/Windows
@dataclasses.dataclass
class PackagingEntry:
"""One entry in the generated packaging strategy matrix."""
artifact_name: str
image: str
distro: str # e.g. "debian" or "rhel"; drives package-format-specific steps
# ---------------------------------------------------------------------------
# Matrix expansion
# ---------------------------------------------------------------------------
_ARCHS: dict[str, Architecture] = {
"amd64": Architecture(
platform="linux/amd64", runner=["self-hosted", "Linux", "X64", "heavy"]
),
"arm64": Architecture(
platform="linux/arm64",
runner=["self-hosted", "Linux", "ARM64", "heavy-arm64"],
),
}
def expand_linux_matrix(
linux: LinuxFile, event: str | None = None
) -> list[MatrixEntry]:
"""Expand a LinuxFile into a flat list of matrix entries.
Each config entry is expanded over the cross-product of its
compiler, build_type, sanitizers, and architecture lists. Configs that
exclude the current event are skipped.
"""
entries: list[MatrixEntry] = []
for distro, configs in linux.configs.items():
for cfg in configs:
if not runs_on_event(cfg.exclude_event_types, event):
continue continue
# An empty sanitizers list means "one entry with no sanitizer".
effective_sanitizers = cfg.sanitizers or [""]
effective_archs = {arch: _ARCHS[arch] for arch in cfg.arch}
# Windows: for compiler, build_type, sanitizer, (arch, arch_info) in itertools.product(
# - Release on windows/amd64. cfg.compiler,
if os["distro_name"] == "windows" and not ( cfg.build_type,
build_type == "Release" and architecture["platform"] == "windows/amd64" effective_sanitizers,
effective_archs.items(),
): ):
continue name = f"{distro}-{compiler}-{build_type.lower()}-{arch}"
suffix_parts = [
s for s in [cfg.suffix, _SANITIZER_SUFFIX.get(sanitizer, "")] if s
]
if suffix_parts:
name += "-" + "-".join(suffix_parts)
# Additional CMake arguments. entries.append(
cmake_args = f"{cmake_args} -Dtests=ON -Dwerr=ON -Dxrpld=ON" MatrixEntry(
if not f"{os['compiler_name']}-{os['compiler_version']}" in [ config_name=name,
"gcc-12", image=f"ghcr.io/xrplf/xrpld/nix-{distro}:{linux.image_tag}",
"clang-16", cmake_args=get_cmake_args(build_type, cfg.extra_cmake_args),
]: cmake_target="all",
cmake_args = f"{cmake_args} -Dwextra=ON" build_only=False,
if build_type == "Release": build_type=build_type,
cmake_args = f"{cmake_args} -Dassert=ON" architecture=arch_info,
sanitizers=sanitizer,
# We skip all RHEL on arm64 due to a build failure that needs further compiler=compiler,
# investigation. )
if os["distro_name"] == "rhel" and architecture["platform"] == "linux/arm64": )
continue
return entries
# We skip all clang 20+ on arm64 due to Boost build error.
if (
os["compiler_name"] == "clang" def expand_linux_packaging(linux: LinuxFile) -> list[PackagingEntry]:
and os["compiler_version"].isdigit() """Generate the packaging matrix from a LinuxFile's package_configs section.
and int(os["compiler_version"]) >= 20
and architecture["platform"] == "linux/arm64" Packaging uses vanilla distro images (debian:bookworm, ubi9, …) instead of
): the nix-based build images, because deb/rpm tooling (debhelper, rpm-build)
continue is taken from the distro's archive rather than from nixpkgs. Each config
entry carries its own 'image'.
# Enable code coverage for Debian Bookworm using GCC 13 in Debug on """
# linux/amd64. entries = []
if ( for distro, configs in linux.package_configs.items():
f"{os['distro_name']}-{os['distro_version']}" == "debian-bookworm" for cfg in configs:
and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-13" for compiler, build_type in itertools.product(cfg.compiler, cfg.build_type):
and build_type == "Debug" entries.append(
and architecture["platform"] == "linux/amd64" PackagingEntry(
): artifact_name=f"xrpld-{distro}-{compiler}-{build_type.lower()}-amd64",
cmake_args = f"{cmake_args} -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0" image=cfg.image,
distro=distro,
# Enable unity build for Ubuntu Jammy using GCC 12 in Debug on )
# linux/amd64. )
if (
f"{os['distro_name']}-{os['distro_version']}" == "ubuntu-jammy" return entries
and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
and build_type == "Debug"
and architecture["platform"] == "linux/amd64" def expand_platform_matrix(
): pf: PlatformFile, event: str | None = None
cmake_args = f"{cmake_args} -Dunity=ON" ) -> list[MatrixEntry]:
"""Expand a PlatformFile (macOS or Windows) into matrix entries.
# Generate a unique name for the configuration, e.g. macos-arm64-debug
# or debian-bookworm-gcc-12-amd64-release. Configs that exclude the current event are skipped.
config_name = build_config_name(os, architecture["platform"], build_type) """
if "-Dcoverage=ON" in cmake_args: platform_name, arch = pf.platform.split("/")
config_name += "-coverage" is_windows = platform_name == "windows"
if "-Dunity=ON" in cmake_args:
config_name += "-unity" entries: list[MatrixEntry] = []
for cfg in pf.configs:
# Add the configuration to the list, with the most unique fields first, if not runs_on_event(cfg.exclude_event_types, event):
# so that they are easier to identify in the GitHub Actions UI, as long continue
# names get truncated. for build_type in cfg.build_type:
# Add Address and UB sanitizers as separate configurations for specific entries.append(
# bookworm distros. Thread sanitizer is currently disabled (see below). MatrixEntry(
# GCC-Asan xrpld-embedded tests are failing because of https://github.com/google/sanitizers/issues/856 config_name=f"{platform_name}-{arch}-{build_type.lower()}",
if ( cmake_args=get_cmake_args(build_type, cfg.extra_cmake_args),
os["distro_version"] == "bookworm" cmake_target="install" if is_windows else "all",
and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15" build_only=cfg.build_only,
) or ( build_type=build_type,
os["distro_version"] == "trixie" architecture=Architecture(platform=pf.platform, runner=pf.runner),
and f"{os['compiler_name']}-{os['compiler_version']}" == "clang-22" sanitizers="",
):
# Add ASAN and UBSAN configurations for both gcc-15 and clang-22
configurations.append(
{
"config_name": config_name + "-asan",
"cmake_args": cmake_args,
"cmake_target": cmake_target,
"build_only": build_only,
"build_type": build_type,
"os": os,
"architecture": architecture,
"sanitizers": "address",
}
)
configurations.append(
{
"config_name": config_name + "-ubsan",
"cmake_args": cmake_args,
"cmake_target": cmake_target,
"build_only": build_only,
"build_type": build_type,
"os": os,
"architecture": architecture,
"sanitizers": "undefinedbehavior",
}
)
# TSAN is deactivated due to seg faults with latest compilers.
activate_tsan = False
if activate_tsan:
configurations.append(
{
"config_name": config_name + "-tsan-ubsan",
"cmake_args": cmake_args,
"cmake_target": cmake_target,
"build_only": build_only,
"build_type": build_type,
"os": os,
"architecture": architecture,
"sanitizers": "thread,undefinedbehavior",
}
) )
else:
configurations.append(
{
"config_name": config_name,
"cmake_args": cmake_args,
"cmake_target": cmake_target,
"build_only": build_only,
"build_type": build_type,
"os": os,
"architecture": architecture,
"sanitizers": "",
}
) )
return entries
return configurations
def read_config(file: Path) -> Config: # ---------------------------------------------------------------------------
config = json.loads(file.read_text()) # Entry point
if ( # ---------------------------------------------------------------------------
config["architecture"] is None
or config["os"] is None
or config["build_type"] is None
or config["cmake_args"] is None
):
raise Exception("Invalid configuration file.")
return Config(**config)
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser(
parser.add_argument( description="Generate a CI strategy matrix for all platforms or a specific one."
"-a",
"--all",
help="Set to generate all configurations (generally used when merging a PR) or leave unset to generate a subset of configurations (generally used when committing to a PR).",
action="store_true",
) )
parser.add_argument( parser.add_argument(
"-c", "-c",
"--config", "--config",
help="Path to the JSON file containing the strategy matrix configurations.", help="Platform to generate for ('linux', 'macos', or 'windows'). Defaults to all platforms.",
required=False, choices=["linux", "macos", "windows"],
type=Path, default=None,
) )
parser.add_argument( parser.add_argument(
"-p", "-p",
"--packaging", "--packaging",
help="Emit the packaging matrix (derived from the 'package' field on os entries) instead of the build/test matrix.", help="Emit the Linux packaging matrix instead of the build/test matrix.",
action="store_true", action="store_true",
) )
parser.add_argument(
"-e",
"--event",
help="The GitHub event name that triggered the workflow (e.g. 'push', "
"'pull_request'). Configs are filtered by their 'event_type'. If "
"omitted, no filtering is applied.",
default=None,
)
args = parser.parse_args() args = parser.parse_args()
matrix = [] matrix: list[MatrixEntry] | list[PackagingEntry] = []
if args.packaging:
config_path = args.config if args.config else THIS_DIR / "linux.json"
matrix += generate_packaging_matrix(read_config(config_path))
elif args.config is None or args.config == "":
matrix += generate_strategy_matrix(
args.all, read_config(THIS_DIR / "linux.json")
)
matrix += generate_strategy_matrix(
args.all, read_config(THIS_DIR / "macos.json")
)
matrix += generate_strategy_matrix(
args.all, read_config(THIS_DIR / "windows.json")
)
else:
matrix += generate_strategy_matrix(args.all, read_config(args.config))
# Generate the strategy matrix. if args.packaging:
print(f"matrix={json.dumps({'include': matrix})}") matrix = expand_linux_packaging(LinuxFile.load(THIS_DIR / "linux.json"))
else:
if args.config in ("linux", None):
matrix += expand_linux_matrix(
LinuxFile.load(THIS_DIR / "linux.json"), args.event
)
if args.config in ("macos", None):
matrix += expand_platform_matrix(
PlatformFile.load(THIS_DIR / "macos.json"), args.event
)
if args.config in ("windows", None):
matrix += expand_platform_matrix(
PlatformFile.load(THIS_DIR / "windows.json"), args.event
)
print(f"matrix={json.dumps({'include': [dataclasses.asdict(e) for e in matrix]})}")

View File

@@ -1,221 +1,84 @@
{ {
"architecture": [ "image_tag": "sha-e29b523",
{ "configs": {
"platform": "linux/amd64", "ubuntu": [
"runner": ["self-hosted", "Linux", "X64", "heavy"] {
}, "compiler": ["gcc", "clang"],
{ "build_type": ["Debug", "Release"],
"platform": "linux/arm64", "arch": ["amd64", "arm64"]
"runner": ["self-hosted", "Linux", "ARM64", "heavy-arm64"] },
}
], {
"os": [ "compiler": ["gcc", "clang"],
{ "build_type": ["Debug", "Release"],
"distro_name": "debian", "arch": ["amd64"],
"distro_version": "bookworm", "sanitizers": ["address", "undefinedbehavior"]
"compiler_name": "gcc", },
"compiler_version": "12",
"image_sha": "4c086b9" {
}, "compiler": ["gcc"],
{ "build_type": ["Debug"],
"distro_name": "debian", "arch": ["amd64"],
"distro_version": "bookworm", "suffix": "coverage",
"compiler_name": "gcc", "extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=500 -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0"
"compiler_version": "13", },
"image_sha": "4c086b9" {
}, "compiler": ["clang"],
{ "build_type": ["Debug"],
"distro_name": "debian", "arch": ["amd64"],
"distro_version": "bookworm", "suffix": "voidstar",
"compiler_name": "gcc", "extra_cmake_args": "-Dvoidstar=ON"
"compiler_version": "14", },
"image_sha": "4c086b9" {
}, "compiler": ["clang"],
{ "build_type": ["Release"],
"distro_name": "debian", "arch": ["amd64"],
"distro_version": "bookworm", "suffix": "reffee",
"compiler_name": "gcc", "extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=1000"
"compiler_version": "15", },
"image_sha": "4c086b9" {
}, "compiler": ["gcc"],
{ "build_type": ["Debug"],
"distro_name": "debian", "arch": ["amd64"],
"distro_version": "bookworm", "suffix": "unity",
"compiler_name": "clang", "extra_cmake_args": "-Dunity=ON",
"compiler_version": "16", "exclude_event_types": ["pull_request"]
"image_sha": "4c086b9" }
}, ],
{
"distro_name": "debian", "debian": [
"distro_version": "bookworm", {
"compiler_name": "clang", "compiler": ["gcc"],
"compiler_version": "17", "build_type": ["Release"],
"image_sha": "4c086b9" "arch": ["amd64"]
}, }
{ ],
"distro_name": "debian",
"distro_version": "bookworm", "rhel": [
"compiler_name": "clang", {
"compiler_version": "18", "compiler": ["gcc"],
"image_sha": "4c086b9" "build_type": ["Release"],
}, "arch": ["amd64"]
{ }
"distro_name": "debian", ]
"distro_version": "bookworm", },
"compiler_name": "clang", "package_configs": {
"compiler_version": "19", "debian": [
"image_sha": "4c086b9" {
}, "compiler": ["gcc"],
{ "build_type": ["Release"],
"distro_name": "debian", "arch": ["amd64"],
"distro_version": "bookworm", "image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-577d745"
"compiler_name": "clang", }
"compiler_version": "20", ],
"image_sha": "4c086b9"
}, "rhel": [
{ {
"distro_name": "debian", "compiler": ["gcc"],
"distro_version": "trixie", "build_type": ["Release"],
"compiler_name": "gcc", "arch": ["amd64"],
"compiler_version": "14", "image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-577d745"
"image_sha": "4c086b9" }
}, ]
{ }
"distro_name": "debian",
"distro_version": "trixie",
"compiler_name": "gcc",
"compiler_version": "15",
"image_sha": "4c086b9"
},
{
"distro_name": "debian",
"distro_version": "trixie",
"compiler_name": "clang",
"compiler_version": "20",
"image_sha": "4c086b9"
},
{
"distro_name": "debian",
"distro_version": "trixie",
"compiler_name": "clang",
"compiler_version": "21",
"image_sha": "4c086b9"
},
{
"distro_name": "debian",
"distro_version": "trixie",
"compiler_name": "clang",
"compiler_version": "22",
"image_sha": "4c086b9"
},
{
"distro_name": "rhel",
"distro_version": "8",
"compiler_name": "gcc",
"compiler_version": "14",
"image_sha": "4c086b9"
},
{
"distro_name": "rhel",
"distro_version": "8",
"compiler_name": "clang",
"compiler_version": "any",
"image_sha": "4c086b9"
},
{
"distro_name": "rhel",
"distro_version": "9",
"compiler_name": "gcc",
"compiler_version": "12",
"image_sha": "4c086b9",
"package": true
},
{
"distro_name": "rhel",
"distro_version": "9",
"compiler_name": "gcc",
"compiler_version": "13",
"image_sha": "4c086b9"
},
{
"distro_name": "rhel",
"distro_version": "9",
"compiler_name": "gcc",
"compiler_version": "14",
"image_sha": "4c086b9"
},
{
"distro_name": "rhel",
"distro_version": "9",
"compiler_name": "clang",
"compiler_version": "any",
"image_sha": "4c086b9"
},
{
"distro_name": "rhel",
"distro_version": "10",
"compiler_name": "gcc",
"compiler_version": "14",
"image_sha": "4c086b9"
},
{
"distro_name": "rhel",
"distro_version": "10",
"compiler_name": "clang",
"compiler_version": "any",
"image_sha": "4c086b9"
},
{
"distro_name": "ubuntu",
"distro_version": "jammy",
"compiler_name": "gcc",
"compiler_version": "12",
"image_sha": "4c086b9",
"package": true
},
{
"distro_name": "ubuntu",
"distro_version": "noble",
"compiler_name": "gcc",
"compiler_version": "13",
"image_sha": "4c086b9"
},
{
"distro_name": "ubuntu",
"distro_version": "noble",
"compiler_name": "gcc",
"compiler_version": "14",
"image_sha": "4c086b9"
},
{
"distro_name": "ubuntu",
"distro_version": "noble",
"compiler_name": "clang",
"compiler_version": "16",
"image_sha": "4c086b9"
},
{
"distro_name": "ubuntu",
"distro_version": "noble",
"compiler_name": "clang",
"compiler_version": "17",
"image_sha": "4c086b9"
},
{
"distro_name": "ubuntu",
"distro_version": "noble",
"compiler_name": "clang",
"compiler_version": "18",
"image_sha": "4c086b9"
},
{
"distro_name": "ubuntu",
"distro_version": "noble",
"compiler_name": "clang",
"compiler_version": "19",
"image_sha": "4c086b9"
}
],
"build_type": ["Debug", "Release"],
"cmake_args": [""]
} }

View File

@@ -1,19 +1,16 @@
{ {
"architecture": [ "platform": "macos/arm64",
"runner": ["self-hosted", "macOS", "ARM64", "mac-runner-m1"],
"configs": [
{ {
"platform": "macos/arm64", "build_type": "Release",
"runner": ["self-hosted", "macOS", "ARM64", "mac-runner-m1"] "extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5"
} },
],
"os": [
{ {
"distro_name": "macos", "build_type": "Debug",
"distro_version": "", "extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
"compiler_name": "", "build_only": true,
"compiler_version": "", "exclude_event_types": ["pull_request"]
"image_sha": ""
} }
], ]
"build_type": ["Debug", "Release"],
"cmake_args": ["-DCMAKE_POLICY_VERSION_MINIMUM=3.5"]
} }

View File

@@ -1,19 +1,12 @@
{ {
"architecture": [ "platform": "windows/amd64",
"runner": ["self-hosted", "Windows", "dev-box-windows-2026"],
"configs": [
{ "build_type": "Release" },
{ {
"platform": "windows/amd64", "build_type": "Debug",
"runner": ["self-hosted", "Windows", "devbox"] "build_only": true,
"exclude_event_types": ["pull_request"]
} }
], ]
"os": [
{
"distro_name": "windows",
"distro_version": "",
"compiler_name": "",
"compiler_version": "",
"image_sha": ""
}
],
"build_type": ["Debug", "Release"],
"cmake_args": [""]
} }

View File

@@ -1,101 +0,0 @@
name: Build Nix Docker image
on:
push:
branches:
- develop
paths:
- ".github/workflows/build-nix-image.yml"
- "docker/nix.Dockerfile"
- "flake.nix"
- "flake.lock"
- "nix/**"
pull_request:
paths:
- ".github/workflows/build-nix-image.yml"
- "docker/nix.Dockerfile"
- "flake.nix"
- "flake.lock"
- "nix/**"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
env:
UBUNTU_VERSION: "20.04"
RHEL_VERSION: "9"
DEBIAN_VERSION: "bookworm"
jobs:
build:
name: Build and push Nix image (${{ matrix.distro }})
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
strategy:
matrix:
include:
- distro: nixos
- distro: ubuntu
- distro: rhel
- distro: debian
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Determine base image
id: vars
run: |
case "${{ matrix.distro }}" in
nixos)
echo "base_image=nixos/nix:latest" >> $GITHUB_OUTPUT
;;
ubuntu)
echo "base_image=ubuntu:${UBUNTU_VERSION}" >> $GITHUB_OUTPUT
;;
rhel)
echo "base_image=registry.access.redhat.com/ubi${RHEL_VERSION}/ubi:latest" >> $GITHUB_OUTPUT
;;
debian)
echo "base_image=debian:${DEBIAN_VERSION}" >> $GITHUB_OUTPUT
;;
esac
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Login to GitHub Container Registry
if: github.event_name == 'push'
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker metadata
id: meta
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
with:
images: ghcr.io/xrplf/ci/nix-${{ matrix.distro }}
tags: |
type=sha,prefix=sha-,format=short
type=raw,value=latest
- name: Build and push
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
file: docker/nix.Dockerfile
platforms: linux/amd64
push: ${{ github.event_name == 'push' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: BASE_IMAGE=${{ steps.vars.outputs.base_image }}

62
.github/workflows/build-nix-images.yml vendored Normal file
View File

@@ -0,0 +1,62 @@
name: Build Nix Docker images
on:
push:
branches:
- develop
paths:
- ".github/workflows/build-nix-images.yml"
- "flake.nix"
- "flake.lock"
- "nix/**"
- "!nix/docker/README.md"
- "!nix/devshell.nix"
- "bin/check-tools.sh"
- "bin/install-sanitizer-libs.sh"
pull_request:
paths:
- ".github/workflows/build-nix-images.yml"
- "flake.nix"
- "flake.lock"
- "nix/**"
- "!nix/docker/README.md"
- "!nix/devshell.nix"
- "bin/check-tools.sh"
- "bin/install-sanitizer-libs.sh"
workflow_dispatch:
concurrency:
# Read `on-trigger.yml` for the rationale behind this concurrency group name.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' && github.sha || github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
build-merge:
name: Build and push nix-${{ matrix.distro.name }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
# The base images are the oldest supported version of each distro
# that we want to build images for.
distro:
- name: nixos
base_image: nixos/nix:latest
- name: ubuntu
base_image: ubuntu:20.04
- name: debian
base_image: debian:bookworm
- name: rhel
base_image: registry.access.redhat.com/ubi9/ubi:latest
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@ee03d31bcc4501d7599dc1b1ecd7a34af582ad1c
with:
image_name: xrpld/nix-${{ matrix.distro.name }}
dockerfile: nix/docker/Dockerfile
base_image: ${{ matrix.distro.base_image }}
push: ${{ github.event_name == 'push' }}

View File

@@ -0,0 +1,46 @@
name: Build packaging Docker images
on:
push:
branches:
- develop
paths:
- ".github/workflows/build-packaging-images.yml"
- "package/Dockerfile"
- "package/install-packaging-tools.sh"
pull_request:
paths:
- ".github/workflows/build-packaging-images.yml"
- "package/Dockerfile"
- "package/install-packaging-tools.sh"
workflow_dispatch:
concurrency:
# Read `on-trigger.yml` for the rationale behind this concurrency group name.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' && github.sha || github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
build-merge:
name: Build and push packaging-${{ matrix.distro.name }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
distro:
- name: debian
base_image: debian:bookworm
- name: rhel
base_image: registry.access.redhat.com/ubi9/ubi:latest
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@ee03d31bcc4501d7599dc1b1ecd7a34af582ad1c
with:
image_name: xrpld/packaging-${{ matrix.distro.name }}
dockerfile: package/Dockerfile
base_image: ${{ matrix.distro.base_image }}
push: ${{ github.event_name == 'push' }}

View File

@@ -5,8 +5,17 @@ on:
types: types:
- checks_requested - checks_requested
pull_request: pull_request:
types: [opened, edited, reopened, synchronize, ready_for_review] types:
branches: [develop] - opened
- edited
- reopened
- synchronize
- ready_for_review
branches:
- develop
- "release-*"
- "release/*"
- "staging/*"
jobs: jobs:
check_description: check_description:
@@ -14,17 +23,17 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Write PR body to file - name: Write PR body to file
env: env:
PR_BODY: ${{ github.event.pull_request.body }} PR_BODY: ${{ github.event.pull_request.body }}
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
run: printenv PR_BODY > pr_body.md run: printenv PR_BODY >pr_body.md
- name: Check PR description differs from template - name: Check PR description differs from template
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
run: > run: |
python .github/scripts/check-pr-description.py python .github/scripts/check-pr-description.py \
--template-file .github/pull_request_template.md --template-file .github/pull_request_template.md \
--pr-body-file pr_body.md --pr-body-file pr_body.md

View File

@@ -5,10 +5,19 @@ on:
types: types:
- checks_requested - checks_requested
pull_request: pull_request:
types: [opened, edited, reopened, synchronize, ready_for_review] types:
branches: [develop] - opened
- edited
- reopened
- synchronize
- ready_for_review
branches:
- develop
- "release-*"
- "release/*"
- "staging/*"
jobs: jobs:
check_title: check_title:
if: ${{ github.event.pull_request.draft != true }} if: ${{ github.event.pull_request.draft != true }}
uses: XRPLF/actions/.github/workflows/check-pr-title.yml@291206777251b4d493641b5afbdf7c23009d2988 uses: XRPLF/actions/.github/workflows/check-pr-title.yml@cba1f0891650baf1a9c88624dc2d72573be2eb81

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check if PRs are dirty - name: Check if PRs are dirty
uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3 uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # v3.1.0
with: with:
dirtyLabel: "PR: has conflicts" dirtyLabel: "PR: has conflicts"
repoToken: "${{ secrets.GITHUB_TOKEN }}" repoToken: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -33,7 +33,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Determine changed files - name: Determine changed files
# This step checks whether any files have changed that should # This step checks whether any files have changed that should
# cause the next jobs to run. We do it this way rather than # cause the next jobs to run. We do it this way rather than
@@ -70,6 +70,7 @@ jobs:
.github/workflows/reusable-upload-recipe.yml .github/workflows/reusable-upload-recipe.yml
.clang-tidy .clang-tidy
.codecov.yml .codecov.yml
bin/check-tools.sh
cfg/** cfg/**
cmake/** cmake/**
conan/** conan/**
@@ -98,7 +99,7 @@ jobs:
READY: ${{ contains(github.event.pull_request.labels.*.name, 'Ready to merge') }} READY: ${{ contains(github.event.pull_request.labels.*.name, 'Ready to merge') }}
MERGE: ${{ github.event_name == 'merge_group' }} MERGE: ${{ github.event_name == 'merge_group' }}
run: | run: |
echo "go=${{ (env.DRAFT != 'true' && env.READY == 'true') || env.FILES == 'true' || env.MERGE == 'true' }}" >> "${GITHUB_OUTPUT}" echo "go=${{ (env.DRAFT != 'true' && env.READY == 'true') || env.FILES == 'true' || env.MERGE == 'true' }}" >>"${GITHUB_OUTPUT}"
cat "${GITHUB_OUTPUT}" cat "${GITHUB_OUTPUT}"
outputs: outputs:
go: ${{ steps.go.outputs.go == 'true' }} go: ${{ steps.go.outputs.go == 'true' }}
@@ -168,9 +169,9 @@ jobs:
PR_URL: ${{ github.event.pull_request.html_url }} PR_URL: ${{ github.event.pull_request.html_url }}
run: | run: |
gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \ gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \
/repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \ /repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \
-F "client_payload[ref]=${{ needs.upload-recipe.outputs.recipe_ref }}" \ -F "client_payload[ref]=${{ needs.upload-recipe.outputs.recipe_ref }}" \
-F "client_payload[pr_url]=${PR_URL}" -F "client_payload[pr_url]=${PR_URL}"
passed: passed:
if: failure() || cancelled() if: failure() || cancelled()

View File

@@ -33,7 +33,6 @@ jobs:
with: with:
ccache_enabled: false ccache_enabled: false
os: ${{ matrix.os }} os: ${{ matrix.os }}
strategy_matrix: minimal
secrets: secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View File

@@ -27,6 +27,7 @@ on:
- ".github/workflows/reusable-upload-recipe.yml" - ".github/workflows/reusable-upload-recipe.yml"
- ".clang-tidy" - ".clang-tidy"
- ".codecov.yml" - ".codecov.yml"
- "bin/check-tools.sh"
- "cfg/**" - "cfg/**"
- "cmake/**" - "cmake/**"
- "conan/**" - "conan/**"
@@ -88,7 +89,6 @@ jobs:
# not identical to a regular compilation. # not identical to a regular compilation.
ccache_enabled: ${{ github.repository_owner == 'XRPLF' && !startsWith(github.ref, 'refs/heads/release') }} ccache_enabled: ${{ github.repository_owner == 'XRPLF' && !startsWith(github.ref, 'refs/heads/release') }}
os: ${{ matrix.os }} os: ${{ matrix.os }}
strategy_matrix: ${{ github.event_name == 'schedule' && 'all' || 'minimal' }}
secrets: secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View File

@@ -14,7 +14,7 @@ on:
jobs: jobs:
# Call the workflow in the XRPLF/actions repo that runs the pre-commit hooks. # Call the workflow in the XRPLF/actions repo that runs the pre-commit hooks.
run-hooks: run-hooks:
uses: XRPLF/actions/.github/workflows/pre-commit.yml@5e942d61bf32f7557a7c159cfac4712a687b3e3a uses: XRPLF/actions/.github/workflows/pre-commit.yml@e06d4138c9ec8dceeb7c818645faa38087ea9e3d
with: with:
runs_on: ubuntu-latest runs_on: ubuntu-latest
container: '{ "image": "ghcr.io/xrplf/ci/tools-rippled-pre-commit:sha-41ec7c1" }' container: '{ "image": "ghcr.io/xrplf/ci/tools-rippled-pre-commit:sha-41ec7c1" }'

View File

@@ -41,13 +41,13 @@ env:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: ghcr.io/xrplf/ci/tools-rippled-documentation:sha-a8c7be1 container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-e29b523
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Prepare runner - name: Prepare runner
uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8
with: with:
enable_ccache: false enable_ccache: false
@@ -57,19 +57,11 @@ jobs:
with: with:
subtract: ${{ env.NPROC_SUBTRACT }} subtract: ${{ env.NPROC_SUBTRACT }}
- name: Check configuration - name: Print build environment
run: | uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574
echo 'Checking path.'
echo ${PATH} | tr ':' '\n'
echo 'Checking environment variables.' - name: Check Doxygen version
env | sort run: doxygen --version
echo 'Checking CMake version.'
cmake --version
echo 'Checking Doxygen version.'
doxygen --version
- name: Build documentation - name: Build documentation
env: env:

View File

@@ -57,6 +57,12 @@ on:
type: string type: string
default: "" default: ""
compiler:
description: 'The compiler to use ("gcc" or "clang"). Leave empty for macOS/Windows (uses system default).'
required: false
type: string
default: ""
secrets: secrets:
CODECOV_TOKEN: CODECOV_TOKEN:
description: "The Codecov token to use for uploading coverage reports." description: "The Codecov token to use for uploading coverage reports."
@@ -76,7 +82,7 @@ jobs:
name: ${{ inputs.config_name }} name: ${{ inputs.config_name }}
runs-on: ${{ fromJSON(inputs.runs_on) }} runs-on: ${{ fromJSON(inputs.runs_on) }}
container: ${{ inputs.image != '' && inputs.image || null }} container: ${{ inputs.image != '' && inputs.image || null }}
timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 60 }} timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 180 }}
env: env:
# Use a namespace to keep the objects separate for each configuration. # Use a namespace to keep the objects separate for each configuration.
CCACHE_NAMESPACE: ${{ inputs.config_name }} CCACHE_NAMESPACE: ${{ inputs.config_name }}
@@ -104,16 +110,21 @@ jobs:
uses: XRPLF/actions/cleanup-workspace@c7d9ce5ebb03c752a354889ecd870cadfc2b1cd4 uses: XRPLF/actions/cleanup-workspace@c7d9ce5ebb03c752a354889ecd870cadfc2b1cd4
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Prepare runner - name: Prepare runner
uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8
with: with:
enable_ccache: ${{ inputs.ccache_enabled }} enable_ccache: ${{ inputs.ccache_enabled }}
- name: Set ccache log file - name: Set ccache log file
if: ${{ inputs.ccache_enabled && runner.debug == '1' }} if: ${{ inputs.ccache_enabled && runner.debug == '1' }}
run: echo "CCACHE_LOGFILE=${{ runner.temp }}/ccache.log" >> "${GITHUB_ENV}" run: echo "CCACHE_LOGFILE=${{ runner.temp }}/ccache.log" >>"${GITHUB_ENV}"
- name: Check tools
env:
CHECK_TOOLS_SKIP_CLONE: "1"
run: ./bin/check-tools.sh
- name: Print build environment - name: Print build environment
uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574 uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574
@@ -124,6 +135,12 @@ jobs:
with: with:
subtract: ${{ inputs.nproc_subtract }} subtract: ${{ inputs.nproc_subtract }}
- name: Set compiler environment (Linux)
if: ${{ runner.os == 'Linux' }}
uses: ./.github/actions/set-compiler-env
with:
compiler: ${{ inputs.compiler }}
- name: Setup Conan - name: Setup Conan
env: env:
SANITIZERS: ${{ inputs.sanitizers }} SANITIZERS: ${{ inputs.sanitizers }}
@@ -146,11 +163,32 @@ jobs:
CMAKE_ARGS: ${{ inputs.cmake_args }} CMAKE_ARGS: ${{ inputs.cmake_args }}
run: | run: |
cmake \ cmake \
-G '${{ runner.os == 'Windows' && 'Visual Studio 17 2022' || 'Ninja' }}' \ -G '${{ runner.os == 'Windows' && 'Visual Studio 18 2026' || 'Ninja' }}' \
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \
-DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \
${CMAKE_ARGS} \ ${CMAKE_ARGS} \
.. ..
# Export the sanitizer options before any instrumented binary runs. The
# protocol code-gen and build steps below invoke instrumented dependency
# tools (protoc, grpc), so setting UBSAN_OPTIONS here lets the UBSan
# suppression list silence their diagnostics too, not just at test time.
# GITHUB_WORKSPACE (not the github.workspace context) is used so the path
# resolves correctly inside the container job.
- name: Set sanitizer options
if: ${{ !inputs.build_only && env.SANITIZERS_ENABLED == 'true' }}
env:
CONFIG_NAME: ${{ inputs.config_name }}
run: |
SUPP="${GITHUB_WORKSPACE}/sanitizers/suppressions"
ASAN_OPTS="include=${SUPP}/runtime-asan-options.txt:suppressions=${SUPP}/asan.supp"
if [[ "${CONFIG_NAME}" == *gcc* ]]; then
ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0"
fi
echo "ASAN_OPTIONS=${ASAN_OPTS}" >>${GITHUB_ENV}
echo "TSAN_OPTIONS=include=${SUPP}/runtime-tsan-options.txt:suppressions=${SUPP}/tsan.supp" >>${GITHUB_ENV}
echo "UBSAN_OPTIONS=include=${SUPP}/runtime-ubsan-options.txt:suppressions=${SUPP}/ubsan.supp" >>${GITHUB_ENV}
echo "LSAN_OPTIONS=include=${SUPP}/runtime-lsan-options.txt:suppressions=${SUPP}/lsan.supp" >>${GITHUB_ENV}
- name: Check protocol autogen files are up-to-date - name: Check protocol autogen files are up-to-date
working-directory: ${{ env.BUILD_DIR }} working-directory: ${{ env.BUILD_DIR }}
@@ -172,32 +210,33 @@ jobs:
cmake --build . --target code_gen cmake --build . --target code_gen
DIFF=$(git -C .. status --porcelain -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen) DIFF=$(git -C .. status --porcelain -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen)
if [ -n "${DIFF}" ]; then if [ -n "${DIFF}" ]; then
echo "::error::Generated protocol files are out of date" echo "::error::Generated protocol files are out of date"
git -C .. diff -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen git -C .. diff -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen
echo "${MESSAGE}" echo "${MESSAGE}"
exit 1 exit 1
fi fi
- name: Build the binary - name: Build the binary
working-directory: ${{ env.BUILD_DIR }} working-directory: ${{ env.BUILD_DIR }}
env: env:
BUILD_NPROC: ${{ runner.os == 'Linux' && '16' || steps.nproc.outputs.nproc }} BUILD_NPROC: ${{ steps.nproc.outputs.nproc }}
BUILD_TYPE: ${{ inputs.build_type }} BUILD_TYPE: ${{ inputs.build_type }}
CMAKE_TARGET: ${{ inputs.cmake_target }} CMAKE_TARGET: ${{ inputs.cmake_target }}
run: | run: |
cmake \ cmake \
--build . \ --build . \
--config "${BUILD_TYPE}" \ --config "${BUILD_TYPE}" \
--parallel "${BUILD_NPROC}" \ --parallel "${BUILD_NPROC}" \
--target "${CMAKE_TARGET}" --target "${CMAKE_TARGET}" \
2>&1 | tee build.log
- name: Show ccache statistics - name: Show ccache statistics
if: ${{ inputs.ccache_enabled }} if: ${{ inputs.ccache_enabled }}
run: | run: |
ccache --show-stats -vv ccache --show-stats -vv
if [ '${{ runner.debug }}' = '1' ]; then if [ '${{ runner.debug }}' = '1' ]; then
cat "${CCACHE_LOGFILE}" cat "${CCACHE_LOGFILE}"
curl ${CCACHE_REMOTE_STORAGE%|*}/status || true curl ${CCACHE_REMOTE_STORAGE%|*}/status || true
fi fi
- name: Upload the binary (Linux) - name: Upload the binary (Linux)
@@ -209,15 +248,24 @@ jobs:
retention-days: 3 retention-days: 3
if-no-files-found: error if-no-files-found: error
- name: Upload the test binary (Linux)
if: ${{ github.event.repository.visibility == 'public' && runner.os == 'Linux' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: xrpl_tests-${{ inputs.config_name }}
path: ${{ env.BUILD_DIR }}/xrpl_tests
retention-days: 3
if-no-files-found: error
- name: Export server definitions - name: Export server definitions
if: ${{ runner.os != 'Windows' && !inputs.build_only && env.VOIDSTAR_ENABLED != 'true' }} if: ${{ runner.os != 'Windows' && !inputs.build_only && env.VOIDSTAR_ENABLED != 'true' }}
working-directory: ${{ env.BUILD_DIR }} working-directory: ${{ env.BUILD_DIR }}
run: | run: |
set -o pipefail set -o pipefail
./xrpld --definitions | python3 -m json.tool > server_definitions.json ./xrpld --definitions | python3 -m json.tool >server_definitions.json
- name: Upload server definitions - name: Upload server definitions
if: ${{ github.event.repository.visibility == 'public' && inputs.config_name == 'debian-bookworm-gcc-13-amd64-release' }} if: ${{ github.event.repository.visibility == 'public' && inputs.config_name == 'debian-gcc-release-amd64' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with: with:
name: server-definitions name: server-definitions
@@ -231,10 +279,10 @@ jobs:
run: | run: |
ldd ./xrpld ldd ./xrpld
if [ "$(ldd ./xrpld | grep -E '(libstdc\+\+|libgcc)' | wc -l)" -eq 0 ]; then if [ "$(ldd ./xrpld | grep -E '(libstdc\+\+|libgcc)' | wc -l)" -eq 0 ]; then
echo 'The binary is statically linked.' echo 'The binary is statically linked.'
else else
echo 'The binary is dynamically linked.' echo 'The binary is dynamically linked.'
exit 1 exit 1
fi fi
- name: Verify presence of instrumentation (Linux) - name: Verify presence of instrumentation (Linux)
@@ -243,32 +291,10 @@ jobs:
run: | run: |
./xrpld --version | grep libvoidstar ./xrpld --version | grep libvoidstar
- name: Set sanitizer options
if: ${{ !inputs.build_only && env.SANITIZERS_ENABLED == 'true' }}
env:
CONFIG_NAME: ${{ inputs.config_name }}
run: |
ASAN_OPTS="include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-asan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp"
if [[ "${CONFIG_NAME}" == *gcc* ]]; then
ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0"
fi
echo "ASAN_OPTIONS=${ASAN_OPTS}" >> ${GITHUB_ENV}
echo "TSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-tsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV}
echo "UBSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV}
echo "LSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-lsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV}
- name: Run the separate tests - name: Run the separate tests
if: ${{ !inputs.build_only }} if: ${{ !inputs.build_only }}
working-directory: ${{ env.BUILD_DIR }} working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }}
# Windows locks some of the build files while running tests, and parallel jobs can collide run: ./xrpl_tests
env:
BUILD_TYPE: ${{ inputs.build_type }}
PARALLELISM: ${{ runner.os == 'Windows' && '1' || steps.nproc.outputs.nproc }}
run: |
ctest \
--output-on-failure \
-C "${BUILD_TYPE}" \
-j "${PARALLELISM}"
- name: Run the embedded tests - name: Run the embedded tests
if: ${{ !inputs.build_only }} if: ${{ !inputs.build_only }}
@@ -278,28 +304,50 @@ jobs:
run: | run: |
set -o pipefail set -o pipefail
# Coverage builds are slower due to instrumentation; use fewer parallel jobs to avoid flakiness # Coverage builds are slower due to instrumentation; use fewer parallel jobs to avoid flakiness
[ "$COVERAGE_ENABLED" = "true" ] && BUILD_NPROC=$(( BUILD_NPROC - 2 )) [ "$COVERAGE_ENABLED" = "true" ] && BUILD_NPROC=$((BUILD_NPROC - 2))
./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log
# The resolver/preload workaround is only correct for the ASan build:
# a regular build doesn't hit the __dn_expand interceptor bug, and must
# NOT have libasan injected. So only preload when xrpld is ASan-built.
#
# libresolv hosts getaddrinfo's resolver helpers (dn_expand, res_*). Under ASan
# these are intercepted via dlsym(RTLD_NEXT, ...), which yields a NULL pointer
# and crashes DNS resolution if libresolv isn't loaded. Linking it guarantees
# the symbols are present; it's a harmless no-op on glibc >= 2.34 (merged into
# libc) and is what the compiler driver already does for sanitizer builds.
# https://github.com/llvm/llvm-project/issues/59007
# https://github.com/google/sanitizers/issues/1592
if ldd ./xrpld | grep -q libasan; then
PRELOAD="$(gcc -print-file-name=libasan.so):/usr/lib/x86_64-linux-gnu/libresolv.so.2"
else
PRELOAD=""
fi
LD_PRELOAD="$PRELOAD" ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log
- name: Show test failure summary - name: Show test failure summary
if: ${{ failure() && !inputs.build_only }} if: ${{ failure() }}
env: env:
WORKING_DIR: ${{ runner.os == 'Windows' && format('{0}\{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }} WORKING_DIR: ${{ runner.os == 'Windows' && format('{0}\{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }}
run: | run: |
if [ ! -d "${WORKING_DIR}" ]; then if [ ! -d "${WORKING_DIR}" ]; then
echo "Working directory '${WORKING_DIR}' does not exist." echo "Working directory '${WORKING_DIR}' does not exist."
exit 0 exit 0
fi fi
cd "${WORKING_DIR}" cd "${WORKING_DIR}"
if [ ! -f unittest.log ]; then if [ -f unittest.log ]; then
echo "unittest.log not found; embedded tests may not have run." if ! grep -E "failed" unittest.log | grep -vE "^I[0-9]|^[0-9]+> (ERR:|FTL:)"; then
exit 0 echo "unittest.log present but no failure lines found."
fi fi
else
if ! grep -E "failed" unittest.log; then echo "unittest.log not found; embedded tests may not have run."
echo "Log present but no failure lines found in unittest.log." if [ -f build.log ]; then
if ! grep -E "error:" build.log; then
echo "build.log present but no compile errors found."
fi
fi
fi fi
- name: Debug failure (Linux) - name: Debug failure (Linux)
if: ${{ failure() && runner.os == 'Linux' && !inputs.build_only }} if: ${{ failure() && runner.os == 'Linux' && !inputs.build_only }}
@@ -317,14 +365,14 @@ jobs:
BUILD_TYPE: ${{ inputs.build_type }} BUILD_TYPE: ${{ inputs.build_type }}
run: | run: |
cmake \ cmake \
--build . \ --build . \
--config "${BUILD_TYPE}" \ --config "${BUILD_TYPE}" \
--parallel "${BUILD_NPROC}" \ --parallel "${BUILD_NPROC}" \
--target coverage --target coverage
- name: Upload coverage report - name: Upload coverage report
if: ${{ github.repository == 'XRPLF/rippled' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }} if: ${{ github.repository == 'XRPLF/rippled' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }}
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with: with:
disable_search: true disable_search: true
disable_telem: true disable_telem: true

View File

@@ -19,13 +19,6 @@ on:
required: true required: true
type: string type: string
strategy_matrix:
# TODO: Support additional strategies, e.g. "ubuntu" for generating all Ubuntu configurations.
description: 'The strategy matrix to use for generating the configurations ("minimal", "all").'
required: false
type: string
default: "minimal"
secrets: secrets:
CODECOV_TOKEN: CODECOV_TOKEN:
description: "The Codecov token to use for uploading coverage reports." description: "The Codecov token to use for uploading coverage reports."
@@ -37,7 +30,6 @@ jobs:
uses: ./.github/workflows/reusable-strategy-matrix.yml uses: ./.github/workflows/reusable-strategy-matrix.yml
with: with:
os: ${{ inputs.os }} os: ${{ inputs.os }}
strategy_matrix: ${{ inputs.strategy_matrix }}
# Build and test the binary for each configuration. # Build and test the binary for each configuration.
build-test-config: build-test-config:
@@ -47,7 +39,6 @@ jobs:
strategy: strategy:
fail-fast: ${{ github.event_name == 'merge_group' }} fail-fast: ${{ github.event_name == 'merge_group' }}
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
max-parallel: 10
with: with:
build_only: ${{ matrix.build_only }} build_only: ${{ matrix.build_only }}
build_type: ${{ matrix.build_type }} build_type: ${{ matrix.build_type }}
@@ -55,8 +46,9 @@ jobs:
cmake_args: ${{ matrix.cmake_args }} cmake_args: ${{ matrix.cmake_args }}
cmake_target: ${{ matrix.cmake_target }} cmake_target: ${{ matrix.cmake_target }}
runs_on: ${{ toJSON(matrix.architecture.runner) }} runs_on: ${{ toJSON(matrix.architecture.runner) }}
image: ${{ contains(matrix.architecture.platform, 'linux') && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) || '' }} image: ${{ matrix.image || '' }}
config_name: ${{ matrix.config_name }} config_name: ${{ matrix.config_name }}
sanitizers: ${{ matrix.sanitizers }} sanitizers: ${{ matrix.sanitizers }}
compiler: ${{ matrix.compiler || '' }}
secrets: secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View File

@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Check levelization - name: Check levelization
run: python .github/scripts/levelization/generate.py run: python .github/scripts/levelization/generate.py
- name: Check for differences - name: Check for differences
@@ -38,9 +38,9 @@ jobs:
run: | run: |
DIFF=$(git status --porcelain) DIFF=$(git status --porcelain)
if [ -n "${DIFF}" ]; then if [ -n "${DIFF}" ]; then
# Print the differences to give the contributor a hint about what to # Print the differences to give the contributor a hint about what to
# expect when running levelization on their own machine. # expect when running levelization on their own machine.
git diff git diff
echo "${MESSAGE}" echo "${MESSAGE}"
exit 1 exit 1
fi fi

View File

@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Check definitions - name: Check definitions
run: .github/scripts/rename/definitions.sh . run: .github/scripts/rename/definitions.sh .
- name: Check copyright notices - name: Check copyright notices
@@ -48,9 +48,9 @@ jobs:
run: | run: |
DIFF=$(git status --porcelain) DIFF=$(git status --porcelain)
if [ -n "${DIFF}" ]; then if [ -n "${DIFF}" ]; then
# Print the differences to give the contributor a hint about what to # Print the differences to give the contributor a hint about what to
# expect when running the renaming scripts on their own machine. # expect when running the renaming scripts on their own machine.
git diff git diff
echo "${MESSAGE}" echo "${MESSAGE}"
exit 1 exit 1
fi fi

View File

@@ -20,32 +20,35 @@ env:
BUILD_DIR: build BUILD_DIR: build
BUILD_TYPE: Debug # Debug so that ASSERTS and such participate in clang-tidy check BUILD_TYPE: Debug # Debug so that ASSERTS and such participate in clang-tidy check
OUTPUT_FILE: clang-tidy-output.txt OUTPUT_FILE: /tmp/clang-tidy-output.txt
DIFF_FILE: clang-tidy-git-diff.txt FILTERED_OUTPUT_FILE: /tmp/clang-tidy-filtered-output.txt
ISSUE_FILE: clang-tidy-issue.md DIFF_FILE: /tmp/clang-tidy-git-diff.txt
ISSUE_FILE: /tmp/clang-tidy-issue.md
COMPILER: clang
jobs: jobs:
determine-files: determine-files:
if: ${{ inputs.check_only_changed }} if: ${{ inputs.check_only_changed }}
permissions: permissions:
contents: read contents: read
uses: XRPLF/actions/.github/workflows/determine-tidy-files.yml@224f3c48d3014d082a1129237b8291ff0b0a331f uses: XRPLF/actions/.github/workflows/determine-tidy-files.yml@c7045074aafe9fb92fa537aa4446f81fbfc17e8b
run-clang-tidy: run-clang-tidy:
name: Run clang tidy name: Run clang tidy
needs: [determine-files] needs: [determine-files]
if: ${{ always() && !cancelled() && (!inputs.check_only_changed || needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }} if: ${{ always() && !cancelled() && (!inputs.check_only_changed || needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }}
runs-on: ["self-hosted", "Linux", "X64", "heavy"] runs-on: ["self-hosted", "Linux", "X64", "heavy"]
container: "ghcr.io/xrplf/ci/debian-trixie:clang-21-sha-53033a2" container: "ghcr.io/xrplf/xrpld/nix-debian:sha-e29b523"
permissions: permissions:
contents: read contents: read
issues: write issues: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Prepare runner - name: Prepare runner
uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8
with: with:
enable_ccache: false enable_ccache: false
@@ -56,6 +59,11 @@ jobs:
uses: XRPLF/actions/get-nproc@cf0433aa74563aead044a1e395610c96d65a37cf uses: XRPLF/actions/get-nproc@cf0433aa74563aead044a1e395610c96d65a37cf
id: nproc id: nproc
- name: Set compiler environment
uses: ./.github/actions/set-compiler-env
with:
compiler: ${{ env.COMPILER }}
- name: Setup Conan - name: Setup Conan
uses: ./.github/actions/setup-conan uses: ./.github/actions/setup-conan
@@ -70,13 +78,13 @@ jobs:
working-directory: ${{ env.BUILD_DIR }} working-directory: ${{ env.BUILD_DIR }}
run: | run: |
cmake \ cmake \
-G 'Ninja' \ -G 'Ninja' \
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \
-DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \
-Dtests=ON \ -Dtests=ON \
-Dwerr=ON \ -Dwerr=ON \
-Dxrpld=ON \ -Dxrpld=ON \
.. ..
# clang-tidy needs headers generated from proto files # clang-tidy needs headers generated from proto files
- name: Build libxrpl.libpb - name: Build libxrpl.libpb
@@ -133,7 +141,7 @@ jobs:
- name: Write issue header - name: Write issue header
if: ${{ steps.run_clang_tidy.outcome != 'success' }} if: ${{ steps.run_clang_tidy.outcome != 'success' }}
run: | run: |
cat > "${ISSUE_FILE}" <<EOF cat >"${ISSUE_FILE}" <<EOF
## Clang-tidy Check Failed ## Clang-tidy Check Failed
### Clang-tidy Output: ### Clang-tidy Output:
@@ -144,30 +152,30 @@ jobs:
if: ${{ steps.run_clang_tidy.outcome != 'success' }} if: ${{ steps.run_clang_tidy.outcome != 'success' }}
run: | run: |
if [ -f "${OUTPUT_FILE}" ]; then if [ -f "${OUTPUT_FILE}" ]; then
# Extract lines containing 'error:', 'warning:', or 'note:' # Extract lines containing 'error:', 'warning:', or 'note:'
grep -E '(error:|warning:|note:)' "${OUTPUT_FILE}" > filtered-output.txt || true grep -E '(error:|warning:|note:)' "${OUTPUT_FILE}" >"${FILTERED_OUTPUT_FILE}" || true
# If filtered output is empty, use original (might be a different error format) # If filtered output is empty, use original (might be a different error format)
if [ ! -s filtered-output.txt ]; then if [ ! -s "${FILTERED_OUTPUT_FILE}" ]; then
cp "${OUTPUT_FILE}" filtered-output.txt cp "${OUTPUT_FILE}" "${FILTERED_OUTPUT_FILE}"
fi fi
# Truncate if too large # Truncate if too large
head -c 60000 filtered-output.txt >> "${ISSUE_FILE}" head -c 60000 "${FILTERED_OUTPUT_FILE}" >>"${ISSUE_FILE}"
if [ "$(wc -c < filtered-output.txt)" -gt 60000 ]; then if [ "$(wc -c <"${FILTERED_OUTPUT_FILE}")" -gt 60000 ]; then
echo "" >> "${ISSUE_FILE}" echo "" >>"${ISSUE_FILE}"
echo "... (output truncated, see artifacts for full output)" >> "${ISSUE_FILE}" echo "... (output truncated, see artifacts for full output)" >>"${ISSUE_FILE}"
fi fi
rm filtered-output.txt rm "${FILTERED_OUTPUT_FILE}"
else else
echo "No output file found" >> "${ISSUE_FILE}" echo "No output file found" >>"${ISSUE_FILE}"
fi fi
- name: Append issue footer - name: Append issue footer
if: ${{ steps.run_clang_tidy.outcome != 'success' }} if: ${{ steps.run_clang_tidy.outcome != 'success' }}
run: | run: |
cat >> "${ISSUE_FILE}" <<EOF cat >>"${ISSUE_FILE}" <<EOF
\`\`\` \`\`\`
--- ---
@@ -176,7 +184,7 @@ jobs:
- name: Create issue - name: Create issue
if: ${{ steps.run_clang_tidy.outcome != 'success' && inputs.create_issue_on_failure }} if: ${{ steps.run_clang_tidy.outcome != 'success' && inputs.create_issue_on_failure }}
uses: XRPLF/actions/create-issue@36d450d12d301e8410c1b7936e5de70c291cbe36 uses: XRPLF/actions/create-issue@2b8bc36af85b88bca0dd7bfac2e2dc05f94ad712
with: with:
title: "Clang-tidy check failed" title: "Clang-tidy check failed"
body_file: ${{ env.ISSUE_FILE }} body_file: ${{ env.ISSUE_FILE }}

View File

@@ -1,8 +1,7 @@
# Build Linux packages (DEB and RPM) from pre-built binary artifacts. # Build Linux packages (DEB and RPM) from pre-built binary artifacts.
# Discovers which configurations to package from linux.json (os entries # Discovers which configurations to package from linux.json (configs in
# with "package": true) and fans out one job per entry. Today only # "package_configs") and fans out one job per distro. Only linux/amd64 is
# linux/amd64 is emitted; the architecture is hardcoded both here # supported; the runner is hardcoded in the job below.
# (runner) and in generate.py.
name: Package name: Package
on: on:
@@ -28,36 +27,20 @@ jobs:
matrix: ${{ steps.generate.outputs.matrix }} matrix: ${{ steps.generate.outputs.matrix }}
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with: with:
python-version: 3.13 python-version: "3.13"
- name: Generate packaging matrix - name: Generate packaging matrix
id: generate id: generate
working-directory: .github/scripts/strategy-matrix working-directory: .github/scripts/strategy-matrix
run: | run: ./generate.py --packaging >>"${GITHUB_OUTPUT}"
./generate.py --packaging --config=linux.json >> "${GITHUB_OUTPUT}"
generate-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
sparse-checkout: |
.github/actions/generate-version
src/libxrpl/protocol/BuildInfo.cpp
- name: Generate version
id: version
uses: ./.github/actions/generate-version
package: package:
needs: [generate-matrix, generate-version] needs: [generate-matrix]
if: ${{ github.event.repository.visibility == 'public' }} if: ${{ github.event.repository.visibility == 'public' }}
strategy: strategy:
fail-fast: false fail-fast: false
@@ -66,12 +49,12 @@ jobs:
permissions: permissions:
contents: read contents: read
runs-on: ["self-hosted", "Linux", "X64", "heavy"] runs-on: ["self-hosted", "Linux", "X64", "heavy"]
container: ${{ format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) }} container: ${{ matrix.image }}
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Download pre-built binary - name: Download pre-built binary
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -84,14 +67,13 @@ jobs:
- name: Build package - name: Build package
env: env:
PKG_VERSION: ${{ needs.generate-version.outputs.version }}
PKG_RELEASE: ${{ inputs.pkg_release }} PKG_RELEASE: ${{ inputs.pkg_release }}
run: ./package/build_pkg.sh run: ./package/build_pkg.sh
- name: Upload package artifact - name: Upload package artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with: with:
name: ${{ matrix.artifact_name }}-pkg-${{ needs.generate-version.outputs.version }} name: ${{ matrix.artifact_name }}-pkg
path: | path: |
${{ env.BUILD_DIR }}/debbuild/*.deb ${{ env.BUILD_DIR }}/debbuild/*.deb
${{ env.BUILD_DIR }}/debbuild/*.ddeb ${{ env.BUILD_DIR }}/debbuild/*.ddeb

View File

@@ -4,15 +4,9 @@ on:
workflow_call: workflow_call:
inputs: inputs:
os: os:
description: 'The operating system to use for the build ("linux", "macos", "windows").' description: 'The operating system to use for the build ("linux", "macos", "windows", or empty for all).'
required: false required: false
type: string type: string
strategy_matrix:
# TODO: Support additional strategies, e.g. "ubuntu" for generating all Ubuntu configurations.
description: 'The strategy matrix to use for generating the configurations ("minimal", "all").'
required: false
type: string
default: "minimal"
outputs: outputs:
matrix: matrix:
description: "The generated strategy matrix." description: "The generated strategy matrix."
@@ -29,17 +23,17 @@ jobs:
matrix: ${{ steps.generate.outputs.matrix }} matrix: ${{ steps.generate.outputs.matrix }}
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with: with:
python-version: 3.13 python-version: "3.13"
- name: Generate strategy matrix - name: Generate strategy matrix
working-directory: .github/scripts/strategy-matrix working-directory: .github/scripts/strategy-matrix
id: generate id: generate
env: env:
GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}.json', inputs.os) || '' }} GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}', inputs.os) || '' }}
GENERATE_OPTION: ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} GENERATE_EVENT: ${{ github.event_name }}
run: ./generate.py ${GENERATE_OPTION} ${GENERATE_CONFIG} >> "${GITHUB_OUTPUT}" run: ./generate.py ${GENERATE_CONFIG} --event="${GENERATE_EVENT}" >>"${GITHUB_OUTPUT}"

View File

@@ -40,10 +40,10 @@ defaults:
jobs: jobs:
upload: upload:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: ghcr.io/xrplf/ci/ubuntu-noble:gcc-13-sha-5dd7158 container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-e29b523
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Generate build version number - name: Generate build version number
id: version id: version

View File

@@ -30,6 +30,7 @@ on:
- ".github/scripts/strategy-matrix/**" - ".github/scripts/strategy-matrix/**"
- conanfile.py - conanfile.py
- conan.lock - conan.lock
- conan/profiles/**
env: env:
CONAN_REMOTE_NAME: xrplf CONAN_REMOTE_NAME: xrplf
@@ -48,8 +49,6 @@ jobs:
# Generate the strategy matrix to be used by the following job. # Generate the strategy matrix to be used by the following job.
generate-matrix: generate-matrix:
uses: ./.github/workflows/reusable-strategy-matrix.yml uses: ./.github/workflows/reusable-strategy-matrix.yml
with:
strategy_matrix: ${{ github.event_name == 'pull_request' && 'minimal' || 'all' }}
# Build and upload the dependencies for each configuration. # Build and upload the dependencies for each configuration.
run-upload-conan-deps: run-upload-conan-deps:
@@ -58,19 +57,18 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
max-parallel: 10
runs-on: ${{ matrix.architecture.runner }} runs-on: ${{ matrix.architecture.runner }}
container: ${{ contains(matrix.architecture.platform, 'linux') && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) || null }} container: ${{ matrix.image || null }}
steps: steps:
- name: Cleanup workspace (macOS and Windows) - name: Cleanup workspace (macOS and Windows)
if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }} if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }}
uses: XRPLF/actions/cleanup-workspace@c7d9ce5ebb03c752a354889ecd870cadfc2b1cd4 uses: XRPLF/actions/cleanup-workspace@c7d9ce5ebb03c752a354889ecd870cadfc2b1cd4
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Prepare runner - name: Prepare runner
uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8
with: with:
enable_ccache: false enable_ccache: false
@@ -83,6 +81,12 @@ jobs:
with: with:
subtract: ${{ env.NPROC_SUBTRACT }} subtract: ${{ env.NPROC_SUBTRACT }}
- name: Set compiler environment (Linux)
if: ${{ runner.os == 'Linux' }}
uses: ./.github/actions/set-compiler-env
with:
compiler: ${{ matrix.compiler }}
- name: Setup Conan - name: Setup Conan
env: env:
SANITIZERS: ${{ matrix.sanitizers }} SANITIZERS: ${{ matrix.sanitizers }}

View File

@@ -15,6 +15,7 @@ repos:
hooks: hooks:
- id: check-added-large-files - id: check-added-large-files
args: [--maxkb=400, --enforce-all] args: [--maxkb=400, --enforce-all]
- id: check-executables-have-shebangs
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
- id: check-merge-conflict - id: check-merge-conflict
@@ -35,39 +36,57 @@ repos:
language: python language: python
types_or: [c++, c] types_or: [c++, c]
exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/ exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/
- id: fix-pragma-once
name: fix missing '#pragma once' declarations in header files
language: python
entry: ./bin/pre-commit/fix_pragma_once.py
files: \.(h|hpp)$
- repo: https://github.com/pre-commit/mirrors-clang-format - repo: https://github.com/pre-commit/mirrors-clang-format
rev: cd481d7b0bfb5c7b3090c21846317f9a8262e891 # frozen: v22.1.0 rev: dd18dad857d6133e90bbe478f4f2f22ec0030269 # frozen: v22.1.5
hooks: hooks:
- id: clang-format - id: clang-format
args: [--style=file] args: [--style=file]
"types_or": [c++, c, proto] types_or: [c++, c, proto]
exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/ exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/
- repo: https://github.com/BlankSpruce/gersemi - repo: https://github.com/BlankSpruce/gersemi-pre-commit
rev: 0.26.0 rev: faadd6a9d852369ca94f4d15b2404c967ba8cb01 # frozen: 0.27.6
hooks: hooks:
- id: gersemi - id: gersemi
- repo: https://github.com/rbubley/mirrors-prettier - repo: https://github.com/rbubley/mirrors-prettier
rev: c2bc67fe8f8f549cc489e00ba8b45aa18ee713b1 # frozen: v3.8.1 rev: 515f543f5718ebfd6ce22e16708bb32c68ff96e1 # frozen: v3.8.3
hooks: hooks:
- id: prettier - id: prettier
args: [--end-of-line=auto] args: [--end-of-line=auto]
- repo: https://github.com/psf/black-pre-commit-mirror - repo: https://github.com/psf/black-pre-commit-mirror
rev: ea488cebbfd88a5f50b8bd95d5c829d0bb76feb8 # frozen: 26.1.0 rev: 4160603246a6b365d4a2af661c6d71b0a0f50478 # frozen: 26.5.1
hooks: hooks:
- id: black - id: black
- repo: https://github.com/openstack/bashate - repo: https://github.com/scop/pre-commit-shfmt
rev: 5798d24d571676fc407e81df574c1ef57b520f23 # frozen: 2.1.1 rev: 05c1426671b9237fb5e1444dd63aa5731bec0dfb # frozen: v3.13.1-1
hooks: hooks:
- id: bashate - id: shfmt
args: ["--ignore=E006"] args: [--write, --indent=4, --case-indent=true]
- repo: local
hooks:
- id: format-inline-bash-workflows
name: "format `run:` blocks in workflows/actions"
entry: ./.github/scripts/format-inline-bash.py
language: python
files: ^\.github/(workflows|actions)/.*\.ya?ml$
- id: format-inline-bash-markdown
name: "format ```bash blocks in markdown"
entry: ./.github/scripts/format-inline-bash.py
language: python
files: \.md$
- repo: https://github.com/streetsidesoftware/cspell-cli - repo: https://github.com/streetsidesoftware/cspell-cli
rev: a42085ade523f591dca134379a595e7859986445 # frozen: v9.7.0 rev: 4643f154907327ee0a2c7038f0296e0dd77d9776 # frozen: v10.0.0
hooks: hooks:
- id: cspell # Spell check changed files - id: cspell # Spell check changed files
exclude: | exclude: |

400
BUILD.md
View File

@@ -1,26 +1,57 @@
| :warning: **WARNING** :warning: | | :warning: **WARNING** :warning: |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| These instructions assume you have a C++ development environment ready with Git, Python, Conan, CMake, and a C++ compiler. For help setting one up on Linux, macOS, or Windows, [see this guide](./docs/build/environment.md). | | These instructions assume you have a C++ development environment ready with Git, Python, Conan, CMake, and a C++ compiler. For help setting one up on Linux, macOS, or Windows, [see this guide](./docs/build/environment.md).<br><br>These instructions also assume a basic familiarity with Conan and CMake. If you are unfamiliar with Conan, you can read our [crash course](./docs/build/conan.md) or the official [Getting Started][conan-getting-started] walkthrough. |
> These instructions also assume a basic familiarity with Conan and CMake. ## Minimum Requirements
> If you are unfamiliar with Conan, you can read our
> [crash course](./docs/build/conan.md) or the official [Getting Started][3]
> walkthrough.
## Branches See [System Requirements](https://xrpl.org/system-requirements.html).
For a stable release, choose the `master` branch or one of the [tagged Building xrpld generally requires Git, Python, Conan, CMake, and a C++
releases](https://github.com/XRPLF/rippled/releases). compiler.
- [Python](https://www.python.org/downloads/)
- [Conan](https://conan.io/downloads.html)
- [CMake](https://cmake.org/download/)
You can verify that the required tools are installed and runnable with:
```bash ```bash
git checkout master ./bin/check-tools.sh
``` ```
For the latest release candidate, choose the `release` branch. `xrpld` is written in the C++23 dialect. The [tested compiler versions][cpp23-support] are:
```bash | Compiler | Version |
git checkout release | ----------- | --------------- |
``` | GCC | 15.2 |
| Clang | 22 |
| Apple Clang | 17 |
| MSVC | 19.44[^windows] |
## Operating Systems
Please see the [environment setup guide](./docs/build/environment.md) for detailed instructions for all platforms.
### Linux
The Ubuntu Linux distribution has received the highest level of quality
assurance, testing, and support. We also support Red Hat and use Debian
internally.
Our Linux CI tooling is distro-independent and uses a Nix-based environment, so it should be possible to build on other Linux distributions as well, although we have not tested them.
### macOS
Many `xrpld` engineers use macOS for development.
### Windows
Windows is used by some engineers for development only.
[^windows]: Windows is not recommended for production use.
## Steps
### Branches
For the latest set of untested features, or to contribute, choose the `develop` For the latest set of untested features, or to contribute, choose the `develop`
branch. branch.
@@ -29,55 +60,15 @@ branch.
git checkout develop git checkout develop
``` ```
## Minimum Requirements For a release candidate, choose the relevant release branch, e.g.
`release/3.2.x`.
See [System Requirements](https://xrpl.org/system-requirements.html). ```bash
git checkout release/3.2.x
```
Building xrpld generally requires git, Python, Conan, CMake, and a C++ For a stable release, choose one of the [tagged
compiler. Some guidance on setting up such a [C++ development environment can be releases](https://github.com/XRPLF/rippled/releases).
found here](./docs/build/environment.md).
- [Python 3.11](https://www.python.org/downloads/), or higher
- [Conan 2.17](https://conan.io/downloads.html)[^1], or higher
- [CMake 3.22](https://cmake.org/download/), or higher
[^1]:
It is possible to build with Conan 1.60+, but the instructions are
significantly different, which is why we are not recommending it.
`xrpld` is written in the C++20 dialect and includes the `<concepts>` header.
The [minimum compiler versions][2] required are:
| Compiler | Version |
| ----------- | --------- |
| GCC | 12 |
| Clang | 16 |
| Apple Clang | 16 |
| MSVC | 19.44[^3] |
### Linux
The Ubuntu Linux distribution has received the highest level of quality
assurance, testing, and support. We also support Red Hat and use Debian
internally.
Here are [sample instructions for setting up a C++ development environment on
Linux](./docs/build/environment.md#linux).
### Mac
Many xrpld engineers use macOS for development.
Here are [sample instructions for setting up a C++ development environment on
macOS](./docs/build/environment.md#macos).
### Windows
Windows is used by some engineers for development only.
[^3]: Windows is not recommended for production use.
## Steps
### Set Up Conan ### Set Up Conan
@@ -86,18 +77,11 @@ Conan, CMake, and a C++ compiler, you may need to set up your Conan profile.
These instructions assume a basic familiarity with Conan and CMake. If you are These instructions assume a basic familiarity with Conan and CMake. If you are
unfamiliar with Conan, then please read [this crash course](./docs/build/conan.md) or the official unfamiliar with Conan, then please read [this crash course](./docs/build/conan.md) or the official
[Getting Started][3] walkthrough. [Getting Started][conan-getting-started] walkthrough.
#### Conan lockfile #### Profiles
To achieve reproducible dependencies, we use a [Conan lockfile](https://docs.conan.io/2/tutorial/versioning/lockfiles.html), We recommend that you install our Conan profiles:
which has to be updated every time dependencies change.
Please see the [instructions on how to regenerate the lockfile](conan/lockfile/README.md).
#### Default profile
We recommend that you import the provided `conan/profiles/default` profile:
```bash ```bash
conan config install conan/profiles/ -tf $(conan config home)/profiles/ conan config install conan/profiles/ -tf $(conan config home)/profiles/
@@ -109,222 +93,15 @@ You can check your Conan profile by running:
conan profile show conan profile show
``` ```
#### Custom profile If the default profile is not suitable for your environment, you can create a custom profile and pass it to Conan.
More information on customizing Conan can be found in the [Advanced Conan configuration](./docs/build/advanced_conan.md).
If the default profile does not work for you and you do not yet have a Conan #### Add xrplf remote
profile, you can create one by running:
Run the following command to add the `xrplf` remote, which hosts some of our dependencies:
```bash ```bash
conan profile detect conan remote add --index 0 --force xrplf https://conan.ripplex.io
```
You may need to make changes to the profile to suit your environment. You can
refer to the provided `conan/profiles/default` profile for inspiration, and you
may also need to apply the required [tweaks](#conan-profile-tweaks) to this
default profile.
### Patched recipes
Occasionally, we need patched recipes or recipes not present in Conan Center.
We maintain a fork of the Conan Center Index
[here](https://github.com/XRPLF/conan-center-index/) containing the modified and newly added recipes.
To ensure our patched recipes are used, you must add our Conan remote at a
higher index than the default Conan Center remote, so it is consulted first. You
can do this by running:
```bash
conan remote add --index 0 xrplf https://conan.ripplex.io
```
Alternatively, you can pull our recipes from the repository and export them locally:
```bash
# Define which recipes to export.
recipes=('abseil' 'ed25519' 'mpt-crypto' 'openssl' 'secp256k1' 'snappy' 'soci' 'wasm-xrplf' 'wasmi')
# Selectively check out the recipes from our CCI fork.
cd external
mkdir -p conan-center-index
cd conan-center-index
git init
git remote add origin git@github.com:XRPLF/conan-center-index.git
git sparse-checkout init
for recipe in "${recipes[@]}"; do
echo "Checking out recipe '${recipe}'..."
git sparse-checkout add recipes/${recipe}
done
git fetch origin master
git checkout master
./export_all.sh
cd ../../
```
In the case we switch to a newer version of a dependency that still requires a
patch or add a new dependency, it will be necessary for you to pull in the changes and re-export the
updated dependencies with the newer version. However, if we switch to a newer
version that no longer requires a patch, no action is required on your part, as
the new recipe will be automatically pulled from the official Conan Center.
> [!NOTE]
> You might need to add `--lockfile=""` to your `conan install` command
> to avoid automatic use of the existing `conan.lock` file when you run
> `conan export` manually on your machine
>
> This is not recommended though, as you might end up using different revisions of recipes.
### Conan profile tweaks
#### Missing compiler version
If you see an error similar to the following after running `conan profile show`:
```bash
ERROR: Invalid setting '17' is not a valid 'settings.compiler.version' value.
Possible values are ['5.0', '5.1', '6.0', '6.1', '7.0', '7.3', '8.0', '8.1',
'9.0', '9.1', '10.0', '11.0', '12.0', '13', '13.0', '13.1', '14', '14.0', '15',
'15.0', '16', '16.0']
Read "http://docs.conan.io/2/knowledge/faq.html#error-invalid-setting"
```
you need to add your compiler to the list of compiler versions in
`$(conan config home)/settings_user.yml`, by adding the required version number(s)
to the `version` array specific for your compiler. For example:
```yaml
compiler:
apple-clang:
version: ["17.0"]
```
#### Multiple compilers
If you have multiple compilers installed, make sure to select the one to use in
your default Conan configuration **before** running `conan profile detect`, by
setting the `CC` and `CXX` environment variables.
For example, if you are running MacOS and have [homebrew
LLVM@18](https://formulae.brew.sh/formula/llvm@18), and want to use it as a
compiler in the new Conan profile:
```bash
export CC=$(brew --prefix llvm@18)/bin/clang
export CXX=$(brew --prefix llvm@18)/bin/clang++
conan profile detect
```
You should also explicitly set the path to the compiler in the profile file,
which helps to avoid errors when `CC` and/or `CXX` are set and disagree with the
selected Conan profile. For example:
```text
[conf]
tools.build:compiler_executables={'c':'/usr/bin/gcc','cpp':'/usr/bin/g++'}
```
#### Multiple profiles
You can manage multiple Conan profiles in the directory
`$(conan config home)/profiles`, for example renaming `default` to a different
name and then creating a new `default` profile for a different compiler.
#### Select language
The default profile created by Conan will typically select different C++ dialect
than C++20 used by this project. You should set `20` in the profile line
starting with `compiler.cppstd=`. For example:
```bash
sed -i.bak -e 's|^compiler\.cppstd=.*$|compiler.cppstd=20|' $(conan config home)/profiles/default
```
#### Select standard library in Linux
**Linux** developers will commonly have a default Conan [profile][] that
compiles with GCC and links with libstdc++. If you are linking with libstdc++
(see profile setting `compiler.libcxx`), then you will need to choose the
`libstdc++11` ABI:
```bash
sed -i.bak -e 's|^compiler\.libcxx=.*$|compiler.libcxx=libstdc++11|' $(conan config home)/profiles/default
```
#### Select architecture and runtime in Windows
**Windows** developers may need to use the x64 native build tools. An easy way
to do that is to run the shortcut "x64 Native Tools Command Prompt" for the
version of Visual Studio that you have installed.
Windows developers must also build `xrpld` and its dependencies for the x64
architecture:
```bash
sed -i.bak -e 's|^arch=.*$|arch=x86_64|' $(conan config home)/profiles/default
```
**Windows** developers also must select static runtime:
```bash
sed -i.bak -e 's|^compiler\.runtime=.*$|compiler.runtime=static|' $(conan config home)/profiles/default
```
#### Clang workaround for grpc
If your compiler is clang, version 19 or later, or apple-clang, version 17 or
later, you may encounter a compilation error while building the `grpc`
dependency:
```text
In file included from .../lib/promise/try_seq.h:26:
.../lib/promise/detail/basic_seq.h:499:38: error: a template argument list is expected after a name prefixed by the template keyword [-Wmissing-template-arg-list-after-template-kw]
499 | Traits::template CallSeqFactory(f_, *cur_, std::move(arg)));
| ^
```
The workaround for this error is to add two lines to profile:
```text
[conf]
tools.build:cxxflags=['-Wno-missing-template-arg-list-after-template-kw']
```
#### Workaround for gcc 12
If your compiler is gcc, version 12, and you have enabled `werr` option, you may
encounter a compilation error such as:
```text
/usr/include/c++/12/bits/char_traits.h:435:56: error: 'void* __builtin_memcpy(void*, const void*, long unsigned int)' accessing 9223372036854775810 or more bytes at offsets [2, 9223372036854775807] and 1 may overlap up to 9223372036854775813 bytes at offset -3 [-Werror=restrict]
435 | return static_cast<char_type*>(__builtin_memcpy(__s1, __s2, __n));
| ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
cc1plus: all warnings being treated as errors
```
The workaround for this error is to add two lines to your profile:
```text
[conf]
tools.build:cxxflags=['-Wno-restrict']
```
#### Workaround for clang 16
If your compiler is clang, version 16, you may encounter compilation error such
as:
```text
In file included from .../boost/beast/websocket/stream.hpp:2857:
.../boost/beast/websocket/impl/read.hpp:695:17: error: call to 'async_teardown' is ambiguous
async_teardown(impl.role, impl.stream(),
^~~~~~~~~~~~~~
```
The workaround for this error is to add two lines to your profile:
```text
[conf]
tools.build:cxxflags=['-DBOOST_ASIO_DISABLE_CONCEPTS']
``` ```
### Set Up Ccache ### Set Up Ccache
@@ -333,14 +110,7 @@ To speed up repeated compilations, we recommend that you install
[ccache](https://ccache.dev), a tool that wraps your compiler so that it can [ccache](https://ccache.dev), a tool that wraps your compiler so that it can
cache build objects locally. cache build objects locally.
#### Linux On Linux and macOS, `ccache` is included in the [Nix development shell](./docs/build/nix.md).
You can install it using the package manager, e.g. `sudo apt install ccache`
(Ubuntu) or `sudo dnf install ccache` (RHEL).
#### macOS
You can install it using Homebrew, i.e. `brew install ccache`.
#### Windows #### Windows
@@ -427,16 +197,19 @@ install ccache --version 4.11.3 --allow-downgrade`.
Single-config generators: Single-config generators:
``` ```
cmake --build . cmake --build . --parallel N
``` ```
Multi-config generators: Multi-config generators:
``` ```
cmake --build . --config Release cmake --build . --config Release --parallel N
cmake --build . --config Debug cmake --build . --config Debug --parallel N
``` ```
Replace the `--parallel` parameter N with the desired number of parallel jobs. A common starting point is half of the number of available CPU
cores.
5. Test xrpld. 5. Test xrpld.
Single-config generators: Single-config generators:
@@ -546,7 +319,7 @@ See [Sanitizers docs](./docs/build/sanitizers.md) for more details.
| Option | Default Value | Description | | Option | Default Value | Description |
| ---------- | ------------- | -------------------------------------------------------------- | | ---------- | ------------- | -------------------------------------------------------------- |
| `assert` | OFF | Enable assertions. | | `assert` | OFF | Force enabling assertions. |
| `coverage` | OFF | Prepare the coverage report. | | `coverage` | OFF | Prepare the coverage report. |
| `tests` | OFF | Build tests. | | `tests` | OFF | Build tests. |
| `unity` | OFF | Configure a unity build. | | `unity` | OFF | Configure a unity build. |
@@ -554,7 +327,7 @@ See [Sanitizers docs](./docs/build/sanitizers.md) for more details.
| `werr` | OFF | Treat compilation warnings as errors | | `werr` | OFF | Treat compilation warnings as errors |
| `wextra` | OFF | Enable additional compilation warnings | | `wextra` | OFF | Enable additional compilation warnings |
[Unity builds][5] may be faster for the first build (at the cost of much more [Unity builds][unity-build] may be faster for the first build (at the cost of much more
memory) since they concatenate sources into fewer translation units. Non-unity memory) since they concatenate sources into fewer translation units. Non-unity
builds may be faster for incremental builds, and can be helpful for detecting builds may be faster for incremental builds, and can be helpful for detecting
`#include` omissions. `#include` omissions.
@@ -580,14 +353,14 @@ After any updates or changes to dependencies, you may need to do the following:
conan remove '*' conan remove '*'
``` ```
3. Re-run [conan export](#patched-recipes) if needed. 3. Re-run [conan export](./docs/build/advanced_conan.md#patched-recipes) if needed.
4. [Regenerate lockfile](#conan-lockfile). 4. [Regenerate lockfile](./docs/build/advanced_conan.md#conan-lockfile).
5. Re-run [conan install](#build-and-test). 5. Re-run [conan install](#build-and-test).
#### ERROR: Package not resolved #### ERROR: Package not resolved
If you're seeing an error like `ERROR: Package 'snappy/1.1.10' not resolved: Unable to find 'snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1756234314.246' in remotes.`, If you're seeing an error like `ERROR: Package 'snappy/1.1.10' not resolved: Unable to find 'snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1756234314.246' in remotes.`,
please add `xrplf` remote or re-run `conan export` for [patched recipes](#patched-recipes). please [add `xrplf` remote](#add-xrplf-remote) or re-run `conan export` for [patched recipes](./docs/build/advanced_conan.md#patched-recipes).
### `protobuf/port_def.inc` file not found ### `protobuf/port_def.inc` file not found
@@ -607,28 +380,9 @@ For example, if you want to build Debug:
1. For conan install, pass `--settings build_type=Debug` 1. For conan install, pass `--settings build_type=Debug`
2. For cmake, pass `-DCMAKE_BUILD_TYPE=Debug` 2. For cmake, pass `-DCMAKE_BUILD_TYPE=Debug`
## Add a Dependency [cpp23-support]: https://en.cppreference.com/w/cpp/compiler_support/23
[conan-getting-started]: https://docs.conan.io/en/latest/getting_started.html
If you want to experiment with a new package, follow these steps: [unity-build]: https://en.wikipedia.org/wiki/Unity_build
1. Search for the package on [Conan Center](https://conan.io/center/).
2. Modify [`conanfile.py`](./conanfile.py):
- Add a version of the package to the `requires` property.
- Change any default options for the package by adding them to the
`default_options` property (with syntax `'$package:$option': $value`).
3. Modify [`CMakeLists.txt`](./CMakeLists.txt):
- Add a call to `find_package($package REQUIRED)`.
- Link a library from the package to the target `xrpl_libs`
(search for the existing call to `target_link_libraries(xrpl_libs INTERFACE ...)`).
4. Start coding! Don't forget to include whatever headers you need from the package.
[1]: https://github.com/conan-io/conan-center-index/issues/13168
[2]: https://en.cppreference.com/w/cpp/compiler_support/20
[3]: https://docs.conan.io/en/latest/getting_started.html
[5]: https://en.wikipedia.org/wiki/Unity_build
[6]: https://github.com/boostorg/beast/issues/2648
[7]: https://github.com/boostorg/beast/issues/2661
[gcovr]: https://gcovr.com/en/stable/getting-started.html [gcovr]: https://gcovr.com/en/stable/getting-started.html
[python-pip]: https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/ [python-pip]: https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/
[build_type]: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html [build_type]: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html
[profile]: https://docs.conan.io/en/latest/reference/profiles.html

View File

@@ -15,7 +15,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
project(xrpl) project(xrpl)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
@@ -57,6 +57,8 @@ if(target)
) )
endif() endif()
include(PatchNixBinary)
include(XrplSanity) include(XrplSanity)
include(XrplVersion) include(XrplVersion)
include(XrplSettings) include(XrplSettings)

View File

@@ -14,9 +14,9 @@ The following branches exist in the main project repository:
- `develop`: The latest set of unreleased features, and the most common - `develop`: The latest set of unreleased features, and the most common
starting point for contributions. starting point for contributions.
- `release`: The latest beta release or release candidate. - `release/*` (e.g. `release/3.2.x`): Release branches, one per release line,
- `master`: The latest stable release. holding the latest release candidate, or stable release for that line.
- `gh-pages`: The documentation for this project, built by Doxygen. Stable releases are published as [tagged releases](https://github.com/XRPLF/rippled/releases).
The tip of each branch must be signed. In order for GitHub to sign a The tip of each branch must be signed. In order for GitHub to sign a
squashed commit that it builds from your pull request, GitHub must know squashed commit that it builds from your pull request, GitHub must know
@@ -130,11 +130,9 @@ tl;dr
## Pull requests ## Pull requests
In general, pull requests use `develop` as the base branch. In general, pull requests use `develop` as the base branch.
The exceptions are
- Fixes and improvements to a release candidate use `release` as the The exceptions are fixes, improvements, and hotfixes for an existing release,
base. which use that release's branch (e.g. `release/3.2.x`) as the base.
- Hotfixes use `master` as the base.
If your changes are not quite ready, but you want to make it easily available If your changes are not quite ready, but you want to make it easily available
for preliminary examination or review, you can create a "Draft" pull request. for preliminary examination or review, you can create a "Draft" pull request.
@@ -216,7 +214,7 @@ coherent rather than a set of _thou shalt not_ commandments.
## Formatting ## Formatting
All code must conform to `clang-format` version 21, All code must conform to `clang-format` version 22,
according to the settings in [`.clang-format`](./.clang-format), according to the settings in [`.clang-format`](./.clang-format),
unless the result would be unreasonably difficult to read or maintain. unless the result would be unreasonably difficult to read or maintain.
To demarcate lines that should be left as-is, surround them with comments like To demarcate lines that should be left as-is, surround them with comments like
@@ -261,7 +259,7 @@ This ensures that configuration changes don't introduce new warnings across the
### Installing clang-tidy ### Installing clang-tidy
See the [environment setup guide](./docs/build/environment.md#clang-tidy) for platform-specific installation instructions. See the [environment setup guide](./docs/build/environment.md#clang-tidy) for how to get clang-tidy.
### Running clang-tidy locally ### Running clang-tidy locally

161
bin/check-tools.sh Executable file
View File

@@ -0,0 +1,161 @@
#!/usr/bin/env bash
#
# check-tools.sh — verify the xrpld development tooling is present and runnable.
#
# Works on Linux, macOS, and Windows (Git Bash / MSYS). For every expected tool
# it runs a version probe, collecting anything that is missing or fails to run,
# and prints a summary at the end (exiting non-zero if anything is missing).
#
# The tool set is platform-aware:
# - Linux: the full Nix CI environment (see nix/packages.nix, nix/ci-env.nix),
# with GCC, Clang and the sanitizer/coverage tooling. This script is
# run during the Nix Docker image build (nix/docker/Dockerfile), so
# the Linux list is kept in sync with that environment.
# - macOS: the same tooling, minus GCC/g++/gcov/mold
# - Windows: the core build tools only (CMake, Conan, Git, Python).
# MSVC is expected to be provided separately and is not checked here.
#
# Some tools (clang-format, doxygen, gcovr, gh, git-cliff, gpg, pre-commit,
# run-clang-tidy) are present in our Linux CI images and in local development
# setups, but not in the macOS CI environment. They are checked everywhere
# except when running in CI on macOS.
#
# Environment variables:
# CI if set, skip the tools above when on macOS.
# CHECK_TOOLS_SKIP_CLONE if set, skip the git-over-HTTPS connectivity check.
set -uo pipefail
missing=()
checked=0
# check <name> [probe-command...]
# Runs the probe (default: "<name> --version") quietly. Records <name> as
# missing if the command is not found or exits non-zero.
check() {
local name="$1"
shift
local -a probe=("$@")
if [ "${#probe[@]}" -eq 0 ]; then
probe=("${name}" --version)
fi
echo "Checking ${name}..."
checked=$((checked + 1))
if "${probe[@]}" | head -n 1; then
printf ' [ ok ] %s\n' "${name}"
else
printf ' [MISS] %s\n' "${name}"
missing+=("${name}")
fi
}
case "$(uname -s)" in
Linux*) os=linux ;;
Darwin*) os=macos ;;
MINGW* | MSYS* | CYGWIN*) os=windows ;;
*)
echo "Unknown OS: $(uname -s)" >&2
exit 1
;;
esac
echo "Detected OS: ${os} ($(uname -s) $(uname -m))"
echo
echo "Core build tools:"
check cmake
check conan
check git
if [ "${os}" = "windows" ]; then
check python python --version
else
check python3
fi
# The full development toolchain. Available from Nix on Linux and macOS; on
# Windows these are typically not installed, so they are skipped.
if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
echo
echo "Development tooling:"
check ccache
check clang
check clang++
check ClangBuildAnalyzer
check curl
check file
check less
check make
check netstat which netstat
check ninja
check perl
check pkg-config
check vim
check zip
# These tools are present in our Linux CI images and in local development
# setups, but not in the macOS CI environment. So check them everywhere
# except when running in CI on macOS.
if [ "${os}" = "linux" ] || [ -z "${CI:-}" ]; then
check clang-format
check dot
check doxygen
check gcovr
check gh
check git-cliff
check git-lfs
check gpg
# pre-commit, or its alternative implementation prek
check pre-commit sh -c 'pre-commit --version || prek --version'
check run-clang-tidy run-clang-tidy --help
fi
fi
# GCC is the default compiler on Linux. macOS uses the system Apple Clang
# instead, so GCC/g++/gcov are not expected there.
if [ "${os}" = "linux" ]; then
echo
echo "GCC toolchain:"
check gcc
check g++
check gcov
echo
echo "Mold:"
check mold
fi
if [ "${os}" = "windows" ]; then
echo
echo "Note: on Windows the C++ compiler is MSVC, which is provided"
echo " separately (e.g. via Visual Studio) and is not checked here."
fi
# A simple test to verify that git can clone a repository over HTTPS
# (i.e. the CA bundle is wired up). Clone to a temp dir and clean up.
if [ -n "${CHECK_TOOLS_SKIP_CLONE:-}" ]; then
echo
echo "Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set)."
else
echo
echo "Connectivity check:"
checked=$((checked + 1))
tmp_clone="$(mktemp -d)"
if git clone --depth 1 https://github.com/XRPLF/actions.git "${tmp_clone}/actions" >/dev/null 2>&1; then
printf ' [ ok ] git clone over HTTPS\n'
else
printf ' [MISS] git clone over HTTPS\n'
missing+=("git-https-clone")
fi
rm -rf "${tmp_clone}"
fi
echo
if [ "${#missing[@]}" -eq 0 ]; then
echo "All ${checked} checked tools are present and runnable."
else
echo "Missing or non-functional tools (${#missing[@]} of ${checked}):" >&2
for tool in "${missing[@]}"; do
echo " - ${tool}" >&2
done
exit 1
fi

View File

@@ -1,8 +1,8 @@
#!/bin/bash #!/bin/bash
if [[ $# -ne 1 || "$1" == "--help" || "$1" == "-h" ]]; then if [[ $# -ne 1 || "$1" == "--help" || "$1" == "-h" ]]; then
name=$( basename $0 ) name=$(basename $0)
cat <<- USAGE cat <<-USAGE
Usage: $name <username> Usage: $name <username>
Where <username> is the Github username of the upstream repo. e.g. XRPLF Where <username> is the Github username of the upstream repo. e.g. XRPLF
@@ -14,7 +14,7 @@ fi
shift shift
user="$1" user="$1"
# Get the origin URL. Expect it be an SSH-style URL # Get the origin URL. Expect it be an SSH-style URL
origin=$( git remote get-url origin ) origin=$(git remote get-url origin)
if [[ "${origin}" == "" ]]; then if [[ "${origin}" == "" ]]; then
echo Invalid origin remote >&2 echo Invalid origin remote >&2
exit 1 exit 1
@@ -22,11 +22,11 @@ fi
# echo "Origin: ${origin}" # echo "Origin: ${origin}"
# Parse the origin # Parse the origin
ifs_orig="${IFS}" ifs_orig="${IFS}"
IFS=':' read remote originpath <<< "${origin}" IFS=':' read remote originpath <<<"${origin}"
# echo "Remote: ${remote}, Originpath: ${originpath}" # echo "Remote: ${remote}, Originpath: ${originpath}"
IFS='@' read sshuser server <<< "${remote}" IFS='@' read sshuser server <<<"${remote}"
# echo "SSHUser: ${sshuser}, Server: ${server}" # echo "SSHUser: ${sshuser}, Server: ${server}"
IFS='/' read originuser repo <<< "${originpath}" IFS='/' read originuser repo <<<"${originpath}"
# echo "Originuser: ${originuser}, Repo: ${repo}" # echo "Originuser: ${originuser}, Repo: ${repo}"
if [[ "${sshuser}" == "" || "${server}" == "" || "${originuser}" == "" || "${repo}" == "" ]]; then if [[ "${sshuser}" == "" || "${server}" == "" || "${originuser}" == "" || "${repo}" == "" ]]; then
echo "Can't parse origin URL: ${origin}" >&2 echo "Can't parse origin URL: ${origin}" >&2
@@ -35,9 +35,9 @@ fi
upstream="https://${server}/${user}/${repo}" upstream="https://${server}/${user}/${repo}"
upstreampush="${remote}:${user}/${repo}" upstreampush="${remote}:${user}/${repo}"
upstreamgroup="upstream upstream-push" upstreamgroup="upstream upstream-push"
current=$( git remote get-url upstream 2>/dev/null ) current=$(git remote get-url upstream 2>/dev/null)
currentpush=$( git remote get-url upstream-push 2>/dev/null ) currentpush=$(git remote get-url upstream-push 2>/dev/null)
currentgroup=$( git config remotes.upstreams ) currentgroup=$(git config remotes.upstreams)
if [[ "${current}" == "${upstream}" ]]; then if [[ "${current}" == "${upstream}" ]]; then
echo "Upstream already set up correctly. Skip" echo "Upstream already set up correctly. Skip"
elif [[ -n "${current}" && "${current}" != "${upstream}" && "${current}" != "${upstreampush}" ]]; then elif [[ -n "${current}" && "${current}" != "${upstream}" && "${current}" != "${upstreampush}" ]]; then
@@ -45,9 +45,9 @@ elif [[ -n "${current}" && "${current}" != "${upstream}" && "${current}" != "${u
else else
if [[ "${current}" == "${upstreampush}" ]]; then if [[ "${current}" == "${upstreampush}" ]]; then
echo "Upstream set to dangerous push URL. Update." echo "Upstream set to dangerous push URL. Update."
_run git remote rename upstream upstream-push || \ _run git remote rename upstream upstream-push ||
_run git remote remove upstream _run git remote remove upstream
currentpush=$( git remote get-url upstream-push 2>/dev/null ) currentpush=$(git remote get-url upstream-push 2>/dev/null)
fi fi
_run git remote add upstream "${upstream}" _run git remote add upstream "${upstream}"
fi fi

View File

@@ -1,8 +1,8 @@
#!/bin/bash #!/bin/bash
if [[ $# -lt 3 || "$1" == "--help" || "$1" = "-h" ]]; then if [[ $# -lt 3 || "$1" == "--help" || "$1" = "-h" ]]; then
name=$( basename $0 ) name=$(basename $0)
cat <<- USAGE cat <<-USAGE
Usage: $name workbranch base/branch user/branch [user/branch [...]] Usage: $name workbranch base/branch user/branch [user/branch [...]]
* workbranch will be created locally from base/branch * workbranch will be created locally from base/branch
@@ -16,7 +16,7 @@ fi
work="$1" work="$1"
shift shift
branches=( $( echo "${@}" | sed "s/:/\//" ) ) branches=($(echo "${@}" | sed "s/:/\//"))
base="${branches[0]}" base="${branches[0]}"
unset branches[0] unset branches[0]
@@ -24,10 +24,10 @@ set -e
users=() users=()
for b in "${branches[@]}"; do for b in "${branches[@]}"; do
users+=( $( echo $b | cut -d/ -f1 ) ) users+=($(echo $b | cut -d/ -f1))
done done
users=( $( printf '%s\n' "${users[@]}" | sort -u ) ) users=($(printf '%s\n' "${users[@]}" | sort -u))
git fetch --multiple upstreams "${users[@]}" git fetch --multiple upstreams "${users[@]}"
git checkout -B "$work" --no-track "$base" git checkout -B "$work" --no-track "$base"
@@ -40,7 +40,7 @@ done
# Make sure the commits look right # Make sure the commits look right
git log --show-signature "$base..HEAD" git log --show-signature "$base..HEAD"
parts=( $( echo $base | sed "s/\// /" ) ) parts=($(echo $base | sed "s/\// /"))
repo="${parts[0]}" repo="${parts[0]}"
b="${parts[1]}" b="${parts[1]}"
push=$repo push=$repo
@@ -50,7 +50,7 @@ fi
if [[ "$repo" == "upstream" ]]; then if [[ "$repo" == "upstream" ]]; then
repo="upstreams" repo="upstreams"
fi fi
cat << PUSH cat <<PUSH
------------------------------------------------------------------- -------------------------------------------------------------------
This script will not push. Verify everything is correct, then push This script will not push. Verify everything is correct, then push

View File

@@ -1,8 +1,8 @@
#!/bin/bash #!/bin/bash
if [[ $# -ne 3 || "$1" == "--help" || "$1" = "-h" ]]; then if [[ $# -ne 3 || "$1" == "--help" || "$1" = "-h" ]]; then
name=$( basename $0 ) name=$(basename $0)
cat <<- USAGE cat <<-USAGE
Usage: $name workbranch base/branch version Usage: $name workbranch base/branch version
* workbranch will be created locally from base/branch. If it exists, * workbranch will be created locally from base/branch. If it exists,
@@ -16,7 +16,7 @@ fi
work="$1" work="$1"
shift shift
base=$( echo "$1" | sed "s/:/\//" ) base=$(echo "$1" | sed "s/:/\//")
shift shift
version=$1 version=$1
@@ -28,16 +28,16 @@ git fetch upstreams
git checkout -B "${work}" --no-track "${base}" git checkout -B "${work}" --no-track "${base}"
push=$( git rev-parse --abbrev-ref --symbolic-full-name '@{push}' \ push=$(git rev-parse --abbrev-ref --symbolic-full-name '@{push}' \
2>/dev/null ) || true 2>/dev/null) || true
if [[ "${push}" != "" ]]; then if [[ "${push}" != "" ]]; then
echo "Warning: ${push} may already exist." echo "Warning: ${push} may already exist."
fi fi
build=$( find -name BuildInfo.cpp ) build=$(find -name BuildInfo.cpp)
sed 's/\(^.*versionString =\).*$/\1 "'${version}'"/' ${build} > version.cpp && \ sed 's/\(^.*versionString =\).*$/\1 "'${version}'"/' ${build} >version.cpp &&
diff "${build}" version.cpp && exit 1 || \ diff "${build}" version.cpp && exit 1 ||
mv -vi version.cpp ${build} mv -vi version.cpp ${build}
git diff git diff
@@ -47,7 +47,7 @@ git commit -S -m "Set version to ${version}"
git log --oneline --first-parent ${base}^.. git log --oneline --first-parent ${base}^..
cat << PUSH cat <<PUSH
------------------------------------------------------------------- -------------------------------------------------------------------
This script will not push. Verify everything is correct, then push This script will not push. Verify everything is correct, then push

113
bin/install-sanitizer-libs.sh Executable file
View File

@@ -0,0 +1,113 @@
#!/bin/bash
# Install sanitizer runtime libraries required to run binaries compiled with:
# -fsanitize=address → libasan.so.8
# -fsanitize=thread → libtsan.so.2
# -fsanitize=undefined → libubsan.so.1
#
# The exact SONAMEs required depend on the compiler toolchain used to build the
# test binaries (see nix/ci-env.nix). If the toolchain is bumped and SONAMEs
# change, update the list below (or detect them from the binaries).
#
# Supported base images:
# debian:bookworm
# ubuntu:20.04
# rhel:9
# nixos/nix — tests are skipped; this script is not called
set -euo pipefail
if [ ! -f /etc/os-release ]; then
echo "ERROR: /etc/os-release not found; cannot detect OS" >&2
exit 1
fi
# shellcheck source=/dev/null
. /etc/os-release
echo "Detected OS: ${ID} ${VERSION_ID:-}"
case "${ID}" in
ubuntu | debian | rhel | centos | rocky | almalinux)
echo "Supported OS detected: ${ID}"
;;
*)
echo "ERROR: unsupported OS '${ID}'. Supported: debian, ubuntu, rhel-family" >&2
exit 1
;;
esac
function preinstall() {
case "${ID}" in
ubuntu)
apt-get update -y
apt-get install -y --no-install-recommends \
gnupg \
software-properties-common
add-apt-repository -y ppa:ubuntu-toolchain-r/test
;;
esac
}
function install() {
case "${ID}" in
debian | ubuntu)
apt-get update -y
apt-get install -y --no-install-recommends \
libasan8 \
libtsan2 \
libubsan1
;;
rhel | centos | rocky | almalinux)
dnf install -y \
libasan8 \
libtsan2 \
libubsan
;;
esac
}
function postinstall() {
# Don't clear cache in non-CI environments
if [ -z "${CI:-}" ]; then
echo "Not running in CI environment; skipping cache cleanup"
return
fi
case "${ID}" in
debian | ubuntu)
apt-get clean
rm -rf /var/lib/apt/lists/*
;;
rhel | centos | rocky | almalinux)
dnf clean -y all
rm -rf /var/cache/dnf/*
;;
esac
}
function verify() {
# Verify that every expected library is now resolvable by the dynamic linker.
missing=0
for lib in libasan.so.8 libtsan.so.2 libubsan.so.1; do
if ldconfig -p | grep -q "${lib}"; then
echo "OK: ${lib} found"
else
echo "ERROR: ${lib} not found after installation" >&2
missing=$((missing + 1))
fi
done
if [ "${missing}" -ne 0 ]; then
echo "ERROR: ${missing} library/libraries missing" >&2
exit 1
fi
}
preinstall
install
postinstall
verify
echo "All sanitizer runtime libraries installed successfully."

View File

@@ -168,7 +168,13 @@ def main():
if not os.environ.get("TIDY"): if not os.environ.get("TIDY"):
return 0 return 0
repo_root = Path(__file__).parent.parent repo_root = Path(
subprocess.check_output(
["git", "rev-parse", "--show-toplevel"],
cwd=Path(__file__).parent,
text=True,
).strip()
)
files = staged_files(repo_root) files = staged_files(repo_root)
if not files: if not files:
return 0 return 0

View File

@@ -0,0 +1,34 @@
#!/usr/bin/env python3
"""
Adds "#pragma once" to the top of header files that don't already have it.
Usage: ./bin/pre-commit/fix_pragma_once.py <file1> <file2> ...
"""
import sys
from pathlib import Path
PRAGMA_ONCE = "#pragma once\n\n"
def fix_pragma_once(path: Path) -> bool:
original = path.read_text(encoding="utf-8")
if PRAGMA_ONCE not in original:
path.write_text(PRAGMA_ONCE + original, encoding="utf-8")
return False
return True
def main() -> int:
files = [Path(f) for f in sys.argv[1:]]
success = True
for path in files:
success &= fix_pragma_once(path)
return 0 if success else 1
if __name__ == "__main__":
sys.exit(main())

View File

@@ -953,6 +953,21 @@
# #
# Optional keys for NuDB and RocksDB: # Optional keys for NuDB and RocksDB:
# #
# cache_size Size of cache for database records. Default is 16384.
# Setting this value to 0 will use the default value.
#
# cache_age Length of time in minutes to keep database records
# cached. Default is 5 minutes. Setting this value to
# 0 will use the default value.
#
# Note: if cache_size or cache_age is not specified,
# default values will be used for the unspecified
# parameter.
#
# Note: the cache will not be created if online_delete
# is specified, because the rotating NodeStore does
# not use this cache).
#
# fast_load Boolean. If set, load the last persisted ledger # fast_load Boolean. If set, load the last persisted ledger
# from disk upon process start before syncing to # from disk upon process start before syncing to
# the network. This is likely to improve performance # the network. This is likely to improve performance

View File

@@ -56,3 +56,16 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64")
else() else()
message(FATAL_ERROR "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}") message(FATAL_ERROR "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
endif() endif()
# --------------------------------------------------------------------
# Sanitizers
# --------------------------------------------------------------------
# SANITIZERS is injected by the Conan toolchain when a sanitizer build is
# requested (see conan/profiles/sanitizers). The flags are applied to the
# 'common' target in XrplSanitizers; this flag lets other modules know a
# sanitizer build is active without depending on that module.
if(DEFINED SANITIZERS)
set(SANITIZERS_ENABLED TRUE)
else()
set(SANITIZERS_ENABLED FALSE)
endif()

View File

@@ -0,0 +1,53 @@
#[===================================================================[
Patch executables to run in non-Nix environments.
The Nix-based CI image links binaries against an ELF interpreter (loader)
that lives in the Nix store, so the resulting binaries don't run elsewhere
(including once installed from the .deb package). `patch_nix_binary` adds a
POST_BUILD step that resets the interpreter to the system default loader and
drops the rpath.
This is only active inside the Nix-based image, detected by the presence of
/tmp/loader-path.sh (shipped by that image, resolves the default loader). It
is skipped for sanitizer builds, whose runtime libraries are resolved through
the rpath. Everywhere else `patch_nix_binary` is a no-op.
#]===================================================================]
include_guard(GLOBAL)
include(CompilationEnv)
# Provided by the Nix-based CI image; prints the system default ELF loader path.
set(_loader_path_script "/tmp/loader-path.sh")
if(is_linux AND NOT SANITIZERS_ENABLED AND EXISTS "${_loader_path_script}")
execute_process(
COMMAND "${_loader_path_script}"
OUTPUT_VARIABLE DEFAULT_LOADER_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL ANY
)
find_program(PATCHELF_COMMAND patchelf REQUIRED)
set(PATCH_NIX_BINARIES TRUE)
message(
STATUS
"Binaries will be patched to use loader '${DEFAULT_LOADER_PATH}'"
)
else()
set(PATCH_NIX_BINARIES FALSE)
endif()
function(patch_nix_binary target)
if(NOT PATCH_NIX_BINARIES)
return()
endif()
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND
"${PATCHELF_COMMAND}" --set-interpreter "${DEFAULT_LOADER_PATH}"
--remove-rpath "$<TARGET_FILE:${target}>"
COMMENT "Patching ${target}: set default loader, remove rpath"
VERBATIM
)
endfunction()

View File

@@ -1,22 +0,0 @@
include(isolate_headers)
function(xrpl_add_test name)
set(target ${PROJECT_NAME}.test.${name})
file(
GLOB_RECURSE sources
CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/${name}.cpp"
)
add_executable(${target} ${ARGN} ${sources})
isolate_headers(
${target}
"${CMAKE_SOURCE_DIR}"
"${CMAKE_SOURCE_DIR}/tests/${name}"
PRIVATE
)
add_test(NAME ${target} COMMAND ${target})
endfunction()

View File

@@ -145,13 +145,48 @@ else()
INTERFACE INTERFACE
-rdynamic -rdynamic
$<$<BOOL:${is_linux}>:-Wl,-z,relro,-z,now,--build-id> $<$<BOOL:${is_linux}>:-Wl,-z,relro,-z,now,--build-id>
# link to static libc/c++ iff: * static option set and * NOT APPLE (AppleClang does not support static # link to static libc/c++ if:
# libc/c++) and * NOT SANITIZERS (sanitizers typically don't work with static libc/c++) # * static option set and
$<$<AND:$<BOOL:${static}>,$<NOT:$<BOOL:${APPLE}>>,$<NOT:$<BOOL:${SANITIZERS_ENABLED}>>>: # * NOT APPLE (AppleClang does not support static libc/c++)
$<$<AND:$<BOOL:${static}>,$<NOT:$<BOOL:${APPLE}>>>:
-static-libstdc++ -static-libstdc++
-static-libgcc -static-libgcc
> >
) )
# On aarch64, libatomic is required for atomic operations. It is not needed on x86_64.
# Linking it statically on Linux
if(is_arm64 AND is_linux)
target_link_options(
common
INTERFACE -Wl,--push-state -Wl,-Bstatic -latomic -Wl,--pop-state
)
endif()
# Keep -stdlib=libstdc++ off the compile commands, but preserve it for linking.
#
# Conan turns `compiler.libcxx=libstdc++` into `-stdlib=libstdc++` and puts it in
# CMAKE_CXX_FLAGS, which CMake passes to BOTH compile and link steps. On a normal Clang
# the compile step consumes it while choosing the C++ stdlib include paths. The Nixpkgs
# Clang wrapper supplies those paths itself (via -nostdinc++), so at compile time the
# flag is unused -> Clang errors under our -Werror. At link time the flag IS consumed
# (it selects the C++ runtime), so we move it there instead of dropping it entirely.
get_filename_component(_cxx_real "${CMAKE_CXX_COMPILER}" REALPATH)
if(
_cxx_real MATCHES "^/nix/store/"
AND is_linux
AND is_clang
AND CMAKE_CXX_FLAGS MATCHES "stdlib=libstdc"
)
string(
REPLACE "-stdlib=libstdc++"
""
CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS}"
)
string(STRIP "${CMAKE_CXX_FLAGS}" CMAKE_CXX_FLAGS)
add_link_options($<$<LINK_LANGUAGE:CXX>:-stdlib=libstdc++>)
endif()
endif() endif()
# Antithesis instrumentation will only be built and deployed using machines running Linux. # Antithesis instrumentation will only be built and deployed using machines running Linux.

View File

@@ -94,6 +94,9 @@ add_module(xrpl basics)
target_link_libraries(xrpl.libxrpl.basics PUBLIC xrpl.libxrpl.beast) target_link_libraries(xrpl.libxrpl.basics PUBLIC xrpl.libxrpl.beast)
# Level 03 # Level 03
add_module(xrpl config)
target_link_libraries(xrpl.libxrpl.config PUBLIC xrpl.libxrpl.basics)
add_module(xrpl json) add_module(xrpl json)
target_link_libraries(xrpl.libxrpl.json PUBLIC xrpl.libxrpl.basics) target_link_libraries(xrpl.libxrpl.json PUBLIC xrpl.libxrpl.basics)
@@ -120,6 +123,7 @@ target_link_libraries(
xrpl.libxrpl.core xrpl.libxrpl.core
PUBLIC PUBLIC
xrpl.libxrpl.basics xrpl.libxrpl.basics
xrpl.libxrpl.config
xrpl.libxrpl.json xrpl.libxrpl.json
xrpl.libxrpl.protocol xrpl.libxrpl.protocol
xrpl.libxrpl.protocol_autogen xrpl.libxrpl.protocol_autogen
@@ -143,7 +147,11 @@ target_link_libraries(
add_module(xrpl nodestore) add_module(xrpl nodestore)
target_link_libraries( target_link_libraries(
xrpl.libxrpl.nodestore xrpl.libxrpl.nodestore
PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json xrpl.libxrpl.protocol PUBLIC
xrpl.libxrpl.basics
xrpl.libxrpl.config
xrpl.libxrpl.json
xrpl.libxrpl.protocol
) )
add_module(xrpl shamap) add_module(xrpl shamap)
@@ -159,13 +167,14 @@ target_link_libraries(
add_module(xrpl rdb) add_module(xrpl rdb)
target_link_libraries( target_link_libraries(
xrpl.libxrpl.rdb xrpl.libxrpl.rdb
PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.core PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.config xrpl.libxrpl.core
) )
add_module(xrpl server) add_module(xrpl server)
target_link_libraries( target_link_libraries(
xrpl.libxrpl.server xrpl.libxrpl.server
PUBLIC PUBLIC
xrpl.libxrpl.config
xrpl.libxrpl.protocol xrpl.libxrpl.protocol
xrpl.libxrpl.core xrpl.libxrpl.core
xrpl.libxrpl.rdb xrpl.libxrpl.rdb
@@ -210,6 +219,7 @@ target_link_modules(
basics basics
beast beast
conditions conditions
config
core core
crypto crypto
git git
@@ -237,6 +247,7 @@ target_link_modules(
if(xrpld) if(xrpld)
add_executable(xrpld) add_executable(xrpld)
patch_nix_binary(xrpld)
if(tests) if(tests)
target_compile_definitions(xrpld PUBLIC ENABLE_TESTS) target_compile_definitions(xrpld PUBLIC ENABLE_TESTS)
target_compile_definitions( target_compile_definitions(

View File

@@ -47,7 +47,7 @@ setup_target_for_coverage_gcovr(
"include/xrpl/beast/test" "include/xrpl/beast/test"
"include/xrpl/beast/unit_test" "include/xrpl/beast/unit_test"
"${CMAKE_BINARY_DIR}/pb-xrpl.libpb" "${CMAKE_BINARY_DIR}/pb-xrpl.libpb"
DEPENDENCIES xrpld xrpl.tests DEPENDENCIES xrpld xrpl_tests
) )
add_code_coverage_to_target(opts INTERFACE) add_code_coverage_to_target(opts INTERFACE)

View File

@@ -28,7 +28,6 @@ endif()
set(package_env set(package_env
SRC_DIR=${CMAKE_SOURCE_DIR} SRC_DIR=${CMAKE_SOURCE_DIR}
BUILD_DIR=${CMAKE_BINARY_DIR} BUILD_DIR=${CMAKE_BINARY_DIR}
PKG_VERSION=${xrpld_version}
PKG_RELEASE=${pkg_release} PKG_RELEASE=${pkg_release}
) )

View File

@@ -14,11 +14,9 @@
include_guard(GLOBAL) include_guard(GLOBAL)
include(CompilationEnv) include(CompilationEnv)
if(NOT DEFINED SANITIZERS) if(NOT SANITIZERS_ENABLED)
set(SANITIZERS_ENABLED FALSE)
return() return()
endif() endif()
set(SANITIZERS_ENABLED TRUE)
message(STATUS "=== Configuring Sanitizers ===") message(STATUS "=== Configuring Sanitizers ===")
message(STATUS " SANITIZERS: ${SANITIZERS}") message(STATUS " SANITIZERS: ${SANITIZERS}")

View File

@@ -0,0 +1,13 @@
# Python dependencies for XRP Ledger code generation scripts
#
# These packages are required to run the code generation scripts that
# parse macro files and generate C++ wrapper classes.
# C preprocessor for Python - used to preprocess macro files
pcpp>=1.30
# Parser combinator library - used to parse the macro DSL
pyparsing>=3.0.0
# Template engine - used to generate C++ code from templates
Mako>=1.2.2

View File

@@ -1,13 +1,105 @@
# Python dependencies for XRP Ledger code generation scripts # This file was autogenerated by uv via the following command:
# # uv pip compile requirements.in --generate-hashes --output-file requirements.txt
# These packages are required to run the code generation scripts that mako==1.3.12 \
# parse macro files and generate C++ wrapper classes. --hash=sha256:8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9 \
--hash=sha256:9f778e93289bd410bb35daadeb4fc66d95a746f0b75777b942088b7fd7af550a
# C preprocessor for Python - used to preprocess macro files # via -r requirements.in
pcpp>=1.30 markupsafe==3.0.3 \
--hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \
# Parser combinator library - used to parse the macro DSL --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \
pyparsing>=3.0.0 --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \
--hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \
# Template engine - used to generate C++ code from templates --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \
Mako>=1.2.2 --hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \
--hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \
--hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \
--hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \
--hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \
--hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \
--hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \
--hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \
--hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \
--hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \
--hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \
--hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \
--hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \
--hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \
--hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \
--hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \
--hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \
--hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \
--hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \
--hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \
--hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \
--hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \
--hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \
--hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \
--hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \
--hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \
--hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \
--hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \
--hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \
--hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \
--hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \
--hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \
--hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \
--hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \
--hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \
--hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \
--hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \
--hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \
--hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \
--hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \
--hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \
--hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \
--hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \
--hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \
--hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \
--hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \
--hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \
--hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \
--hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \
--hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \
--hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \
--hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \
--hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \
--hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \
--hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \
--hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \
--hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \
--hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \
--hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \
--hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \
--hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \
--hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \
--hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \
--hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \
--hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \
--hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \
--hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \
--hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \
--hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \
--hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \
--hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \
--hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \
--hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \
--hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \
--hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \
--hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \
--hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \
--hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \
--hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \
--hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \
--hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \
--hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \
--hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \
--hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50
# via mako
pcpp==1.30 \
--hash=sha256:05fe08292b6da57f385001c891a87f40d6aa7f46787b03e8ba326d20a3297c6e \
--hash=sha256:5af9fbce55f136d7931ae915fae03c34030a3b36c496e72d9636cedc8e2543a1
# via -r requirements.in
pyparsing==3.3.2 \
--hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \
--hash=sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc
# via -r requirements.in

View File

@@ -33,7 +33,7 @@ public:
* @brief Construct a ${name} ledger entry wrapper from an existing SLE object. * @brief Construct a ${name} ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match. * @throws std::runtime_error if the ledger entry type doesn't match.
*/ */
explicit ${name}(std::shared_ptr<SLE const> sle) explicit ${name}(SLE::const_pointer sle)
: LedgerEntryBase(std::move(sle)) : LedgerEntryBase(std::move(sle))
{ {
// Verify ledger entry type // Verify ledger entry type
@@ -168,7 +168,7 @@ ${field['typeData']['setter_type']} ${field['paramName']}${',' if i < len(requir
* @param sle The existing ledger entry to copy from. * @param sle The existing ledger entry to copy from.
* @throws std::runtime_error if the ledger entry type doesn't match. * @throws std::runtime_error if the ledger entry type doesn't match.
*/ */
${name}Builder(std::shared_ptr<SLE const> sle) ${name}Builder(SLE::const_pointer sle)
{ {
if (sle->at(sfLedgerEntryType) != ${tag}) if (sle->at(sfLedgerEntryType) != ${tag})
{ {

View File

@@ -1,9 +1,9 @@
{ {
"version": "0.5", "version": "0.5",
"requires": [ "requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503", "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987", "xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1776096494.149", "sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1778091117.311",
"soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231", "soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878", "snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
"secp256k1/0.7.1#481881709eb0bdd0185a12b912bbe8ad%1770910500.329", "secp256k1/0.7.1#481881709eb0bdd0185a12b912bbe8ad%1770910500.329",
@@ -15,19 +15,19 @@
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914", "lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492", "libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03", "libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1776147552.838", "libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1778091117.848",
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228", "jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152", "gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152",
"grpc/1.78.1#b1a9e74b145cc471bed4dc64dc6eb2c1%1774467387.342", "grpc/1.81.0#2fb144aeb47e7f35c6ebb0e5f35bed31%1781620605.685",
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772", "ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772",
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772", "date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681", "c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681",
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837", "bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778050991.9", "boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778091165.282",
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196" "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
], ],
"build_requires": [ "build_requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503", "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056",
"strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1774447376.964", "strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1774447376.964",
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12", "protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1765850144.707", "nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1765850144.707",

View File

@@ -2,7 +2,7 @@
arch=x86_64 arch=x86_64
build_type=Release build_type=Release
compiler=gcc compiler=gcc
compiler.cppstd=20 compiler.cppstd=23
compiler.libcxx=libstdc++11 compiler.libcxx=libstdc++11
compiler.version=13 compiler.version=13
os=Linux os=Linux

View File

@@ -2,7 +2,7 @@
arch=armv8 arch=armv8
build_type=Release build_type=Release
compiler=apple-clang compiler=apple-clang
compiler.cppstd=20 compiler.cppstd=23
compiler.libcxx=libc++ compiler.libcxx=libc++
compiler.version=17.0 compiler.version=17.0
os=Macos os=Macos

View File

@@ -2,7 +2,7 @@
arch=x86_64 arch=x86_64
build_type=Release build_type=Release
compiler=msvc compiler=msvc
compiler.cppstd=20 compiler.cppstd=23
compiler.runtime=dynamic compiler.runtime=dynamic
compiler.runtime_type=Release compiler.runtime_type=Release
compiler.version=194 compiler.version=194

View File

@@ -1 +1,8 @@
{% set os = detect_api.detect_os() %}
include(sanitizers) include(sanitizers)
[conf]
{% if os == "Linux" %}
user.package:libc_version=2.31
tools.info.package_id:confs+=["user.package:libc_version"]
{% endif %}

View File

@@ -12,7 +12,7 @@ arch={{ arch }}
build_type=Debug build_type=Debug
compiler={{compiler}} compiler={{compiler}}
compiler.version={{ compiler_version }} compiler.version={{ compiler_version }}
compiler.cppstd=20 compiler.cppstd=23
{% if os == "Windows" %} {% if os == "Windows" %}
compiler.runtime=static compiler.runtime=static
{% else %} {% else %}

View File

@@ -52,52 +52,50 @@ include(default)
{% endif %} {% endif %}
{# Frame pointer required for meaningful stack traces; -O1 for reasonable performance #} {# Frame pointer required for meaningful stack traces; -O1 for reasonable performance #}
{% set compile_flags = ["-fno-omit-frame-pointer", "-O1"] %} {% set sanitizer_compiler_flags = ["-fno-omit-frame-pointer", "-O1"] %}
{% if compiler == "gcc" %} {% if compiler == "gcc" %}
{# Suppress false positive warnings with GCC #} {# Suppress false positive warnings with GCC #}
{% set _ = compile_flags.append("-Wno-stringop-overflow") %} {% set _ = sanitizer_compiler_flags.append("-Wno-stringop-overflow") %}
{% set relocation_flags = [] %} {% set relocation_flags = [] %}
{% if arch == "x86_64" and enable_asan %} {% if arch == "x86_64" and enable_asan %}
{# Large code model prevents relocation errors in instrumented ASAN binaries #} {# Large code model prevents relocation errors in instrumented ASAN binaries #}
{% set _ = compile_flags.append("-mcmodel=large") %} {% set _ = sanitizer_compiler_flags.append("-mcmodel=large") %}
{% set _ = relocation_flags.append("-mcmodel=large") %} {% set _ = relocation_flags.append("-mcmodel=large") %}
{% elif enable_tsan %} {% elif enable_tsan %}
{# GCC doesn't support atomic_thread_fence with TSAN; suppress warnings #} {# GCC doesn't support atomic_thread_fence with TSAN; suppress warnings #}
{% set _ = compile_flags.append("-Wno-tsan") %} {% set _ = sanitizer_compiler_flags.append("-Wno-tsan") %}
{% if arch == "x86_64" %} {% if arch == "x86_64" %}
{# Medium code model for TSAN; large is incompatible #} {# Medium code model for TSAN; large is incompatible #}
{% set _ = compile_flags.append("-mcmodel=medium") %} {% set _ = sanitizer_compiler_flags.append("-mcmodel=medium") %}
{% set _ = relocation_flags.append("-mcmodel=medium") %} {% set _ = relocation_flags.append("-mcmodel=medium") %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% set fsanitize = "-fsanitize=" ~ ",".join(sanitizer_types) %} {% set fsanitize = "-fsanitize=" ~ ",".join(sanitizer_types) %}
{% set _ = compile_flags.append(fsanitize) %} {% set _ = sanitizer_compiler_flags.append(fsanitize) %}
{% set _ = relocation_flags.append(fsanitize) %} {% set _ = relocation_flags.append(fsanitize) %}
{% set sanitizer_compiler_flags = " ".join(compile_flags) %} {% set sanitizer_linker_flags = relocation_flags %}
{% set sanitizer_linker_flags = " ".join(relocation_flags) %}
{% elif compiler == "clang" or compiler == "apple-clang" %} {% elif compiler == "clang" or compiler == "apple-clang" %}
{% set fsanitize = "-fsanitize=" ~ ",".join(sanitizer_types) %} {% set fsanitize = "-fsanitize=" ~ ",".join(sanitizer_types) %}
{% set _ = compile_flags.append(fsanitize) %} {% set _ = sanitizer_compiler_flags.append(fsanitize) %}
{% set sanitizer_compiler_flags = " ".join(compile_flags) %} {% set sanitizer_linker_flags = [fsanitize] %}
{% set sanitizer_linker_flags = fsanitize %}
{% endif %} {% endif %}
[conf] [conf]
tools.build:defines+={{defines}} tools.build:defines+={{defines}}
tools.build:cxxflags+=['{{sanitizer_compiler_flags}}'] tools.build:cxxflags+={{sanitizer_compiler_flags}}
tools.build:sharedlinkflags+=['{{sanitizer_linker_flags}}'] tools.build:sharedlinkflags+={{sanitizer_linker_flags}}
tools.build:exelinkflags+=['{{sanitizer_linker_flags}}'] tools.build:exelinkflags+={{sanitizer_linker_flags}}
tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags", "tools.build:defines"] tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags", "tools.build:defines"]
# &: means "apply only to the consumer/root package" # &: means "apply only to the consumer/root package"
&:tools.cmake.cmaketoolchain:extra_variables={"SANITIZERS": "{{sanitizers}}", "SANITIZERS_COMPILER_FLAGS": "{{sanitizer_compiler_flags}}", "SANITIZERS_LINKER_FLAGS": "{{sanitizer_linker_flags}}"} &:tools.cmake.cmaketoolchain:extra_variables={"SANITIZERS": "{{sanitizers}}", "SANITIZERS_COMPILER_FLAGS": "{{sanitizer_compiler_flags | join(' ')}}", "SANITIZERS_LINKER_FLAGS": "{{sanitizer_linker_flags | join(' ')}}"}
[options] [options]
{% if enable_asan %} {% if enable_asan %}

View File

@@ -28,7 +28,7 @@ class Xrpl(ConanFile):
requires = [ requires = [
"ed25519/2015.03", "ed25519/2015.03",
"grpc/1.78.1", "grpc/1.81.0",
"libarchive/3.8.7", "libarchive/3.8.7",
"nudb/2.0.9", "nudb/2.0.9",
"openssl/3.6.2", "openssl/3.6.2",

View File

@@ -50,6 +50,7 @@ words:
- AMMXRP - AMMXRP
- amt - amt
- amts - amts
- archs
- asnode - asnode
- asynchrony - asynchrony
- attestation - attestation
@@ -83,6 +84,7 @@ words:
- coro - coro
- coros - coros
- cowid - cowid
- cpack
- cryptocondition - cryptocondition
- cryptoconditional - cryptoconditional
- cryptoconditions - cryptoconditions
@@ -93,6 +95,7 @@ words:
- daria - daria
- dcmake - dcmake
- dearmor - dearmor
- dedented
- deleteme - deleteme
- demultiplexer - demultiplexer
- deserializaton - deserializaton
@@ -106,6 +109,7 @@ words:
- enabled - enabled
- enablerepo - enablerepo
- endmacro - endmacro
- envrc
- exceptioned - exceptioned
- EXPECT_STREQ - EXPECT_STREQ
- Falco - Falco
@@ -133,6 +137,7 @@ words:
- iou - iou
- ious - ious
- isrdc - isrdc
- isystem
- itype - itype
- jemalloc - jemalloc
- jlog - jlog
@@ -199,11 +204,13 @@ words:
- nonxrp - nonxrp
- noreplace - noreplace
- noripple - noripple
- nostdinc
- notifempty - notifempty
- nudb - nudb
- nullptr - nullptr
- nunl - nunl
- Nyffenegger - Nyffenegger
- onlatest
- ostr - ostr
- pargs - pargs
- partitioner - partitioner
@@ -227,8 +234,10 @@ words:
- pyenv - pyenv
- pyparsing - pyparsing
- qalloc - qalloc
- qbsprofile
- queuable - queuable
- Raphson - Raphson
- rcflags
- replayer - replayer
- rerere - rerere
- retriable - retriable
@@ -254,6 +263,7 @@ words:
- sfields - sfields
- shamap - shamap
- shamapitem - shamapitem
- shfmt
- shlibs - shlibs
- sidechain - sidechain
- SIGGOOD - SIGGOOD
@@ -291,6 +301,7 @@ words:
- txs - txs
- ubsan - ubsan
- UBSAN - UBSAN
- ufdio
- umant - umant
- unacquired - unacquired
- unambiguity - unambiguity
@@ -298,6 +309,7 @@ words:
- unauthorizing - unauthorizing
- unergonomic - unergonomic
- unfetched - unfetched
- unfindable
- unflatten - unflatten
- unfund - unfund
- unimpair - unimpair

View File

@@ -1,66 +0,0 @@
ARG BASE_IMAGE=nixos/nix:latest
# Nix builder
FROM nixos/nix:latest AS builder-source
RUN mkdir -p ~/.config/nix && \
echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
# Copy our source and setup our working dir.
COPY nix/ci-env.nix /tmp/build/nix/ci-env.nix
COPY nix/packages.nix /tmp/build/nix/packages.nix
COPY nix/utils.nix /tmp/build/nix/utils.nix
COPY flake.nix /tmp/build/
COPY flake.lock /tmp/build/
WORKDIR /tmp/build
FROM builder-source AS builder
# Build our Nix CI environment (all build tools in a single store path)
RUN nix \
--option filter-syscalls false \
build
# Copy the Nix store closure into a directory. The Nix store closure is the
# entire set of Nix store values that we need for our build.
RUN mkdir /tmp/nix-store-closure && \
cp -R $(nix-store -qR result/) /tmp/nix-store-closure
# Final image
FROM ${BASE_IMAGE}
# bash is not located at /bin/bash in nixos/nix, so we need to create a symlink to it.
RUN if [ -d /nix ]; then \
ln -s /root/.nix-profile/bin/bash /bin/bash; \
fi
# Use Bash as the default shell for RUN commands, using the options
# `set -o errexit -o pipefail`, and as the entrypoint.
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
ENTRYPOINT ["/bin/bash"]
# Copy /nix/store and the env symlink tree
COPY --from=builder /tmp/nix-store-closure /nix/store
COPY --from=builder /tmp/build/result /nix/ci-env
ENV PATH="/nix/ci-env/bin:$PATH"
RUN <<EOF
ccache --version
clang-format --version
cmake --version
conan --version
g++ --version
gcc --version
gcovr --version
git --version
make --version
mold --version
ninja --version
perl --version
pkg-config --version
pre-commit --version
python3 --version
run-clang-tidy --help
vim --version
EOF

193
docs/build/advanced_conan.md vendored Normal file
View File

@@ -0,0 +1,193 @@
# Advanced Conan configuration
This document provides advanced instructions for setting up and configuring Conan for `xrpld` development: custom profiles, the lockfile, patched recipes, and profile tweaks.
## Custom profile
If the default profile does not work for you and you do not yet have a Conan
profile, you can create one by running:
```bash
conan profile detect
```
You may need to make changes to the profile to suit your environment. You can
refer to the provided `conan/profiles/default` profile for inspiration, and you
may also need to apply the required [tweaks](#conan-profile-tweaks) to this
default profile.
## Conan lockfile
To achieve reproducible dependencies, we use a [Conan lockfile](https://docs.conan.io/2/tutorial/versioning/lockfiles.html),
which has to be updated every time dependencies change.
Please see the [instructions on how to regenerate the lockfile](../../conan/lockfile/README.md).
## Patched recipes
Occasionally, we need patched recipes or recipes not present in Conan Center.
We maintain a fork of the Conan Center Index
[here](https://github.com/XRPLF/conan-center-index/) containing the modified and newly added recipes.
To ensure our patched recipes are used, you must add our Conan remote at a
higher index than the default Conan Center remote, so it is consulted first. You
can do this by running:
```bash
conan remote add --index 0 --force xrplf https://conan.ripplex.io
```
Alternatively, you can pull our recipes from the repository and export them locally:
```bash
# Define which recipes to export.
recipes=('abseil' 'ed25519' 'mpt-crypto' 'openssl' 'secp256k1' 'snappy' 'soci' 'wasm-xrplf' 'wasmi')
# Selectively check out the recipes from our CCI fork.
cd external
mkdir -p conan-center-index
cd conan-center-index
git init
git remote add origin git@github.com:XRPLF/conan-center-index.git
git sparse-checkout init
for recipe in "${recipes[@]}"; do
echo "Checking out recipe '${recipe}'..."
git sparse-checkout add recipes/${recipe}
done
git fetch origin master
git checkout master
./export_all.sh
cd ../../
```
In the case we switch to a newer version of a dependency that still requires a
patch or add a new dependency, it will be necessary for you to pull in the changes and re-export the
updated dependencies with the newer version. However, if we switch to a newer
version that no longer requires a patch, no action is required on your part, as
the new recipe will be automatically pulled from the official Conan Center.
> [!NOTE]
> You might need to add `--lockfile=""` to your `conan install` command
> to avoid automatic use of the existing `conan.lock` file when you run
> `conan export` manually on your machine
>
> This is not recommended though, as you might end up using different revisions of recipes.
## Conan profile tweaks
### Missing compiler version
If you see an error similar to the following after running `conan profile show`:
```text
ERROR: Invalid setting '17' is not a valid 'settings.compiler.version' value.
Possible values are ['5.0', '5.1', '6.0', '6.1', '7.0', '7.3', '8.0', '8.1',
'9.0', '9.1', '10.0', '11.0', '12.0', '13', '13.0', '13.1', '14', '14.0', '15',
'15.0', '16', '16.0']
Read "http://docs.conan.io/2/knowledge/faq.html#error-invalid-setting"
```
you need to create `$(conan config home)/settings_user.yml` file if it doesn't exist and add the required version number(s)
to the `version` array specific for your compiler. For example:
```yaml
compiler:
apple-clang:
version: ["17.0"]
```
### Multiple compilers
If you have multiple compilers installed, make sure to select the one to use in
your default Conan configuration **before** running `conan profile detect`, by
setting the `CC` and `CXX` environment variables.
For example, if you are running MacOS and have [homebrew
LLVM@18](https://formulae.brew.sh/formula/llvm@18), and want to use it as a
compiler in the new Conan profile:
```bash
export CC=$(brew --prefix llvm@18)/bin/clang
export CXX=$(brew --prefix llvm@18)/bin/clang++
conan profile detect
```
You should also explicitly set the path to the compiler in the profile file,
which helps to avoid errors when `CC` and/or `CXX` are set and disagree with the
selected Conan profile. For example:
```text
[conf]
tools.build:compiler_executables={'c':'/usr/bin/gcc','cpp':'/usr/bin/g++'}
```
### Multiple profiles
You can manage multiple Conan profiles in the directory
`$(conan config home)/profiles`, for example renaming `default` to a different
name and then creating a new `default` profile for a different compiler.
### Select language
The default profile created by Conan will typically select different C++ dialect
than C++23 used by this project. You should set `23` in the profile line
starting with `compiler.cppstd=`. For example:
```bash
sed -i.bak -e 's|^compiler\.cppstd=.*$|compiler.cppstd=23|' $(conan config home)/profiles/default
```
### Select standard library in Linux
**Linux** developers will commonly have a default Conan [profile][] that
compiles with GCC and links with libstdc++. If you are linking with libstdc++
(see profile setting `compiler.libcxx`), then you will need to choose the
`libstdc++11` ABI:
```bash
sed -i.bak -e 's|^compiler\.libcxx=.*$|compiler.libcxx=libstdc++11|' $(conan config home)/profiles/default
```
### Select architecture and runtime in Windows
**Windows** developers may need to use the x64 native build tools. An easy way
to do that is to run the shortcut "x64 Native Tools Command Prompt" for the
version of Visual Studio that you have installed.
Windows developers must also build `xrpld` and its dependencies for the x64
architecture:
```bash
sed -i.bak -e 's|^arch=.*$|arch=x86_64|' $(conan config home)/profiles/default
```
**Windows** developers also must select static runtime:
```bash
sed -i.bak -e 's|^compiler\.runtime=.*$|compiler.runtime=static|' $(conan config home)/profiles/default
```
## Add a Dependency
If you want to experiment with a new package, follow these steps:
1. Search for the package on [Conan Center](https://conan.io/center/).
2. Modify [`conanfile.py`](../../conanfile.py):
- Add a version of the package to the `requires` property.
- Change any default options for the package by adding them to the
`default_options` property (with syntax `'$package:$option': $value`).
3. Regenerate the [Conan lockfile](../../conan/lockfile/README.md) so the new
dependency is captured:
```bash
./conan/lockfile/regenerate.sh
```
4. Modify [`CMakeLists.txt`](../../CMakeLists.txt):
- Add a call to `find_package($package REQUIRED)`.
- Link a library from the package to the target `xrpl_libs`
(search for the existing call to `target_link_libraries(xrpl_libs INTERFACE ...)`).
5. Start coding! Don't forget to include whatever headers you need from the package.
[profile]: https://docs.conan.io/2/reference/config_files/profiles.html

2
docs/build/conan.md vendored
View File

@@ -115,7 +115,7 @@ By default, Conan will use the profile named "default".
[find_package]: https://cmake.org/cmake/help/latest/command/find_package.html [find_package]: https://cmake.org/cmake/help/latest/command/find_package.html
[pcf]: https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#package-configuration-file [pcf]: https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#package-configuration-file
[prefix_path]: https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html [prefix_path]: https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html
[profile]: https://docs.conan.io/en/latest/reference/profiles.html [profile]: https://docs.conan.io/2/reference/config_files/profiles.html
[pvf]: https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#package-version-file [pvf]: https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#package-version-file
[runtime]: https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html [runtime]: https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html
[search]: https://cmake.org/cmake/help/latest/command/find_package.html#search-procedure [search]: https://cmake.org/cmake/help/latest/command/find_package.html#search-procedure

View File

@@ -1,69 +1,73 @@
Our [build instructions][BUILD.md] assume you have a C++ development Our [build instructions][BUILD.md] assume you have a C++ development
environment complete with Git, Python, Conan, CMake, and a C++ compiler. environment complete with Git, Python, Conan, CMake, and a C++ compiler.
This document exists to help readers set one up on any of the Big Three This document explains how to set one up.
platforms: Linux, macOS, or Windows.
As an alternative to system packages, the Nix development shell can be used to provide a development environment. See [using nix development shell](./nix.md) for more details.
[BUILD.md]: ../../BUILD.md [BUILD.md]: ../../BUILD.md
## Linux ## Tested compiler versions
Package ecosystems vary across Linux distributions, `xrpld` is built in the **C++23** dialect by default.
so there is no one set of instructions that will work for every Linux user. Make sure your toolchain is recent enough — the compiler versions currently tested in CI are:
The instructions below are written for Debian 12 (Bookworm).
``` | Compiler | Version |
export GCC_RELEASE=12 | ----------- | ------- |
sudo apt update | GCC | 15.2 |
sudo apt install --yes gcc-${GCC_RELEASE} g++-${GCC_RELEASE} python3-pip \ | Clang | 22 |
python-is-python3 python3-venv python3-dev curl wget ca-certificates \ | Apple Clang | 17 |
git build-essential cmake ninja-build libc6-dev | MSVC | 19.44 |
sudo pip install --break-system-packages conan
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-${GCC_RELEASE} 999 LLVM tools (`clang-tidy` and `clang-format`) are also pinned to version 22.
sudo update-alternatives --install \
/usr/bin/gcc gcc /usr/bin/gcc-${GCC_RELEASE} 100 \ Older compilers may fail to build the latest `develop` code: the codebase now
--slave /usr/bin/g++ g++ /usr/bin/g++-${GCC_RELEASE} \ relies on C++23 features and has been adjusted for `clang-tidy`.
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-${GCC_RELEASE} \ If the latest code doesn't build for you, update your build toolchain first.
--slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-${GCC_RELEASE} \
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-${GCC_RELEASE} \ ## Linux and macOS
--slave /usr/bin/gcov gcov /usr/bin/gcov-${GCC_RELEASE} \
--slave /usr/bin/gcov-tool gcov-tool /usr/bin/gcov-tool-${GCC_RELEASE} \ The **recommended way** to get a development environment on Linux and macOS is
--slave /usr/bin/gcov-dump gcov-dump /usr/bin/gcov-dump-${GCC_RELEASE} \ the Nix development shell. It provides the exact tooling used in CI — `git`,
--slave /usr/bin/lto-dump lto-dump /usr/bin/lto-dump-${GCC_RELEASE} `python`, `conan`, `cmake`, `clang-tidy`, `clang-format`, and everything else —
sudo update-alternatives --auto cc with a single command and without installing anything system-wide:
sudo update-alternatives --auto gcc
```bash
nix --experimental-features 'nix-command flakes' develop
``` ```
If you use different Linux distribution, hope the instruction above can guide On **Linux**, Nix also provides the compiler (GCC). On **macOS**, the shell uses
you in the right direction. We try to maintain compatibility with all recent your **system-wide Apple Clang** as the compiler, so you still need to manage
compiler releases, so if you use a rolling distribution like e.g. Arch or CentOS its version (see below).
then there is a chance that everything will "just work".
## macOS See [Using the Nix development shell](./nix.md) for installation and usage
details, including how to select a different compiler.
Open a Terminal and enter the below command to bring up a dialog to install > [!NOTE]
the command line developer tools. > Using Nix is not mandatory. Any custom environment (Homebrew packages or
Once it is finished, this command should return a version greater than the > anything else) will continue to work, but then it is up to you to keep it in
minimum required (see [BUILD.md][]). > sync with the environment used in CI. Nix unifies the development environment
> for everyone and synchronizes updates, which is why we recommend it.
``` ### macOS: managing the Apple Clang version
Because the Nix shell uses the system-wide Apple Clang on macOS, the compiler
version is whatever your installed Xcode (or Command Line Tools) provides. The
following command should return a version greater than or equal to the
[minimum required](#tested-compiler-versions):
```bash
clang --version clang --version
``` ```
### Install Xcode Specific Version (Optional) If you develop other applications using Xcode, you might be consistently
updating to the newest version of Apple Clang, which will likely cause issues
If you develop other applications using XCode you might be consistently updating to the newest version of Apple Clang. building xrpld. You may want to install and pin a specific version of Xcode:
This will likely cause issues building xrpld. You may want to install a specific version of Xcode:
1. **Download Xcode** 1. **Download Xcode**
- Visit [Apple Developer Downloads](https://developer.apple.com/download/more/) - Visit [Apple Developer Downloads](https://developer.apple.com/download/more/)
- Sign in with your Apple Developer account - Sign in with your Apple Developer account
- Search for an Xcode version that includes **Apple Clang (Expected Version)** - Search for an Xcode version that includes the expected Apple Clang version
- Download the `.xip` file - Download the `.xip` file
2. **Install and Configure Xcode** 2. **Install and configure Xcode**
```bash ```bash
# Extract the .xip file and rename for version management # Extract the .xip file and rename for version management
@@ -79,62 +83,28 @@ This will likely cause issues building xrpld. You may want to install a specific
export DEVELOPER_DIR=/Applications/Xcode_16.2.app/Contents/Developer export DEVELOPER_DIR=/Applications/Xcode_16.2.app/Contents/Developer
``` ```
The command line developer tools should include Git too: ## Windows
``` Nix is not available on Windows, so the required tools have to be installed
git --version manually:
```
Install [Homebrew][], - [Visual Studio 2022](https://visualstudio.microsoft.com/) with the
use it to install [pyenv][], **"Desktop development with C++"** workload — this provides MSVC and the
use it to install Python, "x64 Native Tools Command Prompt".
and use it to install Conan: - [Git for Windows](https://git-scm.com/download/win)
- [Python 3.11](https://www.python.org/downloads/), or higher
- [Conan 2.17](https://conan.io/downloads.html), or higher
- [CMake 3.22](https://cmake.org/download/), or higher
[Homebrew]: https://brew.sh/ > [!NOTE]
[pyenv]: https://github.com/pyenv/pyenv > Windows is used for development only and is not recommended for production.
```
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew update
brew install xz
brew install pyenv
pyenv install 3.11
pyenv global 3.11
eval "$(pyenv init -)"
pip install 'conan'
```
Install CMake with Homebrew too:
```
brew install cmake
```
## Clang-tidy ## Clang-tidy
Clang-tidy is required to run static analysis checks locally (see [CONTRIBUTING.md](../../CONTRIBUTING.md)). `clang-tidy` is required to run static analysis checks locally (see
It is not required to build the project. Currently this project uses clang-tidy version 21. [CONTRIBUTING.md](../../CONTRIBUTING.md)). It is not required to build the
project. This project currently uses `clang-tidy` version 22.
### Linux On Linux and macOS, the [Nix development shell](./nix.md) provides `clang-tidy`
22 out of the box — run it via `run-clang-tidy`. No separate installation is
LLVM 21 is not available in the default Debian 12 (Bookworm) repositories. needed.
Install it using the official LLVM apt installer:
```
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 21
sudo apt install --yes clang-tidy-21
```
Then use `run-clang-tidy-21` when running clang-tidy locally.
### macOS
Install LLVM 21 via Homebrew:
```
brew install llvm@21
```
Then use `run-clang-tidy` from the LLVM 21 Homebrew prefix when running clang-tidy locally.

45
docs/build/nix.md vendored
View File

@@ -2,9 +2,12 @@
This guide explains how to use Nix to set up a reproducible development environment for xrpld. Using Nix eliminates the need to manually install utilities and ensures consistent tooling across different machines. This guide explains how to use Nix to set up a reproducible development environment for xrpld. Using Nix eliminates the need to manually install utilities and ensures consistent tooling across different machines.
**The Nix development shell is the recommended way to develop xrpld.** It unifies the development environment for everyone and synchronizes updates: the same tooling and compiler versions are used both here and in CI. Any custom environment (Homebrew packages or anything else) will continue to work, but then it is up to you to keep it in sync with the environment used in CI.
## Benefits of Using Nix ## Benefits of Using Nix
- **Reproducible environment**: Everyone gets the same versions of tools and compilers - **Reproducible environment**: Everyone gets the same versions of tools and compilers
- **Matches CI**: The Linux CI runs in Docker images built from this exact Nix environment
- **No system pollution**: Dependencies are isolated and don't affect your system packages - **No system pollution**: Dependencies are isolated and don't affect your system packages
- **Multiple compiler versions**: Easily switch between different GCC and Clang versions - **Multiple compiler versions**: Easily switch between different GCC and Clang versions
- **Quick setup**: Get started with a single command - **Quick setup**: Get started with a single command
@@ -28,11 +31,22 @@ This will:
- Download and set up all required development tools (CMake, Ninja, Conan, etc.) - Download and set up all required development tools (CMake, Ninja, Conan, etc.)
- Configure the appropriate compiler for your platform: - Configure the appropriate compiler for your platform:
- **macOS**: Apple Clang (default system compiler) - **Linux**: GCC 15.2 (provided by Nix)
- **Linux**: GCC 15 - **macOS**: Apple Clang (your system compiler)
The first time you run this command, it will take a few minutes to download and build the environment. Subsequent runs will be much faster. The first time you run this command, it will take a few minutes to download and build the environment. Subsequent runs will be much faster.
### Platform notes
- **Linux**: `nix develop` gives you a shell with all the tooling necessary to
develop xrpld and with GCC 15.2 (also provided by Nix). There are no caveats.
- **macOS**: `nix develop` gives you a full environment too. The compiler is
your system-wide Apple Clang, while every other tool — including Conan — is
provided by Nix. Conan has no binary in the Nix cache for macOS, so it is
built from source the first time you enter the shell, which makes the initial
setup slower (this is handled automatically; see
[`nix/devshell.nix`](../../nix/devshell.nix)).
> [!TIP] > [!TIP]
> To avoid typing `--experimental-features 'nix-command flakes'` every time, you can permanently enable flakes by creating `~/.config/nix/nix.conf`: > To avoid typing `--experimental-features 'nix-command flakes'` every time, you can permanently enable flakes by creating `~/.config/nix/nix.conf`:
> >
@@ -51,7 +65,7 @@ The first time you run this command, it will take a few minutes to download and
A compiler can be chosen by providing its name with the `.#` prefix, e.g. `nix develop .#gcc15`. A compiler can be chosen by providing its name with the `.#` prefix, e.g. `nix develop .#gcc15`.
Use `nix flake show` to see all the available development shells. Use `nix flake show` to see all the available development shells.
Use `nix develop .#no_compiler` to use the compiler from your system. Use `nix develop .#no-compiler` to use the compiler from your system.
### Example Usage ### Example Usage
@@ -68,12 +82,28 @@ nix develop
### Using a different shell ### Using a different shell
`nix develop` opens bash by default. If you want to use another shell this could be done by adding `-c` flag. For example: `nix develop` opens bash by default. To use another shell, pass it with the `-c` flag — this works with any shell, e.g. `zsh` or `fish`:
```bash ```bash
# Use zsh
nix develop -c zsh nix develop -c zsh
# Use fish
nix develop -c fish
# Use your login shell
nix develop -c "$SHELL"
``` ```
> [!WARNING]
> Your shell's interactive startup files (e.g. `config.fish`, `.zshrc`) may prepend other directories — most commonly Homebrew — to `$PATH`, which can shadow the tools provided by the Nix shell. After entering, verify that tools resolve into the Nix store:
>
> ```bash
> command -v cmake # should print a /nix/store/... path
> ```
>
> If it doesn't, either adjust your shell configuration so it doesn't override `$PATH`, or use [direnv](#automatic-activation-with-direnv) (below), which loads the environment _after_ your shell config and so takes precedence regardless of the shell you use.
## Building xrpld with Nix ## Building xrpld with Nix
Once inside the Nix development shell, follow the standard [build instructions](../../BUILD.md#steps). The Nix shell provides all necessary tools (CMake, Ninja, Conan, etc.). Once inside the Nix development shell, follow the standard [build instructions](../../BUILD.md#steps). The Nix shell provides all necessary tools (CMake, Ninja, Conan, etc.).
@@ -82,6 +112,8 @@ Once inside the Nix development shell, follow the standard [build instructions](
[direnv](https://direnv.net/) or [nix-direnv](https://github.com/nix-community/nix-direnv) can automatically activate the Nix development shell when you enter the repository directory. [direnv](https://direnv.net/) or [nix-direnv](https://github.com/nix-community/nix-direnv) can automatically activate the Nix development shell when you enter the repository directory.
This is also the most robust way to use the environment from **any shell** (bash, zsh, fish, …): direnv stays in your current shell and loads the environment _after_ your shell's startup files have run, so the Nix-provided tools take precedence over anything your shell configuration adds to `$PATH`. To use it, install direnv for your shell, then add an `.envrc` containing `use flake` at the repository root and run `direnv allow`.
## Conan and Prebuilt Packages ## Conan and Prebuilt Packages
Please note that there is no guarantee that binaries from conan cache will work when using nix. If you encounter any errors, please use `--build '*'` to force conan to compile everything from source: Please note that there is no guarantee that binaries from conan cache will work when using nix. If you encounter any errors, please use `--build '*'` to force conan to compile everything from source:
@@ -93,3 +125,8 @@ conan install .. --output-folder . --build '*' --settings build_type=Release
## Updating `flake.lock` file ## Updating `flake.lock` file
To update `flake.lock` to the latest revision use `nix flake update` command. To update `flake.lock` to the latest revision use `nix flake update` command.
## Troubleshooting
See [Troubleshooting Nix problems](./nix_troubleshooting.md) for common issues,
such as `nix develop` failing inside Git worktrees.

61
docs/build/nix_troubleshooting.md vendored Normal file
View File

@@ -0,0 +1,61 @@
# Troubleshooting Nix problems
Common issues encountered when using the [Nix development shell](./nix.md), and
how to resolve them.
## Git worktrees
If `nix develop` fails with an error like:
```
error:
… while fetching the input 'git+file:///path/to/rippled'
error: opening Git repository "/path/to/rippled": unsupported extension name extensions.relativeworktrees (libgit2 error code = 6)
```
then your Nix is linked against a libgit2 older than **1.9.4**. Git 2.48+ writes
the `extensions.relativeWorktrees` config entry when a worktree is created with
relative paths (`git worktree add --relative-paths`, or with
`worktree.useRelativePaths=true`), and older libgit2 versions refuse to open a
repository that uses it. Nix uses libgit2 to read the flake, so evaluation
fails.
> [!IMPORTANT]
> This entry is written to the **shared** repository config, so once any
> relative worktree exists, `nix develop` fails in the main checkout too — not
> just inside the worktree.
### Workarounds
These work today, with any Nix version:
- bypass libgit2 with a `path:` flakeref: `nix develop "path:$PWD"`
(note: this copies the working tree to the store and ignores `.gitignore`); or
- create worktrees with absolute paths (omit `--relative-paths`); or
- clear the extension if you don't need relative worktrees:
`git config --unset extensions.relativeWorktrees`.
### Permanent fix
The fix is in [libgit2 1.9.4](https://github.com/libgit2/libgit2/releases/tag/v1.9.4),
so the real solution is a Nix that links against libgit2 `1.9.4` or newer. Check
which version yours links against:
```bash
nix-store -qR "$(readlink -f "$(command -v nix)")" | grep libgit2
```
> [!WARNING]
> `nix upgrade-nix` does **not** help yet. It installs the build from the
> official [`nix-fallback-paths`](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/installer/tools/nix-fallback-paths.nix),
> which is still linked against libgit2 `1.9.2` — there is no new upstream Nix
> release with the fix. (On some systems that build is even the exact store path
> you already have, making the upgrade a no-op.)
nixpkgs has already rebuilt Nix against the fixed libgit2 (e.g. `nix-2.34.7+1`),
so the cleanest path is to reinstall Nix using your usual installation method
once it picks up that rebuild, then re-run the `grep libgit2` check above to
confirm it reports `1.9.4` or newer.
Until then, prefer the workarounds above.

17
flake.lock generated
View File

@@ -2,20 +2,21 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1777954456, "lastModified": 1781173989,
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=", "narHash": "sha256-fnzKKPvS+oieI/pTzotA5tkoM47EB1NpaBcgk4R97hE=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1", "rev": "8c91a71d13451abc40eb9dae8910f972f979852f",
"type": "github" "type": "github"
}, },
"original": { "original": {
"id": "nixpkgs", "owner": "NixOS",
"ref": "nixos-unstable", "ref": "nixpkgs-unstable",
"type": "indirect" "repo": "nixpkgs",
"type": "github"
} }
}, },
"nixpkgs-glibc231": { "nixpkgs-custom-glibc": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1593520194, "lastModified": 1593520194,
@@ -35,7 +36,7 @@
"root": { "root": {
"inputs": { "inputs": {
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"nixpkgs-glibc231": "nixpkgs-glibc231" "nixpkgs-custom-glibc": "nixpkgs-custom-glibc"
} }
} }
}, },

View File

@@ -1,21 +1,21 @@
{ {
description = "Nix related things for xrpld"; description = "Nix related things for xrpld";
inputs = { inputs = {
nixpkgs.url = "nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# nixpkgs snapshot (2020-06-30) that shipped glibc 2.31 as the primary # nixpkgs snapshot (2020-06-30) that shipped glibc 2.31 as the primary
# version — matches the system libc on Ubuntu 20.04 LTS. Imported # version — matches the system libc on Ubuntu 20.04 LTS. Imported
# manually (flake = false) because this revision predates nixpkgs' # manually (flake = false) because this revision predates nixpkgs'
# own flake.nix. # own flake.nix.
nixpkgs-glibc231 = { nixpkgs-custom-glibc = {
url = "github:NixOS/nixpkgs/9cd98386a38891d1074fc18036b842dc4416f562"; url = "github:NixOS/nixpkgs/9cd98386a38891d1074fc18036b842dc4416f562";
flake = false; flake = false;
}; };
}; };
outputs = outputs =
{ nixpkgs, nixpkgs-glibc231, ... }: { nixpkgs, nixpkgs-custom-glibc, ... }:
let let
forEachSystem = import ./nix/utils.nix { inherit nixpkgs nixpkgs-glibc231; }; forEachSystem = import ./nix/utils.nix { inherit nixpkgs nixpkgs-custom-glibc; };
in in
{ {
devShells = forEachSystem (import ./nix/devshell.nix); devShells = forEachSystem (import ./nix/devshell.nix);

View File

@@ -12,8 +12,8 @@ template <int Window, typename Clock>
class DecayingSample class DecayingSample
{ {
public: public:
using value_type = typename Clock::duration::rep; using value_type = Clock::duration::rep;
using time_point = typename Clock::time_point; using time_point = Clock::time_point;
DecayingSample() = delete; DecayingSample() = delete;
@@ -93,7 +93,7 @@ template <int HalfLife, class Clock>
class DecayWindow class DecayWindow
{ {
public: public:
using time_point = typename Clock::time_point; using time_point = Clock::time_point;
explicit DecayWindow(time_point now) : when_(now) explicit DecayWindow(time_point now) : when_(now)
{ {

View File

@@ -1,248 +0,0 @@
#pragma once
#include <xrpl/basics/contract.h>
#include <boost/outcome.hpp>
#include <stdexcept>
namespace xrpl {
/** Expected is an approximation of std::expected (hoped for in C++23)
See: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html
The implementation is entirely based on boost::outcome_v2::result.
*/
// Exception thrown by an invalid access to Expected.
struct BadExpectedAccess : public std::runtime_error
{
BadExpectedAccess() : runtime_error("bad expected access")
{
}
};
namespace detail {
// Custom policy for Expected. Always throw on an invalid access.
struct ThrowPolicy : public boost::outcome_v2::policy::base
{
template <class Impl>
static constexpr void
// NOLINTNEXTLINE(readability-identifier-naming)
wide_value_check(Impl&& self)
{
if (!base::_has_value(std::forward<Impl>(self)))
Throw<BadExpectedAccess>();
}
template <class Impl>
static constexpr void
// NOLINTNEXTLINE(readability-identifier-naming)
wide_error_check(Impl&& self)
{
if (!base::_has_error(std::forward<Impl>(self)))
Throw<BadExpectedAccess>();
}
template <class Impl>
static constexpr void
// NOLINTNEXTLINE(readability-identifier-naming)
wide_exception_check(Impl&& self)
{
if (!base::_has_exception(std::forward<Impl>(self)))
Throw<BadExpectedAccess>();
}
};
} // namespace detail
// Definition of Unexpected, which is used to construct the unexpected
// return type of an Expected.
template <class E>
class Unexpected
{
public:
static_assert(!std::is_same_v<E, void>, "E must not be void");
Unexpected() = delete;
constexpr explicit Unexpected(E const& e) : val_(e)
{
}
constexpr explicit Unexpected(E&& e) : val_(std::move(e))
{
}
[[nodiscard]] constexpr E const&
value() const&
{
return val_;
}
constexpr E&
value() &
{
return val_;
}
constexpr E&&
value() &&
{
return std::move(val_);
}
[[nodiscard]] constexpr E const&&
value() const&&
{
return std::move(val_);
}
private:
E val_;
};
// Unexpected deduction guide that converts array to const*.
template <typename E, std::size_t N>
Unexpected(E (&)[N]) -> Unexpected<E const*>;
// Definition of Expected. All of the machinery comes from boost::result.
template <class T, class E>
class [[nodiscard]] Expected : private boost::outcome_v2::result<T, E, detail::ThrowPolicy>
{
using Base = boost::outcome_v2::result<T, E, detail::ThrowPolicy>;
public:
template <typename U>
requires std::convertible_to<U, T>
constexpr Expected(U&& r) : Base(boost::outcome_v2::in_place_type_t<T>{}, std::forward<U>(r))
{
}
template <typename U>
requires std::convertible_to<U, E> && (!std::is_reference_v<U>)
constexpr Expected(Unexpected<U> e)
: Base(boost::outcome_v2::in_place_type_t<E>{}, std::move(e.value()))
{
}
[[nodiscard]] constexpr bool
// NOLINTNEXTLINE(readability-identifier-naming)
has_value() const
{
return Base::has_value();
}
[[nodiscard]] constexpr T const&
value() const
{
return Base::value();
}
constexpr T&
value()
{
return Base::value();
}
[[nodiscard]] constexpr E const&
error() const&
{
return Base::error();
}
[[nodiscard]] constexpr E&
error() &
{
return Base::error();
}
[[nodiscard]] constexpr E&&
error() &&
{
return std::move(Base::error());
}
constexpr explicit
operator bool() const
{
return has_value();
}
// Add operator* and operator-> so the Expected API looks a bit more like
// what std::expected is likely to look like. See:
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html
[[nodiscard]] constexpr T&
operator*()
{
return this->value();
}
[[nodiscard]] constexpr T const&
operator*() const
{
return this->value();
}
[[nodiscard]] constexpr T*
operator->()
{
return &this->value();
}
[[nodiscard]] constexpr T const*
operator->() const
{
return &this->value();
}
};
// Specialization of Expected<void, E>. Allows returning either success
// (without a value) or the reason for the failure.
template <class E>
class [[nodiscard]]
Expected<void, E> : private boost::outcome_v2::result<void, E, detail::ThrowPolicy>
{
using Base = boost::outcome_v2::result<void, E, detail::ThrowPolicy>;
public:
// The default constructor makes a successful Expected<void, E>.
// This aligns with std::expected behavior proposed in P0323R10.
constexpr Expected() : Base(boost::outcome_v2::success())
{
}
template <typename U>
requires std::convertible_to<U, E> && (!std::is_reference_v<U>)
constexpr Expected(Unexpected<U> e) : Base(E(std::move(e.value())))
{
}
[[nodiscard]] constexpr E const&
error() const&
{
return Base::error();
}
[[nodiscard]] constexpr E&
error() &
{
return Base::error();
}
[[nodiscard]] constexpr E&&
error() &&
{
return std::move(Base::error());
}
constexpr explicit
operator bool() const
{
return Base::has_value();
}
};
} // namespace xrpl

View File

@@ -206,8 +206,7 @@ private:
#ifndef JLOG #ifndef JLOG
#define JLOG(x) \ #define JLOG(x) \
if (!(x)) \ if (!(x)) \
{ \ ; \
} \
else \ else \
x x
#endif #endif

View File

@@ -2,12 +2,16 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <array>
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <limits> #include <limits>
#include <optional> #include <optional>
#include <ostream> #include <ostream>
#include <set>
#include <stdexcept>
#include <string> #include <string>
#include <unordered_map>
namespace xrpl { namespace xrpl {
@@ -38,17 +42,58 @@ isPowerOfTen(T value)
return logTen(value).has_value(); return logTen(value).has_value();
} }
namespace detail {
/** Builds a table of the powers of 10
*
* This function is marked consteval, so it can only be run in
* a constexpr context. This assures that it is and can only be run at
* compile time. Doing it at runtime would be pretty wasteful and
* inefficient.
*/
constexpr std::size_t kInt64Digits = 20;
consteval std::array<std::uint64_t, kInt64Digits>
buildPowersOfTen()
{
std::array<std::uint64_t, kInt64Digits> result{};
std::uint64_t power = 1;
std::size_t exponent = 0;
// end the loop early so it doesn't overflow;
for (; exponent < result.size() - 1; ++exponent, power *= 10)
{
result[exponent] = power;
if (power > std::numeric_limits<std::uint64_t>::max() / 10)
throw std::logic_error("Power of 10 table is too big");
}
result[exponent] = power;
if (power < std::numeric_limits<std::uint64_t>::max() / 10)
throw std::logic_error("Power of 10 table is not big enough for the uint64_t type");
return result;
}
} // namespace detail
constexpr std::array<std::uint64_t, detail::kInt64Digits> kPowerOfTen = detail::buildPowersOfTen();
static_assert(kPowerOfTen[0] == 1);
static_assert(kPowerOfTen[1] == 10);
static_assert(kPowerOfTen[10] == 10'000'000'000);
static_assert(
isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kInt64Digits - 1);
/** MantissaRange defines a range for the mantissa of a normalized Number. /** MantissaRange defines a range for the mantissa of a normalized Number.
* *
* The mantissa is in the range [min, max], where * The mantissa is in the range [min, max], where
* * min is a power of 10, and * * min is a power of 10, and
* * max = min * 10 - 1. * * max = min * 10 - 1.
* *
* The mantissa_scale enum indicates whether the range is "small" or "large". * The MantissaScale enum indicates properties of the range: size, and some behavioral
* This intentionally restricts the number of MantissaRanges that can be * options. This intentionally restricts the number of unique MantissaRanges that can
* instantiated to two: one for each scale. * be instantiated: one for each scale.
* *
* The "small" scale is based on the behavior of STAmount for IOUs. It has a min * The "Small" scale is based on the behavior of STAmount for IOUs. It has a min
* value of 10^15, and a max value of 10^16-1. This was sufficient for * value of 10^15, and a max value of 10^16-1. This was sufficient for
* uses before Lending Protocol was implemented, mostly related to AMM. * uses before Lending Protocol was implemented, mostly related to AMM.
* *
@@ -59,46 +104,100 @@ isPowerOfTen(T value)
* STNumber field type, and for internal calculations. That necessitated the * STNumber field type, and for internal calculations. That necessitated the
* "large" scale. * "large" scale.
* *
* The "large" scale is intended to represent all values that can be represented * The "Large" scales are intended to represent all values that can be represented
* by an STAmount - IOUs, XRP, and MPTs. It has a min value of 10^18, and a max * by an STAmount - IOUs, XRP, and MPTs. It has a min value of 10^18, and a max
* value of 10^19-1. * value of 10^19-1. "LargeLegacy" is like "Large", but preserves
* a rounding error when a computation results in a mantissa of
* Number::kMaxRep that needs to be rounded up, but rounds down
* instead. It will maintain consistent behavior until the fixCleanup3_2_0
* amendment is enabled.
* *
* Note that if the mentioned amendments are eventually retired, this class * Note that if the mentioned amendments are eventually retired, this class
* should be left in place, but the "small" scale option should be removed. This * should be left in place, but the "Small" scale option should be removed. This
* will allow for future expansion beyond 64-bits if it is ever needed. * will allow for future expansion beyond 64-bits if it is ever needed.
*/ */
struct MantissaRange struct MantissaRange final
{ {
using rep = std::uint64_t; using rep = std::uint64_t;
enum class MantissaScale { Small, Large };
explicit constexpr MantissaRange(MantissaScale scale) enum class MantissaScale {
: min(getMin(scale)), log(logTen(min).value_or(-1)), scale(scale) Small,
// LargeLegacy can be removed when fixCleanup3_2_0 is retired
LargeLegacy,
Large,
};
// This entire enum can be removed when fixCleanup3_2_0 is retired
enum class CuspRoundingFix : bool {
Disabled = false,
Enabled = true,
};
explicit constexpr MantissaRange(MantissaScale sc) : scale(sc)
{ {
} }
rep min; MantissaScale const scale;
rep max{(min * 10) - 1}; int const log{getExponent(scale)};
int log; rep const min{getMin(scale, log)};
MantissaScale scale; rep const max{(min * 10) - 1};
CuspRoundingFix const cuspRoundingFixEnabled{isCuspFixEnabled(scale)};
static MantissaRange const&
getMantissaRange(MantissaScale scale);
static std::set<MantissaScale> const&
getAllScales();
private: private:
static constexpr rep static constexpr int
getMin(MantissaScale scale) getExponent(MantissaScale scale)
{ {
switch (scale) switch (scale)
{ {
case MantissaScale::Small: case MantissaScale::Small:
return 1'000'000'000'000'000ULL; return 15;
case MantissaScale::LargeLegacy:
case MantissaScale::Large: case MantissaScale::Large:
return 1'000'000'000'000'000'000ULL; return 18;
// LCOV_EXCL_START
default: default:
// Since this can never be called outside a non-constexpr // If called in a constexpr context, this throw assures that the build fails if an
// context, this throw assures that the build fails if an
// invalid scale is used. // invalid scale is used.
throw std::runtime_error("Unknown mantissa scale"); throw std::runtime_error("Unknown mantissa scale");
// LCOV_EXCL_STOP
} }
} }
// Keep this function for future use with different ways to compute
// the ranges.
static constexpr rep
getMin(MantissaScale scale, int exponent)
{
if (exponent < 0 || exponent >= kPowerOfTen.size())
throw std::runtime_error("Invalid exponent"); // LCOV_EXCL_LINE
return kPowerOfTen[exponent];
}
static constexpr CuspRoundingFix
isCuspFixEnabled(MantissaScale scale)
{
switch (scale)
{
case MantissaScale::Small:
case MantissaScale::LargeLegacy:
return CuspRoundingFix::Disabled;
case MantissaScale::Large:
return CuspRoundingFix::Enabled;
default:
// If called in a constexpr context, this throw assures that the build fails if an
// invalid scale is used.
throw std::runtime_error("Unknown mantissa scale"); // LCOV_EXCL_LINE
}
}
static std::unordered_map<MantissaScale, MantissaRange> const&
getRanges();
}; };
// Like std::integral, but only 64-bit integral types. // Like std::integral, but only 64-bit integral types.
@@ -203,7 +302,7 @@ concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::u
* amendments are enabled to determine which result to expect. * amendments are enabled to determine which result to expect.
* *
*/ */
class Number class Number final
{ {
using rep = std::int64_t; using rep = std::int64_t;
using internalrep = MantissaRange::rep; using internalrep = MantissaRange::rep;
@@ -309,33 +408,40 @@ public:
} }
friend constexpr bool friend constexpr bool
operator<(Number const& x, Number const& y) noexcept operator<(Number const& l, Number const& r) noexcept
{ {
bool const lneg = l.negative_;
bool const rneg = r.negative_;
// If the two amounts have different signs (zero is treated as positive) // If the two amounts have different signs (zero is treated as positive)
// then the comparison is true iff the left is negative. // then the comparison is true iff the left is negative.
bool const lneg = x.negative_;
bool const rneg = y.negative_;
if (lneg != rneg) if (lneg != rneg)
return lneg; return lneg;
// Both have same sign and the left is zero: the right must be // Both have same sign and the left is zero: both must be non-negative.
// greater than 0. // If the right is greater than 0, then it is larger, so the comparison is true.
if (x.mantissa_ == 0) if (l.mantissa_ == 0)
return y.mantissa_ > 0; return r.mantissa_ > 0;
// Both have same sign, the right is zero and the left is non-zero. // Both have same sign, the right is zero and the left is non-zero, so the left must be
if (y.mantissa_ == 0) // positive, and thus is larger, so the comparison is false.
if (r.mantissa_ == 0)
return false; return false;
// Both have the same sign, compare by exponents: // Both have the same sign, compare by exponents:
if (x.exponent_ > y.exponent_) if (l.exponent_ > r.exponent_)
return lneg; return lneg;
if (x.exponent_ < y.exponent_) if (l.exponent_ < r.exponent_)
return !lneg; return !lneg;
// If equal exponents, compare mantissas // If equal signs and exponents, compare mantissas.
return x.mantissa_ < y.mantissa_; if (lneg)
{
// If negative, the operator is reversed.
return l.mantissa_ > r.mantissa_;
}
return l.mantissa_ < r.mantissa_;
} }
/** Return the sign of the amount */ /** Return the sign of the amount */
@@ -424,49 +530,28 @@ public:
return kRange.get().log; return kRange.get().log;
} }
/// oneSmall is needed because the ranges are private
static constexpr Number
oneSmall();
/// oneLarge is needed because the ranges are private
static constexpr Number
oneLarge();
// And one is needed because it needs to choose between oneSmall and
// oneLarge based on the current range
static Number static Number
one(); one();
template <Integral64 T> template <
auto MinMantissa,
auto MaxMantissa,
Integral64 T = std::decay_t<decltype(MinMantissa)>>
[[nodiscard]] [[nodiscard]]
std::pair<T, int> std::pair<T, int>
normalizeToRange(T minMantissa, T maxMantissa) const; normalizeToRange() const;
private: private:
static thread_local RoundingMode mode; static thread_local RoundingMode mode;
// The available ranges for mantissa // The available ranges for mantissa
static constexpr MantissaRange kSmallRange{MantissaRange::MantissaScale::Small};
static_assert(isPowerOfTen(kSmallRange.min));
static_assert(kSmallRange.min == 1'000'000'000'000'000LL);
static_assert(kSmallRange.max == 9'999'999'999'999'999LL);
static_assert(kSmallRange.log == 15);
static_assert(kSmallRange.min < kMaxRep);
static_assert(kSmallRange.max < kMaxRep);
static constexpr MantissaRange kLargeRange{MantissaRange::MantissaScale::Large};
static_assert(isPowerOfTen(kLargeRange.min));
static_assert(kLargeRange.min == 1'000'000'000'000'000'000ULL);
static_assert(kLargeRange.max == internalrep(9'999'999'999'999'999'999ULL));
static_assert(kLargeRange.log == 18);
static_assert(kLargeRange.min < kMaxRep);
static_assert(kLargeRange.max > kMaxRep);
// The range for the mantissa when normalized. // The range for the mantissa when normalized.
// Use reference_wrapper to avoid making copies, and prevent accidentally // Use reference_wrapper to avoid making copies, and prevent accidentally
// changing the values inside the range. // changing the values inside the range.
static thread_local std::reference_wrapper<MantissaRange const> kRange; static thread_local std::reference_wrapper<MantissaRange const> kRange;
void void
normalize(); normalize(MantissaRange const& range);
/** Normalize Number components to an arbitrary range. /** Normalize Number components to an arbitrary range.
* *
@@ -481,7 +566,8 @@ private:
T& mantissa, T& mantissa,
int& exponent, int& exponent,
internalrep const& minMantissa, internalrep const& minMantissa,
internalrep const& maxMantissa); internalrep const& maxMantissa,
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled);
template <class T> template <class T>
friend void friend void
@@ -490,7 +576,9 @@ private:
T& mantissa, T& mantissa,
int& exponent, int& exponent,
MantissaRange::rep const& minMantissa, MantissaRange::rep const& minMantissa,
MantissaRange::rep const& maxMantissa); MantissaRange::rep const& maxMantissa,
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled,
bool dropped);
[[nodiscard]] bool [[nodiscard]] bool
isnormal() const noexcept; isnormal() const noexcept;
@@ -526,7 +614,7 @@ static constexpr Number kNumZero{};
inline Number::Number(bool negative, internalrep mantissa, int exponent, Normalized) inline Number::Number(bool negative, internalrep mantissa, int exponent, Normalized)
: Number(negative, mantissa, exponent, Unchecked{}) : Number(negative, mantissa, exponent, Unchecked{})
{ {
normalize(); normalize(kRange);
} }
inline Number::Number(internalrep mantissa, int exponent, Normalized) inline Number::Number(internalrep mantissa, int exponent, Normalized)
@@ -696,10 +784,21 @@ Number::isnormal() const noexcept
kMinExponent <= exponent_ && exponent_ <= kMaxExponent); kMinExponent <= exponent_ && exponent_ <= kMaxExponent);
} }
template <Integral64 T> template <auto MinMantissa, auto MaxMantissa, Integral64 T>
std::pair<T, int> std::pair<T, int>
Number::normalizeToRange(T minMantissa, T maxMantissa) const Number::normalizeToRange() const
{ {
static_assert(std::is_same_v<T, std::uint64_t> || std::is_same_v<T, std::int64_t>);
static_assert(std::is_same_v<T, std::decay_t<decltype(MinMantissa)>>);
static_assert(std::is_same_v<T, std::decay_t<decltype(MaxMantissa)>>);
auto constexpr kMIN = static_cast<T>(MinMantissa);
auto constexpr kMAX = static_cast<T>(MaxMantissa);
static_assert(kMIN > 0);
static_assert(kMIN % 10 == 0);
static_assert(isPowerOfTen(kMIN));
static_assert(kMAX % 10 == 9);
static_assert((kMAX + 1) / 10 == kMIN);
bool negative = negative_; bool negative = negative_;
internalrep mantissa = mantissa_; internalrep mantissa = mantissa_;
int exponent = exponent_; int exponent = exponent_;
@@ -711,7 +810,10 @@ Number::normalizeToRange(T minMantissa, T maxMantissa) const
"xrpl::Number::normalizeToRange", "xrpl::Number::normalizeToRange",
"Number is non-negative for unsigned range."); "Number is non-negative for unsigned range.");
} }
Number::normalize(negative, mantissa, exponent, minMantissa, maxMantissa); // Don't need to worry about the cuspRounding fix because rounding up will never take the
// mantissa over maxMantissa with a ones digit value other than 0. 0 can safely be truncated.
Number::normalize(
negative, mantissa, exponent, kMIN, kMAX, MantissaRange::CuspRoundingFix::Disabled);
auto const sign = negative ? -1 : 1; auto const sign = negative ? -1 : 1;
return std::make_pair(static_cast<T>(sign * mantissa), exponent); return std::make_pair(static_cast<T>(sign * mantissa), exponent);
@@ -763,6 +865,8 @@ to_string(MantissaRange::MantissaScale const& scale)
{ {
case MantissaRange::MantissaScale::Small: case MantissaRange::MantissaScale::Small:
return "small"; return "small";
case MantissaRange::MantissaScale::LargeLegacy:
return "largeLegacy";
case MantissaRange::MantissaScale::Large: case MantissaRange::MantissaScale::Large:
return "large"; return "large";
default: default:

View File

@@ -11,6 +11,7 @@
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
#include <string> #include <string>
#include <string_view>
#include <type_traits> #include <type_traits>
namespace xrpl { namespace xrpl {
@@ -95,13 +96,7 @@ strUnHex(std::size_t strSize, Iterator begin, Iterator end)
} }
inline std::optional<Blob> inline std::optional<Blob>
strUnHex(std::string const& strSrc) strUnHex(std::string_view strSrc)
{
return strUnHex(strSrc.size(), strSrc.cbegin(), strSrc.cend());
}
inline std::optional<Blob>
strViewUnHex(std::string_view strSrc)
{ {
return strUnHex(strSrc.size(), strSrc.cbegin(), strSrc.cend()); return strUnHex(strSrc.size(), strSrc.cbegin(), strSrc.cend());
} }

View File

@@ -9,6 +9,7 @@
#include <xrpl/beast/insight/Insight.h> #include <xrpl/beast/insight/Insight.h>
#include <atomic> #include <atomic>
#include <cstddef>
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <thread> #include <thread>
@@ -17,6 +18,22 @@
namespace xrpl { namespace xrpl {
namespace detail {
// Replace-policy tags selecting how TaggedCache::canonicalizeImpl resolves a
// collision when the key already exists (defined in TaggedCache.ipp):
// - ReplaceCached: always replace the cached value with `data`. `data` is
// never written back and may be const.
// - ReplaceClient: keep the cached value and write it back into `data` (the
// client's pointer), which must therefore be writable.
// - ReplaceDynamically: call the supplied callback to decide per call; `data`
// is written back when the cached value is kept, so it must be writable.
struct ReplaceCached;
struct ReplaceClient;
struct ReplaceDynamically;
} // namespace detail
/** Map/cache combination. /** Map/cache combination.
This class implements a cache and a map. The cache keeps objects alive This class implements a cache and a map. The cache keeps objects alive
in the map. The map allows multiple code paths that reference objects in the map. The map allows multiple code paths that reference objects
@@ -96,6 +113,32 @@ public:
bool bool
del(key_type const& key, bool valid); del(key_type const& key, bool valid);
private:
// Selects the `data` parameter type of canonicalizeImpl from the replace
// policy: const for detail::ReplaceCached (never written back), otherwise
// writable.
template <typename Policy>
using CanonicalizeClientPointerType = std::conditional_t<
std::is_same_v<detail::ReplaceCached, Policy>,
SharedPointerType const&,
SharedPointerType&>;
/** Shared implementation of the canonicalize family.
`policy` selects how a collision is resolved when `key` already exists:
detail::ReplaceCached, detail::ReplaceClient or
detail::ReplaceDynamically. For ReplaceDynamically `replaceCallback` is
invoked with the existing strong pointer and returns whether to replace
the cached value with `data`; for the tag policies it is unused.
*/
template <class Policy, class Callback = std::nullptr_t>
bool
canonicalizeImpl(
key_type const& key,
CanonicalizeClientPointerType<Policy> data,
Policy policy,
Callback&& replaceCallback = nullptr);
public: public:
/** Replace aliased objects with originals. /** Replace aliased objects with originals.
@@ -104,19 +147,52 @@ public:
This routine eliminates the duplicate and performs a replacement This routine eliminates the duplicate and performs a replacement
on the callers shared pointer if needed. on the callers shared pointer if needed.
`replaceCallback` is a callable taking the existing strong pointer and
returning whether to replace the cached value with `data` (true) or to
keep the cached value and write it back into `data` (false). Because the
write-back case mutates `data`, `data` must be writable.
@param key The key corresponding to the object @param key The key corresponding to the object
@param data A shared pointer to the data corresponding to the object. @param data A shared pointer to the data corresponding to the object.
@param replace Function that decides if cache should be replaced @param replaceCallback A callable (existing strong pointer -> bool).
@return `true` If the key already existed. @return `true` if an existing live entry was found and used; `false` if a new entry was
*/ inserted or an expired tracked entry was re-cached.
template <class R> **/
template <class Callback>
bool bool
canonicalize(key_type const& key, SharedPointerType& data, R&& replaceCallback); canonicalize(key_type const& key, SharedPointerType& data, Callback&& replaceCallback);
/** Insert/update the canonical entry for `key`, always replacing the
cached value with `data`.
If an entry already exists for `key`, the cached value is unconditionally
replaced with `data`; otherwise `data` is inserted. `data` is never
written back, so it may be const.
@param key The key corresponding to the object.
@param data A shared pointer to the data corresponding to the object.
@return `true` if an existing live entry was found and used; `false` if a new entry was
inserted or an expired tracked entry was re-cached.
**/
bool bool
canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data); canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data);
/** Insert the canonical entry for `key`, keeping any existing cached value.
If an entry already exists for `key`, the cached value is kept and
written back into `data` so the caller ends up with the canonical
object; otherwise `data` is inserted. Because `data` may be overwritten
it must be writable.
@param key The key corresponding to the object.
@param data A shared pointer to the data corresponding to the object;
updated to the canonical value when one already exists.
@return `true` if an existing live entry was found and used; `false` if a new entry was
inserted or an expired tracked entry was re-cached.
**/
bool bool
canonicalizeReplaceClient(key_type const& key, SharedPointerType& data); canonicalizeReplaceClient(key_type const& key, SharedPointerType& data);
@@ -157,7 +233,7 @@ public:
/** Fetch an item from the cache. /** Fetch an item from the cache.
If the digest was not found, Handler If the digest was not found, Handler
will be called with this signature: will be called with this signature:
std::shared_ptr<SLE const>(void) SLE::const_pointer(void)
*/ */
template <class Handler> template <class Handler>
SharedPointerType SharedPointerType
@@ -181,14 +257,14 @@ private:
beast::insight::Collector::ptr const& collector) beast::insight::Collector::ptr const& collector)
: hook(collector->makeHook(handler)) : hook(collector->makeHook(handler))
, size(collector->makeGauge(prefix, "size")) , size(collector->makeGauge(prefix, "size"))
, hit_rate(collector->makeGauge(prefix, "hit_rate")) , hitRate(collector->makeGauge(prefix, "hit_rate"))
{ {
} }
beast::insight::Hook hook; beast::insight::Hook hook;
beast::insight::Gauge size; beast::insight::Gauge size;
beast::insight::Gauge hit_rate; beast::insight::Gauge hitRate;
std::size_t hits{0}; std::size_t hits{0};
std::size_t misses{0}; std::size_t misses{0};
@@ -197,16 +273,16 @@ private:
class KeyOnlyEntry class KeyOnlyEntry
{ {
public: public:
clock_type::time_point last_access; clock_type::time_point lastAccess;
explicit KeyOnlyEntry(clock_type::time_point const& lastAccess) : last_access(lastAccess) explicit KeyOnlyEntry(clock_type::time_point const& lastAccess) : lastAccess(lastAccess)
{ {
} }
void void
touch(clock_type::time_point const& now) touch(clock_type::time_point const& now)
{ {
last_access = now; lastAccess = now;
} }
}; };
@@ -214,10 +290,10 @@ private:
{ {
public: public:
shared_weak_combo_pointer_type ptr; shared_weak_combo_pointer_type ptr;
clock_type::time_point last_access; clock_type::time_point lastAccess;
ValueEntry(clock_type::time_point const& lastAccess, shared_pointer_type const& ptr) ValueEntry(clock_type::time_point const& lastAccess, shared_pointer_type const& ptr)
: ptr(ptr), last_access(lastAccess) : ptr(ptr), lastAccess(lastAccess)
{ {
} }
@@ -246,7 +322,7 @@ private:
void void
touch(clock_type::time_point const& now) touch(clock_type::time_point const& now)
{ {
last_access = now; lastAccess = now;
} }
}; };
@@ -262,7 +338,7 @@ private:
sweepHelper( sweepHelper(
clock_type::time_point const& whenExpire, clock_type::time_point const& whenExpire,
[[maybe_unused]] clock_type::time_point const& now, [[maybe_unused]] clock_type::time_point const& now,
typename KeyValueCacheType::map_type& partition, KeyValueCacheType::map_type& partition,
SweptPointersVector& stuffToSweep, SweptPointersVector& stuffToSweep,
std::atomic<int>& allRemovals, std::atomic<int>& allRemovals,
std::scoped_lock<std::recursive_mutex> const&); std::scoped_lock<std::recursive_mutex> const&);
@@ -271,7 +347,7 @@ private:
sweepHelper( sweepHelper(
clock_type::time_point const& whenExpire, clock_type::time_point const& whenExpire,
clock_type::time_point const& now, clock_type::time_point const& now,
typename KeyOnlyCacheType::map_type& partition, KeyOnlyCacheType::map_type& partition,
SweptPointersVector&, SweptPointersVector&,
std::atomic<int>& allRemovals, std::atomic<int>& allRemovals,
std::scoped_lock<std::recursive_mutex> const&); std::scoped_lock<std::recursive_mutex> const&);
@@ -286,13 +362,13 @@ private:
std::string name_; std::string name_;
// Desired number of cache entries (0 = ignore) // Desired number of cache entries (0 = ignore)
int const target_size_; int const targetSize_;
// Desired maximum cache age // Desired maximum cache age
clock_type::duration const target_age_; clock_type::duration const targetAge_;
// Number of items cached // Number of items cached
int cache_count_{0}; int cacheCount_{0};
cache_type cache_; // Hold strong reference to recent objects cache_type cache_; // Hold strong reference to recent objects
std::uint64_t hits_{0}; std::uint64_t hits_{0};
std::uint64_t misses_{0}; std::uint64_t misses_{0};

View File

@@ -5,6 +5,30 @@
namespace xrpl { namespace xrpl {
namespace detail {
// Replace-policy tags selecting how TaggedCache::canonicalizeImpl resolves a
// collision when the key already exists:
// - ReplaceCached: always replace the cached value with `data`. `data` is
// never written back and may be const.
// - ReplaceClient: keep the cached value and write it back into `data` (the
// client's pointer), which must therefore be writable.
// - ReplaceDynamically: call the supplied callback to decide per call; `data`
// is written back when the cached value is kept, so it must be writable.
struct ReplaceCached
{
};
struct ReplaceClient
{
};
struct ReplaceDynamically
{
};
} // namespace detail
template < template <
class Key, class Key,
class T, class T,
@@ -34,8 +58,8 @@ inline TaggedCache<
, clock_(clock) , clock_(clock)
, stats_(name, std::bind(&TaggedCache::collectMetrics, this), collector) , stats_(name, std::bind(&TaggedCache::collectMetrics, this), collector)
, name_(name) , name_(name)
, target_size_(size) , targetSize_(size)
, target_age_(expiration) , targetAge_(expiration)
{ {
} }
@@ -86,7 +110,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
getCacheSize() const getCacheSize() const
{ {
std::scoped_lock const lock(mutex_); std::scoped_lock const lock(mutex_);
return cache_count_; return cacheCount_;
} }
template < template <
@@ -139,7 +163,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
{ {
std::scoped_lock const lock(mutex_); std::scoped_lock const lock(mutex_);
cache_.clear(); cache_.clear();
cache_count_ = 0; cacheCount_ = 0;
} }
template < template <
@@ -157,7 +181,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
{ {
std::scoped_lock const lock(mutex_); std::scoped_lock const lock(mutex_);
cache_.clear(); cache_.clear();
cache_count_ = 0; cacheCount_ = 0;
hits_ = 0; hits_ = 0;
misses_ = 0; misses_ = 0;
} }
@@ -213,21 +237,21 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
{ {
std::scoped_lock const lock(mutex_); std::scoped_lock const lock(mutex_);
if (target_size_ == 0 || (static_cast<int>(cache_.size()) <= target_size_)) if (targetSize_ == 0 || (static_cast<int>(cache_.size()) <= targetSize_))
{ {
whenExpire = now - target_age_; whenExpire = now - targetAge_;
} }
else else
{ {
whenExpire = now - (target_age_ * target_size_ / cache_.size()); whenExpire = now - (targetAge_ * targetSize_ / cache_.size());
clock_type::duration const minimumAge(std::chrono::seconds(1)); clock_type::duration const minimumAge(std::chrono::seconds(1));
if (whenExpire > (now - minimumAge)) if (whenExpire > (now - minimumAge))
whenExpire = now - minimumAge; whenExpire = now - minimumAge;
JLOG(journal_.trace()) JLOG(journal_.trace())
<< name_ << " is growing fast " << cache_.size() << " of " << target_size_ << name_ << " is growing fast " << cache_.size() << " of " << targetSize_
<< " aging at " << (now - whenExpire).count() << " of " << target_age_.count(); << " aging at " << (now - whenExpire).count() << " of " << targetAge_.count();
} }
std::vector<std::thread> workers; std::vector<std::thread> workers;
@@ -242,7 +266,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
for (std::thread& worker : workers) for (std::thread& worker : workers)
worker.join(); worker.join();
cache_count_ -= allRemovals; cacheCount_ -= allRemovals;
} }
// At this point allStuffToSweep will go out of scope outside the lock // At this point allStuffToSweep will go out of scope outside the lock
// and decrement the reference count on each strong pointer. // and decrement the reference count on each strong pointer.
@@ -280,7 +304,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
if (entry.isCached()) if (entry.isCached())
{ {
--cache_count_; --cacheCount_;
entry.ptr.convertToWeak(); entry.ptr.convertToWeak();
ret = true; ret = true;
} }
@@ -300,13 +324,29 @@ template <
class Hash, class Hash,
class KeyEqual, class KeyEqual,
class Mutex> class Mutex>
template <class R> template <class Policy, class Callback>
inline bool inline bool
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>:: TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
canonicalize(key_type const& key, SharedPointerType& data, R&& replaceCallback) canonicalizeImpl(
key_type const& key,
CanonicalizeClientPointerType<Policy> data,
[[maybe_unused]] Policy policy,
[[maybe_unused]] Callback&& replaceCallback)
{ {
// Return canonical value, store if needed, refresh in cache // Return canonical value, store if needed, refresh in cache
// Return values: true=we had the data already // Return values: true=we had the data already
// `Policy` is one of:
// - detail::ReplaceCached: always replace the cached value with `data`;
// `data` is never written back and may be const.
// - detail::ReplaceClient: keep the cached value and write it back into
// `data` (the client's pointer), which must therefore be writable.
// - detail::ReplaceDynamically: call `replaceCallback` to decide at run
// time; `data` must be writable.
// For the latter two the write-back below requires a mutable `data`, so
// passing a const argument is a compile error.
constexpr bool replaceCached = std::is_same_v<Policy, detail::ReplaceCached>;
std::scoped_lock const lock(mutex_); std::scoped_lock const lock(mutex_);
auto cit = cache_.find(key); auto cit = cache_.find(key);
@@ -317,20 +357,21 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
std::piecewise_construct, std::piecewise_construct,
std::forward_as_tuple(key), std::forward_as_tuple(key),
std::forward_as_tuple(clock_.now(), data)); std::forward_as_tuple(clock_.now(), data));
++cache_count_; ++cacheCount_;
return false; return false;
} }
Entry& entry = cit->second; Entry& entry = cit->second;
entry.touch(clock_.now()); entry.touch(clock_.now());
auto shouldReplace = [&] { auto shouldReplaceCached = [&] {
if constexpr (std::is_invocable_r_v<bool, R>) if constexpr (replaceCached)
{ {
// The reason for this extra complexity is for intrusive return true;
// strong/weak combo getting a strong is relatively expensive }
// and not needed for many cases. else if constexpr (std::is_same_v<Policy, detail::ReplaceClient>)
return replaceCallback(); {
return false;
} }
else else
{ {
@@ -340,11 +381,11 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
if (entry.isCached()) if (entry.isCached())
{ {
if (shouldReplace()) if (shouldReplaceCached())
{ {
entry.ptr = data; entry.ptr = data;
} }
else else if constexpr (!replaceCached)
{ {
data = entry.ptr.getStrong(); data = entry.ptr.getStrong();
} }
@@ -356,26 +397,44 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
if (cachedData) if (cachedData)
{ {
if (shouldReplace()) if (shouldReplaceCached())
{ {
entry.ptr = data; entry.ptr = data;
} }
else else if constexpr (!replaceCached)
{ {
entry.ptr.convertToStrong(); entry.ptr.convertToStrong();
data = cachedData; data = cachedData;
} }
++cache_count_; ++cacheCount_;
return true; return true;
} }
entry.ptr = data; entry.ptr = data;
++cache_count_; ++cacheCount_;
return false; return false;
} }
template <
class Key,
class T,
bool IsKeyCache,
class SharedWeakUnionPointer,
class SharedPointerType,
class Hash,
class KeyEqual,
class Mutex>
template <class Callback>
inline bool
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
canonicalize(key_type const& key, SharedPointerType& data, Callback&& replaceCallback)
{
return canonicalizeImpl(
key, data, detail::ReplaceDynamically{}, std::forward<Callback>(replaceCallback));
}
template < template <
class Key, class Key,
class T, class T,
@@ -389,7 +448,7 @@ inline bool
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>:: TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data) canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data)
{ {
return canonicalize(key, const_cast<SharedPointerType&>(data), []() { return true; }); return canonicalizeImpl(key, data, detail::ReplaceCached{});
} }
template < template <
@@ -405,7 +464,7 @@ inline bool
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>:: TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
canonicalizeReplaceClient(key_type const& key, SharedPointerType& data) canonicalizeReplaceClient(key_type const& key, SharedPointerType& data)
{ {
return canonicalize(key, data, []() { return false; }); return canonicalizeImpl(key, data, detail::ReplaceClient{});
} }
template < template <
@@ -477,7 +536,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
auto [it, inserted] = cache_.emplace( auto [it, inserted] = cache_.emplace(
std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(now)); std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(now));
if (!inserted) if (!inserted)
it->second.last_access = now; it->second.lastAccess = now;
return inserted; return inserted;
} }
@@ -626,7 +685,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
if (entry.isCached()) if (entry.isCached())
{ {
// independent of cache size, so not counted as a hit // independent of cache size, so not counted as a hit
++cache_count_; ++cacheCount_;
entry.touch(clock_.now()); entry.touch(clock_.now());
return entry.ptr.getStrong(); return entry.ptr.getStrong();
} }
@@ -658,7 +717,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
if (total != 0) if (total != 0)
hitRate = (hits_ * 100) / total; hitRate = (hits_ * 100) / total;
} }
stats_.hit_rate.set(hitRate); stats_.hitRate.set(hitRate);
} }
} }
@@ -676,7 +735,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
sweepHelper( sweepHelper(
clock_type::time_point const& whenExpire, clock_type::time_point const& whenExpire,
[[maybe_unused]] clock_type::time_point const& now, [[maybe_unused]] clock_type::time_point const& now,
typename KeyValueCacheType::map_type& partition, KeyValueCacheType::map_type& partition,
SweptPointersVector& stuffToSweep, SweptPointersVector& stuffToSweep,
std::atomic<int>& allRemovals, std::atomic<int>& allRemovals,
std::scoped_lock<std::recursive_mutex> const&) std::scoped_lock<std::recursive_mutex> const&)
@@ -706,7 +765,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
++cit; ++cit;
} }
} }
else if (cit->second.last_access <= whenExpire) else if (cit->second.lastAccess <= whenExpire)
{ {
// strong, expired // strong, expired
++cacheRemovals; ++cacheRemovals;
@@ -756,7 +815,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
sweepHelper( sweepHelper(
clock_type::time_point const& whenExpire, clock_type::time_point const& whenExpire,
clock_type::time_point const& now, clock_type::time_point const& now,
typename KeyOnlyCacheType::map_type& partition, KeyOnlyCacheType::map_type& partition,
SweptPointersVector&, SweptPointersVector&,
std::atomic<int>& allRemovals, std::atomic<int>& allRemovals,
std::scoped_lock<std::recursive_mutex> const&) std::scoped_lock<std::recursive_mutex> const&)
@@ -773,12 +832,12 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
auto cit = partition.begin(); auto cit = partition.begin();
while (cit != partition.end()) while (cit != partition.end())
{ {
if (cit->second.last_access > now) if (cit->second.lastAccess > now)
{ {
cit->second.last_access = now; cit->second.lastAccess = now;
++cit; ++cit;
} }
else if (cit->second.last_access <= whenExpire) else if (cit->second.lastAccess <= whenExpire)
{ {
cit = partition.erase(cit); cit = partition.erase(cit);
} }

View File

@@ -36,6 +36,7 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <string_view>
namespace xrpl { namespace xrpl {
@@ -43,7 +44,7 @@ std::string
base64Encode(std::uint8_t const* data, std::size_t len); base64Encode(std::uint8_t const* data, std::size_t len);
inline std::string inline std::string
base64Encode(std::string const& s) base64Encode(std::string_view s)
{ {
return base64Encode(reinterpret_cast<std::uint8_t const*>(s.data()), s.size()); return base64Encode(reinterpret_cast<std::uint8_t const*>(s.data()), s.size());
} }

View File

@@ -5,7 +5,6 @@
#pragma once #pragma once
#include <xrpl/basics/Expected.h>
#include <xrpl/basics/Slice.h> #include <xrpl/basics/Slice.h>
#include <xrpl/basics/contract.h> #include <xrpl/basics/contract.h>
#include <xrpl/basics/hardened_hash.h> #include <xrpl/basics/hardened_hash.h>
@@ -20,6 +19,7 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cstring> #include <cstring>
#include <expected>
#include <type_traits> #include <type_traits>
namespace xrpl { namespace xrpl {
@@ -177,7 +177,7 @@ private:
BadChar, BadChar,
}; };
constexpr Expected<decltype(data_), ParseResult> constexpr std::expected<decltype(data_), ParseResult>
parseFromStringView(std::string_view sv) noexcept parseFromStringView(std::string_view sv) noexcept
{ {
// Local lambda that converts a single hex char to four bits and // Local lambda that converts a single hex char to four bits and
@@ -216,7 +216,7 @@ private:
} }
if (sv.size() != size() * 2) if (sv.size() != size() * 2)
return Unexpected(ParseResult::BadLength); return std::unexpected(ParseResult::BadLength);
std::size_t i = 0u; std::size_t i = 0u;
auto in = sv.begin(); auto in = sv.begin();
@@ -227,7 +227,7 @@ private:
{ {
if (auto const result = hexCharToUInt(*in++, shift, accum); if (auto const result = hexCharToUInt(*in++, shift, accum);
result != ParseResult::Okay) result != ParseResult::Okay)
return Unexpected(result); return std::unexpected(result);
} }
ret[i++] = accum; ret[i++] = accum;
} }

View File

@@ -75,7 +75,7 @@ private:
detail::seed_pair seeds_{detail::makeSeedPair<>()}; detail::seed_pair seeds_{detail::makeSeedPair<>()};
public: public:
using result_type = typename HashAlgorithm::result_type; using result_type = HashAlgorithm::result_type;
HardenedHash() = default; HardenedHash() = default;

View File

@@ -1,12 +1,13 @@
#pragma once #pragma once
#include <string> #include <string>
#include <string_view>
namespace xrpl { namespace xrpl {
template <class Stream, class Iter> template <class Stream, class Iter>
Stream& Stream&
join(Stream& s, Iter iter, Iter end, std::string const& delimiter) join(Stream& s, Iter iter, Iter end, std::string_view delimiter)
{ {
if (iter == end) if (iter == end)
return s; return s;

View File

@@ -57,8 +57,8 @@ public:
{ {
using iterator_category = std::forward_iterator_tag; using iterator_category = std::forward_iterator_tag;
partition_map_type* map{nullptr}; partition_map_type* map{nullptr};
typename partition_map_type::iterator ait{}; partition_map_type::iterator ait{};
typename map_type::iterator mit; map_type::iterator mit;
Iterator() = default; Iterator() = default;
@@ -126,8 +126,8 @@ public:
using iterator_category = std::forward_iterator_tag; using iterator_category = std::forward_iterator_tag;
partition_map_type* map{nullptr}; partition_map_type* map{nullptr};
typename partition_map_type::iterator ait{}; partition_map_type::iterator ait{};
typename map_type::iterator mit; map_type::iterator mit;
ConstIterator() = default; ConstIterator() = default;

View File

@@ -29,6 +29,7 @@ static_assert(
namespace detail { namespace detail {
// Determines if a type can be called like an Engine // Determines if a type can be called like an Engine
// NOLINTNEXTLINE(readability-redundant-typename): typename required by MSVC
template <class Engine, class Result = typename Engine::result_type> template <class Engine, class Result = typename Engine::result_type>
using is_engine = std::is_invocable_r<Result, Engine>; using is_engine = std::is_invocable_r<Result, Engine>;
} // namespace detail } // namespace detail

View File

@@ -1,29 +0,0 @@
#pragma once
#if XRPL_ROCKSDB_AVAILABLE
// #include <rocksdb2/port/port_posix.h>
#include <rocksdb/cache.h>
#include <rocksdb/compaction_filter.h>
#include <rocksdb/comparator.h>
#include <rocksdb/convenience.h>
#include <rocksdb/db.h>
#include <rocksdb/env.h>
#include <rocksdb/filter_policy.h>
#include <rocksdb/flush_block_policy.h>
#include <rocksdb/iterator.h>
#include <rocksdb/memtablerep.h>
#include <rocksdb/merge_operator.h>
#include <rocksdb/options.h>
#include <rocksdb/perf_context.h>
#include <rocksdb/slice.h>
#include <rocksdb/slice_transform.h>
#include <rocksdb/statistics.h>
#include <rocksdb/status.h>
#include <rocksdb/table.h>
#include <rocksdb/table_properties.h>
#include <rocksdb/transaction_log.h>
#include <rocksdb/types.h>
#include <rocksdb/universal_compaction.h>
#include <rocksdb/write_batch.h>
#endif

View File

@@ -4,7 +4,7 @@
/* /*
ASAN flags some false positives with sudden jumps in control flow, like ASAN flags some false positives with sudden jumps in control flow, like
exceptions, or when encountering coroutine stack switches. This macro can be used to disable ASAN exceptions, or when encountering coroutine stack switches. This macro can be used to disable ASAN
intrumentation for specific functions. instrumentation for specific functions.
*/ */
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define XRPL_NO_SANITIZE_ADDRESS __attribute__((no_sanitize("address", "hwaddress"))) #define XRPL_NO_SANITIZE_ADDRESS __attribute__((no_sanitize("address", "hwaddress")))

View File

@@ -24,20 +24,20 @@ namespace xrpl {
template <class EF> template <class EF>
class ScopeExit class ScopeExit
{ {
EF exit_function_; EF exitFunction_;
bool execute_on_destruction_{true}; bool executeOnDestruction_{true};
public: public:
~ScopeExit() ~ScopeExit()
{ {
if (execute_on_destruction_) if (executeOnDestruction_)
exit_function_(); exitFunction_();
} }
ScopeExit(ScopeExit&& rhs) noexcept( ScopeExit(ScopeExit&& rhs) noexcept(
std::is_nothrow_move_constructible_v<EF> || std::is_nothrow_copy_constructible_v<EF>) std::is_nothrow_move_constructible_v<EF> || std::is_nothrow_copy_constructible_v<EF>)
: exit_function_{std::forward<EF>(rhs.exit_function_)} : exitFunction_{std::forward<EF>(rhs.exitFunction_)}
, execute_on_destruction_{rhs.execute_on_destruction_} , executeOnDestruction_{rhs.executeOnDestruction_}
{ {
rhs.release(); rhs.release();
} }
@@ -51,7 +51,7 @@ public:
std::enable_if_t< std::enable_if_t<
!std::is_same_v<std::remove_cv_t<EFP>, ScopeExit> && !std::is_same_v<std::remove_cv_t<EFP>, ScopeExit> &&
std::is_constructible_v<EF, EFP>>* = 0) noexcept std::is_constructible_v<EF, EFP>>* = 0) noexcept
: exit_function_{std::forward<EFP>(f)} : exitFunction_{std::forward<EFP>(f)}
{ {
static_assert(std::is_nothrow_constructible_v<EF, decltype(std::forward<EFP>(f))>); static_assert(std::is_nothrow_constructible_v<EF, decltype(std::forward<EFP>(f))>);
} }
@@ -59,7 +59,7 @@ public:
void void
release() noexcept release() noexcept
{ {
execute_on_destruction_ = false; executeOnDestruction_ = false;
} }
}; };
@@ -69,22 +69,22 @@ ScopeExit(EF) -> ScopeExit<EF>;
template <class EF> template <class EF>
class ScopeFail class ScopeFail
{ {
EF exit_function_; EF exitFunction_;
bool execute_on_destruction_{true}; bool executeOnDestruction_{true};
int uncaught_on_creation_{std::uncaught_exceptions()}; int uncaughtOnCreation_{std::uncaught_exceptions()};
public: public:
~ScopeFail() ~ScopeFail()
{ {
if (execute_on_destruction_ && std::uncaught_exceptions() > uncaught_on_creation_) if (executeOnDestruction_ && std::uncaught_exceptions() > uncaughtOnCreation_)
exit_function_(); exitFunction_();
} }
ScopeFail(ScopeFail&& rhs) noexcept( ScopeFail(ScopeFail&& rhs) noexcept(
std::is_nothrow_move_constructible_v<EF> || std::is_nothrow_copy_constructible_v<EF>) std::is_nothrow_move_constructible_v<EF> || std::is_nothrow_copy_constructible_v<EF>)
: exit_function_{std::forward<EF>(rhs.exit_function_)} : exitFunction_{std::forward<EF>(rhs.exitFunction_)}
, execute_on_destruction_{rhs.execute_on_destruction_} , executeOnDestruction_{rhs.executeOnDestruction_}
, uncaught_on_creation_{rhs.uncaught_on_creation_} , uncaughtOnCreation_{rhs.uncaughtOnCreation_}
{ {
rhs.release(); rhs.release();
} }
@@ -98,7 +98,7 @@ public:
std::enable_if_t< std::enable_if_t<
!std::is_same_v<std::remove_cv_t<EFP>, ScopeFail> && !std::is_same_v<std::remove_cv_t<EFP>, ScopeFail> &&
std::is_constructible_v<EF, EFP>>* = 0) noexcept std::is_constructible_v<EF, EFP>>* = 0) noexcept
: exit_function_{std::forward<EFP>(f)} : exitFunction_{std::forward<EFP>(f)}
{ {
static_assert(std::is_nothrow_constructible_v<EF, decltype(std::forward<EFP>(f))>); static_assert(std::is_nothrow_constructible_v<EF, decltype(std::forward<EFP>(f))>);
} }
@@ -106,7 +106,7 @@ public:
void void
release() noexcept release() noexcept
{ {
execute_on_destruction_ = false; executeOnDestruction_ = false;
} }
}; };
@@ -116,22 +116,22 @@ ScopeFail(EF) -> ScopeFail<EF>;
template <class EF> template <class EF>
class ScopeSuccess class ScopeSuccess
{ {
EF exit_function_; EF exitFunction_;
bool execute_on_destruction_{true}; bool executeOnDestruction_{true};
int uncaught_on_creation_{std::uncaught_exceptions()}; int uncaughtOnCreation_{std::uncaught_exceptions()};
public: public:
~ScopeSuccess() noexcept(noexcept(exit_function_())) ~ScopeSuccess() noexcept(noexcept(exitFunction_()))
{ {
if (execute_on_destruction_ && std::uncaught_exceptions() <= uncaught_on_creation_) if (executeOnDestruction_ && std::uncaught_exceptions() <= uncaughtOnCreation_)
exit_function_(); exitFunction_();
} }
ScopeSuccess(ScopeSuccess&& rhs) noexcept( ScopeSuccess(ScopeSuccess&& rhs) noexcept(
std::is_nothrow_move_constructible_v<EF> || std::is_nothrow_copy_constructible_v<EF>) std::is_nothrow_move_constructible_v<EF> || std::is_nothrow_copy_constructible_v<EF>)
: exit_function_{std::forward<EF>(rhs.exit_function_)} : exitFunction_{std::forward<EF>(rhs.exitFunction_)}
, execute_on_destruction_{rhs.execute_on_destruction_} , executeOnDestruction_{rhs.executeOnDestruction_}
, uncaught_on_creation_{rhs.uncaught_on_creation_} , uncaughtOnCreation_{rhs.uncaughtOnCreation_}
{ {
rhs.release(); rhs.release();
} }
@@ -146,14 +146,14 @@ public:
!std::is_same_v<std::remove_cv_t<EFP>, ScopeSuccess> && !std::is_same_v<std::remove_cv_t<EFP>, ScopeSuccess> &&
std::is_constructible_v<EF, EFP>>* = std::is_constructible_v<EF, EFP>>* =
0) noexcept(std::is_nothrow_constructible_v<EF, EFP> || std::is_nothrow_constructible_v<EF, EFP&>) 0) noexcept(std::is_nothrow_constructible_v<EF, EFP> || std::is_nothrow_constructible_v<EF, EFP&>)
: exit_function_{std::forward<EFP>(f)} : exitFunction_{std::forward<EFP>(f)}
{ {
} }
void void
release() noexcept release() noexcept
{ {
execute_on_destruction_ = false; executeOnDestruction_ = false;
} }
}; };

View File

@@ -18,8 +18,8 @@ template <class Clock>
class IOLatencyProbe class IOLatencyProbe
{ {
private: private:
using duration = typename Clock::duration; using duration = Clock::duration;
using time_point = typename Clock::time_point; using time_point = Clock::time_point;
std::recursive_mutex mutex_; std::recursive_mutex mutex_;
std::condition_variable_any cond_; std::condition_variable_any cond_;

View File

@@ -34,10 +34,10 @@ template <class Clock>
class AbstractClock class AbstractClock
{ {
public: public:
using rep = typename Clock::rep; using rep = Clock::rep;
using period = typename Clock::period; using period = Clock::period;
using duration = typename Clock::duration; using duration = Clock::duration;
using time_point = typename Clock::time_point; using time_point = Clock::time_point;
using clock_type = Clock; using clock_type = Clock;
static bool const is_steady = Clock::is_steady; // NOLINT(readability-identifier-naming) static bool const is_steady = Clock::is_steady; // NOLINT(readability-identifier-naming)

View File

@@ -20,10 +20,10 @@ public:
explicit BasicSecondsClock() = default; explicit BasicSecondsClock() = default;
using rep = typename Clock::rep; using rep = Clock::rep;
using period = typename Clock::period; using period = Clock::period;
using duration = typename Clock::duration; using duration = Clock::duration;
using time_point = typename Clock::time_point; using time_point = Clock::time_point;
static bool const is_steady = // NOLINT(readability-identifier-naming) static bool const is_steady = // NOLINT(readability-identifier-naming)
Clock::is_steady; Clock::is_steady;

Some files were not shown because too many files have changed in this diff Show More