Mercurial > hg > truffle
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 |