-
Notifications
You must be signed in to change notification settings - Fork 153
fix: Allow west commands to be imported from a project subdirectory if manifest is located in subdirectory #920
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2496,7 +2496,7 @@ def _load_project(self, pd: dict, url_bases: dict[str, str], defaults: _defaults | |
| path=path, | ||
| submodules=self._load_submodules(pd.get('submodules'), f'project {name}'), | ||
| clone_depth=pd.get('clone-depth'), | ||
| west_commands=pd.get('west-commands'), | ||
| west_commands=Path(pd.get('west-commands')).as_posix() if pd.get('west-commands') else None, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This normalizes
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we go for |
||
| topdir=self.topdir, | ||
| remote_name=remote, | ||
| groups=groups, | ||
|
|
@@ -2628,7 +2628,7 @@ def _import_path_from_project(self, project: Project, path: str) -> None: | |
| return | ||
|
|
||
| for data in imported: | ||
| self._import_data_from_project(project, data, None) | ||
| self._import_data_from_project(project, data, path) | ||
|
|
||
| _logger.debug(f'done resolving import {path} for {project}') | ||
|
|
||
|
|
@@ -2647,16 +2647,22 @@ def _import_map_from_project(self, project: Project, imp: dict) -> None: | |
| _logger.debug(f'done resolving import {imap} for {project}') | ||
|
|
||
| def _import_data_from_project( | ||
| self, project: Project, data: Any, imap: _import_map | None | ||
| self, project: Project, data: Any, imap_or_mfpath: _import_map | str | ||
| ) -> None: | ||
| # Destructively add the imported data into our 'projects' map. | ||
|
|
||
| if imap is not None: | ||
| imap_filter = _compose_imap_filters(self._ctx.imap_filter, _imap_filter(imap)) | ||
| imap_path_prefix = imap.path_prefix | ||
| else: | ||
| imap_filter = self._ctx.imap_filter | ||
| imap_path_prefix = '.' | ||
| match imap_or_mfpath: | ||
| case _import_map(): | ||
| imap_filter = _compose_imap_filters( | ||
| self._ctx.imap_filter, _imap_filter(imap_or_mfpath) | ||
| ) | ||
| imap_path_prefix = imap_or_mfpath.path_prefix | ||
| mfst_path = imap_or_mfpath.file | ||
| case str(): | ||
| imap_filter = self._ctx.imap_filter | ||
| imap_path_prefix = '.' | ||
| mfst_path = imap_or_mfpath | ||
| case _: | ||
| raise AssertionError(f'imap_or_mfpath has unexpected type {type(imap_or_mfpath)}') | ||
|
|
||
| child_ctx = self._ctx._replace( | ||
| imap_filter=imap_filter, | ||
|
|
@@ -2674,13 +2680,21 @@ def _import_data_from_project( | |
| try: | ||
| submanifest = Manifest(topdir=self.topdir, internal_import_ctx=child_ctx) | ||
| except RecursionError as e: | ||
| raise _ManifestImportDepth(None, imap.file if imap else None) from e | ||
| raise _ManifestImportDepth(None, mfst_path) from e | ||
|
|
||
| # Patch up any extension commands in the imported data | ||
| # by allocating them to the project. | ||
| project.west_commands = _west_commands_merge( | ||
| project.west_commands, submanifest._ctx.manifest_west_commands | ||
| ) | ||
|
|
||
| # If the manifest was imported from a project subdirectory | ||
| # (manifest_path is a relative path within the project), | ||
| # we need to adjust the west_commands paths to be relative | ||
| # to the project root, not to the manifest subdirectory. | ||
| mfst_dir = Path(mfst_path).parent if _is_yml(mfst_path) else Path(mfst_path) | ||
| west_commands_to_merge = [ | ||
| (mfst_dir / cmd).as_posix() for cmd in submanifest._ctx.manifest_west_commands | ||
| ] | ||
|
|
||
| project.west_commands = _west_commands_merge(project.west_commands, west_commands_to_merge) | ||
|
|
||
| def _import_content_from_project(self, project: Project, path: str) -> ImportedContentType: | ||
| if not (self._ctx.import_flags & ImportFlag.FORCE_PROJECTS) and project.is_cloned(): | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2095,6 +2095,67 @@ def test_import_project_submanifest_commands_both(manifest_repo): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert p1.west_commands == expected | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_import_project_submanifest_commands_from_project_subdirectory(manifest_repo): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # When a manifest is imported from a project subdirectory (e.g., mf_subdir/west.yml), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # and that manifest defines west-commands, the paths should be | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # resolved relative to the manifest subdirectory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # This tests _import_path_from_project with a string path. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with open(manifest_repo / 'west.yml', 'w') as f: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f.write('''\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| manifest: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| projects: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: p1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url: url-placeholder | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import: mf_subdir/west.yml | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ''') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| p1 = manifest_repo / '..' / 'p1' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| create_repo(p1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| create_branch(p1, 'manifest-rev', checkout=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| add_commit( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| p1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'add mf_subdir/west.yml with west-commands', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| files={ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'mf_subdir/west.yml': '''\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| manifest: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| projects: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: p2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url: url-placeholder2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| west-commands: p2subdir/west-commands.yml | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ''', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| checkout_branch(p1, 'master') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Case A: import as a string path to the submanifest file. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| p1_proj = MF().get_projects(['p1'])[0] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # The west_commands path should be 'mf_subdir/p2subdir/west-commands.yml', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # not 'west-commands.yml', to be resolved correctly | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # relative to the project root. See issue #725. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expected = ['mf_subdir/p2subdir/west-commands.yml'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert p1_proj.west_commands == expected | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Case B: import using an import-map whose 'file' is a directory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Re-write the top-level manifest to use an import map instead of | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # a string; the imported manifest still lives at mf_subdir/west.yml. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with open(manifest_repo / 'west.yml', 'w') as f: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f.write('''\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| manifest: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| projects: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: p1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url: url-placeholder | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| file: mf_subdir | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ''') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Reload and check the west_commands were resolved the same way. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| p1_proj = MF().get_projects(['p1'])[0] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expected = ['mf_subdir/p2subdir/west-commands.yml'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert p1_proj.west_commands == expected | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_import_map_error_handling(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Make sure we handle expected errors when loading import: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # values that are maps. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
2159
to
2161
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_import_map_error_handling(): | |
| # Make sure we handle expected errors when loading import: | |
| # values that are maps. | |
| def test_import_project_submanifest_commands_from_project_subdirectory_import_map( | |
| manifest_repo, | |
| ): | |
| # Similar to test_import_project_submanifest_commands_from_project_subdirectory, | |
| # but this tests using an import map with a 'file' key instead of a string path. | |
| # When a manifest is imported from a project subdirectory (e.g., mf_subdir/west.yml), | |
| # and that manifest defines west-commands, the paths should be resolved relative to | |
| # the manifest subdirectory. This tests _import_path_from_project with an import map. | |
| with open(manifest_repo / 'west.yml', 'w') as f: | |
| f.write('''\ | |
| manifest: | |
| projects: | |
| - name: p1 | |
| url: url-placeholder | |
| import: | |
| file: mf_subdir/west.yml | |
| ''') | |
| p1 = manifest_repo / '..' / 'p1' | |
| create_repo(p1) | |
| create_branch(p1, 'manifest-rev', checkout=True) | |
| add_commit( | |
| p1, | |
| 'add mf_subdir/west.yml with west-commands (import map)', | |
| files={ | |
| 'mf_subdir/west.yml': '''\ | |
| manifest: | |
| projects: | |
| - name: p2 | |
| url: url-placeholder2 | |
| self: | |
| west-commands: p2subdir/west-commands.yml | |
| ''', | |
| }, | |
| ) | |
| checkout_branch(p1, 'master') | |
| p1_proj = MF().get_projects(['p1'])[0] | |
| # The west_commands path should be 'mf_subdir/p2subdir/west-commands.yml', | |
| # not 'west-commands.yml', to be resolved correctly relative to the project root. | |
| expected = ['mf_subdir/p2subdir/west-commands.yml'] | |
| for a, e in zip(p1_proj.west_commands, expected, strict=True): | |
| assert PurePath(a) == PurePath(e) | |
| def test_import_map_error_handling(): | |
| # Make sure we handle expected errors when loading import: | |
| # values that are maps. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's an interesting suggestion. But that's also a MASSIVE duplication of the other test code for very little additional coverage :-( @nmunnich maybe you could easily combine both tests (yours and the copilot ~duplicate) into a single test like this:
manifest:
projects:
- name: pA
url: url-placeholderA
import: mf_subdirA/west.yml
- name: pB
url: url-placeholderB
import:
file: mf_subdirB/west.ymlThen you can re-use the identical add_commit() twice in both A and B.
Uh oh!
There was an error while loading. Please reload this page.