Skip to content

Commit eb96aeb

Browse files
committed
fix(iar): honor per-config <excluded> source files
collect_sources walked every <file> via root.iter('file') and ignored IAR's <excluded><configuration> markers, so for the selected build configuration it listed files that config does not compile -- over- reporting the artifact's source set. Drop a file whose <excluded> block names the chosen configuration. Adds tests/test_iar_sbom.py (a file excluded from the selected config is dropped; one excluded from a different config is kept), wired into selftest.yml.
1 parent bd02c67 commit eb96aeb

3 files changed

Lines changed: 85 additions & 4 deletions

File tree

.github/workflows/selftest.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,14 @@ jobs:
3333
provenance/bomsh_verify.py \
3434
tools/wolfglass-sync \
3535
tests/test_gen_sbom.py \
36-
tests/test_sbom.py
36+
tests/test_sbom.py \
37+
tests/test_iar_sbom.py
3738
3839
- name: Run generator unit tests
3940
run: python -m unittest tests/test_gen_sbom.py
4041

42+
- name: Run IAR frontend tests
43+
run: python -m unittest tests/test_iar_sbom.py
44+
4145
- name: Run self-test
4246
run: python tests/test_sbom.py

share/frontends/iar_sbom.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,20 @@ def parse_configs(root):
6161
return configs
6262

6363

64-
def collect_sources(root, proj_dir):
65-
"""Return (present, missing) absolute paths of compiled source files."""
64+
def _excluded_from(file_el, cfg_name):
65+
"""True if <file> carries an <excluded> block naming cfg_name -- IAR drops
66+
the file from that build configuration, so it is not compiled in."""
67+
excluded = file_el.find('excluded')
68+
if excluded is None:
69+
return False
70+
return any((c.text or '').strip() == cfg_name
71+
for c in excluded.findall('configuration'))
72+
73+
74+
def collect_sources(root, proj_dir, cfg_name):
75+
"""Return (present, missing) absolute paths of the source files compiled in
76+
configuration cfg_name. Files IAR marks <excluded> for cfg_name are
77+
dropped; listing them would over-report the compiled source set."""
6678
srcs = []
6779
for file_el in root.iter('file'):
6880
name_el = file_el.find('name')
@@ -71,6 +83,8 @@ def collect_sources(root, proj_dir):
7183
raw = name_el.text.strip()
7284
if not raw.lower().endswith(SRC_EXTS):
7385
continue
86+
if _excluded_from(file_el, cfg_name):
87+
continue
7488
srcs.append(resolve_proj_dir(raw, proj_dir))
7589
seen, present, missing = set(), [], []
7690
for s in srcs:
@@ -124,7 +138,7 @@ def main():
124138
cfg_name = max(configs, key=lambda k: len(configs[k]))
125139

126140
defines = configs[cfg_name]
127-
srcs, missing = collect_sources(root, proj_dir)
141+
srcs, missing = collect_sources(root, proj_dir, cfg_name)
128142
if not srcs:
129143
sys.exit("ERROR: no existing source files found in .ewp")
130144
if missing:

tests/test_iar_sbom.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env python3
2+
"""Tests for the IAR frontend (share/frontends/iar_sbom.py).
3+
4+
Focus: collect_sources must honour IAR's per-configuration <excluded> markers,
5+
so a file excluded from the selected build configuration is not reported as a
6+
compiled source (which would over-report the artifact's source set)."""
7+
8+
import importlib.util
9+
import os
10+
import tempfile
11+
import unittest
12+
import xml.etree.ElementTree as ET
13+
14+
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
15+
IAR = os.path.join(REPO, "share", "frontends", "iar_sbom.py")
16+
17+
_spec = importlib.util.spec_from_file_location("iar_sbom", IAR)
18+
iar = importlib.util.module_from_spec(_spec)
19+
_spec.loader.exec_module(iar)
20+
21+
# a.c: compiled in every config; b.c: excluded from Release;
22+
# c.c: excluded from Debug only.
23+
_EWP = """<project>
24+
<file><name>$PROJ_DIR$/a.c</name></file>
25+
<file><name>$PROJ_DIR$/b.c</name>
26+
<excluded><configuration>Release</configuration></excluded>
27+
</file>
28+
<file><name>$PROJ_DIR$/c.c</name>
29+
<excluded><configuration>Debug</configuration></excluded>
30+
</file>
31+
</project>"""
32+
33+
34+
class TestExcludedFiles(unittest.TestCase):
35+
def setUp(self):
36+
self.tmp = tempfile.TemporaryDirectory()
37+
self.proj = self.tmp.name
38+
for n in ("a.c", "b.c", "c.c"):
39+
open(os.path.join(self.proj, n), "w").close()
40+
self.root = ET.fromstring(_EWP)
41+
42+
def tearDown(self):
43+
self.tmp.cleanup()
44+
45+
def _names(self, cfg):
46+
present, _missing = iar.collect_sources(self.root, self.proj, cfg)
47+
return {os.path.basename(p) for p in present}
48+
49+
def test_release_drops_file_excluded_from_release(self):
50+
names = self._names("Release")
51+
self.assertIn("a.c", names)
52+
self.assertIn("c.c", names) # excluded from Debug, not Release
53+
self.assertNotIn("b.c", names) # excluded from Release
54+
55+
def test_debug_drops_file_excluded_from_debug(self):
56+
names = self._names("Debug")
57+
self.assertIn("a.c", names)
58+
self.assertIn("b.c", names) # excluded from Release, not Debug
59+
self.assertNotIn("c.c", names) # excluded from Debug
60+
61+
62+
if __name__ == "__main__":
63+
unittest.main(verbosity=2)

0 commit comments

Comments
 (0)