Skip to content

Commit 1af8405

Browse files
committed
Add REMOVE_EXTENSIONS blacklist support to generate_targets.py
Add ability to blacklist specific extensions from being added to boards, overriding auto-added extensions (like mesa-vpu for fast HDMI boards). - Add load_remove_extensions_map() function to parse blacklist file - Modify get_soc_extensions() to accept and process remove_extensions_map - Update extract_boards_by_support_level() to pass through remove map - Update main() to load targets-extensions.map.blacklist from release-targets Format: BOARD:branch:REMOVE_EXTENSIONS="ext1,ext2" Empty branches (::) apply to all branches for that board. Example: radxa-e54c:::REMOVE_EXTENSIONS="v4l2loopback-dkms,mesa-vpu" Signed-off-by: Igor Pecovnik <igor@armbian.com>
1 parent 8dbe67d commit 1af8405

File tree

3 files changed

+149
-11
lines changed

3 files changed

+149
-11
lines changed

release-targets/README.md

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ python3 scripts/generate_targets.py image-info.json release-targets/
1616
The script reads the following files from the output directory (same location as generated files):
1717

1818
- **`targets-extensions.map`** - Manual extensions for specific boards/branches (optional)
19+
- **`targets-extensions.map.blacklist`** - Extensions to remove from specific boards/branches (optional)
1920
- **`targets-release-<type>.blacklist`** - Boards to exclude from each target type (optional)
2021
- **`targets-release-<type>.manual`** - Additional YAML to append to each target (optional)
2122

