Morpheus / Mbed OS mbed-Client-Morpheus-hg

Dependencies:   mbed-os

Committer:
Christopher Haster
Date:
Wed Mar 30 02:41:05 2016 -0500
Revision:
18:1b4252106474
Parent:
17:1e487c450f06
Child:
19:6ace1080b8bb
Added better handling for publish command

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Christopher Haster 15:a6b1f4e65bf4 1 #!/usr/bin/env python
Christopher Haster 15:a6b1f4e65bf4 2
Christopher Haster 15:a6b1f4e65bf4 3 import argparse
Christopher Haster 15:a6b1f4e65bf4 4 import sys
Christopher Haster 15:a6b1f4e65bf4 5 import re
Christopher Haster 15:a6b1f4e65bf4 6 import subprocess
Christopher Haster 15:a6b1f4e65bf4 7 import os
Christopher Haster 15:a6b1f4e65bf4 8 import contextlib
Christopher Haster 15:a6b1f4e65bf4 9 import collections
Christopher Haster 15:a6b1f4e65bf4 10 import shutil
Christopher Haster 15:a6b1f4e65bf4 11 from itertools import *
Christopher Haster 15:a6b1f4e65bf4 12
Christopher Haster 15:a6b1f4e65bf4 13 # Subparser handling
Christopher Haster 15:a6b1f4e65bf4 14 parser = argparse.ArgumentParser()
Christopher Haster 15:a6b1f4e65bf4 15 subparsers = parser.add_subparsers()
Christopher Haster 15:a6b1f4e65bf4 16
Christopher Haster 15:a6b1f4e65bf4 17 def subcommand(name, *args, **kwargs):
Christopher Haster 15:a6b1f4e65bf4 18 def subcommand(command):
Christopher Haster 15:a6b1f4e65bf4 19 subparser = subparsers.add_parser(name, **kwargs)
Christopher Haster 15:a6b1f4e65bf4 20
Christopher Haster 15:a6b1f4e65bf4 21 for arg in args:
Christopher Haster 15:a6b1f4e65bf4 22 if arg.endswith('?'):
Christopher Haster 15:a6b1f4e65bf4 23 subparser.add_argument(arg.strip('?'), nargs='?')
Christopher Haster 15:a6b1f4e65bf4 24 elif arg.endswith('*'):
Christopher Haster 15:a6b1f4e65bf4 25 pass
Christopher Haster 15:a6b1f4e65bf4 26 else:
Christopher Haster 15:a6b1f4e65bf4 27 subparser.add_argument(arg)
Christopher Haster 15:a6b1f4e65bf4 28
Christopher Haster 15:a6b1f4e65bf4 29 def thunk(parsed_args):
Christopher Haster 15:a6b1f4e65bf4 30 ordered_args = [vars(parsed_args)[arg.strip('?*')]
Christopher Haster 15:a6b1f4e65bf4 31 if not arg.endswith('*') else remainder
Christopher Haster 15:a6b1f4e65bf4 32 for arg in args]
Christopher Haster 15:a6b1f4e65bf4 33 return command(*ordered_args)
Christopher Haster 15:a6b1f4e65bf4 34
Christopher Haster 15:a6b1f4e65bf4 35 subparser.set_defaults(command=thunk)
Christopher Haster 15:a6b1f4e65bf4 36 return command
Christopher Haster 15:a6b1f4e65bf4 37 return subcommand
Christopher Haster 15:a6b1f4e65bf4 38
Christopher Haster 15:a6b1f4e65bf4 39 # Process execution
Christopher Haster 15:a6b1f4e65bf4 40 class ProcessException(Exception):
Christopher Haster 15:a6b1f4e65bf4 41 pass
Christopher Haster 15:a6b1f4e65bf4 42
Christopher Haster 15:a6b1f4e65bf4 43 def popen(command, stdin=None, **kwargs):
Christopher Haster 15:a6b1f4e65bf4 44 # print for debugging
Christopher Haster 15:a6b1f4e65bf4 45 print ' '.join(command)
Christopher Haster 15:a6b1f4e65bf4 46 proc = subprocess.Popen(command, **kwargs)
Christopher Haster 15:a6b1f4e65bf4 47
Christopher Haster 15:a6b1f4e65bf4 48 if proc.wait() != 0:
Christopher Haster 15:a6b1f4e65bf4 49 raise ProcessException(proc.returncode)
Christopher Haster 15:a6b1f4e65bf4 50
Christopher Haster 15:a6b1f4e65bf4 51 def pquery(command, stdin=None, **kwargs):
Christopher Haster 15:a6b1f4e65bf4 52 proc = subprocess.Popen(command, stdout=subprocess.PIPE, **kwargs)
Christopher Haster 15:a6b1f4e65bf4 53 stdout, _ = proc.communicate(stdin)
Christopher Haster 15:a6b1f4e65bf4 54
Christopher Haster 15:a6b1f4e65bf4 55 if proc.returncode != 0:
Christopher Haster 15:a6b1f4e65bf4 56 raise ProcessException(proc.returncode)
Christopher Haster 15:a6b1f4e65bf4 57
Christopher Haster 15:a6b1f4e65bf4 58 return stdout
Christopher Haster 15:a6b1f4e65bf4 59
Christopher Haster 15:a6b1f4e65bf4 60 # Directory navigation
Christopher Haster 15:a6b1f4e65bf4 61 @contextlib.contextmanager
Christopher Haster 15:a6b1f4e65bf4 62 def cd(newdir):
Christopher Haster 15:a6b1f4e65bf4 63 prevdir = os.getcwd()
Christopher Haster 15:a6b1f4e65bf4 64 os.chdir(newdir)
Christopher Haster 15:a6b1f4e65bf4 65 try:
Christopher Haster 15:a6b1f4e65bf4 66 yield
Christopher Haster 15:a6b1f4e65bf4 67 finally:
Christopher Haster 15:a6b1f4e65bf4 68 os.chdir(prevdir)
Christopher Haster 15:a6b1f4e65bf4 69
Christopher Haster 15:a6b1f4e65bf4 70 def iterlibs(dir=None):
Christopher Haster 15:a6b1f4e65bf4 71 for root, dirs, files in os.walk(dir or os.getcwd()):
Christopher Haster 15:a6b1f4e65bf4 72 if os.path.basename(root).startswith('.'):
Christopher Haster 15:a6b1f4e65bf4 73 del dirs[:]
Christopher Haster 15:a6b1f4e65bf4 74 continue
Christopher Haster 15:a6b1f4e65bf4 75
Christopher Haster 15:a6b1f4e65bf4 76 for file in files:
Christopher Haster 15:a6b1f4e65bf4 77 if file.startswith('.'):
Christopher Haster 15:a6b1f4e65bf4 78 continue
Christopher Haster 15:a6b1f4e65bf4 79 elif file.endswith('.lib'):
Christopher Haster 15:a6b1f4e65bf4 80 with open(os.path.join(root, file)) as f:
Christopher Haster 15:a6b1f4e65bf4 81 yield f.read().strip(), os.path.join(root, file[:-4])
Christopher Haster 15:a6b1f4e65bf4 82 if file[:-4] in dirs:
Christopher Haster 15:a6b1f4e65bf4 83 dirs.remove(file[:-4])
Christopher Haster 15:a6b1f4e65bf4 84
Christopher Haster 15:a6b1f4e65bf4 85 def savelib(repo):
Christopher Haster 15:a6b1f4e65bf4 86 print repo.name, '->', repo.url
Christopher Haster 15:a6b1f4e65bf4 87
Christopher Haster 15:a6b1f4e65bf4 88 with open(repo.lib, 'w') as f:
Christopher Haster 15:a6b1f4e65bf4 89 f.write(repo.url + '\n')
Christopher Haster 15:a6b1f4e65bf4 90
Christopher Haster 15:a6b1f4e65bf4 91
Christopher Haster 15:a6b1f4e65bf4 92 # Handling for multiple version controls
Christopher Haster 15:a6b1f4e65bf4 93 class Repo(object):
Christopher Haster 15:a6b1f4e65bf4 94 def __init__(self, path=None):
Christopher Haster 15:a6b1f4e65bf4 95 self.path = path or os.getcwd()
Christopher Haster 15:a6b1f4e65bf4 96 self.name = os.path.basename(self.path)
Christopher Haster 15:a6b1f4e65bf4 97 self.update()
Christopher Haster 15:a6b1f4e65bf4 98
Christopher Haster 15:a6b1f4e65bf4 99 @classmethod
Christopher Haster 15:a6b1f4e65bf4 100 def fromurl(cls, url, name=None):
Christopher Haster 15:a6b1f4e65bf4 101 repo = cls.__new__(cls)
Christopher Haster 15:a6b1f4e65bf4 102
Christopher Haster 15:a6b1f4e65bf4 103 m = re.match('^(.*/([+a-zA-Z0-9_-]+)/?)(?:#(.*))?$', url)
Christopher Haster 15:a6b1f4e65bf4 104 repo.repo = m.group(1)
Christopher Haster 15:a6b1f4e65bf4 105 repo.name = name or m.group(2)
Christopher Haster 15:a6b1f4e65bf4 106 repo.hash = m.group(3)
Christopher Haster 15:a6b1f4e65bf4 107
Christopher Haster 15:a6b1f4e65bf4 108 repo.path = os.path.join(os.getcwd(), repo.name)
Christopher Haster 15:a6b1f4e65bf4 109 return repo
Christopher Haster 15:a6b1f4e65bf4 110
Christopher Haster 15:a6b1f4e65bf4 111 @property
Christopher Haster 15:a6b1f4e65bf4 112 def lib(self):
Christopher Haster 15:a6b1f4e65bf4 113 return self.path + '.lib'
Christopher Haster 15:a6b1f4e65bf4 114
Christopher Haster 15:a6b1f4e65bf4 115 @property
Christopher Haster 15:a6b1f4e65bf4 116 def url(self):
Christopher Haster 15:a6b1f4e65bf4 117 if self.repo:
Christopher Haster 15:a6b1f4e65bf4 118 if self.hash:
Christopher Haster 15:a6b1f4e65bf4 119 return self.repo + '#' + self.hash
Christopher Haster 15:a6b1f4e65bf4 120 else:
Christopher Haster 15:a6b1f4e65bf4 121 return self.repo
Christopher Haster 15:a6b1f4e65bf4 122
Christopher Haster 15:a6b1f4e65bf4 123 def update(self):
Christopher Haster 15:a6b1f4e65bf4 124 if os.path.isdir(self.path):
Christopher Haster 15:a6b1f4e65bf4 125 self.type = self.gettype()
Christopher Haster 15:a6b1f4e65bf4 126 self.hash = self.gethash()
Christopher Haster 15:a6b1f4e65bf4 127
Christopher Haster 15:a6b1f4e65bf4 128 if os.path.isfile(self.lib):
Christopher Haster 15:a6b1f4e65bf4 129 with open(self.lib) as f:
Christopher Haster 15:a6b1f4e65bf4 130 temp = Repo.fromurl(f.read())
Christopher Haster 15:a6b1f4e65bf4 131 self.repo = temp.repo
Christopher Haster 15:a6b1f4e65bf4 132
Christopher Haster 15:a6b1f4e65bf4 133 def gettype(self):
Christopher Haster 15:a6b1f4e65bf4 134 for type, dir in [('hg', '.hg'), ('git', '.git')]:
Christopher Haster 15:a6b1f4e65bf4 135 if os.path.isdir(os.path.join(self.path, dir)):
Christopher Haster 15:a6b1f4e65bf4 136 return type
Christopher Haster 15:a6b1f4e65bf4 137
Christopher Haster 15:a6b1f4e65bf4 138 def gethash(self):
Christopher Haster 15:a6b1f4e65bf4 139 with cd(self.path):
Christopher Haster 15:a6b1f4e65bf4 140 if self.type == 'git':
Christopher Haster 15:a6b1f4e65bf4 141 return pquery(['git', 'rev-parse', '--short', 'HEAD']).strip()
Christopher Haster 15:a6b1f4e65bf4 142 elif self.type == 'hg':
Christopher Haster 15:a6b1f4e65bf4 143 return pquery(['hg', 'id', '-i']).strip()
Christopher Haster 15:a6b1f4e65bf4 144
Christopher Haster 15:a6b1f4e65bf4 145 # Clone command
Christopher Haster 15:a6b1f4e65bf4 146 @subcommand('import', 'url', 'name?',
Christopher Haster 15:a6b1f4e65bf4 147 help='recursively import a repository')
Christopher Haster 15:a6b1f4e65bf4 148 def import_(url, name=None):
Christopher Haster 15:a6b1f4e65bf4 149 repo = Repo.fromurl(url, name)
Christopher Haster 15:a6b1f4e65bf4 150
Christopher Haster 15:a6b1f4e65bf4 151 for type in ['hg', 'git']:
Christopher Haster 15:a6b1f4e65bf4 152 try:
Christopher Haster 15:a6b1f4e65bf4 153 popen([type, 'clone', repo.repo, repo.path])
Christopher Haster 15:a6b1f4e65bf4 154 break
Christopher Haster 15:a6b1f4e65bf4 155 except:
Christopher Haster 15:a6b1f4e65bf4 156 pass
Christopher Haster 15:a6b1f4e65bf4 157
Christopher Haster 15:a6b1f4e65bf4 158 repo.type = repo.gettype()
Christopher Haster 15:a6b1f4e65bf4 159
Christopher Haster 15:a6b1f4e65bf4 160 if repo.hash:
Christopher Haster 15:a6b1f4e65bf4 161 with cd(repo.path):
Christopher Haster 15:a6b1f4e65bf4 162 popen([repo.type, 'checkout', repo.hash])
Christopher Haster 15:a6b1f4e65bf4 163
Christopher Haster 15:a6b1f4e65bf4 164 repo.update()
Christopher Haster 15:a6b1f4e65bf4 165
Christopher Haster 15:a6b1f4e65bf4 166 with cd(repo.path):
Christopher Haster 15:a6b1f4e65bf4 167 for url, lib in iterlibs():
Christopher Haster 15:a6b1f4e65bf4 168 import_(url, lib)
Christopher Haster 15:a6b1f4e65bf4 169
Christopher Haster 15:a6b1f4e65bf4 170 # Deploy command
Christopher Haster 15:a6b1f4e65bf4 171 @subcommand('deploy',
Christopher Haster 15:a6b1f4e65bf4 172 help='recursively import libraries in current directory')
Christopher Haster 15:a6b1f4e65bf4 173 def deploy():
Christopher Haster 15:a6b1f4e65bf4 174 for url, lib in iterlibs():
Christopher Haster 15:a6b1f4e65bf4 175 import_(url, lib)
Christopher Haster 15:a6b1f4e65bf4 176
Christopher Haster 15:a6b1f4e65bf4 177 # Install/uninstall command
Christopher Haster 15:a6b1f4e65bf4 178 @subcommand('add', 'url', 'name?',
Christopher Haster 15:a6b1f4e65bf4 179 help='add a library to the current repository')
Christopher Haster 15:a6b1f4e65bf4 180 def add(url, name):
Christopher Haster 15:a6b1f4e65bf4 181 cwd = Repo()
Christopher Haster 15:a6b1f4e65bf4 182 repo = Repo.fromurl(url, name)
Christopher Haster 15:a6b1f4e65bf4 183
Christopher Haster 15:a6b1f4e65bf4 184 import_(url)
Christopher Haster 15:a6b1f4e65bf4 185
Christopher Haster 15:a6b1f4e65bf4 186 repo.update()
Christopher Haster 15:a6b1f4e65bf4 187 savelib(repo)
Christopher Haster 15:a6b1f4e65bf4 188
Christopher Haster 15:a6b1f4e65bf4 189 popen([cwd.type, 'add', repo.lib])
Christopher Haster 15:a6b1f4e65bf4 190
Christopher Haster 15:a6b1f4e65bf4 191 @subcommand('remove', 'library',
Christopher Haster 15:a6b1f4e65bf4 192 help='remove a library from the current repository folder')
Christopher Haster 15:a6b1f4e65bf4 193 def remove(library):
Christopher Haster 15:a6b1f4e65bf4 194 cwd = Repo()
Christopher Haster 15:a6b1f4e65bf4 195 repo = Repo(library)
Christopher Haster 15:a6b1f4e65bf4 196
Christopher Haster 15:a6b1f4e65bf4 197 popen([cwd.type, 'rm', '-f', repo.lib])
Christopher Haster 15:a6b1f4e65bf4 198
Christopher Haster 16:b54f256e339e 199 try:
Christopher Haster 16:b54f256e339e 200 if cwd.type == 'hg':
Christopher Haster 16:b54f256e339e 201 os.remove(repo.lib)
Christopher Haster 16:b54f256e339e 202
Christopher Haster 16:b54f256e339e 203 shutil.rmtree(repo.path)
Christopher Haster 16:b54f256e339e 204 except OSError:
Christopher Haster 16:b54f256e339e 205 pass
Christopher Haster 15:a6b1f4e65bf4 206
Christopher Haster 15:a6b1f4e65bf4 207 # Publish command
Christopher Haster 15:a6b1f4e65bf4 208 @subcommand('publish',
Christopher Haster 15:a6b1f4e65bf4 209 help='recursively publish changes to remote repositories')
Christopher Haster 18:1b4252106474 210 def publish(always=True):
Christopher Haster 15:a6b1f4e65bf4 211 cwd = Repo()
Christopher Haster 15:a6b1f4e65bf4 212
Christopher Haster 15:a6b1f4e65bf4 213 for url, lib in iterlibs():
Christopher Haster 15:a6b1f4e65bf4 214 if os.path.isdir(lib):
Christopher Haster 15:a6b1f4e65bf4 215 with cd(lib):
Christopher Haster 18:1b4252106474 216 publish(False)
Christopher Haster 15:a6b1f4e65bf4 217
Christopher Haster 17:1e487c450f06 218 synch()
Christopher Haster 17:1e487c450f06 219
Christopher Haster 17:1e487c450f06 220 if cwd.type == 'hg':
Christopher Haster 17:1e487c450f06 221 modified = pquery(['hg', 'status'])
Christopher Haster 17:1e487c450f06 222 elif cwd.type == 'git':
Christopher Haster 17:1e487c450f06 223 modified = pquery(['git', 'diff', '--name-only', 'HEAD'])
Christopher Haster 17:1e487c450f06 224
Christopher Haster 17:1e487c450f06 225 if modified:
Christopher Haster 17:1e487c450f06 226 print cwd.path
Christopher Haster 17:1e487c450f06 227 print 'Uncommitted changes in %s' % cwd.name
Christopher Haster 17:1e487c450f06 228 raw_input('Press enter to commit/push')
Christopher Haster 17:1e487c450f06 229
Christopher Haster 17:1e487c450f06 230 popen([cwd.type, 'commit'] + (['-a'] if cwd.type == 'git' else []))
Christopher Haster 18:1b4252106474 231
Christopher Haster 18:1b4252106474 232 if modified or always:
Christopher Haster 18:1b4252106474 233 try:
Christopher Haster 18:1b4252106474 234 popen([cwd.type, 'push'] + (['-a'] if cwd.type == 'git' else []))
Christopher Haster 18:1b4252106474 235 except ProcessException as e:
Christopher Haster 18:1b4252106474 236 sys.exit(e[0])
Christopher Haster 15:a6b1f4e65bf4 237
Christopher Haster 15:a6b1f4e65bf4 238 # Synch command
Christopher Haster 15:a6b1f4e65bf4 239 @subcommand('synch',
Christopher Haster 15:a6b1f4e65bf4 240 help='synchronize lib files')
Christopher Haster 15:a6b1f4e65bf4 241 def synch():
Christopher Haster 15:a6b1f4e65bf4 242 cwd = Repo()
Christopher Haster 15:a6b1f4e65bf4 243
Christopher Haster 15:a6b1f4e65bf4 244 for url, lib in iterlibs():
Christopher Haster 15:a6b1f4e65bf4 245 repo = Repo.fromurl(url, lib)
Christopher Haster 15:a6b1f4e65bf4 246 repo.update()
Christopher Haster 15:a6b1f4e65bf4 247 if lib == repo.url:
Christopher Haster 15:a6b1f4e65bf4 248 continue
Christopher Haster 15:a6b1f4e65bf4 249
Christopher Haster 15:a6b1f4e65bf4 250 savelib(repo)
Christopher Haster 15:a6b1f4e65bf4 251
Christopher Haster 15:a6b1f4e65bf4 252 if cwd.type == 'git':
Christopher Haster 15:a6b1f4e65bf4 253 popen([cwd.type, 'add', repo.lib])
Christopher Haster 15:a6b1f4e65bf4 254
Christopher Haster 15:a6b1f4e65bf4 255 # Compile command
Christopher Haster 15:a6b1f4e65bf4 256 @subcommand('compile', 'args*',
Christopher Haster 15:a6b1f4e65bf4 257 help='compile project using workspace_tools')
Christopher Haster 15:a6b1f4e65bf4 258 def compile(args):
Christopher Haster 15:a6b1f4e65bf4 259 cwd = Repo()
Christopher Haster 15:a6b1f4e65bf4 260
Christopher Haster 15:a6b1f4e65bf4 261 if not os.path.isdir('mbed-os'):
Christopher Haster 15:a6b1f4e65bf4 262 sys.stderr.write('Warning! mbed-os not found?')
Christopher Haster 15:a6b1f4e65bf4 263 sys.exit(-1)
Christopher Haster 15:a6b1f4e65bf4 264
Christopher Haster 15:a6b1f4e65bf4 265 if not os.path.isfile('mbed_settings.py'):
Christopher Haster 15:a6b1f4e65bf4 266 shutil.copy('mbed-os/tools/settings.py', 'mbed_settings.py')
Christopher Haster 15:a6b1f4e65bf4 267
Christopher Haster 15:a6b1f4e65bf4 268 if os.path.isfile('MACROS.txt'):
Christopher Haster 15:a6b1f4e65bf4 269 with open('MACROS.txt') as f:
Christopher Haster 15:a6b1f4e65bf4 270 macros = f.read().splitlines()
Christopher Haster 15:a6b1f4e65bf4 271
Christopher Haster 15:a6b1f4e65bf4 272 env = os.environ.copy()
Christopher Haster 15:a6b1f4e65bf4 273 env['PYTHONPATH'] = '.'
Christopher Haster 15:a6b1f4e65bf4 274 popen(['python', 'mbed-os/tools/make.py']
Christopher Haster 15:a6b1f4e65bf4 275 + list(chain.from_iterable(izip(repeat('-D'), macros)))
Christopher Haster 15:a6b1f4e65bf4 276 + ['--source=%s' % cwd.path,
Christopher Haster 15:a6b1f4e65bf4 277 '--build=%s' % os.path.join(cwd.path, '.build')]
Christopher Haster 15:a6b1f4e65bf4 278 + args,
Christopher Haster 15:a6b1f4e65bf4 279 env=env)
Christopher Haster 15:a6b1f4e65bf4 280
Christopher Haster 15:a6b1f4e65bf4 281 # Export command
Christopher Haster 15:a6b1f4e65bf4 282 @subcommand('export', 'args*',
Christopher Haster 15:a6b1f4e65bf4 283 help='generate project files')
Christopher Haster 15:a6b1f4e65bf4 284 def export(args):
Christopher Haster 15:a6b1f4e65bf4 285 cwd = Repo()
Christopher Haster 15:a6b1f4e65bf4 286
Christopher Haster 15:a6b1f4e65bf4 287 if not os.path.isdir('mbed-os'):
Christopher Haster 15:a6b1f4e65bf4 288 sys.stderr.write('Warning! mbed-os not found?')
Christopher Haster 15:a6b1f4e65bf4 289 sys.exit(-1)
Christopher Haster 15:a6b1f4e65bf4 290
Christopher Haster 15:a6b1f4e65bf4 291 if not os.path.isfile('mbed_settings.py'):
Christopher Haster 15:a6b1f4e65bf4 292 shutil.copy('mbed-os/tools/settings.py', 'mbed_settings.py')
Christopher Haster 15:a6b1f4e65bf4 293
Christopher Haster 18:1b4252106474 294 env = os.environ.copy()
Christopher Haster 18:1b4252106474 295 env['PYTHONPATH'] = '.'
Christopher Haster 15:a6b1f4e65bf4 296 popen(['python', 'mbed-os/tools/project.py',
Christopher Haster 17:1e487c450f06 297 '--source=%s' % cwd.path]
Christopher Haster 18:1b4252106474 298 + args,
Christopher Haster 18:1b4252106474 299 env=env)
Christopher Haster 15:a6b1f4e65bf4 300
Christopher Haster 15:a6b1f4e65bf4 301 # Parse/run command
Christopher Haster 15:a6b1f4e65bf4 302 args, remainder = parser.parse_known_args()
Christopher Haster 15:a6b1f4e65bf4 303 status = args.command(args)
Christopher Haster 15:a6b1f4e65bf4 304 sys.exit(status or 0)
Christopher Haster 15:a6b1f4e65bf4 305