Skip to content

Commit 34985ff

Browse files
support 'west init --topdir'
Added new argument for west init to specify the west topdir when a workspace is inizialized.
1 parent a3af732 commit 34985ff

File tree

2 files changed

+315
-74
lines changed

2 files changed

+315
-74
lines changed

src/west/app/project.py

Lines changed: 127 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,18 @@
2424
from west import util
2525
from west.commands import CommandError, Verbosity, WestCommand
2626
from west.configuration import Configuration
27-
from west.manifest import MANIFEST_REV_BRANCH as MANIFEST_REV
28-
from west.manifest import QUAL_MANIFEST_REV_BRANCH as QUAL_MANIFEST_REV
29-
from west.manifest import QUAL_REFS_WEST as QUAL_REFS
3027
from west.manifest import (
28+
_WEST_YML,
3129
ImportFlag,
3230
Manifest,
3331
ManifestImportFailed,
3432
ManifestProject,
3533
Submodule,
3634
_manifest_content_at,
3735
)
36+
from west.manifest import MANIFEST_REV_BRANCH as MANIFEST_REV
37+
from west.manifest import QUAL_MANIFEST_REV_BRANCH as QUAL_MANIFEST_REV
38+
from west.manifest import QUAL_REFS_WEST as QUAL_REFS
3839
from west.manifest import is_group as is_project_group
3940

4041
#
@@ -155,19 +156,60 @@ def __init__(self):
155156
'init',
156157
'create a west workspace',
157158
f'''\
158-
Creates a west workspace.
159-
160-
With -l, creates a workspace around an existing local repository;
161-
without -l, creates a workspace by cloning a manifest repository
162-
by URL.
163-
164-
With -m, clones the repository at that URL and uses it as the
165-
manifest repository. If --mr is not given, the remote's default
166-
branch will be used, if it exists.
167-
168-
With neither, -m {MANIFEST_URL_DEFAULT} is assumed.
169-
170-
Warning: 'west init' renames and/or deletes temporary files inside the
159+
Initialize a west workspace (topdir) around a west manifest (`west.yml`) by
160+
creating a `.west` directory in the topdir and a local configuration file
161+
`.west/config`.
162+
The West manifest can come from either a remote repository (that will be cloned
163+
during workspace initialization) or from an already existing local directory.
164+
Config option `manifest.path` is set accordingly (see below).
165+
166+
Arguments
167+
---------
168+
--mf / --manifest-file
169+
The relative path to the manifest file within the manifest repository
170+
or directory. Config option manifest.file will be set to this value.
171+
Defaults to `{_WEST_YML}` if not provided.
172+
173+
-t / --topdir
174+
Specifies the directory where west should create the workspace.
175+
The `.west` folder will be created inside this directory.
176+
177+
178+
1. Remote Manifest Repository (default)
179+
---------------------------------------
180+
West clones a remote repository (provided via `-m / --manifest-url`) into a
181+
folder in the west workspace. Config option `manifest.path` is set to point to
182+
this git clone. The repository must contain a west manifest.
183+
184+
If no `-m / --manifest-url` is provided, west uses Zephyr URL by default:
185+
{MANIFEST_URL_DEFAULT}.
186+
187+
The topdir (where `.west` is created) is determined as follows:
188+
- argument `-t / --topdir` if provided
189+
- otherwise: argument `directory` if provided
190+
- otherwise: the current working directory
191+
192+
If both `-t / --topdir` and `directory` are provided, `-t / --topdir`
193+
specifies the workspace directory, while `directory` specifies the subfolder
194+
within the workspace where the remote repository is cloned during
195+
initialization (e.g. it results in <directory>/zephyr/.git).
196+
With `--mr`, the revision (branch, tag, or sha) of the remote repository can be
197+
specified that will be cloned. It defaults to the repository's default branch.
198+
199+
2. Local Manifest
200+
-----------------
201+
If `-l / --local` is given, west initializes a workspace around an already
202+
existing `west.yml`, which is located in `manifest_directory` (defaults to
203+
current working directory). Config option `manifest.path` is set to point to
204+
`manifest_directory`.
205+
206+
The topdir (where `.west` is created) is determined as follows:
207+
- argument `-t / --topdir` if provided
208+
- otherwise: `manifest_directory`'s parent
209+
210+
Known Issues
211+
------------
212+
'west init' renames and/or deletes temporary files inside the
171213
workspace being created. This fails on some filesystems when some
172214
development tool or any other program is trying to read/index these
173215
temporary files at the same time. For instance, it is required to stop
@@ -192,9 +234,10 @@ def do_add_parser(self, parser_adder):
192234
parser = self._parser(
193235
parser_adder,
194236
usage='''
195-
196-
%(prog)s [-m URL] [--mr REVISION] [--mf FILE] [-o=GIT_CLONE_OPTION] [directory]
197-
%(prog)s -l [--mf FILE] directory
237+
remote repository:
238+
%(prog)s [-m URL] [--mr REVISION] [--mf FILE] [-o=GIT_CLONE_OPTION] [-t WORKSPACE_DIR] [directory]
239+
local manifest:
240+
%(prog)s -l [-t WORKSPACE_DIR] [--mf FILE] [manifest_directory]
198241
''',
199242
)
200243

@@ -203,14 +246,16 @@ def do_add_parser(self, parser_adder):
203246
parser.add_argument(
204247
'-m',
205248
'--manifest-url',
206-
help='''manifest repository URL to clone;
249+
metavar='URL',
250+
help='''remote manifest repository URL to clone;
207251
cannot be combined with -l''',
208252
)
209253
parser.add_argument(
210254
'-o',
211255
'--clone-opt',
212256
action='append',
213257
default=[],
258+
metavar='GIT_CLONE_OPTION',
214259
help='''additional option to pass to 'git clone'
215260
(e.g. '-o=--depth=1'); may be given more than once;
216261
cannot be combined with -l''',
@@ -219,21 +264,33 @@ def do_add_parser(self, parser_adder):
219264
'--mr',
220265
'--manifest-rev',
221266
dest='manifest_rev',
267+
metavar='REVISION',
222268
help='''manifest repository branch or tag name
223269
to check out first; cannot be combined with -l''',
224270
)
225-
parser.add_argument(
226-
'--mf', '--manifest-file', dest='manifest_file', help='manifest file name to use'
227-
)
228271
parser.add_argument(
229272
'-l',
230273
'--local',
231274
action='store_true',
232-
help='''use "directory" as an existing local
233-
manifest repository instead of cloning one from
234-
MANIFEST_URL; .west is created next to "directory"
235-
in this case, and manifest.path points at
236-
"directory"''',
275+
help='''initialize workspace around an already existing local
276+
manifest instead of cloning a remote manifest.''',
277+
)
278+
parser.add_argument(
279+
'--mf',
280+
'--manifest-file',
281+
dest='manifest_file',
282+
metavar='FILE',
283+
help=f'''manifest file to use. It is the relative
284+
path of the manifest file within the repository
285+
(remote or local). Defaults to {_WEST_YML}.''',
286+
)
287+
parser.add_argument(
288+
'-t',
289+
'--topdir',
290+
dest='topdir',
291+
metavar='WORKSPACE_DIR',
292+
help='''the directory of the west workspace, where
293+
.west will be created in.''',
237294
)
238295
parser.add_argument(
239296
'--rename-delay',
@@ -249,9 +306,13 @@ def do_add_parser(self, parser_adder):
249306
'directory',
250307
nargs='?',
251308
default=None,
252-
help='''with -l, the path to the local manifest repository;
253-
without it, the directory to create the workspace in (defaulting
254-
to the current working directory in this case)''',
309+
metavar='directory',
310+
help='''With --local: the path to the local manifest repository
311+
containing a west.yml;
312+
Otherwise: it depends whether --topdir is used or not.
313+
It is either the workspace directory being created (if no
314+
--topdir is provided), or the directory where west will
315+
clone the remote manifest (<directory>/<name>/.git)''',
255316
)
256317