@@ -107,6 +108,25 @@ nanopi-r4s:::ENABLE_EXTENSIONS="test-extension"
107108
board-name:current::ENABLE_EXTENSIONS="ext1,ext2,ext3"
108109
```
109110

111+
### targets-extensions.map.blacklist
112+
113+
Remove extensions from specific boards (overrides automatic and manual extensions):
114+
115+
```ini
116+
# Format: BOARD_NAME:branch1:branch2:...:REMOVE_EXTENSIONS="extension1,extension2"
117+
118+
# Remove from all branches
119+
radxa-e54c:::REMOVE_EXTENSIONS="v4l2loopback-dkms,mesa-vpu"
120+
121+
# Remove from specific branch only
122+
uefi-x86:current::REMOVE_EXTENSIONS="mesa-vpu"
123+
124+
# Remove from multiple branches
125+
board-name:vendor:edge::REMOVE_EXTENSIONS="ext1,ext2"
126+
```
127+
128+
**Note**: The REMOVE_EXTENSIONS blacklist takes precedence over both automatic extensions (like `mesa-vpu` for fast HDMI boards) AND manual extensions from `targets-extensions.map`. Extensions listed here will be removed even if they were added by either mechanism.
129+
110130
### targets-release-<type>.blacklist
111131

112132
Exclude specific boards from a target type (one board name per line):
@@ -145,7 +165,10 @@ All fast HDMI boards (64-bit boards with video output) automatically get:
145165
- `v4l2loopback-dkms`
146166
- `mesa-vpu`
147167

148-
**Note**: Manual extensions from `targets-extensions.map` are MERGED with automatic extensions.
168+
**Note**:
169+
- Manual extensions from `targets-extensions.map` are MERGED with automatic extensions.
170+
- Extensions in `targets-extensions.map.blacklist` are REMOVED from both automatic and manual extensions.
171+
- The blacklist takes precedence over all other extension sources.
149172

150173
## Usage
151174

@@ -169,5 +192,7 @@ python3 scripts/generate_targets.py image-info.json release-targets/
169192

170193
- Python 3.6+
171194
- image-info.json (from Armbian build framework or github.armbian.com)
172-
- targets-extensions.map (optional, but recommended in output directory)
173-
- .blacklist and .manual files (optional, per target type)
195+
- targets-extensions.map (optional, for adding manual extensions)
196+
- targets-extensions.map.blacklist (optional, for removing unwanted extensions)
197+
- targets-release-<type>.blacklist (optional, per target type)
198+
- targets-release-<type>.manual (optional, per target type)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Extensions Blacklist
2+
# ------------------
3+
# This file defines extensions that should be REMOVED from specific boards,
4+
# overriding the auto-added extensions (like v4l2loopback-dkms, mesa-vpu for fast HDMI boards).
5+
#
6+
# Format: BOARD_NAME:branch1:branch2:...:REMOVE_EXTENSIONS="ext1,ext2"
7+
#
8+
# - BOARD_NAME: board identifier (must match BOARD in image-info.json)
9+
# - branch1:branch2: optional, specific kernel branches (current, vendor, edge)
10+
# - If empty (::), applies to ALL branches for that board
11+
# - REMOVE_EXTENSIONS: comma-separated list of extensions to remove
12+
#
13+
# Examples:
14+
# boardx:::REMOVE_EXTENSIONS="mesa-vpu" # Remove mesa-vpu from all branches
15+
# boardy:current::REMOVE_EXTENSIONS="mesa-vpu" # Remove only from current branch
16+
# boardz::vendor:edge:REMOVE_EXTENSIONS="ext1" # Remove from vendor and edge only
17+
#
18+
# Boards listed below:
19+
radxa-e54c:::REMOVE_EXTENSIONS="v4l2loopback-dkms,mesa-vpu"

scripts/generate_targets.py

Lines changed: 102 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,69 @@ def load_extensions_map(map_path):
7777
return extensions_map
7878

7979

80+
def load_remove_extensions_map(map_path):
81+
"""
82+
Load remove extensions mapping file (blacklist).
83+
84+
Format: BOARD_NAME:branch1:branch2:...:REMOVE_EXTENSIONS="ext1,ext2"
85+
Returns dict: {(BOARD, BRANCH): set(["ext1", "ext2"])}
86+
If branches are empty (just ::), applies to all branches for that board.
87+
Extensions in this set are removed from both auto-added and manual extensions.
88+
"""
89+
remove_map = {}
90+
91+
if not Path(map_path).exists():
92+
return remove_map
93+
94+
with open(map_path, 'r') as f:
95+
for line in f:
96+
line = line.strip()
97+
# Skip comments and empty lines
98+
if not line or line.startswith('#'):
99+
continue
100+
101+
# Parse line: BOARD:branch1:branch2:...:REMOVE_EXTENSIONS="..."
102+
if '::REMOVE_EXTENSIONS=' not in line:
103+
continue
104+
105+
try:
106+
# Split on REMOVE_EXTENSIONS
107+
parts = line.split('REMOVE_EXTENSIONS=')
108+
if len(parts) != 2:
109+
continue
110+
111+
# Parse board and branches
112+
board_part = parts[0].rstrip(':')
113+
extensions = parts[1].strip('"')
114+
115+
# Split by : to get board and branches
116+
if ':' in board_part:
117+
board_branches = board_part.split(':')
118+
board = board_branches[0]
119+
branches = [b for b in board_branches[1:] if b] # Filter empty strings
120+
else:
121+
board = board_part
122+
branches = []
123+
124+
# Convert to set for easy removal
125+
ext_set = {ext.strip() for ext in extensions.split(',') if ext.strip()}
126+
127+
# Store mapping
128+
if branches:
129+
# Specific branches
130+
for branch in branches:
131+
remove_map[(board, branch)] = ext_set
132+
else:
133+
# All branches - use empty branch as wildcard
134+
remove_map[(board, '')] = ext_set
135+
136+
except Exception as e:
137+
print(f"Warning: Failed to parse line: {line} ({e})", file=sys.stderr)
138+
continue
139+
140+
return remove_map
141+
142+
80143
def load_manual_overrides(base_path):
81144
"""
82145
Load manual target overrides from .manual files.
@@ -203,11 +266,12 @@ def is_fast_hardware(entry):
203266
return True
204267

205268

206-
def get_soc_extensions(entry, extensions_map=None):
269+
def get_soc_extensions(entry, extensions_map=None, remove_extensions_map=None):
207270
"""
208271
Determine if board needs v4l2loopback-dkms and mesa-vpu extensions.
209272
For fast HDMI boards, automatically adds these extensions.
210273
Also merges manual extensions from the extensions map.
274+
Removes extensions specified in remove_extensions_map.
211275
Returns a comma-separated string of extensions or empty string.
212276
"""
213277
inventory = entry.get('in', {}).get('inventory', {})
@@ -239,16 +303,31 @@ def get_soc_extensions(entry, extensions_map=None):
239303
if ext and ext not in extensions:
240304
extensions.append(ext)
241305

306+
# Remove extensions specified in remove_extensions_map
307+
if remove_extensions_map:
308+
remove_ext = None
309+
# Check for specific (board, branch) match
310+
if (board, branch) in remove_extensions_map:
311+
remove_ext = remove_extensions_map[(board, branch)]
312+
# Check for wildcard (board, '') match
313+
elif (board, '') in remove_extensions_map:
314+
remove_ext = remove_extensions_map[(board, '')]
315+
316+
if remove_ext:
317+
# Filter out extensions in the remove list
318+
extensions = [ext for ext in extensions if ext not in remove_ext]
319+
242320
return ','.join(extensions) if extensions else ''
243321

244322

245-
def extract_boards_by_support_level(image_info, extensions_map=None, blacklist=None):
323+
def extract_boards_by_support_level(image_info, extensions_map=None, remove_extensions_map=None, blacklist=None):
246324
"""
247325
Extract and categorize boards by support level.
248326
249327
Args:
250328
image_info: Image information data
251329
extensions_map: Optional extensions mapping
330+
remove_extensions_map: Optional remove extensions mapping
252331
blacklist: Optional set of board names to exclude
253332
254333
Returns:
@@ -294,7 +373,7 @@ def extract_boards_by_support_level(image_info, extensions_map=None, blacklist=N
294373
'arch': arch,
295374
'entry': entry,
296375
'has_desktop_variant': build_desktop == 'yes',
297-
'extensions': get_soc_extensions(entry, extensions_map),
376+
'extensions': get_soc_extensions(entry, extensions_map, remove_extensions_map),
298377
'is_fast': is_fast_hardware(entry)
299378
}
300379
else:
@@ -1416,8 +1495,10 @@ def main():
14161495

14171496
output_dir.mkdir(parents=True, exist_ok=True)
14181497

1419-
# Load extensions map (optional) - look in output directory first, then script directory
1498+
# Load extensions map (optional) - look in output directory, then release-targets, then script directory
14201499
extensions_map_path = output_dir / 'targets-extensions.map'
1500+
if not extensions_map_path.exists():
1501+
extensions_map_path = Path(__file__).parent.parent / 'release-targets' / 'targets-extensions.map'
14211502
if not extensions_map_path.exists():
14221503
extensions_map_path = Path(__file__).parent / 'targets-extensions.map'
14231504
print(f"Loading extensions map from {extensions_map_path}...", file=sys.stderr)
@@ -1427,6 +1508,19 @@ def main():
14271508
else:
14281509
print(" No extensions map found or empty", file=sys.stderr)
14291510

1511+
# Load remove extensions map (optional) - look in output directory, then release-targets, then script directory
1512+
remove_extensions_map_path = output_dir / 'targets-extensions.map.blacklist'
1513+
if not remove_extensions_map_path.exists():
1514+
remove_extensions_map_path = Path(__file__).parent.parent / 'release-targets' / 'targets-extensions.map.blacklist'
1515+
if not remove_extensions_map_path.exists():
1516+
remove_extensions_map_path = Path(__file__).parent / 'targets-extensions.map.blacklist'
1517+
print(f"Loading remove extensions map from {remove_extensions_map_path}...", file=sys.stderr)
1518+
remove_extensions_map = load_remove_extensions_map(remove_extensions_map_path)
1519+
if remove_extensions_map:
1520+
print(f" Loaded {len(remove_extensions_map)} remove extension rules", file=sys.stderr)
1521+
else:
1522+
print(" No remove extensions map found or empty", file=sys.stderr)
1523+
14301524
# Load image info
14311525
print(f"Loading {json_path}...", file=sys.stderr)
14321526
image_info = load_image_info(json_path)
@@ -1439,7 +1533,7 @@ def main():
14391533
apps_path = output_dir / 'targets-release-apps.yaml'
14401534
blacklist_apps = load_blacklist(str(apps_path))
14411535
manual_apps = load_manual_overrides(str(apps_path))
1442-
conf_wip_boards_apps, _ = extract_boards_by_support_level(image_info, extensions_map, blacklist_apps)
1536+
conf_wip_boards_apps, _ = extract_boards_by_support_level(image_info, extensions_map, remove_extensions_map, blacklist_apps)
14431537
print(f" apps: {len(conf_wip_boards_apps)} boards after blacklist", file=sys.stderr)
14441538
apps_yaml = generate_apps_yaml(conf_wip_boards_apps, manual_apps)
14451539
apps_path.write_text(apps_yaml)
@@ -1449,7 +1543,7 @@ def main():
14491543
stable_path = output_dir / 'targets-release-standard-support.yaml'
14501544
blacklist_stable = load_blacklist(str(stable_path))
14511545
manual_stable = load_manual_overrides(str(stable_path))
1452-
conf_wip_boards_stable, _ = extract_boards_by_support_level(image_info, extensions_map, blacklist_stable)
1546+
conf_wip_boards_stable, _ = extract_boards_by_support_level(image_info, extensions_map, remove_extensions_map, blacklist_stable)
14531547
print(f" stable: {len(conf_wip_boards_stable)} boards after blacklist", file=sys.stderr)
14541548
stable_yaml = generate_stable_yaml(conf_wip_boards_stable, manual_stable)
14551549
stable_path.write_text(stable_yaml)
@@ -1459,7 +1553,7 @@ def main():
14591553
nightly_path = output_dir / 'targets-release-nightly.yaml'
14601554
blacklist_nightly = load_blacklist(str(nightly_path))
14611555
manual_nightly = load_manual_overrides(str(nightly_path))
1462-
conf_wip_boards_nightly, _ = extract_boards_by_support_level(image_info, extensions_map, blacklist_nightly)
1556+
conf_wip_boards_nightly, _ = extract_boards_by_support_level(image_info, extensions_map, remove_extensions_map, blacklist_nightly)
14631557
print(f" nightly: {len(conf_wip_boards_nightly)} boards after blacklist", file=sys.stderr)
14641558
nightly_yaml = generate_nightly_yaml(conf_wip_boards_nightly, manual_nightly)
14651559
nightly_path.write_text(nightly_yaml)
@@ -1469,7 +1563,7 @@ def main():
14691563
community_path = output_dir / 'targets-release-community-maintained.yaml'
14701564
blacklist_community = load_blacklist(str(community_path))
14711565
manual_community = load_manual_overrides(str(community_path))
1472-
_, csc_tvb_boards_community = extract_boards_by_support_level(image_info, extensions_map, blacklist_community)
1566+
_, csc_tvb_boards_community = extract_boards_by_support_level(image_info, extensions_map, remove_extensions_map, blacklist_community)
14731567
print(f" community: {len(csc_tvb_boards_community)} boards after blacklist", file=sys.stderr)
14741568
community_yaml = generate_community_yaml(csc_tvb_boards_community, manual_community)
14751569
community_path.write_text(community_yaml)

0 commit comments

Comments
 (0)