. "$BST_LIB" # json-c is a reference-counting JSON library for C: json_tokener parses a # document into a tree of json_object nodes, typed accessors walk and edit it, # and json_object_to_json_string prints it back plain or pretty. 0.17 carries # JSON Pointer (RFC 6901) and JSON Patch (RFC 6902) INSIDE the same library -- # the structural difference from cjson next door, which ships those as a second # archive, and the reason this suite has one archive to assert where 81-cjson # has two. # # The distro is static-only and json-c has no CLI of its own. Upstream builds # apps/json_parse but installs nothing from it -- "it is intentional that there # are no install instructions here yet" -- so there is no binary to register # against even upstream, and the bbappend turns that build off rather than pay a # wasm32 link for a program nobody installs. The whole shipped payload is # therefore sixteen headers, one pkg-config file and the find_package(json-c) # cmake files (-dev) plus the archive libjson-c.a (-staticdev). With no consumer # on the image and no on-target C compiler, every case asserts the shipped # artifact, in the shape the header-and-archive libraries before it established. # # What keeps that from being a file listing is the completeness check below: it # reads EVERY function the INSTALLED headers declare through JSON_EXPORT and # requires each one in the archive, so a partial compile, a visibility mistake or # a header/library skew all fail it. The assertion tracks the headers rather than # a list written here, which is what makes it survive a version bump. # # --------------------------------------------------------------------------- # WHY THIS SUITE USES BUILTIN MATCHING INSTEAD OF check_out ... cat # # The files are read ONCE into shell variables and every text assertion is a # `case` glob against them -- no fork per case -- and the archive symbol lookup # is a single batched grep whose result is matched the same way. The cjson suite # next door was killed by the runner's 60s per-suite bound (BST_SUITE_TIMEOUT) # at 156 cases when written as check_out+cat: ~200 cases is ~400 process spawns, # and on the wasm32-hwjs runtime the forking IS the cost, not the work. # --------------------------------------------------------------------------- incd=/usr/include/json-c pc=/usr/lib/pkgconfig/json-c.pc lib=/usr/lib/libjson-c.a cm=/usr/lib/cmake/json-c nl=' ' # hasfile -- ok iff is a non-empty regular file. # has -- ok iff contains the literal . # hasline -- ok iff carries as a WHOLE line. # Substring matching would let json_pointer_getf # satisfy a lookup for json_pointer_get, and # json_object_get_string satisfy json_object_get. hasfile() { if [ -s "$2" ]; then bst_ok "$1"; else bst_notok "$1" "absent or empty: $2"; fi; } has() { case "$3" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "not found: $2" ;; esac; } hasline() { case "$nl$3$nl" in *"$nl$2$nl"*) bst_ok "$1" ;; *) bst_notok "$1" "not in archive: $2" ;; esac; } # --- the sixteen installed headers ------------------------------------------- for h in arraylist debug json json_c_version json_config json_inttypes \ json_object json_object_iterator json_patch json_pointer json_tokener \ json_types json_util json_visit linkhash printbuf; do hasfile "json-c/header-$h" "$incd/$h.h" done # json.h is the umbrella "may be included instead of other individual ones" # header, and it is GENERATED from json.h.cmakein: the json_pointer.h and # json_patch.h lines are substituted in only when DISABLE_JSON_POINTER and # DISABLE_JSON_PATCH are off. Asserting them here is therefore a check that the # port kept RFC 6901/6902 support, not just that a file exists. umb=$(cat "$incd/json.h" 2>/dev/null) for h in arraylist.h debug.h json_c_version.h json_object.h \ json_object_iterator.h json_patch.h json_pointer.h json_tokener.h \ json_util.h linkhash.h; do has "json-c/umbrella-$h" "#include \"$h\"" "$umb" done # --- version, pinned in three places that must agree ------------------------- verh=$(cat "$incd/json_c_version.h" 2>/dev/null) has json-c/version-major "#define JSON_C_MAJOR_VERSION 0" "$verh" has json-c/version-minor "#define JSON_C_MINOR_VERSION 17" "$verh" has json-c/version-micro "#define JSON_C_MICRO_VERSION 0" "$verh" has json-c/version-string '#define JSON_C_VERSION "0.17"' "$verh" # json_config.h is written by the cmake configure probes, so it is the one # header that records what the cross compile actually found rather than what # upstream shipped. cfg=$(cat "$incd/json_config.h" 2>/dev/null) has json-c/config-inttypes "JSON_C_HAVE_INTTYPES_H 1" "$cfg" has json-c/config-stdint "JSON_C_HAVE_STDINT_H 1" "$cfg" # --- pkg-config --------------------------------------------------------------- hasfile json-c/pc-present "$pc" pct=$(cat "$pc" 2>/dev/null) has json-c/pc-name "Name: json-c" "$pct" has json-c/pc-version "Version: 0.17" "$pct" has json-c/pc-libs "-ljson-c" "$pct" has json-c/pc-cflags '-I${includedir}/json-c' "$pct" # --- find_package(json-c) cmake package -------------------------------------- hasfile json-c/cmake-config "$cm/json-c-config.cmake" hasfile json-c/cmake-targets "$cm/json-c-targets.cmake" # The per-configuration targets file is named for CMAKE_BUILD_TYPE, so it is # matched by glob rather than by a name this suite would have to keep in step # with the distro's build type. set -- "$cm"/json-c-targets-*.cmake if [ -s "$1" ]; then bst_ok json-c/cmake-targets-config else bst_notok json-c/cmake-targets-config "no json-c-targets-.cmake in $cm"; fi # The exported target must name the ARCHIVE. If the static selection were ever # lost, this is where find_package() would start handing consumers a .so. tgt=$(cat "$cm"/json-c-targets*.cmake 2>/dev/null) has json-c/cmake-namespace "json-c::json-c" "$tgt" has json-c/cmake-static-archive "libjson-c.a" "$tgt" # --- the archive -------------------------------------------------------------- hasfile json-c/archive-present "$lib" check_eq json-c/archive-format '!' "$(head -c 7 "$lib" 2>/dev/null)" # --- completeness: every declared entry point is in the archive --------------- # # Flatten the archive's printable runs to one token per line first: a raw # `grep -o` over binary data reports "Binary file matches" instead of the # matches, which would silently under-count and read as a partial compile. tmp=${TMPDIR:-/tmp} flat=$tmp/json-c-flat.$$ namef=$tmp/json-c-names.$$ tr -c '[:print:]' '\n' < "$lib" > "$flat" 2>/dev/null # JSON_EXPORT marks every public entry point. Requiring the '(' keeps this to # functions -- json-c also JSON_EXPORTs data symbols such as json_number_chars, # which are not what the archive's symbol table is being asked about here. grep -hoE 'JSON_EXPORT[^;{]*\(' "$incd"/*.h 2>/dev/null \ | sed 's/(.*//' | awk '{print $NF}' | sed 's/^\**//' \ | grep -E '^json' | sort -u > "$namef" # One batched lookup instead of one grep per symbol: -o -F -f reports every # listed name it finds, and hasline below reads that result without forking. found=$(grep -oFf "$namef" "$flat" 2>/dev/null | sort -u) ndecl=$(grep -c . "$namef"); nfound=$(printf '%s' "$found" | grep -c .) # The floor guards the check itself: headers that lost their declarations, or an # extraction that silently matched nothing, would otherwise pass vacuously with # zero names to look up. 0.17 declares 94. if [ "$ndecl" -ge 80 ] && [ "$nfound" -eq "$ndecl" ]; then bst_ok json-c/sym-complete else bst_notok json-c/sym-complete "declared=$ndecl found=$nfound"; fi # A named sample across the layers as well, so a failure names the layer that # broke rather than only reporting a count. for s in json_tokener_new json_tokener_free json_tokener_parse \ json_tokener_parse_ex json_tokener_get_error json_tokener_error_desc \ json_object_new_object json_object_new_array json_object_new_string \ json_object_new_int64 json_object_new_double json_object_new_boolean \ json_object_object_add json_object_object_get_ex json_object_object_length \ json_object_array_add json_object_array_get_idx json_object_array_length \ json_object_get_string json_object_get_int64 json_object_get_double \ json_object_get_type json_object_is_type json_object_to_json_string \ json_object_to_json_string_ext json_object_get json_object_put \ json_object_deep_copy json_object_equal json_object_set_serializer \ json_pointer_get json_pointer_set json_pointer_getf json_patch_apply \ json_c_visit json_c_version json_util_get_last_err json_type_to_name; do hasline "json-c/sym-$s" "$s" "$found" done # The version string json_c_version() returns is compiled in from the same macro # json_c_version.h defines, so finding it in the archive pins the compiled code # to the header rather than only to the .pc. a=$(cat "$flat" 2>/dev/null) has json-c/lib-version-string "0.17" "$a" rm -f "$flat" "$namef" # --- static-only guards ------------------------------------------------------- # # The stock recipe leaves json-c's own default of BUILD_SHARED_LIBS=ON, and on # this arm that does not merely fail to link -- it stops cmake's GENERATE step, # because the install rule for a shared target needs an RPATH edit that has no # meaning for a wasm side module. A stray .so or .la here would mean the # bbappend's static selection was lost. noglob_ok() { # ok iff nothing matched the glob already expanded into "$@" _id=$1; shift if [ ! -e "$1" ]; then bst_ok "$_id"; else bst_notok "$_id" "present: $1"; fi } set -- /usr/lib/libjson-c.so*; noglob_ok json-c/no-shared "$@" set -- /usr/lib/libjson-c.la; noglob_ok json-c/no-libtool "$@" # apps/json_parse is built by default and installed by nobody; BUILD_APPS=OFF # should mean it never reaches the image under any name. set -- /usr/bin/json_parse; noglob_ok json-c/no-apps "$@"