commit 48c40c365ac671cbbe1ac2fb359e1a5cf7e165a3 parent 8699495029e894ebdb7677a79f173787cd41422b Author: Christian Grothoff <christian@grothoff.org> Date: Tue, 4 Aug 2026 10:12:56 +0200 remove need for / use of rustup, remove assert_matches for now to work with canonical Debian testing cargo Diffstat:
23 files changed, 208 insertions(+), 48 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml @@ -5,6 +5,12 @@ members = ["common/*", "adapters/*", "taler-apns-relay"] [workspace.package] version = "1.5.0" edition = "2024" +# Newest standard-library item the tree uses is std::fmt::from_fn (1.93); let +# chains (1.88) and Vec::pop_if (1.86) sit below it, edition 2024 needs 1.85. +# Declared so that cargo rejects an old toolchain with one clear message instead +# of hundreds of errors. Keep in step with RUST_MIN_VERSION in +# build-system/configure.py. +rust-version = "1.93" authors = ["Taler Systems SA <deb@taler.net>"] homepage = "https://taler.net/" repository = "https://git.taler.net/taler-rust.git" diff --git a/Makefile b/Makefile @@ -1,6 +1,12 @@ # This Makefile has been placed under the public domain -include build-system/config.mk +# Defaults for an unconfigured tree. config.mk is included above, so whatever +# ./configure resolved wins over these; a plain `make` without ./configure still +# works and uses whichever cargo is on $PATH. No rustup required either way. +cargo ?= cargo +prefix ?= /usr/local + # Absolute DESTDIR or empty string if DESTDIR unset/empty abs_destdir=$(abspath $(DESTDIR)) @@ -13,7 +19,7 @@ all: build .PHONY: build build: - cargo build --release --bin taler-magnet-bank --bin taler-cyclos --bin taler-wise --bin taler-apns-relay + $(cargo) build --release --bin taler-magnet-bank --bin taler-cyclos --bin taler-wise --bin taler-apns-relay .PHONY: install-files install-files: @@ -51,12 +57,12 @@ install: build install-files .PHONY: check check: install-files - cargo test - cargo clippy --all-targets + $(cargo) test + $(cargo) clippy --all-targets .PHONY: doc doc: - cargo doc + $(cargo) doc .PHONY: deb deb: @@ -72,7 +78,7 @@ fmt: .PHONY: coverage-cyclos coverage-cyclos: - cargo llvm-cov clean --workspace - cargo llvm-cov test --no-clean - cargo llvm-cov run --bin cyclos-harness --no-clean -- -c dev.conf logic - cargo llvm-cov report --lcov --output-path ./target/lcov.info -\ No newline at end of file + $(cargo) llvm-cov clean --workspace + $(cargo) llvm-cov test --no-clean + $(cargo) llvm-cov run --bin cyclos-harness --no-clean -- -c dev.conf logic + $(cargo) llvm-cov report --lcov --output-path ./target/lcov.info +\ No newline at end of file diff --git a/README.md b/README.md @@ -35,10 +35,23 @@ Setup documentation can be found [here](https://docs.taler.net/taler-cyclos-manu ## Getting Started for Development +Any Rust toolchain on `$PATH` will do, distribution packages included; `rustup` +is not required. The tree needs **rustc >= 1.93**, so on Debian stable install +it from backports (`sudo apt install -t trixie-backports rustc cargo`). + ```sh -sudo apt install rustup -rustup default stable +sudo apt install cargo rustc # or any other toolchain, e.g. via rustup ./bootstrap ./configure make check ``` + +`./configure` reports the cargo and rustc it found and records them in +`build-system/config.mk`; override the choice with `CARGO=` / `RUSTC=`: + +```sh +CARGO=$HOME/.cargo/bin/cargo RUSTC=$HOME/.cargo/bin/rustc ./configure +``` + +`make check` needs a PostgreSQL cluster it can reach as the current user, with a +`taler_rust_check` database it can create further test databases from. diff --git a/adapters/taler-cyclos/Cargo.toml b/adapters/taler-cyclos/Cargo.toml @@ -3,6 +3,7 @@ name = "taler-cyclos" version = "0.0.0" description = "Taler Cyclos adapter" edition.workspace = true +rust-version.workspace = true authors.workspace = true homepage.workspace = true repository.workspace = true diff --git a/adapters/taler-cyclos/src/bin/cyclos-harness.rs b/adapters/taler-cyclos/src/bin/cyclos-harness.rs @@ -14,7 +14,7 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -use std::{assert_matches, time::Duration}; +use std::time::Duration; use clap::Parser as _; use compact_str::CompactString; @@ -175,10 +175,11 @@ impl<'a> Harness<'a> { ) .await .unwrap(); - assert_matches!( + // TODO revert to assert_matches! once rust-lang#82775 is stable + assert!(matches!( transfer.first().unwrap(), IncomingBankTransaction::Reserve { reserve_pub, .. } if *reserve_pub == key, - ); + )); } async fn custom_transfer(&self, amount: Decimal, creditor_id: i64, creditor_name: &str) -> u64 { @@ -496,10 +497,11 @@ async fn logic_harness(cfg: &Config, reset: bool) -> anyhow::Result<()> { let amount = decimal("10.3"); harness.transfer(amount).await; set_failure_scenario(&["direct-payment"]); - assert_matches!( + // TODO revert to assert_matches! once rust-lang#82775 is stable + assert!(matches!( harness.worker().await.unwrap_err(), WorkerError::Injected(InjectedErr("direct-payment")) - ); + )); harness.worker().await?; balance.expect_sub(amount).await; harness.worker().await?; @@ -513,10 +515,11 @@ async fn logic_harness(cfg: &Config, reset: bool) -> anyhow::Result<()> { balance.expect_add(amount).await; // Sync and bounce set_failure_scenario(&["chargeback"]); - assert_matches!( + // TODO revert to assert_matches! once rust-lang#82775 is stable + assert!(matches!( harness.worker().await.unwrap_err(), WorkerError::Injected(InjectedErr("chargeback")) - ); + )); balance.expect_sub(amount).await; // Sync recover harness.worker().await?; diff --git a/adapters/taler-cyclos/src/db.rs b/adapters/taler-cyclos/src/db.rs @@ -913,8 +913,6 @@ impl CyclosTypeHelper for PgRow { #[cfg(test)] mod test { - use std::assert_matches; - use compact_str::CompactString; use jiff::{Span, Timestamp}; use serde_json::json; @@ -1189,7 +1187,8 @@ mod test { creditor_name: "Name".into(), valued_at: now, }; - assert_matches!( + // TODO revert to assert_matches! once rust-lang#82775 is stable + assert!(matches!( db::make_transfer( &pool, &Transfer { @@ -1206,7 +1205,7 @@ mod test { .await .unwrap(), TransferResult::Success { .. } - ); + )); db::initiated_submit_success(&mut db, 1, &Timestamp::now(), transfer_id) .await .expect("status success"); diff --git a/adapters/taler-magnet-bank/Cargo.toml b/adapters/taler-magnet-bank/Cargo.toml @@ -3,6 +3,7 @@ name = "taler-magnet-bank" version.workspace = true description = "Taler Magnet Bank adapter" edition.workspace = true +rust-version.workspace = true authors.workspace = true homepage.workspace = true repository.workspace = true diff --git a/adapters/taler-magnet-bank/src/bin/magnet-bank-harness.rs b/adapters/taler-magnet-bank/src/bin/magnet-bank-harness.rs @@ -14,7 +14,7 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -use std::{assert_matches, fmt::Debug, time::Duration}; +use std::{fmt::Debug, time::Duration}; use aws_lc_rs::signature::EcdsaKeyPair; use clap::Parser as _; @@ -196,10 +196,11 @@ impl<'a> Harness<'a> { ) .await .unwrap(); - assert_matches!( + // TODO revert to assert_matches! once rust-lang#82775 is stable + assert!(matches!( transfer.first().unwrap(), IncomingBankTransaction::Reserve { reserve_pub, .. } if *reserve_pub == key, - ); + )); } /// Send a transaction between two magnet accounts @@ -436,10 +437,11 @@ async fn logic_harness(cfg: &Config, reset: bool) -> anyhow::Result<()> { step("Test transfer failure init-tx"); harness.transfer(10).await; set_failure_scenario(&["init-tx"]); - assert_matches!( + // TODO revert to assert_matches! once rust-lang#82775 is stable + assert!(matches!( harness.worker().await, Err(WorkerError::Injected(InjectedErr("init-tx"))) - ); + )); harness.worker().await?; balance.expect(-10).await; harness.worker().await?; @@ -447,10 +449,11 @@ async fn logic_harness(cfg: &Config, reset: bool) -> anyhow::Result<()> { step("Test transfer failure submit-tx"); harness.transfer(11).await; set_failure_scenario(&["submit-tx"]); - assert_matches!( + // TODO revert to assert_matches! once rust-lang#82775 is stable + assert!(matches!( harness.worker().await, Err(WorkerError::Injected(InjectedErr("submit-tx"))) - ); + )); harness.worker().await?; balance.expect(-11).await; harness.worker().await?; @@ -458,14 +461,16 @@ async fn logic_harness(cfg: &Config, reset: bool) -> anyhow::Result<()> { step("Test transfer all failures"); harness.transfer(13).await; set_failure_scenario(&["init-tx", "submit-tx"]); - assert_matches!( + // TODO revert to assert_matches! once rust-lang#82775 is stable + assert!(matches!( harness.worker().await, Err(WorkerError::Injected(InjectedErr("init-tx"))) - ); - assert_matches!( + )); + // TODO revert to assert_matches! once rust-lang#82775 is stable + assert!(matches!( harness.worker().await, Err(WorkerError::Injected(InjectedErr("submit-tx"))) - ); + )); harness.worker().await?; balance.expect(-13).await; harness.worker().await?; diff --git a/adapters/taler-magnet-bank/src/db.rs b/adapters/taler-magnet-bank/src/db.rs @@ -879,8 +879,6 @@ pub async fn transfer_unregister(db: &PgPool, req: &Unregistration) -> sqlx::Res #[cfg(test)] mod test { - use std::assert_matches; - use jiff::{Span, Timestamp, Zoned}; use serde_json::json; use sqlx::{PgPool, Postgres, pool::PoolConnection, postgres::PgRow}; @@ -1153,7 +1151,8 @@ mod test { value_date: date, status: TxStatus::Completed, }; - assert_matches!( + // TODO revert to assert_matches! once rust-lang#82775 is stable + assert!(matches!( make_transfer( &pool, &db::Transfer { @@ -1169,7 +1168,7 @@ mod test { .await .unwrap(), TransferResult::Success { .. } - ); + )); db::initiated_submit_success(&mut db, 1, &Timestamp::now(), tx.code) .await .expect("status success"); diff --git a/adapters/taler-wise/Cargo.toml b/adapters/taler-wise/Cargo.toml @@ -2,6 +2,7 @@ name = "taler-wise" version.workspace = true edition.workspace = true +rust-version.workspace = true authors.workspace = true homepage.workspace = true repository.workspace = true diff --git a/build-system/configure.py b/build-system/configure.py @@ -1,10 +1,90 @@ # This configure.py.template file is in the public domain. +import os +import re +import shutil +import subprocess + from talerbuildconfig import * +# Oldest Rust release that can build this tree. Keep the reason for the number +# next to it -- the point of the check is to report an unusable toolchain here, +# once, instead of as a wall of parse errors in the middle of the build. +# +# 1.85 edition 2024 / resolver "3" (Cargo.toml) +# 1.86 Vec::pop_if (common/failure-injection) +# 1.88 let chains (used throughout) +# 1.93 std::fmt::from_fn (taler-common, taler-api, taler-wise) +# +# Debian trixie ships 1.85, which is too old; trixie-backports and testing both +# ship a new enough rustc. Keep this in step with `rust-version` in Cargo.toml. +RUST_MIN_VERSION = "1.93" + + +def _version_tuple(version): + parts = [int(p) for p in version.split(".")[:3]] + return tuple(parts + [0] * (3 - len(parts))) + + +class RustTool(Tool): + """A Rust toolchain program (cargo, rustc) found on $PATH. + + Unlike PosixTool this honours a CARGO=/RUSTC= environment override and + records the resolved path *and* version in config.mk. Nothing here wants + rustup: any cargo/rustc on $PATH will do, including the distribution's. + """ + + def __init__(self, name, min_version=None): + self.name = name + self.min_version = min_version + self.hint = ( + f"install a Rust toolchain providing '{name}' -- the distribution's" + f" rustc/cargo packages are fine (on Debian stable they come from" + f" trixie-backports), as is rustup -- or point {name.upper()}= at one" + ) + + def args(self, parser): + pass + + def check(self, buildconfig): + prog = os.environ.get(self.name.upper()) or self.name + path = shutil.which(prog) + if path is None: + return False + + version = self._version(path) + buildconfig._set_tool(self.name, path, version=version) + + if self.min_version is None or version is None: + return True + + if _version_tuple(version) < _version_tuple(self.min_version): + # A warning, not an error: configuring is still useful (the + # non-build targets work), and `rust-version` in Cargo.toml makes + # cargo itself refuse the build with a precise message. + buildconfig._warn( + f"{self.name} {version} is older than {self.min_version};" + f" this tree will not compile with it." + f" On Debian stable: apt install -t trixie-backports rustc cargo" + ) + return True + + @staticmethod + def _version(path): + try: + out = subprocess.run( + [path, "--version"], capture_output=True, text=True, check=True + ).stdout + except (OSError, subprocess.SubprocessError): + return None + # "cargo 1.95.0 (f2d3ce0bd 2026-03-21)", "rustc 1.95.0 (59807616e ...)" + found = re.search(r"\d+(?:\.\d+)+", out) + return found.group(0) if found else None + + b = BuildConfig() b.enable_prefix() b.enable_configmk() -b.add_tool(PosixTool("rustup")) -b.add_tool(PosixTool("cargo")) +b.add_tool(RustTool("cargo")) +b.add_tool(RustTool("rustc", min_version=RUST_MIN_VERSION)) b.run() diff --git a/common/failure-injection/Cargo.toml b/common/failure-injection/Cargo.toml @@ -2,6 +2,7 @@ name = "failure-injection" version.workspace = true edition.workspace = true +rust-version.workspace = true authors.workspace = true homepage.workspace = true repository.workspace = true diff --git a/common/http-client/Cargo.toml b/common/http-client/Cargo.toml @@ -2,6 +2,7 @@ name = "http-client" version.workspace = true edition.workspace = true +rust-version.workspace = true authors.workspace = true homepage.workspace = true repository.workspace = true diff --git a/common/taler-api/Cargo.toml b/common/taler-api/Cargo.toml @@ -2,6 +2,7 @@ name = "taler-api" version.workspace = true edition.workspace = true +rust-version.workspace = true authors.workspace = true homepage.workspace = true repository.workspace = true diff --git a/common/taler-build/Cargo.toml b/common/taler-build/Cargo.toml @@ -2,6 +2,7 @@ name = "taler-build" version.workspace = true edition.workspace = true +rust-version.workspace = true authors.workspace = true homepage.workspace = true repository.workspace = true diff --git a/common/taler-common/Cargo.toml b/common/taler-common/Cargo.toml @@ -2,6 +2,7 @@ name = "taler-common" version.workspace = true edition.workspace = true +rust-version.workspace = true authors.workspace = true homepage.workspace = true repository.workspace = true diff --git a/common/taler-common/src/config.rs b/common/taler-common/src/config.rs @@ -1070,6 +1070,23 @@ mod test { assert_eq!(err.as_ref(), fmt); } + /// [`check_err`] for messages with an environment-dependent middle: only + /// the head and the tail are compared. + #[track_caller] + fn check_err_loose<T: Debug, E: Display>( + head: impl AsRef<str>, + tail: impl AsRef<str>, + lambda: Result<T, E>, + ) { + let failure = lambda.unwrap_err(); + let fmt = failure.to_string(); + let (head, tail) = (head.as_ref(), tail.as_ref()); + assert!( + fmt.starts_with(head) && fmt.ends_with(tail), + "expected an error starting with '{head}' and ending with '{tail}', got '{fmt}'" + ); + } + #[test] fn fs() { let dir = tempfile::tempdir().unwrap(); @@ -1116,9 +1133,15 @@ mod test { )); std::fs::write(&config_path, "@inline-matching@[*").unwrap(); - check_err(format!( - "Malformed glob regex at '{config_path_fmt}:1': Pattern syntax error near position 60: invalid range pattern" - )); + // glob reports the offset of the '[' within the *expanded* pattern + // (<tempdir>/[*), so the position moves with the length of $TMPDIR. + check_err_loose( + format!( + "Malformed glob regex at '{config_path_fmt}:1': Pattern syntax error near position " + ), + ": invalid range pattern", + Config::load(SOURCE, Some(&config_path)), + ); std::fs::write(&config_path, "@inline-matching@*second-conf.conf").unwrap(); check_err(format!( diff --git a/common/taler-macros/Cargo.toml b/common/taler-macros/Cargo.toml @@ -2,6 +2,7 @@ name = "taler-macros" version.workspace = true edition.workspace = true +rust-version.workspace = true authors.workspace = true homepage.workspace = true repository.workspace = true diff --git a/common/taler-test-utils/Cargo.toml b/common/taler-test-utils/Cargo.toml @@ -2,6 +2,7 @@ name = "taler-test-utils" version.workspace = true edition.workspace = true +rust-version.workspace = true authors.workspace = true homepage.workspace = true repository.workspace = true diff --git a/contrib/ci/Containerfile b/contrib/ci/Containerfile @@ -1,13 +1,12 @@ FROM docker.io/library/debian:trixie ENV DEBIAN_FRONTEND=noninteractive - + # Apt sync RUN apt-get update -yq && \ apt-get upgrade -yq && \ # Install build & test dependencies apt-get install -yq \ - rustup \ git \ python3 \ sudo \ @@ -17,8 +16,15 @@ RUN apt-get update -yq && \ debhelper \ build-essential \ git-buildpackage && \ - # Install rustup toolchain - rustup default stable + # Rust from trixie-backports: trixie itself ships 1.85, this tree needs + # >= 1.93 (std::fmt::from_fn). Backports keeps the base image -- and + # therefore the glibc the .deb is linked against -- on stable, which + # matters because the artifacts are published to a trixie repository. + echo 'deb http://deb.debian.org/debian trixie-backports main' \ + > /etc/apt/sources.list.d/backports.list && \ + apt-get update -yq && \ + apt-get install -yq -t trixie-backports rustc cargo && \ + rm -rf /var/lib/apt/lists/* WORKDIR /workdir diff --git a/debian/control b/debian/control @@ -6,7 +6,12 @@ Uploaders: Christian Grothoff <grothoff@gnu.org>, Florian Dold <dold@taler.net> Rules-Requires-Root: no Build-Depends: debhelper-compat (= 13), - rustup + cargo, +# 1.93, not 1.85: taler-common/taler-api use std::fmt::from_fn, stabilised in +# 1.93 (let-chains, 1.88, and Vec::pop_if, 1.86, put the floor above trixie's +# 1.85 already). Trixie ships 1.85, so on stable this comes from +# trixie-backports; no rustup needed. + rustc (>= 1.93) Standards-Version: 4.7.2 Vcs-Git: https://git.taler.net/taler-rust.git Homepage: https://taler.net/ diff --git a/debian/rules b/debian/rules @@ -4,7 +4,11 @@ dh $@ --no-parallel override_dh_auto_configure: - rustup default stable + # Nothing to configure: debian/*.install hardcodes the /usr paths and the + # build only needs a cargo from $PATH (see Build-Depends). The step is + # still overridden so that dh does not hand ./configure the autoconf-style + # arguments it does not accept. + true override_dh_auto_build: make build diff --git a/taler-apns-relay/Cargo.toml b/taler-apns-relay/Cargo.toml @@ -2,6 +2,7 @@ name = "taler-apns-relay" version = "0.0.0" edition.workspace = true +rust-version.workspace = true authors.workspace = true homepage.workspace = true repository.workspace = true