2424from west import util
2525from west .commands import CommandError , Verbosity , WestCommand
2626from 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
3027from 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
3839from 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
171213workspace being created. This fails on some filesystems when some
172214development tool or any other program is trying to read/index these
173215temporary 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