Skip to content

Commit 250fd5b

Browse files
committed
fix: Resolve west-command paths correctly from manifests in subdirectories
Allow west commands to be imported from a project subdirectory if the manifest is located in a subdirectory, e.g. `import: mf_subdir/west.yml`. Fixes issue #725
1 parent 548b9ee commit 250fd5b

2 files changed

Lines changed: 70 additions & 11 deletions

File tree

src/west/manifest.py

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2628,7 +2628,7 @@ def _import_path_from_project(self, project: Project, path: str) -> None:
26282628
return
26292629

26302630
for data in imported:
2631-
self._import_data_from_project(project, data, None)
2631+
self._import_data_from_project(project, data, path)
26322632

26332633
_logger.debug(f'done resolving import {path} for {project}')
26342634

@@ -2647,16 +2647,21 @@ def _import_map_from_project(self, project: Project, imp: dict) -> None:
26472647
_logger.debug(f'done resolving import {imap} for {project}')
26482648

26492649
def _import_data_from_project(
2650-
self, project: Project, data: Any, imap: _import_map | None
2650+
self, project: Project, data: Any, imap_or_path: _import_map | str
26512651
) -> None:
26522652
# Destructively add the imported data into our 'projects' map.
2653-
2654-
if imap is not None:
2655-
imap_filter = _compose_imap_filters(self._ctx.imap_filter, _imap_filter(imap))
2656-
imap_path_prefix = imap.path_prefix
2657-
else:
2658-
imap_filter = self._ctx.imap_filter
2659-
imap_path_prefix = '.'
2653+
match imap_or_path:
2654+
case _import_map():
2655+
imap_filter = _compose_imap_filters(self._ctx.imap_filter,
2656+
_imap_filter(imap_or_path))
2657+
imap_path_prefix = imap_or_path.path_prefix
2658+
path = imap_or_path.file
2659+
case str():
2660+
imap_filter = self._ctx.imap_filter
2661+
imap_path_prefix = '.'
2662+
path = imap_or_path
2663+
case _:
2664+
raise AssertionError(f'imap_or_path has unexpected type {type(imap_or_path)}')
26602665

26612666
child_ctx = self._ctx._replace(
26622667
imap_filter=imap_filter,
@@ -2674,12 +2679,24 @@ def _import_data_from_project(
26742679
try:
26752680
submanifest = Manifest(topdir=self.topdir, internal_import_ctx=child_ctx)
26762681
except RecursionError as e:
2677-
raise _ManifestImportDepth(None, imap.file if imap else None) from e
2682+
raise _ManifestImportDepth(None, path) from e
26782683

26792684
# Patch up any extension commands in the imported data
26802685
# by allocating them to the project.
2686+
2687+
# If the manifest was imported from a project subdirectory
2688+
# (manifest_path is a relative path within the project),
2689+
# we need to adjust the west_commands paths to be relative
2690+
# to the project root, not to the manifest subdirectory.
2691+
west_commands_to_merge = submanifest._ctx.manifest_west_commands
2692+
manifest_dir = PurePosixPath(path).parent
2693+
if manifest_dir != PurePosixPath('.'):
2694+
west_commands_to_merge = [
2695+
(manifest_dir / cmd).as_posix() for cmd in west_commands_to_merge
2696+
]
2697+
26812698
project.west_commands = _west_commands_merge(
2682-
project.west_commands, submanifest._ctx.manifest_west_commands
2699+
project.west_commands, west_commands_to_merge
26832700
)
26842701

26852702
def _import_content_from_project(self, project: Project, path: str) -> ImportedContentType:

tests/test_manifest.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2095,6 +2095,48 @@ def test_import_project_submanifest_commands_both(manifest_repo):
20952095
assert p1.west_commands == expected
20962096

20972097

2098+
def test_import_project_submanifest_commands_from_project_subdirectory(manifest_repo):
2099+
# When a manifest is imported from a project subdirectory (e.g., mf_subdir/west.yml),
2100+
# and that manifest defines west-commands, the paths should be
2101+
# resolved relative to the manifest subdirectory.
2102+
# This tests _import_path_from_project with a string path.
2103+
2104+
with open(manifest_repo / 'west.yml', 'w') as f:
2105+
f.write('''\
2106+
manifest:
2107+
projects:
2108+
- name: p1
2109+
url: url-placeholder
2110+
import: mf_subdir/west.yml
2111+
''')
2112+
2113+
p1 = manifest_repo / '..' / 'p1'
2114+
create_repo(p1)
2115+
create_branch(p1, 'manifest-rev', checkout=True)
2116+
add_commit(
2117+
p1,
2118+
'add mf_subdir/west.yml with west-commands',
2119+
files={
2120+
'mf_subdir/west.yml': '''\
2121+
manifest:
2122+
projects:
2123+
- name: p2
2124+
url: url-placeholder2
2125+
self:
2126+
west-commands: p2subdir/west-commands.yml
2127+
''',
2128+
},
2129+
)
2130+
checkout_branch(p1, 'master')
2131+
2132+
p1_proj = MF().get_projects(['p1'])[0]
2133+
# The west_commands path should be 'mf_subdir/p2subdir/west-commands.yml',
2134+
# not 'west-commands.yml', to be resolved correctly
2135+
# relative to the project root. See issue #725.
2136+
expected = ['mf_subdir/p2subdir/west-commands.yml']
2137+
assert p1_proj.west_commands == expected
2138+
2139+
20982140
def test_import_map_error_handling():
20992141
# Make sure we handle expected errors when loading import:
21002142
# values that are maps.

0 commit comments

Comments
 (0)