Skip to content

Commit 02208e5

Browse files
committed
update-build: i586 x87 excess-precision float==double %check FTBFS
Document the i586-only exact-float-compare test failure pattern: x87 80-bit excess precision; -fexcess-precision=standard is a no-op; -mfpmath=sse/-msse2 is unsafe (no SSE2 in the i586 baseline -> SIGILL); fix by narrowing both sides to float (asFloat()/cast); and verify via the server-side i586 build (not flaky local QEMU) before the Factory SR. Real case: jsoncpp 1.9.8 ValueTest/objects, upstream PR gh#open-source-parsers/jsoncpp#1700.
1 parent 3ce5b6b commit 02208e5

1 file changed

Lines changed: 1 addition & 0 deletions

File tree

references/update-build.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ When a build fails locally, check this list before debugging from scratch.
284284
- **A major version bump of old C code often breaks on Factory's current GCC — expect it, it is not your spec's fault.** GCC 14 turned `-Wimplicit-function-declaration`, `-Wincompatible-pointer-types` and `-Wint-conversion` into **errors** by default; GCC 15 defaults to **C23** (`-std=gnu23`), where an empty parameter list `T (*fp)()` means *zero* args (not "unspecified"), so a call `fp(arg)` fails with `too many arguments to function 'fp'`. These are hard errors, not warnings — they are not caused by `-Werror` and `-Wno-error=` will not silence the C23 one. Prefer a **targeted source patch** when there are few sites: `grep -rnE '\(\s*\*\s*[A-Za-z_]+\s*\)\s*\(\s*\)' src/*.c` counts empty-paren function-pointer declarations (ior 4.0.0 had exactly one — patched `void(*fp)()` → `void(*fp)(void*)`). `-std=gnu17` (set in `CFLAGS`; `export CFLAGS="%{optflags} -std=gnu17"` before `%cmake`) is a blunt whole-package fallback that restores pre-C23 semantics — appropriate for old K&R C (e.g. an inline `double atof();`) where patching every site is excessive. **Check upstream `main` first** — the fix is almost always already there but unreleased: `gh api repos/<o>/<r>/contents/<path>?ref=main --jq .content | base64 -d` shows the current file, so you can backport the exact upstream change and tag it `PATCH-FIX-UPSTREAM`.
285285
- **C23 also promotes `bool`/`true`/`false`/`nullptr`/`static_assert`/`thread_local` to keywords**, so old code that does `typedef _Bool bool;` (or `typedef unsigned char bool;`) now fails with `'bool' cannot be defined via 'typedef'`. Same `-std=gnu17` fix.
286286
- **A package can FTBFS on *some arches only* because upstream forces the C standard for just one arch.** Watch for a CMake/configure branch like `if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64") … set(CMAKE_C_FLAGS "… -std=gnu99") …`: x86_64 then gets a pre-C23 standard and builds, while aarch64/ppc64le/s390x inherit GCC 15's C23 default and break — so Factory shows the package green on x86_64 but **failed on every other arch**. Fix by forcing the standard for *all* arches (`export CFLAGS="%{optflags} -std=gnu17"` before `%cmake`). (Real case: blosc 1.21.6 — `bool` typedef in `shuffle.c`, only non-x86 failed.)
287+
- **An i586-only `%check` failure on an exact float==double comparison is x87 excess precision — and the two "obvious" fixes are a no-op and a footgun respectively.** A test that stores a `float` literal into a `double` (or otherwise round-trips float↔double) and then asserts *exact* equality passes on x86_64/aarch64/… but fails **only on i586**, because GCC defaults to x87 (`-mfpmath=387`, 80-bit) on 32-bit x86: the stored/reloaded value comes back as the true double instead of the float-widened value (e.g. `0.12345` vs `0.12345000356435776`). Two tempting fixes are wrong: (a) **`-fexcess-precision=standard` is usually a no-op** here — it governs *expression* excess precision, not the x87 store/convert, so it builds but doesn't change the result (verify per the "prove a flag fix is effective" HARD RULE above — diff the failing test output, don't assume). (b) **`-mfpmath=sse -msse2` is unsafe**: the openSUSE i586 baseline has **no SSE2** (`Optflags: i586` carries no `-march`, so it's the `i586-suse-linux`/Pentium default; the separate `i686` line is also pre-SSE2), so forcing SSE2 makes the library **SIGILL on the very hardware the i586 port targets**. The correct fix is to make the *test* representation-robust — **narrow both sides back to `float`** (cast, or the project's `asFloat()`/`toFloat()` accessor) so they collapse to the same value on every arch; this keeps the round-trip check and is upstreamable. **Verifying an i586 fix from a non-x86 host:** local `osc build … i586` runs under QEMU user emulation and is slow/flaky (package-cache `hdrmd5` mismatch loops); instead commit to the devel project and watch the **server-side** i586 result, confirming the specific test now passes, *before* the Factory SR. (Real case: jsoncpp 1.9.8 — `ValueTest/objects` compared `0.12345f` exactly against the stored double; patched to `numericFound->asFloat()`, server i586 went green, submitted upstream as gh#open-source-parsers/jsoncpp#1700.)
287288
- **The C++ analogue: a dependency's headers may newly *require* C++17, while the project's own code needs an older standard — and C++17 library names can clash with the project's identifiers.** Current **googletest** headers `#error` on `< C++17`; building the test suite then forces C++17 on code that may use constructs C++17 removed or names it now defines. Set `CMAKE_CXX_STANDARD 17` (or `-std=gnu++17`) for the whole package, and fix clashes by qualifying: under `using namespace std;` + `using namespace itpp;`, a call to the project's `any(...)` became ambiguous with C++17's `std::any` → qualify as `itpp::any(...)`. (Real case: itpp — re-introduced to Factory after this fixed the FTBFS that got it bot-deleted.)
288289
- **A big version jump can cross an upstream *build-system migration* — budget for rewriting `%build`/`%check`/`%install` and the whole `BuildRequires` set, not just bumping `Version`.** The common case is **Autotools → CMake**: replace `autoreconf`/`%configure`/`%make_build` with `%cmake`/`%cmake_build` (add `BuildRequires: cmake` + `ninja` and `%define __builder ninja`), `make check` → `%ctest`, `%make_install` → `%cmake_install`, and **drop `autoconf`/`automake`/`libtool`**. Map the old `--with-foo`/`--enable-foo` switches to the new `-DWITH_FOO=`/`-DBUILD_FOO=`/`-DENABLE_FOO=` options — read them from `option()`/`cmake_dependent_option()` in the top `CMakeLists.txt` (don't guess names). The same jump often **removes whole features** (and thus their deps) — read the release notes / `doc/dependencies.md`: e.g. bitcoin 28→31 dropped BerkeleyDB (legacy wallet), miniupnpc (UPnP), protobuf+openssl (BIP70), so `libdb-4_8-devel`/`libminiupnpc-devel`/`pkgconfig(protobuf)`/`pkgconfig(openssl)` all became dead BRs. A **Qt5 → Qt6** migration rides along similarly: take the required modules from the upstream `find_package(Qt6 …COMPONENTS…)` list, express them as `pkgconfig(Qt6Core)` … (with the upstream min version, e.g. `>= 6.2`); `lrelease`/`Qt6LinguistTools` comes from a *separate* `qt6-linguist-devel` (no pkgconfig form, like its qt5 sibling); and **rename the GUI subpackage** (`foo-qt5` → `foo-qt6`) with **`Obsoletes: foo-qt5 < %{version}-%{release}` only — do NOT add a matching `Provides: foo-qt5`.** The `Obsoletes` carries the upgrade (the new package replaces the installed `foo-qt5`), but a `Provides: foo-qt5` would be a *lie*: the Qt6 build does not provide the "qt5" capability, and falsely advertising it could wrongly satisfy something that genuinely wants the qt5 GUI. (Real case: a Factory reviewer flagged exactly this on the bitcoin 31.0 SR — *"that is likely a lie… the qt5 capability is not available; only keep the obsoletes"*.) This is the general rule for a **flavor/toolkit rename**: `Provides` of the old name is only honest when the new package is a true drop-in providing the same capability; for a Qt5→Qt6 (or similar) change it isn't, so `Obsoletes`-only. Disable optional new subsystems that drag in heavy new deps unless wanted (bitcoin's `-DENABLE_IPC=OFF` avoids Cap'n Proto).
289290
- **For a hard repackaging problem (build-system migration, install-layout change, soname-scheme change), consult how OTHER distros already package it before inventing a solution.** Pull the recipe from a fast-moving distro: Arch `PKGBUILD` (`https://gitlab.archlinux.org/archlinux/packaging/packages/<pkg>/-/raw/main/PKGBUILD`), AUR (`https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=<pkg>`), Alpine `APKBUILD`, Fedora (`https://src.fedoraproject.org/rpms/<pkg>/raw/rawhide/f/<pkg>.spec`), Debian. They've usually already solved the same wall, and their `prepare()`/`build()`/`package()` shows the canonical patch or install-rearrange. (Real case: vapoursynth R74+ switched to a **wheel-only meson build** that installs everything into Python `site-packages`; Arch's PKGBUILD showed the approach — build the wheel and symlink/redirect the libs/headers/pkgconfig into FHS, with the proper upstream SONAME `libvapoursynth.so.4`.) **Corollary:** if the package is *not* packaged by any of them (e.g. it's normally vendored into a larger project), then **upstream's own files are authoritative** — follow upstream's `*.service`/install layout/defaults rather than guessing. (Real case: nqptp is bundled into shairport-sync everywhere, not packaged standalone — so upstream's `DynamicUser` service file was the reference, doubly trustworthy because the same author maintains both ends.)

0 commit comments

Comments
 (0)