@@ -40,7 +40,16 @@ SAN=${MUTATION_SANITIZE:-none}
4040
4141COMPONENTS=(" $@ " )
4242if [ ${# COMPONENTS[@]} -eq 0 ]; then
43- COMPONENTS=(Map Vec Str List BitVec Graph Int Float)
43+ COMPONENTS=(
44+ # containers
45+ Map Vec Str List BitVec Graph Int Float
46+ # std subsystems
47+ Io File ArgParse Iter Allocator AllocDebug
48+ # parsers
49+ Elf MachO Pe Pdb Dwarf Json KvConfig Http Dns
50+ # sys
51+ Backtrace Socket ProcMaps SymbolResolver MachoCache PdbCache SysDns
52+ )
4453fi
4554
4655# Locate the pass plugin if the environment did not hand it to us.
@@ -61,35 +70,79 @@ echo "==> runner : $MULL_RUNNER"
6170echo " ==> components : ${COMPONENTS[*]} "
6271echo
6372
64- # Resolve a component name to its implementation source file.
73+ # Explicit component -> source overrides, for components whose test prefix does
74+ # not match a unique <Component>.c basename under Source/ (case, ambiguity, or
75+ # a differently-named file).
76+ declare -A SRC_OVERRIDE=(
77+ [Json]=" Source/Misra/Parsers/JSON.c"
78+ [Dns]=" Source/Misra/Parsers/Dns.c"
79+ [SysDns]=" Source/Misra/Sys/Dns.c"
80+ [AllocDebug]=" Source/Misra/Std/Allocator/Debug.c"
81+ [Iter]=" Source/Misra/Std/Utility/Iter.c"
82+ )
83+
84+ # Resolve a component name to its implementation source file: an explicit
85+ # override, else a unique <Component>.c anywhere under Source/. Returns nonzero
86+ # (caller skips) when there is no match or an ambiguous one.
6587component_source () {
66- local c=$1 p
67- for p in " Source/Misra/Std/Container/$c .c" " Source/Misra/Std/$c .c" ; do
68- [ -f " $p " ] && { echo " $p " ; return 0; }
69- done
88+ local c=$1
89+ if [ -n " ${SRC_OVERRIDE[$c]:- } " ]; then
90+ [ -f " ${SRC_OVERRIDE[$c]} " ] && { echo " ${SRC_OVERRIDE[$c]} " ; return 0; }
91+ return 1
92+ fi
93+ local hits n
94+ hits=$( find Source -type f -name " $c .c" 2> /dev/null)
95+ n=$( printf ' %s\n' " $hits " | grep -c .)
96+ if [ " $n " = " 1" ]; then
97+ echo " $hits "
98+ return 0
99+ fi
70100 return 1
71101}
72102
103+ # One build dir per invocation, named by the first component so parallel runs
104+ # over disjoint component sets don't collide. The library is built ONCE; each
105+ # component is then scoped incrementally -- we delete just that source's library
106+ # object so ninja recompiles that single file (seconds) under the component's
107+ # MULL_CONFIG, instead of rebuilding the whole library per component (minutes).
108+ BUILD_DIR=" $ROOT /${COMPONENTS[0]} "
109+ mkdir -p " $BUILD_DIR "
110+
111+ # Source/Misra/Parsers/Elf.c -> Source_Misra_Parsers_Elf.c.o (meson object name)
112+ obj_name () { printf ' %s.o' " $( printf ' %s' " $1 " | tr ' /' ' _' ) " ; }
113+
73114overall_fail=0
115+ prev_src=" "
74116for comp in " ${COMPONENTS[@]} " ; do
75117 src=$( component_source " $comp " ) || { echo " ::warning::no source for component '$comp ', skipping" ; continue ; }
76- bdir=" $ROOT /$comp "
77- cfg=" $bdir /mull.yml"
78- mkdir -p " $bdir "
79118
80- # Scope mutations to this component's source for both compile and run.
119+ cfg= " $BUILD_DIR /mull- $comp .yml "
81120 printf ' includePaths:\n - %s\nmutators:\n - cxx_all\n' " $src " > " $cfg "
82121 export MULL_CONFIG=" $PWD /$cfg "
83122
84- if [ ! -f " $bdir /build.ninja" ]; then
85- CC=" $CC " meson setup " $bdir " \
123+ # Force recompile of the current target (to embed its mutants) and the
124+ # previously-mutated one (to drop its mutants, now out of includePaths).
125+ # Deleting the library object -- not touching the shared source -- avoids
126+ # churning a parallel run's build of the same file.
127+ for s in " $src " " $prev_src " ; do
128+ [ -n " $s " ] && find " $BUILD_DIR " -path " *.a.p/$( obj_name " $s " ) " -delete 2> /dev/null
129+ true
130+ done
131+ prev_src=" $src "
132+
133+ # Configure once; MULL_CONFIG is already set so the initial full build embeds
134+ # the first component's mutants.
135+ if [ ! -f " $BUILD_DIR /build.ninja" ]; then
136+ CC=" $CC " meson setup " $BUILD_DIR " \
86137 -Db_sanitize=" $SAN " \
87138 -Db_lundef=false \
88139 -Dc_args=" -g -O0 -grecord-command-line -fno-discard-value-names -fpass-plugin=$MULL_IR_FRONTEND "
89140 fi
90141
91- # Suites for this component: meson tests named "<comp>.*".
92- mapfile -t suites < <( meson test -C " $bdir " --list 2> /dev/null | grep -oE " (^|[^A-Za-z0-9_])$comp \.[A-Za-z0-9_]+" | grep -oE " $comp \.[A-Za-z0-9_]+" | sort -u)
142+ # Suites for this component: meson tests named exactly "<comp>" or "<comp>.*"
143+ # (single-token like Elf, dotted like Io.Write, multi-dot like
144+ # Json.Read.Simple). meson prints "<project>:<testname>"; strip the prefix.
145+ mapfile -t suites < <( meson test -C " $BUILD_DIR " --list 2> /dev/null | sed ' s/^[^:]*://' | grep -E " ^$comp (\.|\$ )" | sort -u)
93146 if [ ${# suites[@]} -eq 0 ]; then
94147 echo " ::warning::no suites found for component '$comp ', skipping"
95148 continue
@@ -98,9 +151,16 @@ for comp in "${COMPONENTS[@]}"; do
98151 echo " ==================== $comp ($src ) [${# suites[@]} suites] ===================="
99152 for suite in " ${suites[@]} " ; do
100153 echo " -------------------- $suite --------------------"
101- ninja -C " $bdir " " Tests/$suite "
102- bin=" $bdir /Tests/$suite "
103- report=" $bdir /mutation-$suite .txt"
154+ bin=" $BUILD_DIR /Tests/$suite "
155+ report=" $BUILD_DIR /mutation-$suite .txt"
156+
157+ # Most suites build as a "Tests/<name>" target; a few (custom test exes)
158+ # don't -- skip those rather than aborting the whole sweep.
159+ if ! ninja -C " $BUILD_DIR " " Tests/$suite " 2> /dev/null || [ ! -x " $bin " ]; then
160+ echo " ::warning::$suite : no buildable 'Tests/$suite ' binary -- skipping"
161+ echo " $suite : no binary" > " $report "
162+ continue
163+ fi
104164
105165 if ! " $bin " > /dev/null 2>&1 ; then
106166 echo " ::warning::$suite baseline FAILS unmutated -- skipping mutation run"
@@ -124,4 +184,8 @@ for comp in "${COMPONENTS[@]}"; do
124184 done
125185done
126186
187+ # mull-runner drops per-mutant artifact files (16-hex-char names) in the CWD;
188+ # clean them so they don't litter the repo root.
189+ find . -maxdepth 1 -type f -regextype posix-extended -regex ' \./[0-9a-f]{16}' -delete 2> /dev/null || true
190+
127191exit $overall_fail
0 commit comments