257318
return parser
@@ -302,16 +363,28 @@ def local(self, args) -> Path:
302363
#
303364
# https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.parent
304365
manifest_dir = Path(args.directory or os.getcwd()).resolve()
305-
manifest_filename = args.manifest_file or 'west.yml'
366+
manifest_filename = args.manifest_file or _WEST_YML
306367
manifest_file = manifest_dir / manifest_filename
307-
topdir = manifest_dir.parent
308-
rel_manifest = manifest_dir.name
309-
west_dir = topdir / WEST_DIR
310-
311368
if not manifest_file.is_file():
312369
self.die(f'can\'t init: no {manifest_filename} found in {manifest_dir}')
313370

314-
self.banner('Initializing from existing manifest repository', rel_manifest)
371+
if args.topdir:
372+
topdir = Path(args.topdir).resolve()
373+
try:
374+
already = util.west_topdir(topdir, fall_back=False)
375+
self.die_already(already)
376+
except util.WestNotFound:
377+
pass
378+
else:
379+
topdir = manifest_dir.parent.resolve()
380+
381+
if not manifest_file.is_relative_to(topdir):
382+
self.die(f'{manifest_file} must be relative to west topdir')
383+
384+
rel_manifest = manifest_dir.relative_to(topdir)
385+
west_dir = topdir / WEST_DIR
386+
387+
self.banner('Initializing around existing manifest', rel_manifest)
315388
self.small_banner(f'Creating {west_dir} and local configuration file')
316389
self.create(west_dir)
317390
os.chdir(topdir)
@@ -322,8 +395,21 @@ def local(self, args) -> Path:
322395
return topdir
323396

324397
def bootstrap(self, args) -> Path:
325-
topdir = Path(abspath(args.directory or os.getcwd()))
326-
self.banner('Initializing in', topdir)
398+
subdir = '.'
399+
if args.topdir:
400+
topdir = Path(abspath(args.topdir))
401+
if args.directory:
402+
if not Path(abspath(args.directory)).is_relative_to(topdir):
403+
self.die(
404+
f"directory '{args.directory}' must be relative "
405+
f"to west topdir '{args.topdir}'"
406+
)
407+
subdir = os.path.relpath(args.directory, topdir)
408+
elif args.directory:
409+
topdir = Path(abspath(args.directory))
410+
else:
411+
topdir = Path(abspath(os.getcwd()))
412+
self.banner(f'Initializing in {topdir}')
327413

328414
manifest_url = args.manifest_url or MANIFEST_URL_DEFAULT
329415
if args.manifest_rev:
@@ -378,7 +464,7 @@ def bootstrap(self, args) -> Path:
378464
raise
379465

380466
# Verify the manifest file exists.
381-
temp_manifest_filename = args.manifest_file or 'west.yml'
467+
temp_manifest_filename = args.manifest_file or _WEST_YML
382468
temp_manifest = tempdir / temp_manifest_filename
383469
if not temp_manifest.is_file():
384470
self.die(
@@ -404,6 +490,7 @@ def bootstrap(self, args) -> Path:
404490
# is PurePosixPath.
405491
manifest_path = PurePath(urlparse(manifest_url).path).name
406492

493+
manifest_path = str(Path(subdir) / manifest_path)
407494
manifest_abspath = topdir / manifest_path
408495

409496
# Some filesystems like NTFS can't rename files in use.

0 commit comments

Comments
 (0)