add support for manifest filenames other than west.yml
Signed-off-by: Michael Zimmermann <michael.zimmermann@grandcentrix.net>
This commit is contained in:
parent
6350ca4f86
commit
80bf5cf381
11
README.rst
11
README.rst
|
@ -15,10 +15,10 @@ Basic Usage
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
West lets you manage multiple Git repositories under a single directory using a
|
West lets you manage multiple Git repositories under a single directory using a
|
||||||
single file, called the *west manifest file*, or *manifest* for short. The
|
single file, called the *west manifest file*, or *manifest* for short.
|
||||||
manifest file is named ``west.yml``. You use ``west init`` to set up this
|
By default the manifest file is named ``west.yml``.
|
||||||
directory, then ``west update`` to fetch and/or update the repositories named
|
You use ``west init`` to set up this directory, then ``west update`` to fetch
|
||||||
in the manifest.
|
and/or update the repositories named in the manifest.
|
||||||
|
|
||||||
By default, west uses `upstream Zephyr's manifest file
|
By default, west uses `upstream Zephyr's manifest file
|
||||||
<https://github.com/zephyrproject-rtos/zephyr/blob/master/west.yml>`_, but west
|
<https://github.com/zephyrproject-rtos/zephyr/blob/master/west.yml>`_, but west
|
||||||
|
@ -45,7 +45,8 @@ What just happened:
|
||||||
creating working trees in the installation directory ``zephyrproject``.
|
creating working trees in the installation directory ``zephyrproject``.
|
||||||
|
|
||||||
Use ``west init -m`` to specify another manifest repository. Use ``--mr`` to
|
Use ``west init -m`` to specify another manifest repository. Use ``--mr`` to
|
||||||
use a revision other than ``master``.
|
use a revision other than ``master``. Use ``--mf`` to use a manifest file other
|
||||||
|
than ``west.yml``.
|
||||||
|
|
||||||
Additional Commands
|
Additional Commands
|
||||||
-------------------
|
-------------------
|
||||||
|
|
|
@ -181,7 +181,7 @@ class WestApp:
|
||||||
list(self.mle.args)))
|
list(self.mle.args)))
|
||||||
elif isinst(FileNotFoundError):
|
elif isinst(FileNotFoundError):
|
||||||
# This should ordinarily only happen when the top
|
# This should ordinarily only happen when the top
|
||||||
# level west.yml is not found.
|
# level manifest is not found.
|
||||||
log.die(f"file not found: {self.mle.filename}")
|
log.die(f"file not found: {self.mle.filename}")
|
||||||
elif isinst(_ManifestImportDepth):
|
elif isinst(_ManifestImportDepth):
|
||||||
log.die('failed, likely due to manifest import loop')
|
log.die('failed, likely due to manifest import loop')
|
||||||
|
|
|
@ -124,7 +124,7 @@ class Init(_ProjectCommand):
|
||||||
1. Creates a .west directory and clones a manifest repository
|
1. Creates a .west directory and clones a manifest repository
|
||||||
from a git URL to a temporary subdirectory of .west,
|
from a git URL to a temporary subdirectory of .west,
|
||||||
.west/<tmpdir>.
|
.west/<tmpdir>.
|
||||||
2. Parses the manifest file, .west/<tmpdir>/west.yml.
|
2. Parses the manifest file, .west/<tmpdir>/<manifest.file>.
|
||||||
This file's contents can specify manifest.path, the location
|
This file's contents can specify manifest.path, the location
|
||||||
of the manifest repository in the workspace, like so:
|
of the manifest repository in the workspace, like so:
|
||||||
|
|
||||||
|
@ -161,6 +161,8 @@ class Init(_ProjectCommand):
|
||||||
parser.add_argument('--mr', '--manifest-rev', dest='manifest_rev',
|
parser.add_argument('--mr', '--manifest-rev', dest='manifest_rev',
|
||||||
help='''manifest revision to check out and use;
|
help='''manifest revision to check out and use;
|
||||||
cannot be combined with -l''')
|
cannot be combined with -l''')
|
||||||
|
parser.add_argument('--mf', '--manifest-file', dest='manifest_file',
|
||||||
|
help='manifest file name to use')
|
||||||
parser.add_argument('-l', '--local', action='store_true',
|
parser.add_argument('-l', '--local', action='store_true',
|
||||||
help='''use an existing local manifest repository
|
help='''use an existing local manifest repository
|
||||||
instead of cloning one; cannot be combined with
|
instead of cloning one; cannot be combined with
|
||||||
|
@ -212,13 +214,15 @@ class Init(_ProjectCommand):
|
||||||
log.die('--mr cannot be used with -l')
|
log.die('--mr cannot be used with -l')
|
||||||
|
|
||||||
manifest_dir = Path(args.directory or os.getcwd())
|
manifest_dir = Path(args.directory or os.getcwd())
|
||||||
manifest_file = manifest_dir / 'west.yml'
|
manifest_filename = args.manifest_file or 'west.yml'
|
||||||
|
manifest_file = manifest_dir / manifest_filename
|
||||||
topdir = manifest_dir.parent
|
topdir = manifest_dir.parent
|
||||||
rel_manifest = manifest_dir.name
|
rel_manifest = manifest_dir.name
|
||||||
west_dir = topdir / WEST_DIR
|
west_dir = topdir / WEST_DIR
|
||||||
|
|
||||||
if not manifest_file.is_file():
|
if not manifest_file.is_file():
|
||||||
log.die(f'can\'t init: no "west.yml" found in {manifest_dir}')
|
log.die(f'can\'t init: no {manifest_filename} found in '
|
||||||
|
f'{manifest_dir}')
|
||||||
|
|
||||||
log.banner('Initializing from existing manifest repository',
|
log.banner('Initializing from existing manifest repository',
|
||||||
rel_manifest)
|
rel_manifest)
|
||||||
|
@ -226,6 +230,7 @@ class Init(_ProjectCommand):
|
||||||
self.create(west_dir)
|
self.create(west_dir)
|
||||||
os.chdir(topdir)
|
os.chdir(topdir)
|
||||||
update_config('manifest', 'path', os.fspath(rel_manifest))
|
update_config('manifest', 'path', os.fspath(rel_manifest))
|
||||||
|
update_config('manifest', 'file', manifest_filename, topdir=topdir)
|
||||||
|
|
||||||
return topdir
|
return topdir
|
||||||
|
|
||||||
|
@ -257,9 +262,11 @@ class Init(_ProjectCommand):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# Verify the manifest file exists.
|
# Verify the manifest file exists.
|
||||||
temp_manifest = tempdir / 'west.yml'
|
temp_manifest_filename = args.manifest_file or 'west.yml'
|
||||||
|
temp_manifest = tempdir / temp_manifest_filename
|
||||||
if not temp_manifest.is_file():
|
if not temp_manifest.is_file():
|
||||||
log.die(f'can\'t init: no "west.yml" found in {tempdir}\n'
|
log.die(f'can\'t init: no {temp_manifest_filename} found in '
|
||||||
|
f'{tempdir}\n'
|
||||||
f' Hint: check --manifest-url={manifest_url} and '
|
f' Hint: check --manifest-url={manifest_url} and '
|
||||||
f'--manifest-rev={manifest_rev}\n'
|
f'--manifest-rev={manifest_rev}\n'
|
||||||
f' You may need to remove {west_dir} before retrying.')
|
f' You may need to remove {west_dir} before retrying.')
|
||||||
|
@ -290,6 +297,8 @@ class Init(_ProjectCommand):
|
||||||
log.die(e)
|
log.die(e)
|
||||||
log.small_banner('setting manifest.path to', manifest_path)
|
log.small_banner('setting manifest.path to', manifest_path)
|
||||||
update_config('manifest', 'path', manifest_path, topdir=topdir)
|
update_config('manifest', 'path', manifest_path, topdir=topdir)
|
||||||
|
update_config('manifest', 'file', temp_manifest_filename,
|
||||||
|
topdir=topdir)
|
||||||
|
|
||||||
return topdir
|
return topdir
|
||||||
|
|
||||||
|
@ -640,15 +649,15 @@ class Update(_ProjectCommand):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
'update',
|
'update',
|
||||||
'update projects described in west.yml',
|
'update projects described in west manifest',
|
||||||
textwrap.dedent('''\
|
textwrap.dedent('''\
|
||||||
Updates each project repository to the revision specified in
|
Updates each project repository to the revision specified in
|
||||||
the manifest file, west.yml, as follows:
|
the manifest file, as follows:
|
||||||
|
|
||||||
1. Fetch the project's remote to ensure the manifest
|
1. Fetch the project's remote to ensure the manifest
|
||||||
revision is available locally
|
revision is available locally
|
||||||
2. Reset the manifest-rev branch to the revision in
|
2. Reset the manifest-rev branch to the revision in
|
||||||
west.yml
|
the manifest
|
||||||
3. Check out the new manifest-rev commit as a detached HEAD
|
3. Check out the new manifest-rev commit as a detached HEAD
|
||||||
(but see "checked out branches")
|
(but see "checked out branches")
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from typing import Any, Callable, Dict, Iterable, List, NoReturn, \
|
from typing import Any, Callable, Dict, Iterable, List, NoReturn, \
|
||||||
NamedTuple, Optional, TYPE_CHECKING, Union
|
NamedTuple, Optional, Tuple, TYPE_CHECKING, Union
|
||||||
|
|
||||||
from packaging.version import parse as parse_version
|
from packaging.version import parse as parse_version
|
||||||
import pykwalify.core
|
import pykwalify.core
|
||||||
|
@ -138,7 +138,7 @@ def _west_commands_merge(wc1: List[str], wc2: List[str]) -> List[str]:
|
||||||
return wc1 or wc2
|
return wc1 or wc2
|
||||||
|
|
||||||
def _mpath(cp: Optional[configparser.ConfigParser] = None,
|
def _mpath(cp: Optional[configparser.ConfigParser] = None,
|
||||||
topdir: Optional[PathType] = None) -> str:
|
topdir: Optional[PathType] = None) -> Tuple[str, str]:
|
||||||
# Return the value of the manifest.path configuration option
|
# Return the value of the manifest.path configuration option
|
||||||
# in *cp*, a ConfigParser. If not given, create a new one and
|
# in *cp*, a ConfigParser. If not given, create a new one and
|
||||||
# load configuration options with the given *topdir* as west
|
# load configuration options with the given *topdir* as west
|
||||||
|
@ -152,7 +152,10 @@ def _mpath(cp: Optional[configparser.ConfigParser] = None,
|
||||||
cfg.read_config(configfile=cfg.ConfigFile.LOCAL, config=cp, topdir=topdir)
|
cfg.read_config(configfile=cfg.ConfigFile.LOCAL, config=cp, topdir=topdir)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return cp.get('manifest', 'path')
|
path = cp.get('manifest', 'path')
|
||||||
|
filename = cp.get('manifest', 'file', fallback=_WEST_YML)
|
||||||
|
|
||||||
|
return (path, filename)
|
||||||
except (configparser.NoOptionError, configparser.NoSectionError) as e:
|
except (configparser.NoOptionError, configparser.NoSectionError) as e:
|
||||||
raise MalformedConfig('no "manifest.path" config option is set') from e
|
raise MalformedConfig('no "manifest.path" config option is set') from e
|
||||||
|
|
||||||
|
@ -308,10 +311,11 @@ def manifest_path() -> str:
|
||||||
- `MalformedConfig` if the configuration file has no
|
- `MalformedConfig` if the configuration file has no
|
||||||
``manifest.path`` key
|
``manifest.path`` key
|
||||||
|
|
||||||
- ``FileNotFoundError`` if no ``west.yml`` exists in
|
- ``FileNotFoundError`` if no manifest file exists as determined by
|
||||||
``manifest.path``
|
``manifest.path`` and ``manifest.file``
|
||||||
'''
|
'''
|
||||||
ret = os.path.join(util.west_topdir(), _mpath(), _WEST_YML)
|
(mpath, mname) = _mpath()
|
||||||
|
ret = os.path.join(util.west_topdir(), mpath, mname)
|
||||||
# It's kind of annoying to manually instantiate a FileNotFoundError.
|
# It's kind of annoying to manually instantiate a FileNotFoundError.
|
||||||
# This seems to be the best way.
|
# This seems to be the best way.
|
||||||
if not os.path.isfile(ret):
|
if not os.path.isfile(ret):
|
||||||
|
@ -897,7 +901,7 @@ class Manifest:
|
||||||
- If neither *source_file* nor *topdir* is given, the file
|
- If neither *source_file* nor *topdir* is given, the file
|
||||||
system is searched for *topdir*. That workspace's
|
system is searched for *topdir*. That workspace's
|
||||||
``manifest.path`` configuration option is used to find
|
``manifest.path`` configuration option is used to find
|
||||||
*source_file*, ``topdir/<manifest.path>/west.yml``.
|
*source_file*, ``topdir/<manifest.path>/<manifest.file>``.
|
||||||
|
|
||||||
- If only *source_file* is given, *topdir* is found
|
- If only *source_file* is given, *topdir* is found
|
||||||
starting there. The directory containing *source_file*
|
starting there. The directory containing *source_file*
|
||||||
|
@ -932,10 +936,10 @@ class Manifest:
|
||||||
# neither source_file nor topdir: search the filesystem
|
# neither source_file nor topdir: search the filesystem
|
||||||
# for the workspace and use its manifest.path.
|
# for the workspace and use its manifest.path.
|
||||||
topdir = util.west_topdir()
|
topdir = util.west_topdir()
|
||||||
mpath = _mpath(topdir=topdir)
|
(mpath, mname) = _mpath(topdir=topdir)
|
||||||
kwargs.update({
|
kwargs.update({
|
||||||
'topdir': topdir,
|
'topdir': topdir,
|
||||||
'source_file': os.path.join(topdir, mpath, _WEST_YML),
|
'source_file': os.path.join(topdir, mpath, mname),
|
||||||
'manifest_path': mpath
|
'manifest_path': mpath
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
|
@ -960,8 +964,8 @@ class Manifest:
|
||||||
|
|
||||||
# Read manifest.path from topdir/.west/config, and use it
|
# Read manifest.path from topdir/.west/config, and use it
|
||||||
# to locate source_file.
|
# to locate source_file.
|
||||||
mpath = _mpath(topdir=topdir)
|
(mpath, mname) = _mpath(topdir=topdir)
|
||||||
source_file = os.path.join(topdir, mpath, _WEST_YML)
|
source_file = os.path.join(topdir, mpath, mname)
|
||||||
kwargs.update({
|
kwargs.update({
|
||||||
'source_file': source_file,
|
'source_file': source_file,
|
||||||
'manifest_path': mpath,
|
'manifest_path': mpath,
|
||||||
|
@ -1747,7 +1751,7 @@ class Manifest:
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
# We may need to fetch a new manifest-rev, e.g. if
|
# We may need to fetch a new manifest-rev, e.g. if
|
||||||
# revision is a branch that didn't used to have a
|
# revision is a branch that didn't used to have a
|
||||||
# west.yml, but now does.
|
# manifest, but now does.
|
||||||
content = self._importer(project, path)
|
content = self._importer(project, path)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
# We may need a new manifest-rev, e.g. if revision is
|
# We may need a new manifest-rev, e.g. if revision is
|
||||||
|
|
|
@ -589,6 +589,70 @@ def test_init_local_missing_west_yml_failure(repos_tmpdir):
|
||||||
cmd(f'init -l "{zephyr_install_dir}"')
|
cmd(f'init -l "{zephyr_install_dir}"')
|
||||||
|
|
||||||
|
|
||||||
|
def test_init_local_with_manifest_filename(repos_tmpdir):
|
||||||
|
# Test 'west init --mf -l' on a local repo
|
||||||
|
|
||||||
|
manifest = repos_tmpdir / 'repos' / 'zephyr'
|
||||||
|
workspace = repos_tmpdir / 'workspace'
|
||||||
|
zephyr_install_dir = workspace / 'zephyr'
|
||||||
|
|
||||||
|
# Do a local clone of manifest repo
|
||||||
|
clone(str(manifest), str(zephyr_install_dir))
|
||||||
|
os.rename(str(zephyr_install_dir / 'west.yml'),
|
||||||
|
str(zephyr_install_dir / 'project.yml'))
|
||||||
|
|
||||||
|
# fails because west.yml is missing
|
||||||
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
|
cmd(f'init -l "{zephyr_install_dir}"')
|
||||||
|
|
||||||
|
# create a manifest with a syntax error so we can test if it's being parsed
|
||||||
|
with open(zephyr_install_dir / 'west.yml', 'w') as f:
|
||||||
|
f.write('[')
|
||||||
|
|
||||||
|
cwd = os.getcwd()
|
||||||
|
cmd(f'init -l "{zephyr_install_dir}"')
|
||||||
|
|
||||||
|
# init with a local manifest doesn't parse the file, so let's access it
|
||||||
|
workspace.chdir()
|
||||||
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
|
cmd('list')
|
||||||
|
|
||||||
|
os.chdir(cwd)
|
||||||
|
shutil.move(workspace / '.west', workspace / '.west-syntaxerror')
|
||||||
|
|
||||||
|
# success
|
||||||
|
cmd(f'init --mf project.yml -l "{zephyr_install_dir}"')
|
||||||
|
workspace.chdir()
|
||||||
|
config.read_config()
|
||||||
|
cmd('update')
|
||||||
|
|
||||||
|
|
||||||
|
def test_init_with_manifest_filename(repos_tmpdir):
|
||||||
|
# Test 'west init --mf' on a normal repo
|
||||||
|
|
||||||
|
west_tmpdir = repos_tmpdir / 'workspace'
|
||||||
|
manifest = repos_tmpdir / 'repos' / 'zephyr'
|
||||||
|
|
||||||
|
with open(manifest / 'west.yml', 'r') as f:
|
||||||
|
manifest_data = f.read()
|
||||||
|
|
||||||
|
# also creates a west.yml with a syntax error to verify west doesn't even
|
||||||
|
# try to load the file
|
||||||
|
add_commit(str(manifest), 'rename manifest',
|
||||||
|
files={'west.yml': '[', 'project.yml': manifest_data})
|
||||||
|
|
||||||
|
# syntax error
|
||||||
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
|
cmd(f'init -m "{manifest}" "{west_tmpdir}"')
|
||||||
|
shutil.move(west_tmpdir, repos_tmpdir / 'workspace-syntaxerror')
|
||||||
|
|
||||||
|
# success
|
||||||
|
cmd(f'init -m "{manifest}" --mf project.yml "{west_tmpdir}"')
|
||||||
|
west_tmpdir.chdir()
|
||||||
|
config.read_config()
|
||||||
|
cmd('update')
|
||||||
|
|
||||||
|
|
||||||
def test_extension_command_execution(west_init_tmpdir):
|
def test_extension_command_execution(west_init_tmpdir):
|
||||||
with pytest.raises(subprocess.CalledProcessError):
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
cmd('test-extension')
|
cmd('test-extension')
|
||||||
|
|
Loading…
Reference in New Issue