# BST-TIMEOUT: 180 . "$BST_LIB" # libeigen 3.4.0 (SRCREV 3147391d, the 3.4 branch) -- Eigen, the C++ template # library for linear algebra. Header-only: the port ships 336 files under # Eigen/, 190 more under unsupported/Eigen/, eigen3.pc and the # find_package(Eigen3) cmake package. No archive, no shared object, no CLI. # # WHAT IS ACTUALLY TRUE OF THIS PORT, and it is the OPPOSITE of what its two # batch siblings found. cli11 (76-cli11.test) and exprtk (81-exprtk.test) ship # headers that CANNOT be compiled on this arm: both are ordinary C++ written # for a toolchain with exceptions, and both have to assert their own failure. # libcereal (81-libcereal.test) sits between, with a working core and four # unusable archives. Eigen has no such boundary anywhere: it compiles clean, # it runs, and target code can be built against it today. # # The reason is one line of upstream's own design rather than anything we did. # Eigen routes every throw through EIGEN_THROW / EIGEN_TRY / EIGEN_CATCH # (Eigen/src/Core/util/Macros.h:1391-1408) and Macros.h:1385 defines # EIGEN_EXCEPTIONS only when __EXCEPTIONS or _CPPUNWIND is set -- so the # COMPILER'S state turns the guard off by itself and EIGEN_THROW becomes # std::abort(). Nothing is passed to arrange that; the recipe carries no # exceptions switch at all. The single raw `throw std::bad_alloc()` in the tree # (Memory.h:70) sits inside that same #ifdef. # # SECTION 3 IS THE TRIPWIRE FOR THAT, and it is the case to read first if this # suite ever goes red. It asserts the macro state the numbers rest on. A red # there means the toolchain's exceptions state changed under the port -- which # is not necessarily bad news, but every number below was measured under # exceptions-off and would have to be re-measured. # # WHY THERE IS A PROBE. A header-only port ships bytes NO COMPILER ON THIS ARM # NEED EVER HAVE LOOKED AT, so "the files are present" is not merely thin -- # it is compatible with headers that do not compile, and for a SOLVER it is # also compatible with headers that compile and return confident wrong numbers. # The recipe therefore builds libeigen-probe from ${S} during the port, which # makes do_compile the port's first consumer, and the probe RUNS here. The two # halves answer different questions: # # * sections 1-2 assert the PAYLOAD -- that the headers arrived, that the # integration surface resolves, and that every version the port states # agrees with every other; # * sections 3-5 assert the LIBRARY -- that it compiles, that it computes, # and that what it computes on wasm32 is what the same source computes on # x86-64. # # THE NUMERIC COMPARISON IS BYTE-EXACT AND DELIBERATELY NOT A TOLERANCE. Every # reference below is the stdout of the probe's own source compiled with g++ on # x86-64 and run there, at %.12e. That it can be byte-exact at all is a # measured fact rather than a hope: on x86-64 the same source produces # identical output at vectorized, non-vectorized, -O0, -O2 and -Os, and does so # even at %.17e -- full double round-trip precision. So these decompositions # are bit-deterministic under codegen, the non-vectorized arm is the closest # analogue to wasm32, and a divergence HERE is a real arm finding to attribute # rather than summation order to widen a window around. # # Section 5's residual cases are the separate question a byte comparison cannot # ask -- whether the answer is CORRECT -- and are tolerance-based because that # is what a residual is. Both have to hold. # # Style follows 81-libcereal and 81-rapidjson: the probe is run ONCE and its # output parsed in a single fork-free loop, because the runner's per-suite # bound is a budget in process spawns rather than in cases. inc=/usr/include/eigen3 cm=/usr/share/eigen3/cmake PT=/usr/libexec/libeigen-tests # --------------------------------------------------------------------------- # 1. The payload. check libeigen/inc-dir test -d "$inc" check libeigen/eigen-dir test -d "$inc/Eigen" check libeigen/src-dir test -d "$inc/Eigen/src" check libeigen/unsupported-dir test -d "$inc/unsupported/Eigen" check libeigen/signature-file test -f "$inc/signature_of_eigen3_matrix_library" # The module headers a consumer actually writes #include against. # Dense is the one the probe uses; the rest are asserted because the port # installs upstream's tree whole and a truncated install is what this catches. for m in Core Dense Eigen Cholesky Eigenvalues Geometry Householder Jacobi LU QR SVD \ Sparse SparseCore SparseCholesky SparseLU SparseQR StdVector StdDeque StdList \ IterativeLinearSolvers OrderingMethods QtAlignedMalloc; do check "libeigen/module-$m" test -f "$inc/Eigen/$m" done # Counted apart, because the two trees move independently: Eigen/ is the # library proper, unsupported/ is upstream's incubator and is installed by its # OWN CMakeLists rather than by the top-level install(DIRECTORY Eigen ...) -- # so a change that drops it would be invisible in the main tree's count. ne=$(find "$inc/Eigen" -type f 2>/dev/null | grep -c .) nu=$(find "$inc/unsupported" -type f 2>/dev/null | grep -c .) check_eq libeigen/eigen-file-count "336" "$ne" check_eq libeigen/unsupported-file-count "190" "$nu" # --------------------------------------------------------------------------- # 2. The integration surface, and the versions agreeing with each other. # # Three independent statements of the version ship in this package -- the # header macros, the .pc and the cmake package -- and a port that got one of # them wrong would still pass any existence check. The recipe is # libeigen_3.4.0.bb, so 3.4.0 is what all three must say. (The port ticket said # 3.4.1; meta-openembedded at this layer pin carries 3.4.0, and these cases are # what settle which number is the measured one.) check libeigen/pc-file test -f /usr/share/pkgconfig/eigen3.pc check libeigen/cmake-dir test -d "$cm" check libeigen/cmake-config test -f "$cm/Eigen3Config.cmake" check libeigen/cmake-version test -f "$cm/Eigen3ConfigVersion.cmake" check libeigen/cmake-targets test -f "$cm/Eigen3Targets.cmake" check libeigen/cmake-use test -f "$cm/UseEigen3.cmake" macros=$(cat "$inc/Eigen/src/Core/util/Macros.h" 2>/dev/null) case "$macros" in *"#define EIGEN_WORLD_VERSION 3"*) w=3 ;; *) w=? ;; esac case "$macros" in *"#define EIGEN_MAJOR_VERSION 4"*) j=4 ;; *) j=? ;; esac case "$macros" in *"#define EIGEN_MINOR_VERSION 0"*) n=0 ;; *) n=? ;; esac check_eq libeigen/header-version "3.4.0" "$w.$j.$n" pc=$(cat /usr/share/pkgconfig/eigen3.pc 2>/dev/null) case "$pc" in *"Version: 3.4.0"*) pv=3.4.0 ;; *) pv=absent ;; esac check_eq libeigen/pc-version "3.4.0" "$pv" # The include path a consumer compiles against must be the SHIPPED one, not a # build-tree path -- the failure a bare existence check cannot see. case "$pc" in *"-I\${prefix}/include/eigen3"*) pi=ok ;; *) pi="$pc" ;; esac check_eq libeigen/pc-cflags "ok" "$pi" cfgv=$(cat "$cm/Eigen3ConfigVersion.cmake" 2>/dev/null) case "$cfgv" in *'set(PACKAGE_VERSION "3.4.0")'*) cv=3.4.0 ;; *) cv=absent ;; esac check_eq libeigen/cmake-pkg-version "3.4.0" "$cv" cfg=$(cat "$cm/Eigen3Config.cmake" 2>/dev/null) case "$cfg" in *'${PACKAGE_PREFIX_DIR}/include/eigen3'*) ci=relative ;; *) ci=other ;; esac check_eq libeigen/cmake-include-dir "relative" "$ci" # --------------------------------------------------------------------------- # 3-5. The probe. Run ONCE; everything below reads the captured output. if [ ! -x "$PT/libeigen-probe" ]; then bst_skip libeigen/probe-present "libeigen-probe not installed on this image" exit 0 fi bst_ok libeigen/probe-present # The probe's size is a functional property on this target, not trivia: the # guest stages an exec image as a segment list capped at WASM32_IMG_MAX_SEGS, # so a large image is refused -ENOMEM on a fragmented heap before it runs # (nopoll measured exactly that at 41 MB). Read against this arm's floor rather # than on its own -- an empty main() costs ~2.3 MB here, glibc and libc++ # merged whole, so most of this is baseline and not Eigen. 8 MB is far above # what the port stages (~2.9 MB) and far below where the cap bites. sz=$(( $(wc -c < "$PT/libeigen-probe") )) if [ "$sz" -lt 8388608 ]; then bst_ok libeigen/probe-size else bst_notok libeigen/probe-size "probe is $sz bytes, over the 8388608 ceiling"; fi out=$("$PT/libeigen-probe" 2>&1); rc=$? # Parse every P- line in ONE pass. No forks in the loop. P_CXXSTD=; P_EXC=; P_EIGEXC=; P_VER=; P_PTR=; P_IDX=; P_VEC= P_SOLVE_LU=; P_SOLVE_LLT=; P_SOLVE_QR=; P_DET=; P_EIGVALS=; P_QR_RDIAG= P_GEN_SOLVE=; P_RANK=; P_EIGINFO=; P_RESULT=; P_FAILURES=; P_SKIPPED=; P_CHECKS= P_BITS_LU=; P_BITS_EIG=; P_BITS_QR= oIFS=$IFS; IFS=' ' for line in $out; do tag=${line%% *}; val=${line#* } case "$tag" in P-CXXSTD) P_CXXSTD=$val ;; P-EXCEPTIONS) P_EXC=$val ;; P-EIGEN-EXCEPTIONS) P_EIGEXC=$val ;; P-VERSION) P_VER=$val ;; P-PTR-BITS) P_PTR=$val ;; P-INDEX-BITS) P_IDX=$val ;; P-VECTORIZE) P_VEC=$val ;; P-SOLVE-LU) P_SOLVE_LU=$val ;; P-SOLVE-LLT) P_SOLVE_LLT=$val ;; P-SOLVE-QR) P_SOLVE_QR=$val ;; P-DET) P_DET=$val ;; P-EIG-INFO) P_EIGINFO=$val ;; P-EIGVALS) P_EIGVALS=$val ;; P-QR-RDIAG) P_QR_RDIAG=$val ;; P-GEN-SOLVE) P_GEN_SOLVE=$val ;; P-RANK) P_RANK=$val ;; P-BITS-SOLVE-LU) P_BITS_LU=$val ;; P-BITS-EIGVALS) P_BITS_EIG=$val ;; P-BITS-QR-RDIAG) P_BITS_QR=$val ;; P-CHECKS) P_CHECKS=$val ;; P-SKIPPED) P_SKIPPED=$val ;; P-FAILURES) P_FAILURES=$val ;; P-RESULT) P_RESULT=$val ;; esac done IFS=$oIFS # --------------------------------------------------------------------------- # 3. The compile-time state every number below rests on. THE TRIPWIRE. # # P-EXCEPTIONS 0 says the distro's C++ driver compiled the probe with # exceptions off; P-EIGEN-EXCEPTIONS 0 says Eigen's own guard followed it, so # EIGEN_THROW is std::abort() rather than a throw. A red on either means the # toolchain moved and the numeric references need re-measuring, not that Eigen # broke. check_eq libeigen/cxx-exceptions-off "0" "$P_EXC" check_eq libeigen/eigen-exceptions-off "0" "$P_EIGEXC" check_eq libeigen/probe-cxxstd "201103" "$P_CXXSTD" # The version the COMPILER saw, which is the one that matters for the numbers # -- section 2 asserted the version the file system reports. check_eq libeigen/probe-version "3.4.0" "$P_VER" # A 32-bit arm. Eigen::Index follows the pointer here, and a mismatch would # mean an indexing type the references were not measured under. check_eq libeigen/probe-ptr-bits "32" "$P_PTR" check_eq libeigen/probe-index-bits "32" "$P_IDX" # Recorded rather than required: wasm32 has no Eigen SIMD backend, so this is # expected 0. It is asserted because it is an INPUT to the numeric comparison # -- the x86-64 reference was confirmed identical with and without # vectorization, so a change here does not invalidate the references, but it # does change what the probe is exercising and a reader should be told. check_eq libeigen/probe-vectorize "0" "$P_VEC" # --------------------------------------------------------------------------- # 4. The numbers, against the x86-64 reference. Byte-exact at %.12e. check_eq libeigen/solve-lu \ "3.714285714286e-01 1.352380952381e+00 2.219047619048e+00 1.771428571429e+00" "$P_SOLVE_LU" check_eq libeigen/solve-llt \ "3.714285714286e-01 1.352380952381e+00 2.219047619048e+00 1.771428571429e+00" "$P_SOLVE_LLT" check_eq libeigen/solve-qr \ "3.714285714286e-01 1.352380952381e+00 2.219047619048e+00 1.771428571429e+00" "$P_SOLVE_QR" # An integer matrix has an integer determinant, so this one SHOULD be exact on # any arm that computes it correctly -- a divergence here is arithmetic rather # than rounding, which makes it the most diagnostic case in the section. check_eq libeigen/determinant "1.050000000000e+02" "$P_DET" check_eq libeigen/eigenvalues \ "1.585786437627e+00 1.837722339832e+00 4.414213562373e+00 8.162277660168e+00" "$P_EIGVALS" check_eq libeigen/qr-rdiag \ "-3.741657386774e+00 -3.035504194411e+00 -3.354968547317e+00 -2.099455524326e-01" "$P_QR_RDIAG" check_eq libeigen/general-solve \ "1.200000000000e+01 -2.750000000000e+00 1.750000000000e+00 -6.750000000000e+00" "$P_GEN_SOLVE" # SelfAdjointEigenSolver::info() == Success. check_eq libeigen/eigensolver-info "0" "$P_EIGINFO" # Rank of a matrix whose third row is the sum of the first two. A rank routine # that ignores its threshold answers 3. check_eq libeigen/rank-deficient "2" "$P_RANK" # If -- and only if -- one of the numeric cases above diverged, put the # IEEE-754 bit patterns of the compared vectors on the console. The probe # prints them on every run, but a suite that swallowed its stdout would leave a # reader with a decimal delta and no way to see how many ULPs it actually was # without rebuilding the image and booting again. Emitting them here costs # nothing on a green run and is the whole difference between "re-measure this" # and "read it off the transcript" on a red one. if [ "$P_SOLVE_LU $P_EIGVALS $P_QR_RDIAG" != \ "3.714285714286e-01 1.352380952381e+00 2.219047619048e+00 1.771428571429e+00 1.585786437627e+00 1.837722339832e+00 4.414213562373e+00 8.162277660168e+00 -3.741657386774e+00 -3.035504194411e+00 -3.354968547317e+00 -2.099455524326e-01" ]; then echo "libeigen-divergence solve-lu-bits $P_BITS_LU" echo "libeigen-divergence eigvals-bits $P_BITS_EIG" echo "libeigen-divergence qr-rdiag-bits $P_BITS_QR" echo "libeigen-divergence x86-64-ref-lu 3fd7c57c57c57c56 3ff5a35a35a35a35 4001c09c09c09c0a 3ffc57c57c57c57d" echo "libeigen-divergence x86-64-ref-eig 3ff95f619980c437 3ffd674f8a4b4959 4011a827999fcef3 402053160eb696d7" echo "libeigen-divergence x86-64-ref-qr c00deeea11683f49 c00848b66c4eec2e c00ad6f9bfeeb10a bfcadf7ef0d1beaf" fi # --------------------------------------------------------------------------- # 5. The probe's own verdict, which covers the residual checks section 4 # cannot express as strings -- Ax-b, QR reconstruction and AV-VD, each # against 1e-12. # # P-SKIPPED must be 0: the probe reports UNREFERENCED rather than OK if any # reference was left empty, so this is what stops a probe passing by having # forgotten to carry the numbers it compares against. check_eq libeigen/probe-no-unreferenced "0" "$P_SKIPPED" check_eq libeigen/probe-failures "0" "$P_FAILURES" check_eq libeigen/probe-result "OK" "$P_RESULT" check_eq libeigen/probe-exit "0" "$rc"