| # Copyright 2018 Open Source Foundries Limited. |
| # |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| '''West's bootstrap/wrapper script. |
| ''' |
| |
| import argparse |
| import os |
| import platform |
| import subprocess |
| import sys |
| |
| import west._bootstrap.version as version |
| |
| if sys.version_info < (3,): |
| sys.exit('fatal error: you are running Python 2') |
| |
| |
| # |
| # Special files and directories in the west installation. |
| # |
| # These are given variable names for clarity, but they can't be |
| # changed without propagating the changes into west itself. |
| # |
| |
| # Top-level west directory, containing west itself and the manifest. |
| WEST_DIR = 'west' |
| # Subdirectory to check out the west source repository into. |
| WEST = 'west' |
| # Default west repository URL. |
| WEST_DEFAULT = 'https://github.com/zephyrproject-rtos/west' |
| # Default revision to check out of the west repository. |
| WEST_REV_DEFAULT = 'master' |
| # File inside of WEST_DIR which marks it as the top level of the |
| # Zephyr project installation. |
| # |
| # (The WEST_DIR name is not distinct enough to use when searching for |
| # the top level; other directories named "west" may exist elsewhere, |
| # e.g. zephyr/doc/west.) |
| WEST_MARKER = '.west_topdir' |
| |
| # Manifest repository directory under WEST_DIR. |
| MANIFEST = 'manifest' |
| # Default manifest repository URL. |
| MANIFEST_DEFAULT = 'https://github.com/zephyrproject-rtos/manifest' |
| # Default revision to check out of the manifest repository. |
| MANIFEST_REV_DEFAULT = 'master' |
| |
| |
| # |
| # Helpers shared between init and wrapper mode |
| # |
| |
| |
| class WestError(RuntimeError): |
| pass |
| |
| |
| class WestNotFound(WestError): |
| '''Neither the current directory nor any parent has a West installation.''' |
| |
| |
| def find_west_topdir(start): |
| '''Find the top-level installation directory, starting at ``start``. |
| |
| If none is found, raises WestNotFound.''' |
| # If you change this function, make sure to update west.util.west_topdir(). |
| |
| cur_dir = start |
| |
| while True: |
| if os.path.isfile(os.path.join(cur_dir, WEST_DIR, WEST_MARKER)): |
| return cur_dir |
| |
| parent_dir = os.path.dirname(cur_dir) |
| if cur_dir == parent_dir: |
| # At the root |
| raise WestNotFound('Could not find a West installation ' |
| 'in this or any parent directory') |
| cur_dir = parent_dir |
| |
| |
| def clone(url, rev, dest): |
| if os.path.exists(dest): |
| raise WestError('refusing to clone into existing location ' + dest) |
| |
| if not url.startswith(('http:', 'https:', 'git:', 'git+shh:', 'file:')): |
| raise WestError('Unknown URL scheme for repository: {}'.format(url)) |
| |
| subprocess.check_call(('git', 'clone', '-b', rev, '--', url, dest)) |
| |
| |
| # |
| # west init |
| # |
| |
| |
| def init(argv): |
| '''Command line handler for ``west init`` invocations. |
| |
| This exits the program with a nonzero exit code if fatal errors occur.''' |
| init_parser = argparse.ArgumentParser( |
| prog='west init', |
| description='Bootstrap initialize a Zephyr installation') |
| init_parser.add_argument( |
| '-b', '--base-url', |
| help='''Base URL for both 'manifest' and 'zephyr' repositories; cannot |
| be given if either -u or -w are''') |
| init_parser.add_argument( |
| '-u', '--manifest-url', |
| help='Zephyr manifest fetch URL, default ' + MANIFEST_DEFAULT) |
| init_parser.add_argument( |
| '--mr', '--manifest-rev', default=MANIFEST_REV_DEFAULT, |
| dest='manifest_rev', |
| help='Manifest revision to fetch, default ' + MANIFEST_REV_DEFAULT) |
| init_parser.add_argument( |
| '-w', '--west-url', |
| help='West fetch URL, default ' + WEST_DEFAULT) |
| init_parser.add_argument( |
| '--wr', '--west-rev', default=WEST_REV_DEFAULT, dest='west_rev', |
| help='West revision to fetch, default ' + WEST_REV_DEFAULT) |
| init_parser.add_argument( |
| 'directory', nargs='?', default=None, |
| help='Initializes in this directory, creating it if necessary') |
| |
| args = init_parser.parse_args(args=argv) |
| directory = args.directory or os.getcwd() |
| |
| if args.base_url: |
| if args.manifest_url or args.west_url: |
| sys.exit('fatal error: -b is incompatible with -u and -w') |
| args.manifest_url = args.base_url.rstrip('/') + '/manifest' |
| args.west_url = args.base_url.rstrip('/') + '/west' |
| else: |
| if not args.manifest_url: |
| args.manifest_url = MANIFEST_DEFAULT |
| if not args.west_url: |
| args.west_url = WEST_DEFAULT |
| |
| try: |
| topdir = find_west_topdir(directory) |
| init_reinit(topdir, args) |
| except WestNotFound: |
| init_bootstrap(directory, args) |
| |
| |
| def hide_file(path): |
| '''Ensure path is a hidden file. |
| |
| On Windows, this uses attrib to hide the file manually. |
| |
| On UNIX systems, this just checks that the path's basename begins |
| with a period ('.'), for it to be hidden already. It's a fatal |
| error if it does not begin with a period in this case. |
| |
| On other systems, this just prints a warning. |
| ''' |
| system = platform.system() |
| |
| if system == 'Windows': |
| subprocess.check_call(['attrib', '+H', path]) |
| elif os.name == 'posix': # Try to check for all Unix, not just macOS/Linux |
| if not os.path.basename(path).startswith('.'): |
| sys.exit("internal error: {} can't be hidden on UNIX".format(path)) |
| else: |
| print("warning: unknown platform {}; {} may not be hidden" |
| .format(system, path), file=sys.stderr) |
| |
| |
| def init_bootstrap(directory, args): |
| '''Bootstrap a new manifest + West installation in the given directory.''' |
| if not os.path.isdir(directory): |
| try: |
| print('Initializing in new directory', directory) |
| os.makedirs(directory, exist_ok=False) |
| except PermissionError: |
| sys.exit('Cannot initialize in {}: permission denied'.format( |
| directory)) |
| except FileExistsError: |
| sys.exit('Something else created {} concurrently; quitting'.format( |
| directory)) |
| except Exception as e: |
| sys.exit("Can't create directory {}: {}".format( |
| directory, e.args)) |
| else: |
| print('Initializing in', directory) |
| |
| # Clone the west source code and the manifest into west/. Git will create |
| # the west/ directory if it does not exist. |
| |
| clone(args.west_url, args.west_rev, |
| os.path.join(directory, WEST_DIR, WEST)) |
| |
| clone(args.manifest_url, args.manifest_rev, |
| os.path.join(directory, WEST_DIR, MANIFEST)) |
| |
| # Create a dotfile to mark the installation. Hide it on Windows. |
| |
| with open(os.path.join(directory, WEST_DIR, WEST_MARKER), 'w') as f: |
| hide_file(f.name) |
| |
| |
| def init_reinit(directory, args): |
| # TODO |
| sys.exit('Re-initializing an existing installation is not yet supported.') |
| |
| |
| # |
| # Wrap a West command |
| # |
| |
| |
| def wrap(argv): |
| printing_version = False |
| |
| if argv and argv[0] in ('-V', '--version'): |
| print('West bootstrapper version: v{} ({})'.format(version.__version__, |
| os.path.dirname(__file__))) |
| printing_version = True |
| |
| start = os.getcwd() |
| try: |
| topdir = find_west_topdir(start) |
| except WestNotFound: |
| if printing_version: |
| sys.exit(0) # run outside of an installation directory |
| else: |
| sys.exit('Error: not a Zephyr directory (or any parent): {}\n' |
| 'Use "west init" to install Zephyr here'.format(start)) |
| |
| west_git_repo = os.path.join(topdir, WEST_DIR, WEST) |
| if printing_version: |
| try: |
| git_describe = subprocess.check_output( |
| ['git', 'describe', '--tags'], |
| stderr=subprocess.DEVNULL, |
| cwd=west_git_repo).decode(sys.getdefaultencoding()).strip() |
| print('West repository version: {} ({})'.format(git_describe, |
| west_git_repo)) |
| except subprocess.CalledProcessError: |
| print('West repository verison: unknown; no tags were found') |
| sys.exit(0) |
| |
| # Replace the wrapper process with the "real" west |
| |
| # sys.argv[1:] strips the argv[0] of the wrapper script itself |
| argv = ([sys.executable, |
| os.path.join(west_git_repo, 'src', 'west', 'main.py')] + |
| argv) |
| |
| try: |
| subprocess.check_call(argv) |
| except subprocess.CalledProcessError as e: |
| sys.exit(1) |
| |
| |
| # |
| # Main entry point |
| # |
| |
| |
| def main(wrap_argv=None): |
| '''Entry point to the wrapper script.''' |
| if wrap_argv is None: |
| wrap_argv = sys.argv[1:] |
| |
| if not wrap_argv or wrap_argv[0] != 'init': |
| wrap(wrap_argv) |
| else: |
| init(wrap_argv[1:]) |
| sys.exit(0) |
| |
| |
| if __name__ == '__main__': |
| main() |