@@ -50,12 +50,26 @@ Modes:
5050 warpgrid branch in the source checkout.
5151 --update <tag> Fetch a new upstream tag, attempt to rebase patches onto it,
5252 and report which patches applied/conflicted.
53- --validate Check patch ordering, numbering, and dependency constraints.
53+ --validate Check patch ordering, numbering, dependency constraints,
54+ and structured headers (WarpGrid-Domain/Deps/WIT).
5455 --help Show this help message.
5556
5657Options:
5758 --src <path> Path to wasi-libc source checkout
5859 (default: build/src-patched)
60+ --subset <domain>
61+ Filter --validate to only check patches matching the given
62+ domain (dns, filesystem, socket). Validates that the subset
63+ is self-consistent — all transitive dependencies are satisfied
64+ within the subset.
65+
66+ Patch Headers:
67+ Each patch file must contain these structured headers in the commit message
68+ body (before the --- separator):
69+
70+ WarpGrid-Domain: <dns|filesystem|socket>
71+ WarpGrid-Deps: <comma-separated patch numbers, or "none">
72+ WarpGrid-WIT: <comma-separated WIT interface identifiers>
5973
6074Environment:
6175 WARPGRID_LIBC_SRC Override default source checkout path
@@ -65,6 +79,8 @@ Examples:
6579 scripts/rebase-libc.sh --export
6680 scripts/rebase-libc.sh --update wasi-sdk-31
6781 scripts/rebase-libc.sh --validate
82+ scripts/rebase-libc.sh --validate --subset dns
83+ scripts/rebase-libc.sh --validate --subset filesystem
6884USAGE
6985}
7086
@@ -396,26 +412,109 @@ EOF
396412# ─── --validate ──────────────────────────────────────────────────────────────
397413
398414do_validate () {
415+ local subset_filter=" ${1:- } "
416+
399417 collect_patches
400418
401419 if [[ ${# PATCH_FILES[@]} -eq 0 ]]; then
402420 log " No patches found — nothing to validate"
403421 return 0
404422 fi
405423
406- log " Validating ${# PATCH_FILES[@]} patch(es)..."
407-
408- local errors=0
409- local prev_num=0
410- local seen_numbers=" "
424+ # First pass: extract headers from all patches
425+ # Use simple indexed arrays for portability (no associative arrays)
426+ local all_num_strs=" "
427+ local all_domains=" "
428+ local all_deps=" "
429+ local all_wits=" "
430+ local all_names=" "
431+ local patch_count=0
411432
412433 for patch in " ${PATCH_FILES[@]} " ; do
413434 local patch_name
414435 patch_name=$( basename " ${patch} " )
415-
416- # Check numbering: must start with numeric prefix
417436 local num_str
418437 num_str=$( echo " ${patch_name} " | grep -oE ' ^[0-9]+' || true)
438+
439+ # Extract structured headers from the commit message (before ---)
440+ local commit_msg
441+ commit_msg=$( sed -n ' 1,/^---$/p' " ${patch} " 2> /dev/null || true)
442+ local domain wit deps
443+ domain=$( echo " ${commit_msg} " | grep -m1 ' ^WarpGrid-Domain:' | sed ' s/^WarpGrid-Domain:[[:space:]]*//' || true)
444+ deps=$( echo " ${commit_msg} " | grep -m1 ' ^WarpGrid-Deps:' | sed ' s/^WarpGrid-Deps:[[:space:]]*//' || true)
445+ wit=$( echo " ${commit_msg} " | grep -m1 ' ^WarpGrid-WIT:' | sed ' s/^WarpGrid-WIT:[[:space:]]*//' || true)
446+
447+ # Store in parallel arrays (space-delimited lists, indexed by position)
448+ all_num_strs=" ${all_num_strs}${num_str} |"
449+ all_domains=" ${all_domains}${domain} |"
450+ all_deps=" ${all_deps}${deps} |"
451+ all_wits=" ${all_wits}${wit} |"
452+ all_names=" ${all_names}${patch_name} |"
453+ patch_count=$(( patch_count + 1 ))
454+ done
455+
456+ # If subset filter is active, determine which patches to validate
457+ local validate_indices=" "
458+ if [[ -n " ${subset_filter} " ]]; then
459+ log " Validating patches for subset: ${subset_filter} "
460+ local idx=0
461+ local remaining=" ${all_domains} "
462+ while [[ -n " ${remaining} " ]]; do
463+ local domain=" ${remaining%% |* } "
464+ remaining=" ${remaining#* |} "
465+ if [[ " ${domain} " == " ${subset_filter} " ]]; then
466+ validate_indices=" ${validate_indices} ${idx} "
467+ fi
468+ idx=$(( idx + 1 ))
469+ done
470+
471+ if [[ -z " ${validate_indices} " ]]; then
472+ warn " No patches found for domain '${subset_filter} '"
473+ return 1
474+ fi
475+ else
476+ local idx=0
477+ while [[ ${idx} -lt ${patch_count} ]]; do
478+ validate_indices=" ${validate_indices} ${idx} "
479+ idx=$(( idx + 1 ))
480+ done
481+ fi
482+
483+ # Collect the set of patch numbers being validated
484+ local validated_num_strs=" "
485+ for idx in ${validate_indices} ; do
486+ local num_str
487+ num_str=$( echo " ${all_num_strs} " | cut -d' |' -f$(( idx + 1 )) )
488+ validated_num_strs=" ${validated_num_strs} ${num_str} "
489+ done
490+
491+ local count_label
492+ count_label=$( echo " ${validate_indices} " | wc -w | tr -d ' ' )
493+ log " Validating ${count_label} patch(es)..."
494+
495+ local errors=0
496+ local prev_num=0
497+ local seen_numbers=" "
498+ local has_socket=false
499+ local has_filesystem=false
500+
501+ for idx in ${validate_indices} ; do
502+ local patch_name num_str domain deps wit
503+ patch_name=$( echo " ${all_names} " | cut -d' |' -f$(( idx + 1 )) )
504+ num_str=$( echo " ${all_num_strs} " | cut -d' |' -f$(( idx + 1 )) )
505+ domain=$( echo " ${all_domains} " | cut -d' |' -f$(( idx + 1 )) )
506+ deps=$( echo " ${all_deps} " | cut -d' |' -f$(( idx + 1 )) )
507+ wit=$( echo " ${all_wits} " | cut -d' |' -f$(( idx + 1 )) )
508+
509+ # Track domains present
510+ if [[ " ${domain} " == " socket" ]]; then
511+ has_socket=true
512+ fi
513+ if [[ " ${domain} " == " filesystem" ]]; then
514+ has_filesystem=true
515+ fi
516+
517+ # Check numbering: must start with numeric prefix
419518 if [[ -z " ${num_str} " ]]; then
420519 warn " ${patch_name} : missing numeric prefix"
421520 errors=$(( errors + 1 ))
@@ -432,36 +531,56 @@ do_validate() {
432531 seen_numbers=" ${seen_numbers} ${num_str} "
433532
434533 # Check that patch file is valid (has diff content)
435- if ! grep -q ' ^diff --git' " ${patch} " 2> /dev/null; then
534+ local patch_path=" ${PATCHES_DIR} /${patch_name} "
535+ if [[ -f " ${patch_path} " ]] && ! grep -q ' ^diff --git' " ${patch_path} " 2> /dev/null; then
436536 warn " ${patch_name} : does not appear to be a valid git patch"
437537 errors=$(( errors + 1 ))
438538 fi
439539
440- # Check dependency constraints (portable — no associative arrays)
441- # Known deps:
442- # 0003→0001 (socket-connect depends on dns-getaddrinfo for proxy config via FS)
443- # 0004→0003 (socket-send-recv depends on socket-connect)
444- # 0005→0004 (socket-close depends on socket-send-recv)
445- # 0006→0001 (gethostbyname depends on dns-getaddrinfo shim)
446- # 0007→0001 (getnameinfo depends on dns-getaddrinfo shim)
447- local dep=" "
448- case " ${num_str} " in
449- 0003) dep=" 0001" ;;
450- 0004) dep=" 0003" ;;
451- 0005) dep=" 0004" ;;
452- 0006|0007) dep=" 0001" ;;
453- esac
540+ # Check structured headers are present
541+ if [[ -z " ${domain} " ]]; then
542+ warn " ${patch_name} : missing WarpGrid-Domain: header"
543+ errors=$(( errors + 1 ))
544+ fi
545+ if [[ -z " ${deps} " ]]; then
546+ warn " ${patch_name} : missing WarpGrid-Deps: header"
547+ errors=$(( errors + 1 ))
548+ fi
549+ if [[ -z " ${wit} " ]]; then
550+ warn " ${patch_name} : missing WarpGrid-WIT: header"
551+ errors=$(( errors + 1 ))
552+ fi
454553
455- if [[ -n " ${dep} " ]]; then
456- if ! echo " ${seen_numbers} " | grep -qw " ${dep} " ; then
457- warn " ${patch_name} : requires patch ${dep} which is not present or comes later"
458- errors=$(( errors + 1 ))
459- fi
554+ # Check dependency constraints from WarpGrid-Deps header
555+ if [[ -n " ${deps} " && " ${deps} " != " none" ]]; then
556+ local IFS_SAVE=" ${IFS} "
557+ IFS=' ,'
558+ for dep in ${deps} ; do
559+ dep=$( echo " ${dep} " | tr -d ' ' )
560+ if ! echo " ${seen_numbers} " | grep -qw " ${dep} " ; then
561+ # Check if the dep is in the validated set at all
562+ if echo " ${validated_num_strs} " | grep -qw " ${dep} " ; then
563+ warn " ${patch_name} : requires patch ${dep} which comes later in the series"
564+ else
565+ warn " ${patch_name} : requires patch ${dep} which is not present"
566+ fi
567+ errors=$(( errors + 1 ))
568+ fi
569+ done
570+ IFS=" ${IFS_SAVE} "
460571 fi
461572
462573 log " OK ${patch_name} "
463574 done
464575
576+ # Check socket-without-filesystem: socket patches need filesystem for proxy.conf
577+ if ${has_socket} && ! ${has_filesystem} ; then
578+ warn " Socket patches require filesystem patches: socket-connect reads proxy"
579+ warn " configuration (/etc/warpgrid/proxy.conf) via the virtual filesystem shim."
580+ warn " Include filesystem patches (0002) or use --subset to validate a domain subset."
581+ errors=$(( errors + 1 ))
582+ fi
583+
465584 echo
466585 if [[ ${errors} -gt 0 ]]; then
467586 warn " Validation found ${errors} issue(s)"
@@ -477,6 +596,7 @@ do_validate() {
477596main () {
478597 local mode=" "
479598 local update_tag=" "
599+ local subset_domain=" "
480600 local src_dir=" ${WARPGRID_LIBC_SRC:- ${DEFAULT_SRC_DIR} } "
481601
482602 if [[ $# -eq 0 ]]; then
@@ -497,6 +617,13 @@ main() {
497617 update_tag=" ${1} "
498618 ;;
499619 --validate) mode=" validate" ;;
620+ --subset)
621+ if [[ $# -lt 2 ]]; then
622+ err " --subset requires a domain argument (dns, filesystem, socket)"
623+ fi
624+ shift
625+ subset_domain=" ${1} "
626+ ;;
500627 --src)
501628 if [[ $# -lt 2 ]]; then
502629 err " --src requires a path argument"
@@ -518,7 +645,7 @@ main() {
518645 apply) do_apply " ${src_dir} " ;;
519646 export) do_export " ${src_dir} " ;;
520647 update) do_update " ${update_tag} " " ${src_dir} " ;;
521- validate) do_validate ;;
648+ validate) do_validate " ${subset_domain} " ;;
522649 esac
523650}
524651
0 commit comments