fix(build): anchor pdf_oxide version gate against suffix versions (#19152)

This commit is contained in:
xugangqiang authored and GitHub committed 2026-09-11 16:43:12 +08:00
1 parent a11f9258a2
commit 60f3347ba6
2 files changed
+155 -20

No files matched your search

+79 -20
View File
@@ -238,6 +238,47 @@ check_pdfium_deps() {
}
# Check pdf_oxide static library.
# pdf_oxide_validate_version <pool_text> <required_version>
#
# Validates the pdf_oxide version embedded in a lib's string constant pool
# against the pinned <required_version>. The marker "pdf_oxide <version>" is
# merged into Rust's constant pool and has no fixed length, so a suffixed build
# (e.g. 0.3.73.1) and a clean 0.3.73 whose next pooled constant begins with a
# digit are byte-identical and cannot be told apart.
#
# Capture exactly three dotted segments and reject any trailing [0-9.] as
# ambiguous, rather than silently truncating it into a (wrong) match.
#
# Returns:
# 0 exact match (prints the found version)
# 1 version missing or mismatched (prints the found version, or empty)
# 2 ambiguous: the marker is followed by extra digits/dots in the pool
pdf_oxide_validate_version() {
local pool_text="$1" required="$2"
local found esc
found=$(printf '%s\n' "$pool_text" \
| grep -oE "pdf_oxide [0-9]+\.[0-9]+\.[0-9]+" | head -1 | cut -d' ' -f2)
if [ -z "$found" ]; then
return 1
fi
# A digit or dot immediately after the captured "pdf_oxide X.Y.Z" marker
# means a suffixed build (0.3.73.1) or a constant concatenated on to the
# version. Treat it as ambiguous rather than as a wrong version.
# Anchor to the exact captured version (dots escaped) so the third segment
# cannot backtrack and swallow the trailing digit, which would otherwise
# flag a clean 0.3.73<letter> marker as ambiguous.
esc="${found//./\\.}"
if printf '%s\n' "$pool_text" | grep -qE "pdf_oxide ${esc}[0-9.]"; then
return 2
fi
if [ "$found" != "$required" ]; then
printf '%s\n' "$found"
return 1
fi
printf '%s\n' "$found"
return 0
}
check_pdf_oxide_deps() {
_seed_from_system "pdf_oxide" || true
# Map platform to tarball-internal subdirectory.
@@ -268,25 +309,39 @@ check_pdf_oxide_deps() {
# lib left over from an earlier pin is reused silently and the upgrade
# becomes a no-op.
#
# The marker here differs from office_oxide: instead of a standalone
# "0.1.9" line it is "pdf_oxide <version>" merged into Rust's string
# constant pool, so a whole-line match cannot be used. Extract the
# version and compare it exactly — a substring match would let a pin of
# "0.3.7" accept a 0.3.73 lib. The "pdf_oxide " prefix keeps bare
# version numbers of vendored dependencies out of the match.
local found_version
found_version=$(strings "$lib_path" 2>/dev/null \
| grep -oE "pdf_oxide [0-9]+\.[0-9]+\.[0-9]+" | head -1 | cut -d' ' -f2)
if [ "$found_version" != "$PDF_OXIDE_VERSION" ]; then
echo -e "${RED}Error: pdf_oxide native lib version mismatch${NC}"
echo " Required: v${PDF_OXIDE_VERSION}; found: ${found_version:-unknown}"
echo " A stale lib silently reverts PDF parsing fixes. Refresh:"
echo " rm -rf ${PDF_OXIDE_PREFIX} ragflow_deps/pdf_oxide-go-ffi-linux-amd64.tar.gz"
echo " uv run python3 ragflow_deps/download_go_deps.py"
return 1
fi
echo " pdf_oxide (static) → ${PDF_OXIDE_PREFIX}"
return 0
# The version marker "pdf_oxide <version>" is merged into Rust's string
# constant pool (unlike office_oxide's standalone "0.1.9" line), so we
# capture exactly three dotted segments and reject any trailing [0-9.]
# as ambiguous — a suffixed build (0.3.73.1) or a constant that is
# byte-identical to one must not be silently accepted. The "pdf_oxide "
# prefix keeps bare version numbers of vendored dependencies out of the
# match.
local pool_text found_version rc
pool_text=$(strings "$lib_path" 2>/dev/null)
found_version=$(pdf_oxide_validate_version "$pool_text" "$PDF_OXIDE_VERSION"); rc=$?
case "$rc" in
0)
echo " pdf_oxide (static) → ${PDF_OXIDE_PREFIX}"
return 0
;;
2)
echo -e "${RED}Error: pdf_oxide native lib version ambiguous${NC}"
echo " The version marker is followed by extra digits/dots in the"
echo " string pool; this is usually a suffixed build (e.g. ${PDF_OXIDE_VERSION}.1)"
echo " or a concatenated constant byte-identical to one. Refresh the lib"
echo " to a clean pin."
echo " Required: v${PDF_OXIDE_VERSION}"
return 1
;;
*)
echo -e "${RED}Error: pdf_oxide native lib version mismatch${NC}"
echo " Required: v${PDF_OXIDE_VERSION}; found: ${found_version:-unknown}"
echo " A stale lib silently reverts PDF parsing fixes. Refresh:"
echo " rm -rf ${PDF_OXIDE_PREFIX} ragflow_deps/pdf_oxide-go-ffi-linux-amd64.tar.gz"
echo " uv run python3 ragflow_deps/download_go_deps.py"
return 1
;;
esac
fi
echo " pdf_oxide (static) not found"
@@ -998,4 +1053,8 @@ main() {
esac
}
main "$@"
# Only run the build when executed directly. When sourced (e.g. by tests),
# skip main so the version-gate helpers can be unit-tested in isolation.
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
main "$@"
fi
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env bash
# TDD test for the pdf_oxide native-lib version gate in build.sh.
#
# The version marker is embedded in Rust's string constant pool as
# "pdf_oxide <version>" and cannot be anchored to a fixed length: a suffixed
# build (0.3.73.1) and a clean 0.3.73 whose next pooled constant begins with a
# digit are byte-identical. So we capture exactly three dotted segments and
# treat any trailing [0-9.] as ambiguous rather than silently truncating it
# into a (wrong) match.
#
# Run: bash test/build_pdf_oxide_version.sh
set -u
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=../build.sh
source "${SCRIPT_DIR}/../build.sh"
# build.sh sets 'set -e'; the test must control its own flow.
set +e
failures=0
# assert_status <desc> <expected_rc> <actual_rc> [<expected_out>] [<actual_out>]
assert_status() {
local desc="$1" expected_rc="$2" actual_rc="$3" expected_out="${4:-}" actual_out="${5:-}"
if [ "$expected_rc" != "$actual_rc" ]; then
echo "FAIL: $desc (expected rc=$expected_rc, got rc=$actual_rc)"
failures=$((failures + 1))
return
fi
if [ -n "$expected_out" ] && [ "$expected_out" != "$actual_out" ]; then
echo "FAIL: $desc (expected out=[$expected_out], got out=[$actual_out])"
failures=$((failures + 1))
return
fi
echo "PASS: $desc"
}
# Helper that runs pdf_oxide_validate_version and captures rc + stdout.
run_validate() {
local pool="$1" required="$2" out rc
out=$(pdf_oxide_validate_version "$pool" "$required"); rc=$?
printf '%s\t%s' "$rc" "$out"
}
# 1. Exact three-segment match passes (rc 0, prints version).
read -r rc out < <(run_validate "pdf_oxide 0.3.73ForPublicRelease" "0.3.73")
assert_status "exact 0.3.73 match" 0 "$rc" "0.3.73" "$out"
# 2. Suffixed build 0.3.73.1 is ambiguous (rc 2), never silently accepted.
read -r rc out < <(run_validate "pdf_oxide 0.3.73.1" "0.3.73")
assert_status "suffix 0.3.73.1 is ambiguous" 2 "$rc"
# 3. A version with an extra appended digit (0.3.731) must be rejected as a
# mismatch (rc 1), NOT silently accepted as 0.3.73.
read -r rc out < <(run_validate "pdf_oxide 0.3.731foobar" "0.3.73")
assert_status "digit-appended 0.3.731 mismatch" 1 "$rc"
# 4. Truncated pin 0.3.7 must mismatch against a 0.3.73 lib (rc 1).
read -r rc out < <(run_validate "pdf_oxide 0.3.73" "0.3.7")
assert_status "truncated pin 0.3.7 mismatch" 1 "$rc"
# 5. Wrong three-segment version mismatches (rc 1).
read -r rc out < <(run_validate "pdf_oxide 0.3.72" "0.3.73")
assert_status "wrong version 0.3.72 mismatch" 1 "$rc"
# 6. No version marker at all mismatches (rc 1).
read -r rc out < <(run_validate "unrelated string pool" "0.3.73")
assert_status "missing marker mismatch" 1 "$rc"
if [ "$failures" -eq 0 ]; then
echo "ALL TESTS PASSED"
exit 0
else
echo "$failures TEST(S) FAILED"
exit 1
fi