. "$BST_LIB" # MD4C 0.5.2 is a SAX-like Markdown parser in C: md_parse() walks the document # and calls back into the caller for every block, span and run of text, handing # each one back as a (pointer, length) slice INTO the caller's own buffer. It # ships here as two static archives (libmd4c.a, libmd4c-html.a), their headers # and pkg-config files, and the md2html(1) CLI that links them. # # It is the SECOND Markdown implementation on this image, beside cmark (76), and # the two are deliberately numbered together. They are not redundant: cmark is # the CommonMark reference implementation and renders a document tree, while # MD4C never builds a tree at all -- it is a streaming parser whose entire output # is indirect calls through a caller-supplied struct of seven function pointers. # That difference is the reason this suite is shaped the way it is. # # --------------------------------------------------------------------------- # WHY THIS SUITE RUNS A PROBE AS WELL AS THE CLI # # md2html exercises the library end to end and is asserted directly below, but # it only ever passes MD4C *its own* renderer. Two properties that matter on this # arm are invisible from the CLI: # # 1. THE SLICES. Every text run MD4C reports is a pointer into the input plus # a length that MD4C computed. BRINTOS-217 is the precedent: oniguruma # compiled and packaged perfectly on this arm and then returned silently # wrong offsets to every caller. A wrong slice here is not a crash -- it is # quietly wrong output -- and md2html would render it without complaint. # 2. THE CALLBACKS. MD_PARSER is seven stored function pointers. On this # coroutinized arm those are reached through a table: the build log shows # `FPCAST-DYN-MARSHAL fn=md_parse site_sig=Fvii arms=3`, i.e. md_parse's # indirect call sites are dynamically marshalled with an open-world # fallback that aborts LOUDLY on an unresolved target. Whether every arm # dispatches, and whether values come back OUT of them, is exactly what a # caller-supplied renderer measures and md2html does not. # # So data/md4c-probe is cross-linked with wasmcc-glibc against THE VERY ARCHIVES # THAT SHIP (/usr/lib/libmd4c.a, /usr/lib/libmd4c-html.a), staged to $TMPDIR and # run here. Its source is src/md4c_probe.c. EVERY EXPECTED VALUE IN IT IS THE # VALUE A NATIVE x86_64 BUILD OF THE IDENTICAL SOURCE PRODUCES: the probe scored # 186/186 natively before this suite was written, and getting it there corrected # 18 wrong expectations of mine -- so a red below is a platform difference, not a # guess of mine. The probe compares internally and prints its own verdict, and on # a failure prints a DIAG line carrying what it actually got. # # --------------------------------------------------------------------------- # TWO THINGS THE NATIVE CONTROL SETTLED, RECORDED HERE BECAUSE THEY LOOK LIKE # TARGET BUGS AND ARE NOT # # a) MD4C DOES NOT HONOUR ITS OWN ABORT CONTRACT. md4c.h promises that any # rendering callback may abort the parse by returning non-zero and that # md_parse() then returns that value. Measured, on x86_64: only leave_block # keeps the promise. enter_block and enter_span stop the walk but md_parse # still returns 0, and the text callback's return is ignored outright. That # is upstream's defect; the probe pins all four to what they ACTUALLY do so # a change in either direction is caught, and leave_block is the case that # carries the platform signal (a value surviving an indirect call). # # b) SOME SLICES LEGITIMATELY POINT OUTSIDE THE INPUT. MD4C emits line # terminators from the static literal _T("\n") (md4c.c:4472, 4486, 4738) and # the NULLCHAR placeholder from _T(""), so a size-1 "\n"/"\0" is not a slice # of the caller's buffer. The probe counts those separately rather than # exempting a whole text type, which makes the range check SHARPER: every # other byte MD4C reports must lie inside the input. # # --------------------------------------------------------------------------- # Unlike libconfuse (73) the main package is NOT empty here -- md2html gives it # real payload -- so all three of md4c, md4c-dev and md4c-staticdev are named in # the image. # # --------------------------------------------------------------------------- # SHARED LIBRARIES (BRINTOS-881 phase 3; BRINTOS-1415) # # This suite used to say "the distro is static-only, so there is no libmd4c.so # and none is expected". Under BRINTOS_PACKAGE_SHARED that is no longer true: # md4c ships BOTH halves, and md2html binds the shared ones at load. # # md4c is the reference case for BRINTOS-1415 because it is a CHAIN -- # md2html -> libmd4c-html.so -> libmd4c.so -- and the MIDDLE module carries no # needed-list of its own. 78-needed-graph-multi already covers a main naming two # package libraries; nothing covered a library naming another one, which is a # different question: the loader has to resolve libmd4c-html's undefined md_parse # from a module it placed, with nothing in libmd4c-html recording where to look. # # The assertions below establish only the SHAPE. What proves the chain at run # time is the ~25 md4c/cli-* cases further down: md2html cannot render a heading # without md_html() out of libmd4c-html.so, which cannot parse without md_parse() # out of libmd4c.so. Those cases pass identically on a static image, which is why # the shape assertions ship with them -- together they discriminate, separately # neither does. # # Skipped, not failed, where the flip is off: both shapes are legitimate builds. require md2html md4c/present tmp=${TMPDIR:-/tmp}/bst-md4c.$$ mkdir -p "$tmp" 2>/dev/null # --------------------------------------------------------------- payload shape # md4c is cmake, so the flip takes the static half away completely -- measured at # recipe scope with BRINTOS_PACKAGE_SHARED = "1": md4c, md4c-dev, md4c-doc and # md4c-dbg are generated, md4c-staticdev is not, and no .a ships. Both shapes are # asserted; neither is waived. if ls /usr/lib/libmd4c.so.* >/dev/null 2>&1; then check md4c/so-lib-parser test -f /usr/lib/libmd4c.so.0.5.2 check md4c/so-lib-html test -f /usr/lib/libmd4c-html.so.0.5.2 else check md4c/lib-parser test -f /usr/lib/libmd4c.a check md4c/lib-html test -f /usr/lib/libmd4c-html.a fi check md4c/hdr-parser test -f /usr/include/md4c.h # --- shared halves, and the chain's shape (BRINTOS-1415) --------------------- if ! ls /usr/lib/libmd4c.so.* >/dev/null 2>&1; then bst_skip md4c/so-chain "no libmd4c.so.* -- built with BRINTOS_PACKAGE_SHARED off" else bst_ok md4c/so-parser check md4c/so-html test -e /usr/lib/libmd4c-html.so.0 _md=$(command -v md2html) # The main must NAME both, or it merged them and the cli-* cases below prove # nothing about linkage. if grep -aq "wasm\.linux\.needed" "$_md" 2>/dev/null; then bst_ok md4c/so-main-dynamic else bst_notok md4c/so-main-dynamic "md2html carries no wasm.linux.needed -- statically merged"; fi for _l in libmd4c-html libmd4c; do if grep -aq "$_l\.so" "$_md" 2>/dev/null; then bst_ok md4c/so-main-needs-${_l#libmd4c} else bst_notok md4c/so-main-needs-${_l#libmd4c} "md2html does not name $_l.so"; fi done # THE BRINTOS-1415 DISCRIMINATOR. The middle module must carry NO needed list # of its own: if it did, the loader would be following a recorded entry and # the chain would prove nothing beyond what 78-needed-graph-multi shows. _mid=$(ls /usr/lib/libmd4c-html.so.*.* 2>/dev/null | head -1) if [ -n "$_mid" ] && grep -aq "wasm\.linux\.needed" "$_mid" 2>/dev/null; then bst_notok md4c/so-middle-unrecorded "libmd4c-html.so carries a needed list -- the chain is recorded, not resolved" else bst_ok md4c/so-middle-unrecorded fi fi check md4c/hdr-html test -f /usr/include/md4c-html.h check md4c/pc-parser test -f /usr/lib/pkgconfig/md4c.pc check md4c/pc-html test -f /usr/lib/pkgconfig/md4c-html.pc check md4c/cmake-config test -f /usr/lib/cmake/md4c/md4cConfig.cmake check md4c/bin-present test -f /usr/bin/md2html check md4c/bin-executable test -x /usr/bin/md2html # The dev symlink follows the SHAPE, and both shapes are correct builds # (BRINTOS-1619 allow-listed md4c, so the conversion is now an intended outcome # rather than a lost BUILD_SHARED_LIBS=OFF). Keyed off the versioned library the # so-chain block above already keys off, so the two can never disagree. if ls /usr/lib/libmd4c.so.* >/dev/null 2>&1; then check md4c/dev-symlink test -e /usr/lib/libmd4c.so else check_rc md4c/no-shared 1 test -e /usr/lib/libmd4c.so fi # The archives must carry the object files, not just exist. libmd4c-html.a is the # one that carries entity.c -- the ~2100-entry HTML entity table the renderer # searches -- so its absence would be invisible until an &entity; came out wrong. # Read the member names straight out of the ar header rather than unpacking: this # image ships no binutils ar (the first boot scored these rc=127), and a member # name short enough for ar's 16-byte name field is stored as plain text, so a # binary-mode grep sees it. # ar member names exist only in an archive -- a side module has no such header -- # so these three assert the archive shape and are skipped, not failed, on the # converted one. entity.c's ~2100-entry table is still reachable there: it is # asserted by the © case in the CLI section below, which is what the member # name was standing in for. if [ -e /usr/lib/libmd4c.a ]; then check_out md4c/ar-parser "md4c.c.o" grep -a -o 'md4c\.c\.o' /usr/lib/libmd4c.a check_out md4c/ar-html "md4c-html.c.o" grep -a -o 'md4c-html\.c\.o' /usr/lib/libmd4c-html.a check_out md4c/ar-entity "entity.c.o" grep -a -o 'entity\.c\.o' /usr/lib/libmd4c-html.a else bst_skip md4c/ar-members "no /usr/lib/libmd4c.a -- md4c converted, so there is no ar header to read" fi # pkg-config text must describe the TARGET layout, not a build host's. Only # prefix is written absolute; libdir stays the unexpanded ${exec_prefix}/lib, so # the RESOLVED value is what has to be asserted -- ask pkg-config to expand it # rather than grepping the file for a literal that is not in it. check_out md4c/pc-prefix "prefix=/usr" cat /usr/lib/pkgconfig/md4c.pc check_out md4c/pc-libdir "/usr/lib" pkg-config --variable=libdir md4c check_out md4c/pc-lname "-lmd4c" cat /usr/lib/pkgconfig/md4c.pc check_out md4c/pc-html-lname "-lmd4c-html" cat /usr/lib/pkgconfig/md4c-html.pc # --------------------------------------------------------------- md2html(1) check_out md4c/cli-version "0.5.2" md2html --version check_out md4c/cli-help "--github" md2html --help # Core rendering, from a file and from stdin. These are CommonMark, and they are # the same bytes the native control emits. printf '# Title\n\nHello *world*.\n' > "$tmp/a.md" check_out md4c/cli-h1 "
Hello world.
" md2html "$tmp/a.md" out=$(printf '**b**\n' | md2html 2>&1) check_eq md4c/cli-stdin "b
" "$out" # -o writes a file rather than stdout. printf 'x\n' > "$tmp/b.md" md2html -o "$tmp/b.html" "$tmp/b.md" 2>/dev/null check md4c/cli-o-created test -s "$tmp/b.html" check_eq md4c/cli-o-content "x
" "$(cat "$tmp/b.html" 2>/dev/null)" # --full-html wraps the body; --xhtml self-closes void elements. check_out md4c/cli-full-doctype "" md2html --full-html "$tmp/b.md" check_out md4c/cli-full-body "" md2html --full-html "$tmp/b.md" check_out md4c/cli-html-title "MyTitle" md2html --full-html --html-title=MyTitle "$tmp/b.md" printf -- '---\n' > "$tmp/hr.md" check_eq md4c/cli-hr "