comparison mxtool/mx.py @ 12477:a5d83166dca6

mxtool hg support for suites
author Mick Jordan <mick.jordan@oracle.com>
date Thu, 17 Oct 2013 19:59:25 -0700
parents d08accd58925
children 28d7a11ba008
comparison
equal deleted inserted replaced
12476:67566565053a 12477:a5d83166dca6
53 dependencies between them. 53 dependencies between them.
54 54
55 commands.py 55 commands.py
56 Suite specific extensions to the commands available to mx. 56 Suite specific extensions to the commands available to mx.
57 57
58 includes 58 imports
59 Other suites to be loaded. This is recursive. Each 59 Other suites to be loaded. This is recursive. Each
60 line in an includes file is a path to a suite directory. 60 line in an imports file is the name of an imported suite.
61 The suite is located using a SuiteModel (cf searchpath)
61 62
62 env 63 env
63 A set of environment variable definitions. These override any 64 A set of environment variable definitions. These override any
64 existing environment variables. Common properties set here 65 existing environment variables. Common properties set here
65 include JAVA_HOME and IGNORED_PROJECTS. 66 include JAVA_HOME and IGNORED_PROJECTS.
66 67
67 The includes and env files are typically not put under version control 68 The env file is typically not put under version control
68 as they usually contain local file-system paths. 69 as it usually contain local file-system paths.
69 70
70 The projects file is like the pom.xml file from Maven except that 71 The projects file is like the pom.xml file from Maven except that
71 it is a properties file (not XML). Each non-comment line 72 it is a properties file (not XML). Each non-comment line
72 in the file specifies an attribute of a project or library. The main 73 in the file specifies an attribute of a project or library. The main
73 difference between a project and a library is that the former contains 74 difference between a project and a library is that the former contains
154 _libs = dict() 155 _libs = dict()
155 _dists = dict() 156 _dists = dict()
156 _suites = dict() 157 _suites = dict()
157 _annotationProcessors = None 158 _annotationProcessors = None
158 _mainSuite = None 159 _mainSuite = None
160 _src_suitemodel = None
161 _dst_suitemodel = None
159 _opts = None 162 _opts = None
160 _java = None 163 _java = None
161 _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
162 166
163 167
164 """ 168 """
165 A distribution is a jar or zip file containing the output from one or more Java projects. 169 A distribution is a jar or zip file containing the output from one or more Java projects.
166 """ 170 """
491 if not includeLibs or not includeSelf: 495 if not includeLibs or not includeSelf:
492 return deps 496 return deps
493 deps.append(self) 497 deps.append(self)
494 return deps 498 return deps
495 499
500 class SuiteModel:
501 """
502 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
504 map from suite name to URL/path for imported suites.
505 Subclasses define a specfic implementation.
506 """
507 def __init__(self):
508 self.primaryDir = None
509 self.suitenamemap = {}
510
511 def _find_suite_dir(self, suitename):
512 """locates the URL/path for suitename or None if not found"""
513 abort('_find_suite_dir not implemented')
514
515 def _set_primary_dir(self, d):
516 """informs that d is the primary suite directory"""
517 self._primaryDir = d
518
519 def _importee_dir(self, importer_dir, suitename):
520 """returns the directory path for an import of suitename, given importer_dir"""
521 abort('_importee_dir not implemented')
522
523 def _nestedsuites_dirname(self):
524 """Returns the dirname that contains any nested suites if the model supports that"""
525 return None
526
527 def _mxDirName(self, name):
528 # temporary workaround until mx.graal exists
529 if name == 'graal':
530 return 'mx'
531 else:
532 return 'mx.' + name
533
534 def _search_dir(self, searchDir, mxDirName):
535 for dd in os.listdir(searchDir):
536 sd = _is_suite_dir(join(searchDir, dd), mxDirName)
537 if sd is not None:
538 return sd
539
540 def _create_suitenamemap(self, optionspec, suitemap):
541 """Three ways to specify a suite name mapping, in order of precedence:
542 1. Explicitly in optionspec.
543 2. In suitemap.
544 3. in MXSUITEMAP environment variable.
545 """
546 if optionspec != '':
547 spec = optionspec
548 elif suitemap is not None:
549 spec = suitemap
550 elif get_env('MXSUITEMAP') is not None:
551 spec = get_env('MXSUITEMAP')
552 else:
553 return
554 pairs = spec.split(',')
555 for pair in pairs:
556 mappair = pair.split('=')
557 self.suitenamemap[mappair[0]] = mappair[1]
558
559 class SiblingSuiteModel(SuiteModel):
560 """All suites are siblings in the same parent directory, recorded as _suiteRootDir"""
561 def __init__(self, suiteRootDir, option, suitemap):
562 SuiteModel.__init__(self)
563 self._suiteRootDir = suiteRootDir
564 self._create_suitenamemap(option[len('sibling:'):], suitemap)
565
566 def _find_suite_dir(self, name):
567 return self._search_dir(self._suiteRootDir, self._mxDirName(name))
568
569 def _set_primary_dir(self, d):
570 SuiteModel._set_primary_dir(self, d)
571 self._suiteRootDir = dirname(d)
572
573 def _importee_dir(self, importer_dir, suitename):
574 if self.suitenamemap.has_key(suitename):
575 suitename = self.suitenamemap[suitename]
576 return join(dirname(importer_dir), suitename)
577
578 class NestedImportsSuiteModel(SuiteModel):
579 """Imported suites are all siblings in an 'imported_suites' directory of the primary suite"""
580 def _imported_suites_dirname(self):
581 return "imported_suites"
582
583 def __init__(self, primaryDir, option, suitemap):
584 SuiteModel.__init__(self)
585 self._primaryDir = primaryDir
586 self._create_suitenamemap(option[len('nested:'):], suitemap)
587
588 def _find_suite_dir(self, name):
589 return self._search_dir(join(self._primaryDir, self._imported_suites_dirname()), self._mxDirName(name))
590
591 def _importee_dir(self, importer_dir, suitename):
592 if self.suitenamemap.has_key(suitename):
593 suitename = self.suitenamemap[suitename]
594 if basename(importer_dir) == basename(self._primaryDir):
595 # primary is importer
596 this_imported_suites_dirname = join(importer_dir, self._imported_suites_dirname())
597 if not exists(this_imported_suites_dirname):
598 os.mkdir(this_imported_suites_dirname)
599 return join(this_imported_suites_dirname, suitename)
600 else:
601 return join(dirname(importer_dir), suitename)
602
603 def _nestedsuites_dirname(self):
604 return self._imported_suites_dirname()
605
606 class PathSuiteModel(SuiteModel):
607 """The most general model. Uses a map from suitename to URL/path provided by the user"""
608 def __init__(self, path):
609 SuiteModel.__init__(self)
610 paths = path.split(',')
611 self.suit_to_url = {}
612 for path in paths:
613 pair = path.split('=')
614 if len(pair) > 1:
615 suitename = pair[0]
616 suiteurl = pair[1]
617 else:
618 suitename = basename(pair[0])
619 suiteurl = pair[0]
620 self.suit_to_url[suitename] = suiteurl
621
622 def _find_suite_dir(self, suitename):
623 if self.suit_to_url.has_key(suitename):
624 return self.suit_to_url[suitename]
625 else:
626 return None
627
628 def _importee_dir(self, importer_dir, suitename):
629 if suitename in self.suit_to_url:
630 return self.suit_to_url[suitename]
631 else:
632 abort('suite ' + suitename + ' not found')
633
634 class SuiteImport:
635 def __init__(self, name, version):
636 self.name = name
637 self.version = version
638
639 @staticmethod
640 def _parse_specification(specification):
641 pair = specification.split(',')
642 name = pair[0]
643 if len(pair) > 1:
644 version = pair[1]
645 else:
646 version = None
647 return SuiteImport(name, version)
648
649 @staticmethod
650 def _tostring(name, version):
651 return name + ',' + version
652
653 def _self_tostring(self):
654 return self.name + ',' + self.version
655
496 class Suite: 656 class Suite:
497 def __init__(self, d, mxDir, primary): 657 def __init__(self, mxDir, primary, load=True):
498 self.dir = d 658 self.dir = dirname(mxDir)
499 self.mxDir = mxDir 659 self.mxDir = mxDir
500 self.projects = [] 660 self.projects = []
501 self.libs = [] 661 self.libs = []
502 self.dists = [] 662 self.dists = []
503 self.includes = [] 663 self.imports = []
504 self.commands = None 664 self.commands = None
505 self.primary = primary 665 self.primary = primary
506 self._load_env(mxDir) 666 self.name = _suitename(mxDir) # validated in _load_projects
507 self._load_commands(mxDir) 667 self.version = None # _hgtip checks current version if not None
508 self._load_includes(mxDir) 668 self.version = _hgtip(self, False)
509 self.name = d # re-initialized in _load_projects 669 if load:
670 # load suites bottom up to make sure command overriding works properly
671 self._load_imports()
672 self._load_env()
673 self._load_commands()
674 _suites[self.name] = self
510 675
511 def __str__(self): 676 def __str__(self):
512 return self.name 677 return self.name
513 678
514 def _load_projects(self, mxDir): 679 def _load_projects(self):
515 libsMap = dict() 680 libsMap = dict()
516 projsMap = dict() 681 projsMap = dict()
517 distsMap = dict() 682 distsMap = dict()
518 projectsFile = join(mxDir, 'projects') 683 projectsFile = join(self.mxDir, 'projects')
519 if not exists(projectsFile): 684 if not exists(projectsFile):
520 return 685 return
521
522 def _find_suite_key():
523 for items in _suites.items():
524 if items[1].dir == self.dir:
525 return items[0]
526 raise KeyError
527 686
528 with open(projectsFile) as f: 687 with open(projectsFile) as f:
529 prefix = '' 688 prefix = ''
530 for line in f: 689 for line in f:
531 line = line.strip() 690 line = line.strip()
542 701
543 if len(parts) == 1: 702 if len(parts) == 1:
544 if parts[0] != 'suite': 703 if parts[0] != 'suite':
545 abort('Single part property must be "suite": ' + key) 704 abort('Single part property must be "suite": ' + key)
546 if self.name != value: 705 if self.name != value:
547 currentKey = _find_suite_key() 706 abort('suite name in project file does not match ' + _suitename(self.mxDir))
548 _suites.pop(currentKey)
549 self.name = value
550 _suites[value] = self
551 continue 707 continue
552 if len(parts) != 3: 708 if len(parts) != 3:
553 abort('Property name does not have 3 parts separated by "@": ' + key) 709 abort('Property name does not have 3 parts separated by "@": ' + key)
554 kind, name, attr = parts 710 kind, name, attr = parts
555 if kind == 'project': 711 if kind == 'project':
614 self.dists.append(d) 770 self.dists.append(d)
615 771
616 if self.name is None: 772 if self.name is None:
617 abort('Missing "suite=<name>" in ' + projectsFile) 773 abort('Missing "suite=<name>" in ' + projectsFile)
618 774
619 def _load_commands(self, mxDir): 775 def _commands_name(self):
620 commandsPath = join(mxDir, 'commands.py') 776 return 'mx_' + self.name
777
778 def _find_commands(self, name):
779 commandsPath = join(self.mxDir, name + '.py')
621 if exists(commandsPath): 780 if exists(commandsPath):
781 return name
782 else:
783 return None
784
785 def _load_commands(self):
786 commandsName = self._find_commands(self._commands_name())
787 if commandsName is None:
788 # backwards compatibility
789 commandsName = self._find_commands('commands')
790 if commandsName is not None:
791 if commandsName in sys.modules:
792 abort(commandsName + '.py in suite ' + self.name + ' duplicates ' + sys.modules[commandsName].__file__)
622 # temporarily extend the Python path 793 # temporarily extend the Python path
623 sys.path.insert(0, mxDir) 794 sys.path.insert(0, self.mxDir)
624 mod = __import__('commands') 795 mod = __import__(commandsName)
625 796
626 self.commands = sys.modules.pop('commands') 797 self.commands = sys.modules.pop(commandsName)
627 sys.modules[join(mxDir, 'commands')] = self.commands 798 sys.modules[commandsName] = self.commands
628 799
629 # revert the Python path 800 # revert the Python path
630 del sys.path[0] 801 del sys.path[0]
631 802
632 if not hasattr(mod, 'mx_init'): 803 if not hasattr(mod, 'mx_init'):
633 abort(commandsPath + ' must define an mx_init(env) function') 804 abort(commandsName + '.py in suite ' + self.name + ' must define an mx_init(suite) function')
634 if hasattr(mod, 'mx_post_parse_cmd_line'): 805 if hasattr(mod, 'mx_post_parse_cmd_line'):
635 self.mx_post_parse_cmd_line = mod.mx_post_parse_cmd_line 806 self.mx_post_parse_cmd_line = mod.mx_post_parse_cmd_line
636 807
637 mod.mx_init(self) 808 mod.mx_init(self)
638 self.commands = mod 809 self.commands = mod
639 810
640 def _load_includes(self, mxDir): 811 def _visit_imports(self, visitor, **extra_args):
641 includes = join(mxDir, 'includes') 812 """
642 if exists(includes): 813 Visitor support for the imports file.
643 with open(includes) as f: 814 For each line of the imports file that specifies an import, the visitor function is
815 called with this suite, a SuiteImport instance created from the line and any extra args
816 passed to this call. In addition, if extra_args contains a key 'update_versions' that is True,
817 a StringIO value is added to extra_args with key 'updated_imports', and the visitor is responsible
818 for writing a (possibly) updated import line to the file, and the file is (possibly) updated after
819 all imports are processed.
820 N.B. There is no built-in support for avoiding visiting the same suite multiple times,
821 as this function only visits the imports of a singkle suite. If a (recursive) visitor function
822 wishes to visit a suite exactly once, it must manage that through extra_args.
823 """
824 importsFile = join(self.mxDir, 'imports')
825 if exists(importsFile):
826 update_versions = extra_args.has_key('update_versions') and extra_args['update_versions']
827 out = StringIO.StringIO() if update_versions else None
828 extra_args['updated_imports'] = out
829 with open(importsFile) as f:
644 for line in f: 830 for line in f:
645 include = expandvars_in_property(line.strip()) 831 sline = line.strip()
646 self.includes.append(include) 832 if len(sline) == 0 or sline.startswith('#'):
647 _loadSuite(os.path.abspath(include), False) 833 if out is not None:
648 834 out.write(sline + '\n')
649 def _load_env(self, mxDir): 835 continue
650 e = join(mxDir, 'env') 836 suite_import = SuiteImport._parse_specification(line.strip())
837 visitor(self, suite_import, **extra_args)
838
839 if out is not None:
840 update_file(importsFile, out.getvalue())
841
842 @staticmethod
843 def _find_and_loadsuite(suite, suite_import, **extra_args):
844 """visitor for the initial suite load"""
845 importMxDir = _src_suitemodel._find_suite_dir(suite_import.name)
846 if importMxDir is None:
847 abort('import ' + suite_import.name + ' not found')
848 suite.imports.append(suite_import)
849 imported_suite = _loadSuite(importMxDir, False)
850 if imported_suite.version != suite.version:
851 warn('import version of ' + imported_suite.name +' does not match tip of ' + suite.version)
852
853 def _load_imports(self):
854 self._visit_imports(self._find_and_loadsuite)
855
856 def _load_env(self):
857 e = join(self.mxDir, 'env')
651 if exists(e): 858 if exists(e):
652 with open(e) as f: 859 with open(e) as f:
653 lineNum = 0 860 lineNum = 0
654 for line in f: 861 for line in f:
655 lineNum = lineNum + 1 862 lineNum = lineNum + 1
659 abort(e + ':' + str(lineNum) + ': line does not match pattern "key=value"') 866 abort(e + ':' + str(lineNum) + ': line does not match pattern "key=value"')
660 key, value = line.split('=', 1) 867 key, value = line.split('=', 1)
661 os.environ[key.strip()] = expandvars_in_property(value.strip()) 868 os.environ[key.strip()] = expandvars_in_property(value.strip())
662 869
663 def _post_init(self, opts): 870 def _post_init(self, opts):
664 self._load_projects(self.mxDir) 871 self._load_projects()
665 # set the global data structures, checking for conflicts unless _global_structures is False 872 # set the global data structures, checking for conflicts unless _check_global_structures is False
666 for p in self.projects: 873 for p in self.projects:
667 existing = _projects.get(p.name) 874 existing = _projects.get(p.name)
668 if existing is not None and _check_global_structures: 875 if existing is not None and _check_global_structures:
669 abort('cannot override project ' + p.name + ' in ' + p.dir + " with project of the same name in " + existing.dir) 876 abort('cannot override project ' + p.name + ' in ' + p.dir + " with project of the same name in " + existing.dir)
670 if not p.name in _opts.ignored_projects: 877 if not p.name in _opts.ignored_projects:
678 for d in self.dists: 885 for d in self.dists:
679 existing = _dists.get(d.name) 886 existing = _dists.get(d.name)
680 if existing is not None and _check_global_structures: 887 if existing is not None and _check_global_structures:
681 # allow redefinition, so use path from existing 888 # allow redefinition, so use path from existing
682 # abort('cannot redefine distribution ' + d.name) 889 # abort('cannot redefine distribution ' + d.name)
683 print('WARNING: distribution ' + d.name + ' redefined') 890 warn('distribution ' + d.name + ' redefined')
684 d.path = existing.path 891 d.path = existing.path
685 _dists[d.name] = d 892 _dists[d.name] = d
686 if hasattr(self, 'mx_post_parse_cmd_line'): 893 if hasattr(self, 'mx_post_parse_cmd_line'):
687 self.mx_post_parse_cmd_line(opts) 894 self.mx_post_parse_cmd_line(opts)
688 895
775 elif sys.platform.startswith('win32') or sys.platform.startswith('cygwin'): 982 elif sys.platform.startswith('win32') or sys.platform.startswith('cygwin'):
776 return 'windows' 983 return 'windows'
777 else: 984 else:
778 abort('Unknown operating system ' + sys.platform) 985 abort('Unknown operating system ' + sys.platform)
779 986
780 def _loadSuite(d, primary=False): 987 def _loadSuite(mxDir, primary=False):
781 """ 988 """
782 Load a suite from the 'mx' or 'mx.bbb' subdirectory of d, where 'bbb' is basename of d 989 Load a suite from 'mxDir'.
783 """ 990 """
784 mxDefaultDir = join(d, 'mx') 991 for s in _suites.itervalues():
785 name = os.path.basename(d) 992 if s.mxDir == mxDir:
786 mxTaggedDir = mxDefaultDir + '.' + name 993 return s
787 mxDir = None 994 # create the new suite
788 if exists(mxTaggedDir) and isdir(mxTaggedDir): 995 s = Suite(mxDir, primary)
789 mxDir = mxTaggedDir 996 return s
790 else:
791 if exists(mxDefaultDir) and isdir(mxDefaultDir):
792 mxDir = mxDefaultDir
793
794
795 if mxDir is None:
796 return None
797 if len([s for s in _suites.itervalues() if s.dir == d]) == 0:
798 s = Suite(d, mxDir, primary)
799 # N.B. this will be updated once the projects file has been read
800 _suites[name] = s
801 return s
802 997
803 def suites(opt_limit_to_suite=False): 998 def suites(opt_limit_to_suite=False):
804 """ 999 """
805 Get the list of all loaded suites. 1000 Get the list of all loaded suites.
806 """ 1001 """
819 """ 1014 """
820 s = _suites.get(name) 1015 s = _suites.get(name)
821 if s is None and fatalIfMissing: 1016 if s is None and fatalIfMissing:
822 abort('suite named ' + name + ' not found') 1017 abort('suite named ' + name + ' not found')
823 return s 1018 return s
1019
824 1020
825 def projects_from_names(projectNames): 1021 def projects_from_names(projectNames):
826 """ 1022 """
827 Get the list of projects corresponding to projectNames; all projects if None 1023 Get the list of projects corresponding to projectNames; all projects if None
828 """ 1024 """
1018 log('Does not appear to be a valid JDK as ' + rtJarPath + ' does not exist') 1214 log('Does not appear to be a valid JDK as ' + rtJarPath + ' does not exist')
1019 javaHome = None 1215 javaHome = None
1020 else: 1216 else:
1021 break 1217 break
1022 1218
1023 envPath = join(_mainSuite.dir, 'mx', 'env') 1219 envPath = join(_mainSuite.mxDir, 'env')
1024 if ask_yes_no('Persist this setting by adding "JAVA_HOME=' + javaHome + '" to ' + envPath, 'y'): 1220 if ask_yes_no('Persist this setting by adding "JAVA_HOME=' + javaHome + '" to ' + envPath, 'y'):
1025 with open(envPath, 'a') as fp: 1221 with open(envPath, 'a') as fp:
1026 print >> fp, 'JAVA_HOME=' + javaHome 1222 print >> fp, 'JAVA_HOME=' + javaHome
1027 1223
1028 return javaHome 1224 return javaHome
1029 1225
1030 class ArgParser(ArgumentParser): 1226 class ArgParser(ArgumentParser):
1031
1032 # Override parent to append the list of available commands 1227 # Override parent to append the list of available commands
1033 def format_help(self): 1228 def format_help(self):
1034 return ArgumentParser.format_help(self) + _format_commands() 1229 return ArgumentParser.format_help(self) + _format_commands()
1035 1230
1036 1231
1039 # this doesn't resolve the right way, but can't figure out how to override _handle_conflict_resolve in _ActionsContainer 1234 # this doesn't resolve the right way, but can't figure out how to override _handle_conflict_resolve in _ActionsContainer
1040 ArgumentParser.__init__(self, prog='mx', conflict_handler='resolve') 1235 ArgumentParser.__init__(self, prog='mx', conflict_handler='resolve')
1041 1236
1042 self.add_argument('-v', action='store_true', dest='verbose', help='enable verbose output') 1237 self.add_argument('-v', action='store_true', dest='verbose', help='enable verbose output')
1043 self.add_argument('-V', action='store_true', dest='very_verbose', help='enable very verbose output') 1238 self.add_argument('-V', action='store_true', dest='very_verbose', help='enable very verbose output')
1239 self.add_argument('-w', action='store_true', dest='warn', help='enable warning messages')
1044 self.add_argument('--dbg', type=int, dest='java_dbg_port', help='make Java processes wait on <port> for a debugger', metavar='<port>') 1240 self.add_argument('--dbg', type=int, dest='java_dbg_port', help='make Java processes wait on <port> for a debugger', metavar='<port>')
1045 self.add_argument('-d', action='store_const', const=8000, dest='java_dbg_port', help='alias for "-dbg 8000"') 1241 self.add_argument('-d', action='store_const', const=8000, dest='java_dbg_port', help='alias for "-dbg 8000"')
1046 self.add_argument('--cp-pfx', dest='cp_prefix', help='class path prefix', metavar='<arg>') 1242 self.add_argument('--cp-pfx', dest='cp_prefix', help='class path prefix', metavar='<arg>')
1047 self.add_argument('--cp-sfx', dest='cp_suffix', help='class path suffix', metavar='<arg>') 1243 self.add_argument('--cp-sfx', dest='cp_suffix', help='class path suffix', metavar='<arg>')
1048 self.add_argument('--J', dest='java_args', help='Java VM arguments (e.g. --J @-dsa)', metavar='@<args>', default='-ea -Xss2m -Xmx1g') 1244 self.add_argument('--J', dest='java_args', help='Java VM arguments (e.g. --J @-dsa)', metavar='@<args>', default='-ea -Xss2m -Xmx1g')
1050 self.add_argument('--Ja', action='append', dest='java_args_sfx', help='suffix Java VM arguments (e.g. --Ja @-dsa)', metavar='@<args>', default=[]) 1246 self.add_argument('--Ja', action='append', dest='java_args_sfx', help='suffix Java VM arguments (e.g. --Ja @-dsa)', metavar='@<args>', default=[])
1051 self.add_argument('--user-home', help='users home directory', metavar='<path>', default=os.path.expanduser('~')) 1247 self.add_argument('--user-home', help='users home directory', metavar='<path>', default=os.path.expanduser('~'))
1052 self.add_argument('--java-home', help='bootstrap JDK installation directory (must be JDK 6 or later)', metavar='<path>') 1248 self.add_argument('--java-home', help='bootstrap JDK installation directory (must be JDK 6 or later)', metavar='<path>')
1053 self.add_argument('--ignore-project', action='append', dest='ignored_projects', help='name of project to ignore', metavar='<name>', default=[]) 1249 self.add_argument('--ignore-project', action='append', dest='ignored_projects', help='name of project to ignore', metavar='<name>', default=[])
1054 self.add_argument('--suite', action='append', dest='specific_suites', help='limit command to given suite', default=[]) 1250 self.add_argument('--suite', action='append', dest='specific_suites', help='limit command to given suite', default=[])
1251 self.add_argument('--src-suitemodel', help='mechanism for locating imported suites', metavar='<arg>', default='sibling')
1252 self.add_argument('--dst-suitemodel', help='mechanism for placing cloned/pushed suites', metavar='<arg>', default='sibling')
1253 self.add_argument('--suitemap', help='explicit remapping of suite names', metavar='<args>')
1055 if get_os() != 'windows': 1254 if get_os() != 'windows':
1056 # Time outs are (currently) implemented with Unix specific functionality 1255 # Time outs are (currently) implemented with Unix specific functionality
1057 self.add_argument('--timeout', help='timeout (in seconds) for command', type=int, default=0, metavar='<secs>') 1256 self.add_argument('--timeout', help='timeout (in seconds) for command', type=int, default=0, metavar='<secs>')
1058 self.add_argument('--ptimeout', help='timeout (in seconds) for subprocesses', type=int, default=0, metavar='<secs>') 1257 self.add_argument('--ptimeout', help='timeout (in seconds) for subprocesses', type=int, default=0, metavar='<secs>')
1059 1258
1089 commandAndArgs = opts.__dict__.pop('commandAndArgs') 1288 commandAndArgs = opts.__dict__.pop('commandAndArgs')
1090 return opts, commandAndArgs 1289 return opts, commandAndArgs
1091 1290
1092 def _handle_conflict_resolve(self, action, conflicting_actions): 1291 def _handle_conflict_resolve(self, action, conflicting_actions):
1093 self._handle_conflict_error(action, conflicting_actions) 1292 self._handle_conflict_error(action, conflicting_actions)
1293
1294 class SMArgParser(ArgParser):
1295 """Parser that just looks for suitemodel options, which must happen before suites are loaded"""
1296 def __init__(self):
1297 ArgParser.__init__(self)
1298
1299 def _suitemodel(self, option, suitemap):
1300 if option.startswith('sibling'):
1301 return SiblingSuiteModel(os.getcwd(), option, suitemap)
1302 elif option.startswith('nested'):
1303 return NestedImportsSuiteModel(os.getcwd(), option, suitemap)
1304 elif option.startswith('path'):
1305 return PathSuiteModel(option[len('path:'):])
1306 else:
1307 abort('unknown suitemodel type: ' + option)
1308
1309 def _parse_suitemodel_options(self):
1310 # the command line may contains args added by suites, so we only parse the currently known args
1311 opts = self.parse_known_args()[0]
1312 # set this early
1313 global _warn
1314 _warn = opts.warn
1315
1316 global _src_suitemodel
1317 _src_suitemodel = self._suitemodel(opts.src_suitemodel, opts.suitemap)
1318 global _dst_suitemodel
1319 _dst_suitemodel = self._suitemodel(opts.dst_suitemodel, opts.suitemap)
1094 1320
1095 def _format_commands(): 1321 def _format_commands():
1096 msg = '\navailable commands:\n\n' 1322 msg = '\navailable commands:\n\n'
1097 for cmd in sorted(_commands.iterkeys()): 1323 for cmd in sorted(_commands.iterkeys()):
1098 c, _ = _commands[cmd][:2] 1324 c, _ = _commands[cmd][:2]
1604 if not suppliedParser: 1830 if not suppliedParser:
1605 parser = ArgumentParser(prog='mx build') 1831 parser = ArgumentParser(prog='mx build')
1606 1832
1607 javaCompliance = java().javaCompliance 1833 javaCompliance = java().javaCompliance
1608 1834
1609 defaultEcjPath = join(_mainSuite.dir, 'mx', 'ecj.jar') 1835 defaultEcjPath = join(_mainSuite.mxDir, 'ecj.jar')
1610 1836
1611 parser = parser if parser is not None else ArgumentParser(prog='mx build') 1837 parser = parser if parser is not None else ArgumentParser(prog='mx build')
1612 parser.add_argument('-f', action='store_true', dest='force', help='force build (disables timestamp checking)') 1838 parser.add_argument('-f', action='store_true', dest='force', help='force build (disables timestamp checking)')
1613 parser.add_argument('-c', action='store_true', dest='clean', help='removes existing build output') 1839 parser.add_argument('-c', action='store_true', dest='clean', help='removes existing build output')
1614 parser.add_argument('--source', dest='compliance', help='Java compliance level for projects without an explicit one', default=str(javaCompliance)) 1840 parser.add_argument('--source', dest='compliance', help='Java compliance level for projects without an explicit one', default=str(javaCompliance))
1801 '-d', outputDir] 2027 '-d', outputDir]
1802 jdtArgs += processorArgs 2028 jdtArgs += processorArgs
1803 2029
1804 2030
1805 jdtProperties = join(p.dir, '.settings', 'org.eclipse.jdt.core.prefs') 2031 jdtProperties = join(p.dir, '.settings', 'org.eclipse.jdt.core.prefs')
1806 rootJdtProperties = join(p.suite.dir, 'mx', 'eclipse-settings', 'org.eclipse.jdt.core.prefs') 2032 rootJdtProperties = join(p.suite.mxDir, 'eclipse-settings', 'org.eclipse.jdt.core.prefs')
1807 if not exists(jdtProperties) or os.path.getmtime(jdtProperties) < os.path.getmtime(rootJdtProperties): 2033 if not exists(jdtProperties) or os.path.getmtime(jdtProperties) < os.path.getmtime(rootJdtProperties):
1808 # Try to fix a missing properties file by running eclipseinit 2034 # Try to fix a missing properties file by running eclipseinit
1809 eclipseinit([], buildProcessorJars=False) 2035 eclipseinit([], buildProcessorJars=False)
1810 if not exists(jdtProperties): 2036 if not exists(jdtProperties):
1811 log('JDT properties file {0} not found'.format(jdtProperties)) 2037 log('JDT properties file {0} not found'.format(jdtProperties))
2120 """process all project files to canonicalize the dependencies 2346 """process all project files to canonicalize the dependencies
2121 2347
2122 The exit code of this command reflects how many files were updated.""" 2348 The exit code of this command reflects how many files were updated."""
2123 2349
2124 changedFiles = 0 2350 changedFiles = 0
2125 for s in suites(): 2351 for s in suites(True):
2126 projectsFile = join(s.mxDir, 'projects') 2352 projectsFile = join(s.mxDir, 'projects')
2127 if not exists(projectsFile): 2353 if not exists(projectsFile):
2128 continue 2354 continue
2129 with open(projectsFile) as f: 2355 with open(projectsFile) as f:
2130 out = StringIO.StringIO() 2356 out = StringIO.StringIO()
2761 if refresh: 2987 if refresh:
2762 launchOut.element('stringAttribute', {'key' : 'org.eclipse.debug.core.ATTR_REFRESH_SCOPE', 'value': '${project}'}) 2988 launchOut.element('stringAttribute', {'key' : 'org.eclipse.debug.core.ATTR_REFRESH_SCOPE', 'value': '${project}'})
2763 launchOut.element('booleanAttribute', {'key' : 'org.eclipse.debug.ui.ATTR_CONSOLE_OUTPUT_ON', 'value': consoleOn}) 2989 launchOut.element('booleanAttribute', {'key' : 'org.eclipse.debug.ui.ATTR_CONSOLE_OUTPUT_ON', 'value': consoleOn})
2764 launchOut.element('booleanAttribute', {'key' : 'org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND', 'value': 'true' if async else 'false'}) 2990 launchOut.element('booleanAttribute', {'key' : 'org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND', 'value': 'true' if async else 'false'})
2765 2991
2766 baseDir = dirname(dirname(os.path.abspath(__file__))) 2992 # expect to find the OS command to invoke mx in the same directory
2993 baseDir = dirname(os.path.abspath(__file__))
2767 2994
2768 cmd = 'mx.sh' 2995 cmd = 'mx.sh'
2769 if get_os() == 'windows': 2996 if get_os() == 'windows':
2770 cmd = 'mx.cmd' 2997 cmd = 'mx.cmd'
2771 launchOut.element('stringAttribute', {'key' : 'org.eclipse.ui.externaltools.ATTR_LOCATION', 'value': join(baseDir, cmd) }) 2998 cmdPath = join(baseDir, cmd)
2999 if not os.path.exists(cmdPath):
3000 # backwards compatibility for when the commands lived in parent of mxtool
3001 cmdPath = join(dirname(baseDir), cmd)
3002 if not os.path.exists(cmdPath):
3003 abort('cannot locate ' + cmd)
3004
3005 launchOut.element('stringAttribute', {'key' : 'org.eclipse.ui.externaltools.ATTR_LOCATION', 'value': cmdPath})
2772 launchOut.element('stringAttribute', {'key' : 'org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS', 'value': 'auto,full,incremental'}) 3006 launchOut.element('stringAttribute', {'key' : 'org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS', 'value': 'auto,full,incremental'})
2773 launchOut.element('stringAttribute', {'key' : 'org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS', 'value': mxCommand}) 3007 launchOut.element('stringAttribute', {'key' : 'org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS', 'value': mxCommand})
2774 launchOut.element('booleanAttribute', {'key' : 'org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED', 'value': 'true'}) 3008 launchOut.element('booleanAttribute', {'key' : 'org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED', 'value': 'true'})
2775 launchOut.element('stringAttribute', {'key' : 'org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY', 'value': p.suite.dir}) 3009 launchOut.element('stringAttribute', {'key' : 'org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY', 'value': p.suite.dir})
2776 3010
3208 3442
3209 def fsckprojects(args): 3443 def fsckprojects(args):
3210 """find directories corresponding to deleted Java projects and delete them""" 3444 """find directories corresponding to deleted Java projects and delete them"""
3211 for suite in suites(True): 3445 for suite in suites(True):
3212 projectDirs = [p.dir for p in suite.projects] 3446 projectDirs = [p.dir for p in suite.projects]
3213 for root, dirnames, files in os.walk(suite.dir): 3447 for dirpath, dirnames, files in os.walk(suite.dir):
3214 currentDir = join(suite.dir, root) 3448 if dirpath == suite.dir:
3215 if currentDir in projectDirs: 3449 # no point in traversing .hg
3216 # don't traverse subdirs of an existing project 3450 if '.hg' in dirnames:
3451 dirnames.remove('.hg')
3452 # if there are nested suites must not scan those now, as they are not in projectDirs
3453 if _src_suitemodel._nestedsuites_dirname() in dirnames:
3454 dirnames.remove(_src_suitemodel._nestedsuites_dirname())
3455 elif dirpath in projectDirs:
3456 # don't traverse subdirs of an existing project in this suite
3217 dirnames[:] = [] 3457 dirnames[:] = []
3218 else: 3458 else:
3219 projectConfigFiles = frozenset(['.classpath', 'nbproject']) 3459 projectConfigFiles = frozenset(['.classpath', 'nbproject'])
3220 indicators = projectConfigFiles.intersection(files) 3460 indicators = projectConfigFiles.intersection(files)
3221 if len(indicators) != 0: 3461 if len(indicators) != 0:
3222 if not sys.stdout.isatty() or ask_yes_no(currentDir + ' looks like a removed project -- delete it', 'n'): 3462 if not sys.stdout.isatty() or ask_yes_no(dirpath + ' looks like a removed project -- delete it', 'n'):
3223 shutil.rmtree(currentDir) 3463 shutil.rmtree(dirpath)
3224 log('Deleted ' + currentDir) 3464 log('Deleted ' + dirpath)
3225 3465
3226 def javadoc(args, parser=None, docDir='javadoc', includeDeps=True, stdDoclet=True): 3466 def javadoc(args, parser=None, docDir='javadoc', includeDeps=True, stdDoclet=True):
3227 """generate javadoc for some/all Java projects""" 3467 """generate javadoc for some/all Java projects"""
3228 3468
3229 parser = ArgumentParser(prog='mx javadoc') if parser is None else parser 3469 parser = ArgumentParser(prog='mx javadoc') if parser is None else parser
3623 3863
3624 finally: 3864 finally:
3625 if exists(tmpbase): 3865 if exists(tmpbase):
3626 shutil.rmtree(tmpbase) 3866 shutil.rmtree(tmpbase)
3627 3867
3868 def _kwArg(kwargs):
3869 if len(kwargs) > 0:
3870 return kwargs.pop(0)
3871 return None
3872
3873 def sclone(args):
3874 """clone a suite repository, and its imported suites"""
3875 parser = ArgumentParser(prog='mx sclone')
3876 parser.add_argument('--source', help='url/path of repo containing suite', metavar='<url>')
3877 parser.add_argument('--dest', help='destination directory (default basename of source)', metavar='<path>')
3878 parser.add_argument("--no-imports", action='store_true', help='do not clone imported suites')
3879 parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...')
3880 args = parser.parse_args(args)
3881 # check for non keyword args
3882 if args.source is None:
3883 args.source = _kwArg(args.nonKWArgs)
3884 if args.dest is None:
3885 args.dest = _kwArg(args.nonKWArgs)
3886 if len(args.nonKWArgs) > 0:
3887 abort('unrecognized args: ' + ' '.join(args.nonKWArgs))
3888
3889 if args.source is None:
3890 # must be primary suite and dest is required
3891 if _mainSuite is None:
3892 abort('--source missing and no primary suite found')
3893 if args.dest is None:
3894 abort('--dest required when --source is not given')
3895 source = _mainSuite.dir
3896 else:
3897 source = args.source
3898
3899 if args.dest is not None:
3900 dest = args.dest
3901 else:
3902 dest = basename(source)
3903
3904 dest = os.path.abspath(dest)
3905 # We can now set the primary dir for the src/dst suitemodel
3906 _dst_suitemodel._set_primary_dir(dest)
3907 _src_suitemodel._set_primary_dir(source)
3908
3909 _sclone(source, dest, None, args.no_imports)
3910
3911 def _sclone(source, dest, version, no_imports):
3912 cmd = ['hg', 'clone']
3913 if version is not None:
3914 cmd.append('-r')
3915 cmd.append(version)
3916 cmd.append(source)
3917 cmd.append(dest)
3918
3919 run(cmd)
3920
3921 mxDir = _is_suite_dir(dest)
3922 if mxDir is None:
3923 warn(source + ' is not an mx suite')
3924 return None
3925
3926 # create a Suite (without loading) to enable imports visitor
3927 s = Suite(mxDir, False, load=False)
3928 if not no_imports:
3929 s._visit_imports(_scloneimports_visitor, source=source)
3930 return s
3931
3932 def _scloneimports_visitor(s, suite_import, source, **extra_args):
3933 """
3934 cloneimports visitor for Suite._visit_imports.
3935 The destination information is encapsulated by 's'
3936 """
3937 _scloneimports(s, suite_import, source)
3938
3939 def _scloneimports_suitehelper(sdir):
3940 mxDir = _is_suite_dir(sdir)
3941 if mxDir is None:
3942 abort(sdir + ' is not an mx suite')
3943 else:
3944 # create a Suite (without loading) to enable imports visitor
3945 return Suite(mxDir, False, load=False)
3946
3947 def _scloneimports(s, suite_import, source):
3948 # clone first, then visit imports once we can locate them
3949 importee_source = _src_suitemodel._importee_dir(source, suite_import.name)
3950 importee_dest = _dst_suitemodel._importee_dir(s.dir, suite_import.name)
3951 if exists(importee_dest):
3952 importee_suite = _scloneimports_suitehelper(importee_dest)
3953 importee_suite._visit_imports(_scloneimports_visitor, source=importee_source)
3954 else:
3955 _sclone(importee_source, importee_dest, suite_import.version, False)
3956 # _clone handles the recursive visit of the new imports
3957
3958 def scloneimports(args):
3959 """clone the imports of an existing suite"""
3960 parser = ArgumentParser(prog='mx scloneimports')
3961 parser.add_argument('--source', help='url/path of repo containing suite', metavar='<url>')
3962 parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...')
3963 args = parser.parse_args(args)
3964 # check for non keyword args
3965 if args.source is None:
3966 args.source = _kwArg(args.nonKWArgs)
3967
3968 if not os.path.isdir(args.source):
3969 abort(args.source + ' is not a directory')
3970
3971 s = _scloneimports_suitehelper(args.source)
3972
3973 default_path = _hgdefault_push(args.source)
3974
3975 if default_path is None:
3976 abort('no default path in ' + join(args.source, '.hg', 'hgrc'))
3977
3978 # We can now set the primary dir for the dst suitemodel
3979 # N.B. source is effectively the destination and the default_path is the (original) source
3980 _dst_suitemodel._set_primary_dir(args.source)
3981
3982 s._visit_imports(_scloneimports_visitor, source=default_path)
3983
3984 def _spush_import_visitor(s, suite_import, dest, checks, clonemissing, **extra_args):
3985 """push visitor for Suite._visit_imports"""
3986 if dest is not None:
3987 dest = _dst_suitemodel._importee_dir(dest, suite_import.name)
3988 _spush(suite(suite_import.name), suite_import, dest, checks, clonemissing)
3989
3990 def _spush_check_import_visitor(s, suite_import, **extra_args):
3991 """push check visitor for Suite._visit_imports"""
3992 currentTip = _hgtip(suite(suite_import.name))
3993 if currentTip != suite_import.version:
3994 abort('import version of ' + suite_import.name + ' in suite ' + s.name + ' does not match tip')
3995
3996 def _spush(s, suite_import, dest, checks, clonemissing):
3997 if checks:
3998 if not _hgcanpush(s):
3999 abort('working directory ' + s.dir + ' contains uncommitted changes, push aborted')
4000
4001 # check imports first
4002 if checks:
4003 s._visit_imports(_spush_check_import_visitor)
4004
4005 # ok, push imports
4006 s._visit_imports(_spush_import_visitor, dest=dest, checks=checks, clonemissing=clonemissing)
4007
4008 dest_exists = True
4009
4010 if clonemissing:
4011 if not os.path.exists(dest):
4012 dest_exists = False
4013
4014 def add_version(cmd, suite_import):
4015 if suite_import is not None and suite_import.version is not None:
4016 cmd.append('-r')
4017 cmd.append(suite_import.version)
4018
4019 if dest_exists:
4020 cmd = ['hg', '-R', s.dir, 'push']
4021 add_version(cmd, suite_import)
4022 if dest is not None:
4023 cmd.append(dest)
4024 rc = run(cmd, nonZeroIsFatal=False)
4025 if rc != 0:
4026 # rc of 1 not an error, means no changes
4027 if rc != 1:
4028 abort("push failed, exit code " + str(rc))
4029 else:
4030 cmd = ['hg', 'clone']
4031 add_version(cmd, suite_import)
4032 cmd.append(s.dir)
4033 cmd.append(dest)
4034 run(cmd)
4035
4036 def spush(args):
4037 """push primary suite and all its imports"""
4038 parser = ArgumentParser(prog='mx spush')
4039 parser.add_argument('--dest', help='url/path of repo to push to (default as per hg push)', metavar='<path>')
4040 parser.add_argument('--no-checks', action='store_true', help='checks on status, versions are disabled')
4041 parser.add_argument('--clonemissing', action='store_true', help='clone missing imported repos at destination (forces --no-checks)')
4042 parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...')
4043 args = parser.parse_args(args)
4044 if args.dest is None:
4045 args.dest = _kwArg(args.nonKWArgs)
4046 if len(args.nonKWArgs) > 0:
4047 abort('unrecognized args: ' + ' '.join(args.nonKWArgs))
4048
4049 if args.dest is not None and not os.path.isdir(args.dest):
4050 abort('destination must be a directory')
4051
4052 s = _check_primary_suite()
4053
4054 if args.clonemissing:
4055 if args.dest is None:
4056 abort('--dest required with --clonemissing')
4057 args.nochecks = True
4058
4059 if args.dest is not None:
4060 _dst_suitemodel._set_primary_dir(args.dest)
4061
4062 _spush(s, None, args.dest, not args.nochecks, args.clonemissing)
4063
4064 def _supdate_import_visitor(s, suite_import, **extra_args):
4065 _supdate(suite(suite_import.name), suite_import)
4066
4067 def _supdate(s, suite_import):
4068 s._visit_imports(_supdate_import_visitor)
4069
4070 run(['hg', '-R', s.dir, 'update'])
4071
4072 def supdate(args):
4073 """update primary suite and all its imports"""
4074
4075 s = _check_primary_suite()
4076
4077 _supdate(s, None)
4078
4079 def _scheck_imports_visitor(s, suite_import, update_versions, updated_imports):
4080 """checkimportversions visitor for Suite._visit_imports"""
4081 _scheck_imports(suite(suite_import.name), suite_import, update_versions, updated_imports)
4082
4083 def _scheck_imports(s, suite_import, update_versions, updated_imports):
4084 # check imports recursively
4085 s._visit_imports(_scheck_imports_visitor, update_versions=update_versions)
4086
4087 currentTip = _hgtip(s)
4088 if currentTip != suite_import.version:
4089 print('import version of ' + s.name + ' does not match tip' + (': updating' if update_versions else ''))
4090
4091 if update_versions:
4092 suite_import.version = currentTip
4093 line = suite_import._self_tostring()
4094 updated_imports.write(line + '\n')
4095
4096 def scheckimports(args):
4097 """check that suite import versions are up to date"""
4098 parser = ArgumentParser(prog='mx scheckimports')
4099 parser.add_argument('--update-versions', help='update imported version ids', action='store_true')
4100 args = parser.parse_args(args)
4101 _check_primary_suite()._visit_imports(_scheck_imports_visitor, update_versions=args.update_versions)
4102
4103 def _hgtip(s, abortOnError=True):
4104 try:
4105 version = subprocess.check_output(['hg', 'tip', '-R', s.dir, '--template', '{node}'])
4106 if s.version is not None and s.version != version:
4107 abort('version of suite ' + s.name +' has changed during run')
4108 return version
4109 except subprocess.CalledProcessError:
4110 if abortOnError:
4111 abort('failed to get tip revision id')
4112 else:
4113 return None
4114
4115 def _hgcanpush(s):
4116 try:
4117 output = subprocess.check_output(['hg', '-R', s.dir, 'status'])
4118 # super strict
4119 return output == ''
4120 except subprocess.CalledProcessError:
4121 return False
4122
4123 def _hgdefault_push(sdir):
4124 with open(join(sdir, '.hg', 'hgrc')) as f:
4125 for line in f:
4126 line = line.rstrip()
4127 if line.startswith('default = '):
4128 return line[len('default = '):]
4129 return None
4130
4131 def _spull_import_visitor(s, suite_import, update_versions, updated_imports):
4132 """pull visitor for Suite._visit_imports"""
4133 _spull(suite(suite_import.name), update_versions, updated_imports)
4134
4135 def _spull(s, update_versions, updated_imports):
4136 # pull imports first
4137 s._visit_imports(_spull_import_visitor, update_versions=update_versions)
4138
4139 run(['hg', '-R', s.dir, 'pull', '-u'])
4140 if update_versions and updated_imports is not None:
4141 tip = _hgtip(s)
4142 updated_imports.write(SuiteImport._tostring(s.name, tip) + '\n')
4143
4144 def spull(args):
4145 """pull primary suite and all its imports"""
4146 parser = ArgumentParser(prog='mx spull')
4147 parser.add_argument('--update-versions', action='store_true', help='update version ids of imported suites')
4148 args = parser.parse_args(args)
4149
4150 _spull(_check_primary_suite(), args.update_versions, None)
4151
3628 def findclass(args, logToConsole=True): 4152 def findclass(args, logToConsole=True):
3629 """find all classes matching a given substring""" 4153 """find all classes matching a given substring"""
3630
3631 matches = [] 4154 matches = []
3632 for entry, filename in classpath_walk(includeBootClasspath=True): 4155 for entry, filename in classpath_walk(includeBootClasspath=True):
3633 if filename.endswith('.class'): 4156 if filename.endswith('.class'):
3634 if isinstance(entry, zipfile.ZipFile): 4157 if isinstance(entry, zipfile.ZipFile):
3635 classname = filename.replace('/', '.') 4158 classname = filename.replace('/', '.')
3732 assert _argParser is not None 4255 assert _argParser is not None
3733 _argParser.add_argument(*args, **kwargs) 4256 _argParser.add_argument(*args, **kwargs)
3734 4257
3735 def update_commands(suite, new_commands): 4258 def update_commands(suite, new_commands):
3736 for key, value in new_commands.iteritems(): 4259 for key, value in new_commands.iteritems():
3737 if _commands.has_key(key) and not suite.primary: 4260 if _commands.has_key(key):
3738 pass 4261 warn("redefining command '" + key + "' in suite " + suite.name)
3739 # print("WARNING: attempt to redefine command '" + key + "' in suite " + suite.dir) 4262 _commands[key] = value
3740 else: 4263
3741 _commands[key] = value 4264 def warn(msg):
4265 if _warn:
4266 print('WARNING: ' + msg)
3742 4267
3743 # Table of commands in alphabetical order. 4268 # Table of commands in alphabetical order.
3744 # Keys are command names, value are lists: [<function>, <usage msg>, <format args to doc string of function>...] 4269 # Keys are command names, value are lists: [<function>, <usage msg>, <format args to doc string of function>...]
3745 # If any of the format args are instances of Callable, then they are called with an 'env' are before being 4270 # If any of the format args are instances of Callable, then they are called with an 'env' are before being
3746 # used in the call to str.format(). 4271 # used in the call to str.format().
3747 # Extensions should update this table directly 4272 # Suite extensions should not update this table directly, but use update_commands
3748 _commands = { 4273 _commands = {
3749 'about': [about, ''], 4274 'about': [about, ''],
3750 'build': [build, '[options]'], 4275 'build': [build, '[options]'],
3751 'checkstyle': [checkstyle, ''], 4276 'checkstyle': [checkstyle, ''],
3752 'canonicalizeprojects': [canonicalizeprojects, ''], 4277 'canonicalizeprojects': [canonicalizeprojects, ''],
3758 'help': [help_, '[command]'], 4283 'help': [help_, '[command]'],
3759 'ideclean': [ideclean, ''], 4284 'ideclean': [ideclean, ''],
3760 'ideinit': [ideinit, ''], 4285 'ideinit': [ideinit, ''],
3761 'archive': [archive, '[options]'], 4286 'archive': [archive, '[options]'],
3762 'projectgraph': [projectgraph, ''], 4287 'projectgraph': [projectgraph, ''],
4288 'sclone': [sclone, '[options]'],
4289 'scheckimports': [scheckimports, ''],
4290 'scloneimports': [scloneimports, '[options]'],
4291 'spull': [spull, '[options'],
4292 'spush': [spush, '[options'],
4293 'supdate': [supdate, ''],
3763 'pylint': [pylint, ''], 4294 'pylint': [pylint, ''],
3764 'javap': [javap, '<class name patterns>'], 4295 'javap': [javap, '<class name patterns>'],
3765 'javadoc': [javadoc, '[options]'], 4296 'javadoc': [javadoc, '[options]'],
3766 'site': [site, '[options]'], 4297 'site': [site, '[options]'],
3767 'netbeansinit': [netbeansinit, ''], 4298 'netbeansinit': [netbeansinit, ''],
3768 'projects': [show_projects, ''], 4299 'projects': [show_projects, ''],
3769 } 4300 }
3770 4301
3771 _argParser = ArgParser() 4302 _argParser = ArgParser()
3772 4303
3773 def _findPrimarySuite(): 4304 def _suitename(mxDir):
3774 def is_suite_dir(d): 4305 base = os.path.basename(mxDir)
4306 parts = base.split('.')
4307 # temporary workaround until mx.graal exists
4308 if len(parts) == 1:
4309 return 'graal'
4310 else:
4311 return parts[1]
4312
4313 def _is_suite_dir(d, mxDirName=None):
4314 """
4315 Checks if d contains a suite.
4316 If mxDirName is None, matches any suite name, otherwise checks for exactly that suite.
4317 """
4318 if os.path.isdir(d):
3775 for f in os.listdir(d): 4319 for f in os.listdir(d):
3776 if f == 'mx' or fnmatch.fnmatch(f, 'mx.*'): 4320 if (mxDirName == None and (f == 'mx' or fnmatch.fnmatch(f, 'mx.*'))) or f == mxDirName:
3777 mxDir = join(d, f) 4321 mxDir = join(d, f)
3778 if exists(mxDir) and isdir(mxDir) and exists(join(mxDir, 'projects')): 4322 if exists(mxDir) and isdir(mxDir) and exists(join(mxDir, 'projects')):
3779 return dirname(mxDir) 4323 return mxDir
3780 4324
3781 4325 def _check_primary_suite():
3782 # try current working directory first 4326 if _mainSuite is None:
3783 if is_suite_dir(os.getcwd()): 4327 abort('no primary suite found')
3784 return os.getcwd() 4328 else:
3785 4329 return _mainSuite
3786 # now search path of my executable 4330
3787 me = sys.argv[0] 4331 def _needs_primary_suite(command):
3788 parent = dirname(me) 4332 return not command.startswith("sclone")
3789 while parent: 4333
3790 if is_suite_dir(parent): 4334 def _findPrimarySuiteMxDir():
3791 return parent 4335 # try current working directory first, the look up the tree
3792 parent = dirname(parent) 4336 curdir = os.getcwd()
4337 while curdir:
4338 mxDir = _is_suite_dir(curdir)
4339 if mxDir is not None:
4340 return mxDir
4341 parent = dirname(curdir)
4342 if curdir == parent:
4343 return None
4344 curdir = parent
4345
3793 return None 4346 return None
3794 4347
3795 def main(): 4348 def main():
3796 primarySuiteDir = _findPrimarySuite() 4349 SMArgParser()._parse_suitemodel_options()
3797 if primarySuiteDir: 4350
4351 primarySuiteMxDir = _findPrimarySuiteMxDir()
4352 if primarySuiteMxDir:
4353 _src_suitemodel._set_primary_dir(dirname(primarySuiteMxDir))
3798 global _mainSuite 4354 global _mainSuite
3799 _mainSuite = _loadSuite(primarySuiteDir, True) 4355 _mainSuite = _loadSuite(primarySuiteMxDir, True)
3800 4356
3801 opts, commandAndArgs = _argParser._parse_cmd_line() 4357 opts, commandAndArgs = _argParser._parse_cmd_line()
4358
4359 if primarySuiteMxDir is None:
4360 msg = 'no primary suite found'
4361 if len(commandAndArgs) > 0 and _needs_primary_suite(commandAndArgs[0]):
4362 abort(msg)
4363 else:
4364 warn(msg)
3802 4365
3803 global _opts, _java 4366 global _opts, _java
3804 _opts = opts 4367 _opts = opts
3805 _java = JavaConfig(opts) 4368 _java = JavaConfig(opts)
3806 4369