comparison mxtool/mx.py @ 12508:5cde653f58f9

mxtool: handle missing hg executable gracefully
author Mick Jordan <mick.jordan@oracle.com>
date Mon, 21 Oct 2013 15:14:43 -0700
parents 1d68b3962a10
children 21aa1ce5c666 8ee3a8dd762e
comparison
equal deleted inserted replaced
12507:d72864a2886e 12508:5cde653f58f9
161 _dst_suitemodel = None 161 _dst_suitemodel = None
162 _opts = None 162 _opts = None
163 _java = None 163 _java = None
164 _check_global_structures = True # can be set False to allow suites with duplicate definitions to load without aborting 164 _check_global_structures = True # can be set False to allow suites with duplicate definitions to load without aborting
165 _warn = False 165 _warn = False
166 _hg = None
166 167
167 168
168 """ 169 """
169 A distribution is a jar or zip file containing the output from one or more Java projects. 170 A distribution is a jar or zip file containing the output from one or more Java projects.
170 """ 171 """
495 if not includeLibs or not includeSelf: 496 if not includeLibs or not includeSelf:
496 return deps 497 return deps
497 deps.append(self) 498 deps.append(self)
498 return deps 499 return deps
499 500
501 class HgConfig:
502 """
503 Encapsulates access to Mercurial (hg)
504 """
505 def __init__(self):
506 self.missing = 'no hg executable found'
507 try:
508 subprocess.check_output(['hg'])
509 self.has_hg = True
510 except OSError:
511 self.has_hg = False
512 warn(self.missing)
513
514 def _check(self, abortOnFail=True):
515 if not self.has_hg:
516 if abortOnFail:
517 abort(self.missing)
518 else:
519 warn(self.missing)
520 return self.has_hg
521
522 def _tip(self, s, abortOnError=True):
523 if not self.has_hg:
524 return None
525 try:
526 version = subprocess.check_output(['hg', 'tip', '-R', s.dir, '--template', '{node}'])
527 if s.version is not None and s.version != version:
528 abort('version of suite ' + s.name +' has changed during run')
529 return version
530 except subprocess.CalledProcessError:
531 if abortOnError:
532 abort('failed to get tip revision id')
533 else:
534 return None
535
536 def _canpush(self, s, strict=True):
537 try:
538 output = subprocess.check_output(['hg', '-R', s.dir, 'status'])
539 # super strict
540 return output == ''
541 except subprocess.CalledProcessError:
542 return False
543
544 def _default_push(self, sdir):
545 with open(join(sdir, '.hg', 'hgrc')) as f:
546 for line in f:
547 line = line.rstrip()
548 if line.startswith('default = '):
549 return line[len('default = '):]
550 return None
551
500 class SuiteModel: 552 class SuiteModel:
501 """ 553 """
502 Defines how to locate a URL/path for a suite, including imported suites. 554 Defines how to locate a URL/path for a suite, including imported suites.
503 Conceptually a SuiteModel is defined by a primary suite URL/path and a 555 Conceptually a SuiteModel is defined by a primary suite URL/path and a
504 map from suite name to URL/path for imported suites. 556 map from suite name to URL/path for imported suites.
588 src_suitemodel_arg = _get_argvalue(arg, args, i + 1) 640 src_suitemodel_arg = _get_argvalue(arg, args, i + 1)
589 elif arg == '--dst-suitemodel': 641 elif arg == '--dst-suitemodel':
590 dst_suitemodel_arg = _get_argvalue(arg, args, i + 1) 642 dst_suitemodel_arg = _get_argvalue(arg, args, i + 1)
591 elif arg == '--suitemap': 643 elif arg == '--suitemap':
592 suitemap_arg = _get_argvalue(arg, args, i + 1) 644 suitemap_arg = _get_argvalue(arg, args, i + 1)
645 elif arg == '-w':
646 # to get warnings on suite loading issues before command line is parsed
647 global _warn
648 _warn = True
649
593 i = i + 1 650 i = i + 1
594 651
595 global _src_suitemodel 652 global _src_suitemodel
596 _src_suitemodel = SuiteModel._set_suitemodel(src_suitemodel_arg, suitemap_arg) 653 _src_suitemodel = SuiteModel._set_suitemodel(src_suitemodel_arg, suitemap_arg)
597 global _dst_suitemodel 654 global _dst_suitemodel
690 747
691 @staticmethod 748 @staticmethod
692 def _tostring(name, version): 749 def _tostring(name, version):
693 return name + ',' + version 750 return name + ',' + version
694 751
695 def _self_tostring(self): 752 def __str__(self):
696 return self.name + ',' + self.version 753 return self.name + ',' + self.version
697 754
698 class Suite: 755 class Suite:
699 def __init__(self, mxDir, primary, load=True): 756 def __init__(self, mxDir, primary, load=True):
700 self.dir = dirname(mxDir) 757 self.dir = dirname(mxDir)
704 self.dists = [] 761 self.dists = []
705 self.imports = [] 762 self.imports = []
706 self.commands = None 763 self.commands = None
707 self.primary = primary 764 self.primary = primary
708 self.name = _suitename(mxDir) # validated in _load_projects 765 self.name = _suitename(mxDir) # validated in _load_projects
709 self.version = None # _hgtip checks current version if not None 766 self.version = None # _hg._tip checks current version if not None
710 self.version = _hgtip(self, False) 767 self.version = _hg._tip(self, False)
711 if load: 768 if load:
712 # load suites bottom up to make sure command overriding works properly 769 # load suites bottom up to make sure command overriding works properly
713 self._load_imports() 770 self._load_imports()
714 self._load_env() 771 self._load_env()
715 self._load_commands() 772 self._load_commands()
888 if importMxDir is None: 945 if importMxDir is None:
889 abort('import ' + suite_import.name + ' not found') 946 abort('import ' + suite_import.name + ' not found')
890 suite.imports.append(suite_import) 947 suite.imports.append(suite_import)
891 imported_suite = _loadSuite(importMxDir, False) 948 imported_suite = _loadSuite(importMxDir, False)
892 if imported_suite.version != suite.version: 949 if imported_suite.version != suite.version:
893 warn('import version of ' + imported_suite.name + ' does not match tip of ' + suite.version) 950 warn('import version of ' + imported_suite.name +' does not match tip of ' + suite.version)
894 951
895 def _load_imports(self): 952 def _load_imports(self):
896 self._visit_imports(self._find_and_loadsuite) 953 self._visit_imports(self._find_and_loadsuite)
897 954
898 def _load_env(self): 955 def _load_env(self):
3884 return kwargs.pop(0) 3941 return kwargs.pop(0)
3885 return None 3942 return None
3886 3943
3887 def sclone(args): 3944 def sclone(args):
3888 """clone a suite repository, and its imported suites""" 3945 """clone a suite repository, and its imported suites"""
3946 _hg._check(True)
3889 parser = ArgumentParser(prog='mx sclone') 3947 parser = ArgumentParser(prog='mx sclone')
3890 parser.add_argument('--source', help='url/path of repo containing suite', metavar='<url>') 3948 parser.add_argument('--source', help='url/path of repo containing suite', metavar='<url>')
3891 parser.add_argument('--dest', help='destination directory (default basename of source)', metavar='<path>') 3949 parser.add_argument('--dest', help='destination directory (default basename of source)', metavar='<path>')
3892 parser.add_argument("--no-imports", action='store_true', help='do not clone imported suites') 3950 parser.add_argument("--no-imports", action='store_true', help='do not clone imported suites')
3893 parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...') 3951 parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...')
3969 _sclone(importee_source, importee_dest, suite_import.version, False) 4027 _sclone(importee_source, importee_dest, suite_import.version, False)
3970 # _clone handles the recursive visit of the new imports 4028 # _clone handles the recursive visit of the new imports
3971 4029
3972 def scloneimports(args): 4030 def scloneimports(args):
3973 """clone the imports of an existing suite""" 4031 """clone the imports of an existing suite"""
4032 _hg._check(True)
3974 parser = ArgumentParser(prog='mx scloneimports') 4033 parser = ArgumentParser(prog='mx scloneimports')
3975 parser.add_argument('--source', help='url/path of repo containing suite', metavar='<url>') 4034 parser.add_argument('--source', help='url/path of repo containing suite', metavar='<url>')
3976 parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...') 4035 parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...')
3977 args = parser.parse_args(args) 4036 args = parser.parse_args(args)
3978 # check for non keyword args 4037 # check for non keyword args
3982 if not os.path.isdir(args.source): 4041 if not os.path.isdir(args.source):
3983 abort(args.source + ' is not a directory') 4042 abort(args.source + ' is not a directory')
3984 4043
3985 s = _scloneimports_suitehelper(args.source) 4044 s = _scloneimports_suitehelper(args.source)
3986 4045
3987 default_path = _hgdefault_push(args.source) 4046 default_path = _hg._default_push(args.source)
3988 4047
3989 if default_path is None: 4048 if default_path is None:
3990 abort('no default path in ' + join(args.source, '.hg', 'hgrc')) 4049 abort('no default path in ' + join(args.source, '.hg', 'hgrc'))
3991 4050
3992 # We can now set the primary dir for the dst suitemodel 4051 # We can now set the primary dir for the dst suitemodel
4001 dest = _dst_suitemodel._importee_dir(dest, suite_import.name) 4060 dest = _dst_suitemodel._importee_dir(dest, suite_import.name)
4002 _spush(suite(suite_import.name), suite_import, dest, checks, clonemissing) 4061 _spush(suite(suite_import.name), suite_import, dest, checks, clonemissing)
4003 4062
4004 def _spush_check_import_visitor(s, suite_import, **extra_args): 4063 def _spush_check_import_visitor(s, suite_import, **extra_args):
4005 """push check visitor for Suite._visit_imports""" 4064 """push check visitor for Suite._visit_imports"""
4006 currentTip = _hgtip(suite(suite_import.name)) 4065 currentTip = _hg._tip(suite(suite_import.name))
4007 if currentTip != suite_import.version: 4066 if currentTip != suite_import.version:
4008 abort('import version of ' + suite_import.name + ' in suite ' + s.name + ' does not match tip') 4067 abort('import version of ' + suite_import.name + ' in suite ' + s.name + ' does not match tip')
4009 4068
4010 def _spush(s, suite_import, dest, checks, clonemissing): 4069 def _spush(s, suite_import, dest, checks, clonemissing):
4011 if checks: 4070 if checks:
4012 if not _hgcanpush(s): 4071 if not _hg._canpush(s):
4013 abort('working directory ' + s.dir + ' contains uncommitted changes, push aborted') 4072 abort('working directory ' + s.dir + ' contains uncommitted changes, push aborted')
4014 4073
4015 # check imports first 4074 # check imports first
4016 if checks: 4075 if checks:
4017 s._visit_imports(_spush_check_import_visitor) 4076 s._visit_imports(_spush_check_import_visitor)
4047 cmd.append(dest) 4106 cmd.append(dest)
4048 run(cmd) 4107 run(cmd)
4049 4108
4050 def spush(args): 4109 def spush(args):
4051 """push primary suite and all its imports""" 4110 """push primary suite and all its imports"""
4111 _hg._check(True)
4052 parser = ArgumentParser(prog='mx spush') 4112 parser = ArgumentParser(prog='mx spush')
4053 parser.add_argument('--dest', help='url/path of repo to push to (default as per hg push)', metavar='<path>') 4113 parser.add_argument('--dest', help='url/path of repo to push to (default as per hg push)', metavar='<path>')
4054 parser.add_argument('--no-checks', action='store_true', help='checks on status, versions are disabled') 4114 parser.add_argument('--no-checks', action='store_true', help='checks on status, versions are disabled')
4055 parser.add_argument('--clonemissing', action='store_true', help='clone missing imported repos at destination (forces --no-checks)') 4115 parser.add_argument('--clonemissing', action='store_true', help='clone missing imported repos at destination (forces --no-checks)')
4056 parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...') 4116 parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...')
4071 args.nochecks = True 4131 args.nochecks = True
4072 4132
4073 if args.dest is not None: 4133 if args.dest is not None:
4074 _dst_suitemodel._set_primary_dir(args.dest) 4134 _dst_suitemodel._set_primary_dir(args.dest)
4075 4135
4076 _spush(s, None, args.dest, not args.nochecks, args.clonemissing) 4136 _spush(s, None, args.dest, not args.no_checks, args.clonemissing)
4077 4137
4078 def _supdate_import_visitor(s, suite_import, **extra_args): 4138 def _supdate_import_visitor(s, suite_import, **extra_args):
4079 _supdate(suite(suite_import.name), suite_import) 4139 _supdate(suite(suite_import.name), suite_import)
4080 4140
4081 def _supdate(s, suite_import): 4141 def _supdate(s, suite_import):
4084 run(['hg', '-R', s.dir, 'update']) 4144 run(['hg', '-R', s.dir, 'update'])
4085 4145
4086 def supdate(args): 4146 def supdate(args):
4087 """update primary suite and all its imports""" 4147 """update primary suite and all its imports"""
4088 4148
4149 _hg._check(True)
4089 s = _check_primary_suite() 4150 s = _check_primary_suite()
4090 4151
4091 _supdate(s, None) 4152 _supdate(s, None)
4092 4153
4093 def _scheck_imports_visitor(s, suite_import, update_versions, updated_imports): 4154 def _scheck_imports_visitor(s, suite_import, update_versions, updated_imports):
4096 4157
4097 def _scheck_imports(s, suite_import, update_versions, updated_imports): 4158 def _scheck_imports(s, suite_import, update_versions, updated_imports):
4098 # check imports recursively 4159 # check imports recursively
4099 s._visit_imports(_scheck_imports_visitor, update_versions=update_versions) 4160 s._visit_imports(_scheck_imports_visitor, update_versions=update_versions)
4100 4161
4101 currentTip = _hgtip(s) 4162 currentTip = _hg._tip(s)
4102 if currentTip != suite_import.version: 4163 if currentTip != suite_import.version:
4103 print('import version of ' + s.name + ' does not match tip' + (': updating' if update_versions else '')) 4164 print('import version of ' + s.name + ' does not match tip' + (': updating' if update_versions else ''))
4104 4165
4105 if update_versions: 4166 if update_versions:
4106 suite_import.version = currentTip 4167 suite_import.version = currentTip
4107 line = suite_import._self_tostring() 4168 line = str(suite_import)
4108 updated_imports.write(line + '\n') 4169 updated_imports.write(line + '\n')
4109 4170
4110 def scheckimports(args): 4171 def scheckimports(args):
4111 """check that suite import versions are up to date""" 4172 """check that suite import versions are up to date"""
4112 parser = ArgumentParser(prog='mx scheckimports') 4173 parser = ArgumentParser(prog='mx scheckimports')
4113 parser.add_argument('--update-versions', help='update imported version ids', action='store_true') 4174 parser.add_argument('--update-versions', help='update imported version ids', action='store_true')
4114 args = parser.parse_args(args) 4175 args = parser.parse_args(args)
4115 _check_primary_suite()._visit_imports(_scheck_imports_visitor, update_versions=args.update_versions) 4176 _check_primary_suite()._visit_imports(_scheck_imports_visitor, update_versions=args.update_versions)
4116 4177
4117 def _hgtip(s, abortOnError=True):
4118 try:
4119 version = subprocess.check_output(['hg', 'tip', '-R', s.dir, '--template', '{node}'])
4120 if s.version is not None and s.version != version:
4121 abort('version of suite ' + s.name + ' has changed during run')
4122 return version
4123 except subprocess.CalledProcessError:
4124 if abortOnError:
4125 abort('failed to get tip revision id')
4126 else:
4127 return None
4128
4129 def _hgcanpush(s):
4130 try:
4131 output = subprocess.check_output(['hg', '-R', s.dir, 'status'])
4132 # super strict
4133 return output == ''
4134 except subprocess.CalledProcessError:
4135 return False
4136
4137 def _hgdefault_push(sdir):
4138 with open(join(sdir, '.hg', 'hgrc')) as f:
4139 for line in f:
4140 line = line.rstrip()
4141 if line.startswith('default = '):
4142 return line[len('default = '):]
4143 return None
4144
4145 def _spull_import_visitor(s, suite_import, update_versions, updated_imports): 4178 def _spull_import_visitor(s, suite_import, update_versions, updated_imports):
4146 """pull visitor for Suite._visit_imports""" 4179 """pull visitor for Suite._visit_imports"""
4147 _spull(suite(suite_import.name), update_versions, updated_imports) 4180 _spull(suite(suite_import.name), update_versions, updated_imports)
4148 4181
4149 def _spull(s, update_versions, updated_imports): 4182 def _spull(s, update_versions, updated_imports):
4183 _hg._check(True)
4150 # pull imports first 4184 # pull imports first
4151 s._visit_imports(_spull_import_visitor, update_versions=update_versions) 4185 s._visit_imports(_spull_import_visitor, update_versions=update_versions)
4152 4186
4153 run(['hg', '-R', s.dir, 'pull', '-u']) 4187 run(['hg', '-R', s.dir, 'pull', '-u'])
4154 if update_versions and updated_imports is not None: 4188 if update_versions and updated_imports is not None:
4155 tip = _hgtip(s) 4189 tip = _hg._tip(s)
4156 updated_imports.write(SuiteImport._tostring(s.name, tip) + '\n') 4190 updated_imports.write(SuiteImport._tostring(s.name, tip) + '\n')
4157 4191
4158 def spull(args): 4192 def spull(args):
4159 """pull primary suite and all its imports""" 4193 """pull primary suite and all its imports"""
4194 _hg._check(True)
4160 parser = ArgumentParser(prog='mx spull') 4195 parser = ArgumentParser(prog='mx spull')
4161 parser.add_argument('--update-versions', action='store_true', help='update version ids of imported suites') 4196 parser.add_argument('--update-versions', action='store_true', help='update version ids of imported suites')
4162 args = parser.parse_args(args) 4197 args = parser.parse_args(args)
4163 4198
4164 _spull(_check_primary_suite(), args.update_versions, None) 4199 _spull(_check_primary_suite(), args.update_versions, None)
4360 return None 4395 return None
4361 4396
4362 def main(): 4397 def main():
4363 SuiteModel._parse_options() 4398 SuiteModel._parse_options()
4364 4399
4400 global _hg
4401 _hg = HgConfig()
4402
4365 primarySuiteMxDir = _findPrimarySuiteMxDir() 4403 primarySuiteMxDir = _findPrimarySuiteMxDir()
4366 if primarySuiteMxDir: 4404 if primarySuiteMxDir:
4367 _src_suitemodel._set_primary_dir(dirname(primarySuiteMxDir)) 4405 _src_suitemodel._set_primary_dir(dirname(primarySuiteMxDir))
4368 global _mainSuite 4406 global _mainSuite
4369 _mainSuite = _loadSuite(primarySuiteMxDir, True) 4407 _mainSuite = _loadSuite(primarySuiteMxDir, True)