@@ -126,21 +126,104 @@ end
126126
127127-- mcpp: neutral-common (from base) + per-OS blocks (each OS's platform-specific keys)
128128-- `deps` is PER-OS: linux/macosx carry compat.ffmpeg (videoio backend); a core-only
129- -- windows profile (no videoio) carries none, so it must not inherit a global dep.
130- local PEROS = { " include_dirs" , " cxxflags" , " cflags" , " flags" , " sources" , " generated_files" , " deps" }
129+ -- profile (no videoio) carries none, so it must not inherit a global dep.
130+ -- `flags` is handled specially below (dnn-group flag-globs relocate into the feature).
131+ local PEROS = { " include_dirs" , " cxxflags" , " cflags" , " sources" , " generated_files" , " deps" }
132+
133+ -- ── per-OS `dnn` feature (mcpp#253 common/delta) ────────────────────────
134+ -- The dnn feature's payload splits into a cross-platform COMMON part (dnn/protobuf/
135+ -- mlas C++, mlas_hgemm_stub) and a per-arch SIMD DELTA (x86: mlas/lib/x86_64/*.S +
136+ -- avx/avx2/avx512 kernels; arm: mlas/lib/aarch64/*.S + neon/neon_fp16 kernels).
137+ -- mcpp 0.0.101 per-OS features append per sub-key, so COMMON rides neutral
138+ -- features.dnn and each OS's DELTA rides mcpp.<os>.features.dnn. dnn-group flag-globs
139+ -- (mlas/protobuf/mlasgemm + per-ISA) ride features.dnn.flags so the feature-off base
140+ -- has no dead globs (mcpp 0.0.101 warning). Inputs are normalized whether they
141+ -- carried those globs in base `flags` (pre-warnfix descriptors) or features.dnn.flags.
142+ local function is_dnn_glob (g )
143+ if type (g ) ~= " string" then return false end
144+ for _ , m in ipairs ({ " 3rdparty/mlas" , " 3rdparty/protobuf" , " modules/dnn" , " tu/mlasgemm" }) do
145+ if g :find (m , 1 , true ) then return true end
146+ end
147+ return false
148+ end
149+ local function ser_id (v ) return ser (v , " " ) end
150+
151+ local pkgs , order = {}, {}
152+ for _ , e in ipairs (INPUTS ) do pkgs [e .os ] = load_pkg (e .path ); order [# order + 1 ] = e .os end
153+ local dnn_src , dnn_flg , base_flg = {}, {}, {}
154+ for _ , os_ in ipairs (order ) do
155+ local p = pkgs [os_ ]
156+ local feat = (p .mcpp .features and p .mcpp .features .dnn ) or {}
157+ local srcs , flgs , cleaned = {}, {}, {}
158+ for _ , s in ipairs (feat .sources or {}) do srcs [# srcs + 1 ] = s end
159+ for _ , f in ipairs (feat .flags or {}) do flgs [# flgs + 1 ] = f end
160+ for _ , f in ipairs (p .mcpp .flags or {}) do
161+ if type (f ) == " table" and is_dnn_glob (f .glob ) then flgs [# flgs + 1 ] = f
162+ else cleaned [# cleaned + 1 ] = f end
163+ end
164+ dnn_src [os_ ], dnn_flg [os_ ], base_flg [os_ ] = srcs , flgs , cleaned
165+ end
166+
167+ -- active = OSes that actually carry a dnn payload; common is their intersection
168+ local active = {}
169+ for _ , os_ in ipairs (order ) do if # dnn_src [os_ ] > 0 then active [# active + 1 ] = os_ end end
170+ local function split_common (map )
171+ if # active == 0 then return {}, {} end
172+ local counts = {}
173+ for _ , os_ in ipairs (active ) do
174+ local seen = {}
175+ for _ , v in ipairs (map [os_ ]) do
176+ local id = ser_id (v )
177+ if not seen [id ] then seen [id ] = true ; counts [id ] = (counts [id ] or 0 ) + 1 end
178+ end
179+ end
180+ local common , cids = {}, {}
181+ for _ , v in ipairs (map [active [1 ]]) do
182+ local id = ser_id (v )
183+ if counts [id ] == # active and not cids [id ] then cids [id ] = true ; common [# common + 1 ] = v end
184+ end
185+ local delta = {}
186+ for _ , os_ in ipairs (order ) do
187+ local d = {}
188+ for _ , v in ipairs (map [os_ ] or {}) do if not cids [ser_id (v )] then d [# d + 1 ] = v end end
189+ delta [os_ ] = d
190+ end
191+ return common , delta
192+ end
193+ local common_src , delta_src = split_common (dnn_src )
194+ local common_flg , delta_flg = split_common (dnn_flg )
195+
196+ -- neutral features: base's non-dnn features (unifont) verbatim; dnn = common-only
197+ local neutral_features = {}
198+ for fname , fdef in pairs (base .mcpp .features or {}) do
199+ if fname ~= " dnn" then neutral_features [fname ] = fdef end
200+ end
201+ local base_dnn = base .mcpp .features and base .mcpp .features .dnn
202+ neutral_features .dnn = {
203+ defines = (base_dnn and base_dnn .defines ) or { " HAVE_OPENCV_DNN" },
204+ flags = common_flg ,
205+ sources = common_src ,
206+ }
207+
131208merged .mcpp = {
132209 language = base .mcpp .language ,
133210 targets = base .mcpp .targets ,
134- features = base . mcpp . features , -- unifont (neutral) + dnn (x86; opencv-dnn stays linux-only)
211+ features = neutral_features ,
135212}
136- for _ , e in ipairs (INPUTS ) do
137- local p = load_pkg ( e . path )
213+ for _ , os_ in ipairs (order ) do
214+ local p = pkgs [ os_ ]
138215 local blk = {}
139216 for _ , k in ipairs (PEROS ) do blk [k ] = p .mcpp [k ] end
140- -- ldflags: the single-OS descriptor carries them in its own mcpp.<os> sub-block
141- local sub = p .mcpp [e . os ]
217+ blk . flags = base_flg [ os_ ] -- dnn-group globs stripped
218+ local sub = p .mcpp [os_ ]
142219 if sub and sub .ldflags then blk .ldflags = sub .ldflags end
143- merged .mcpp [e .os ] = blk
220+ if # dnn_src [os_ ] > 0 then -- this OS supports dnn -> its delta
221+ local fd = {}
222+ if # delta_src [os_ ] > 0 then fd .sources = delta_src [os_ ] end
223+ if # delta_flg [os_ ] > 0 then fd .flags = delta_flg [os_ ] end
224+ if next (fd ) then blk .features = { dnn = fd } end
225+ end
226+ merged .mcpp [os_ ] = blk
144227end
145228
146229-- ---- emit ----
0 commit comments