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
3940from west .util import expand_path
4041
@@ -156,19 +157,58 @@ def __init__(self):
156157 'init' ,
157158 'create a west workspace' ,
158159 f'''\
159- Creates a west workspace.
160-
161- With -l, creates a workspace around an existing local repository;
162- without -l, creates a workspace by cloning a manifest repository
163- by URL.
164-
165- With -m, clones the repository at that URL and uses it as the
166- manifest repository. If --mr is not given, the remote's default
167- branch will be used, if it exists.
160+ Initialize a west workspace (topdir) from a west manifest (`west.yml`) by
161+ creating a `.west` directory in the topdir and a local configuration file
162+ `.west/config`.
163+ The West manifest can come from either a git repository (that will be cloned
164+ during workspace initialization) or from an already existing local directory.
168165
169- With neither, -m { MANIFEST_URL_DEFAULT } is assumed.
170-
171- Warning: 'west init' renames and/or deletes temporary files inside the
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+ If it is an already existing directory, it must not contain a .west folder.
177+
178+
179+ 1. Using a Manifest Repository (default)
180+ ----------------------------------------
181+ West clones the given repository (provided via `-m / --manifest-url`).
182+ Note, that 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: the positional 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 positional argument `directory`
194+ specifies the subfolder within the workspace where the manifest repository is
195+ cloned during initialization (defaults to <directory>/zephyr/.git).
196+ With `--mr`, the revision (branch, tag, or sha) of the repository can be
197+ specified that will be used. It defaults to the repository's default branch.
198+
199+ 2. Using a Local Manifest
200+ -------------------------
201+ If `-l / --local` is given, west initializes a workspace from an already
202+ existing `west.yml`. Its location can be specified in the `manifest_directory`
203+ positional argument, which defaults to current working directory.
204+
205+ The topdir (where `.west` is created) is determined as follows:
206+ - argument `-t / --topdir` if provided
207+ - otherwise: `manifest_directory`'s parent
208+
209+ Known Issues
210+ ------------
211+ 'west init' renames and/or deletes temporary files inside the
172212workspace being created. This fails on some filesystems when some
173213development tool or any other program is trying to read/index these
174214temporary files at the same time. For instance, it is required to stop
@@ -193,9 +233,11 @@ def do_add_parser(self, parser_adder):
193233 parser = self ._parser (
194234 parser_adder ,
195235 usage = '''
236+ init with a repository:
237+ %(prog)s [-m URL] [--mr REVISION] [--mf FILE] [-o=GIT_CLONE_OPTION] [-t WORKSPACE_DIR] [directory]
196238
197- %(prog)s [-m URL] [--mr REVISION] [--mf FILE] [-o=GIT_CLONE_OPTION] [directory]
198- %(prog)s -l [-- mf FILE] directory
239+ init with an existing local manifest:
240+ %(prog)s -l [-t WORKSPACE_DIR] [-- mf FILE] [manifest_directory]
199241''' ,
200242 )
201243
@@ -204,6 +246,7 @@ def do_add_parser(self, parser_adder):
204246 parser .add_argument (
205247 '-m' ,
206248 '--manifest-url' ,
249+ metavar = 'URL' ,
207250 help = '''manifest repository URL to clone;
208251 cannot be combined with -l''' ,
209252 )
@@ -212,6 +255,7 @@ def do_add_parser(self, parser_adder):
212255 '--clone-opt' ,
213256 action = 'append' ,
214257 default = [],
258+ metavar = 'GIT_CLONE_OPTION' ,
215259 help = '''additional option to pass to 'git clone'
216260 (e.g. '-o=--depth=1'); may be given more than once;
217261 cannot be combined with -l''' ,
@@ -220,21 +264,36 @@ def do_add_parser(self, parser_adder):
220264 '--mr' ,
221265 '--manifest-rev' ,
222266 dest = 'manifest_rev' ,
267+ metavar = 'REVISION' ,
223268 help = '''manifest repository branch or tag name
224269 to check out first; cannot be combined with -l''' ,
225270 )
226- parser .add_argument (
227- '--mf' , '--manifest-file' , dest = 'manifest_file' , help = 'manifest file name to use'
228- )
229271 parser .add_argument (
230272 '-l' ,
231273 '--local' ,
232274 action = 'store_true' ,
233- help = '''use "directory" as an existing local
234- manifest repository instead of cloning one from
235- MANIFEST_URL; .west is created next to "directory"
236- in this case, and manifest.path points at
237- "directory"''' ,
275+ help = '''initialize workspace from an already existing local
276+ manifest instead of cloning a manifest;
277+ cannot be combined with -m''' ,
278+ )
279+ parser .add_argument (
280+ '--mf' ,
281+ '--manifest-file' ,
282+ dest = 'manifest_file' ,
283+ metavar = 'FILE' ,
284+ help = f'''manifest file to use. It is the relative
285+ path to the manifest file within the manifest_directory.
286+ Defaults to { _WEST_YML } .''' ,
287+ )
288+ parser .add_argument (
289+ '-t' ,
290+ '--topdir' ,
291+ dest = 'topdir' ,
292+ metavar = 'WORKSPACE_DIR' ,
293+ help = '''the workspace directory where .west directory
294+ will be created (WORKSPACE_DIR/.west);
295+ the workspace directory may already exist
296+ and WORKSPACE_DIR/.west must not exist''' ,
238297 )
239298 parser .add_argument (
240299 '--rename-delay' ,
@@ -250,9 +309,15 @@ def do_add_parser(self, parser_adder):
250309 'directory' ,
251310 nargs = '?' ,
252311 default = None ,
253- help = '''with -l, the path to the local manifest repository;
254- without it, the directory to create the workspace in (defaulting
255- to the current working directory in this case)''' ,
312+ metavar = 'directory' ,
313+ help = '''With --local: the path to a local directory which contains
314+ a west manifest (west.yml);
315+ Otherwise, if no --topdir is provided:
316+ the location of the west workspace where .west directory
317+ will be created (<directory>/.west)
318+ Otherwise:
319+ the location where west will clone the manifest repository
320+ ''' ,
256321 )
257322
258323 return parser
@@ -302,17 +367,27 @@ def local(self, args) -> Path:
302367 # Path('..').
303368 #
304369 # https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.parent
305- manifest_dir = Path (args .directory or os . getcwd () ).resolve ()
306- manifest_filename = args .manifest_file or 'west.yml'
370+ manifest_dir = Path (args .directory or '.' ).resolve ()
371+ manifest_filename = args .manifest_file or _WEST_YML
307372 manifest_file = manifest_dir / manifest_filename
308- topdir = manifest_dir .parent
309- rel_manifest = manifest_dir .name
310- west_dir = topdir / WEST_DIR
311-
312373 if not manifest_file .is_file ():
313374 self .die (f'can\' t init: no { manifest_filename } found in { manifest_dir } ' )
314375
315- self .banner ('Initializing from existing manifest repository' , rel_manifest )
376+ topdir = Path (args .topdir or manifest_dir .parent ).resolve ()
377+
378+ if not manifest_file .is_relative_to (topdir ):
379+ self .die (f'{ manifest_file } must be relative to west topdir { topdir } ' )
380+
381+ try :
382+ already = util .west_topdir (manifest_dir , fall_back = False )
383+ self .die_already (already )
384+ except util .WestNotFound :
385+ pass
386+
387+ rel_manifest = manifest_dir .relative_to (topdir )
388+ west_dir = topdir / WEST_DIR
389+
390+ self .banner ('Initializing around existing manifest' , rel_manifest )
316391 self .small_banner (f'Creating { west_dir } and local configuration file' )
317392 self .create (west_dir )
318393 os .chdir (topdir )
@@ -323,8 +398,22 @@ def local(self, args) -> Path:
323398 return topdir
324399
325400 def bootstrap (self , args ) -> Path :
326- topdir = Path (abspath (args .directory or os .getcwd ()))
327- self .banner ('Initializing in' , topdir )
401+ manifest_subdir = Path ()
402+ if args .topdir :
403+ topdir = Path (args .topdir )
404+ if args .directory :
405+ if not Path (args .directory ).is_relative_to (topdir ):
406+ self .die (
407+ f"directory '{ args .directory } ' must be relative "
408+ f"to west topdir '{ args .topdir } '"
409+ )
410+ manifest_subdir = Path (os .path .relpath (args .directory , topdir ))
411+ elif args .directory :
412+ topdir = Path (args .directory )
413+ else :
414+ topdir = Path ()
415+ topdir = topdir .absolute ()
416+ self .banner (f'Initializing in { topdir } ' )
328417
329418 manifest_url = args .manifest_url or MANIFEST_URL_DEFAULT
330419 if args .manifest_rev :
@@ -379,7 +468,7 @@ def bootstrap(self, args) -> Path:
379468 raise
380469
381470 # Verify the manifest file exists.
382- temp_manifest_filename = args .manifest_file or 'west.yml'
471+ temp_manifest_filename = args .manifest_file or _WEST_YML
383472 temp_manifest = tempdir / temp_manifest_filename
384473 if not temp_manifest .is_file ():
385474 self .die (
@@ -396,15 +485,17 @@ def bootstrap(self, args) -> Path:
396485 manifest = Manifest .from_data (
397486 temp_manifest .read_text (encoding = Manifest .encoding ), import_flags = ImportFlag .IGNORE
398487 )
488+
399489 if manifest .yaml_path :
400- manifest_path = manifest .yaml_path
490+ yaml_path = manifest .yaml_path
401491 else :
402492 # We use PurePath() here in case manifest_url is a
403493 # windows-style path. That does the right thing in that
404494 # case, without affecting POSIX platforms, where PurePath
405495 # is PurePosixPath.
406- manifest_path = PurePath (urlparse (manifest_url ).path ).name
496+ yaml_path = PurePath (urlparse (manifest_url ).path ).name
407497
498+ manifest_path : Path = Path (manifest_subdir ) / yaml_path
408499 manifest_abspath = topdir / manifest_path
409500
410501 # Some filesystems like NTFS can't rename files in use.
@@ -431,7 +522,7 @@ def bootstrap(self, args) -> Path:
431522 self .die (e )
432523 self .small_banner ('setting manifest.path to' , manifest_path )
433524 self .config = Configuration (topdir = topdir )
434- self .config .set ('manifest.path' , manifest_path )
525+ self .config .set ('manifest.path' , str ( manifest_path ) )
435526 self .config .set ('manifest.file' , temp_manifest_filename )
436527
437528 return topdir
0 commit comments