Mercurial > hg > truffle
comparison mxtool/mx.py @ 13855:bc32c9f5719b
remove multiple suite/repo support
author | Mick Jordan <mick.jordan@oracle.com> |
---|---|
date | Sat, 01 Feb 2014 10:47:12 -0800 |
parents | f11d3d5248b5 |
children | 1472b8d3f142 |
comparison
equal
deleted
inserted
replaced
13854:5d455591cfbd | 13855:bc32c9f5719b |
---|---|
26 # ---------------------------------------------------------------------------------------------------- | 26 # ---------------------------------------------------------------------------------------------------- |
27 # | 27 # |
28 r""" | 28 r""" |
29 mx is a command line tool for managing the development of Java code organized as suites of projects. | 29 mx is a command line tool for managing the development of Java code organized as suites of projects. |
30 | 30 |
31 Version 1.x supports a single suite of projects. | |
32 | |
31 Full documentation can be found at https://wiki.openjdk.java.net/display/Graal/The+mx+Tool | 33 Full documentation can be found at https://wiki.openjdk.java.net/display/Graal/The+mx+Tool |
32 """ | 34 """ |
33 | 35 |
34 import sys, os, errno, time, subprocess, shlex, types, urllib2, contextlib, StringIO, zipfile, signal, xml.sax.saxutils, tempfile, fnmatch | 36 import sys, os, errno, time, subprocess, shlex, types, urllib2, contextlib, StringIO, zipfile, signal, xml.sax.saxutils, tempfile, fnmatch |
35 import textwrap | 37 import textwrap |
46 _dists = dict() | 48 _dists = dict() |
47 _suites = dict() | 49 _suites = dict() |
48 _annotationProcessors = None | 50 _annotationProcessors = None |
49 _primary_suite_path = None | 51 _primary_suite_path = None |
50 _primary_suite = None | 52 _primary_suite = None |
51 _src_suitemodel = None | |
52 _dst_suitemodel = None | |
53 _opts = None | 53 _opts = None |
54 _java = None | 54 _java = None |
55 _check_global_structures = True # can be set False to allow suites with duplicate definitions to load without aborting | |
56 _warn = False | 55 _warn = False |
57 _hg = None | |
58 | |
59 | 56 |
60 """ | 57 """ |
61 A distribution is a jar or zip file containing the output from one or more Java projects. | 58 A distribution is a jar or zip file containing the output from one or more Java projects. |
62 """ | 59 """ |
63 class Distribution: | 60 class Distribution: |
440 if abortOnError: | 437 if abortOnError: |
441 abort('failed to get tip revision id') | 438 abort('failed to get tip revision id') |
442 else: | 439 else: |
443 return None | 440 return None |
444 | 441 |
445 def can_push(self, s, strict=True): | |
446 try: | |
447 output = subprocess.check_output(['hg', '-R', s.dir, 'status']) | |
448 # super strict | |
449 return output == '' | |
450 except OSError: | |
451 warn(self.missing) | |
452 except subprocess.CalledProcessError: | |
453 return False | |
454 | |
455 def default_push(self, sdir): | |
456 with open(join(sdir, '.hg', 'hgrc')) as f: | |
457 for line in f: | |
458 line = line.rstrip() | |
459 if line.startswith('default = '): | |
460 return line[len('default = '):] | |
461 return None | |
462 | |
463 class SuiteModel: | |
464 """ | |
465 Defines how to locate a URL/path for a suite, including imported suites. | |
466 Conceptually a SuiteModel is defined by a primary suite URL/path and a | |
467 map from suite name to URL/path for imported suites. | |
468 Subclasses define a specfic implementation. | |
469 """ | |
470 def __init__(self): | |
471 self.primaryDir = None | |
472 self.suitenamemap = {} | |
473 | |
474 def find_suite_dir(self, suitename): | |
475 """locates the URL/path for suitename or None if not found""" | |
476 abort('find_suite_dir not implemented') | |
477 | |
478 def set_primary_dir(self, d): | |
479 """informs that d is the primary suite directory""" | |
480 self._primaryDir = d | |
481 | |
482 def importee_dir(self, importer_dir, suitename): | |
483 """returns the directory path for an import of suitename, given importer_dir""" | |
484 abort('importee_dir not implemented') | |
485 | |
486 def nestedsuites_dirname(self): | |
487 """Returns the dirname that contains any nested suites if the model supports that""" | |
488 return None | |
489 | |
490 def _mxDirName(self, name): | |
491 # temporary workaround until mx.graal exists | |
492 if name == 'graal': | |
493 return 'mx' | |
494 else: | |
495 return 'mx.' + name | |
496 | |
497 def _search_dir(self, searchDir, mxDirName): | |
498 for dd in os.listdir(searchDir): | |
499 sd = _is_suite_dir(join(searchDir, dd), mxDirName) | |
500 if sd is not None: | |
501 return sd | |
502 | |
503 def _create_suitenamemap(self, optionspec, suitemap): | |
504 """Three ways to specify a suite name mapping, in order of precedence: | |
505 1. Explicitly in optionspec. | |
506 2. In suitemap. | |
507 3. in MXSUITEMAP environment variable. | |
508 """ | |
509 if optionspec != '': | |
510 spec = optionspec | |
511 elif suitemap is not None: | |
512 spec = suitemap | |
513 elif get_env('MXSUITEMAP') is not None: | |
514 spec = get_env('MXSUITEMAP') | |
515 else: | |
516 return | |
517 pairs = spec.split(',') | |
518 for pair in pairs: | |
519 mappair = pair.split('=') | |
520 self.suitenamemap[mappair[0]] = mappair[1] | |
521 | |
522 @staticmethod | |
523 def set_suitemodel(option, suitemap): | |
524 if option.startswith('sibling'): | |
525 return SiblingSuiteModel(os.getcwd(), option, suitemap) | |
526 elif option.startswith('nested'): | |
527 return NestedImportsSuiteModel(os.getcwd(), option, suitemap) | |
528 elif option.startswith('path'): | |
529 return PathSuiteModel(option[len('path:'):]) | |
530 else: | |
531 abort('unknown suitemodel type: ' + option) | |
532 | |
533 @staticmethod | |
534 def parse_options(): | |
535 # suite-specific args may match the known args so there is no way at this early stage | |
536 # to use ArgParser to handle the suite model global arguments, so we just do it manually. | |
537 def _get_argvalue(arg, args, i): | |
538 if i < len(args): | |
539 return args[i] | |
540 else: | |
541 abort('value expected with ' + arg) | |
542 | |
543 args = sys.argv[1:] | |
544 src_suitemodel_arg = dst_suitemodel_arg = 'sibling' | |
545 suitemap_arg = None | |
546 | |
547 i = 0 | |
548 while i < len(args): | |
549 arg = args[i] | |
550 if arg == '--src-suitemodel': | |
551 src_suitemodel_arg = _get_argvalue(arg, args, i + 1) | |
552 elif arg == '--dst-suitemodel': | |
553 dst_suitemodel_arg = _get_argvalue(arg, args, i + 1) | |
554 elif arg == '--suitemap': | |
555 suitemap_arg = _get_argvalue(arg, args, i + 1) | |
556 elif arg == '-w': | |
557 # to get warnings on suite loading issues before command line is parsed | |
558 global _warn | |
559 _warn = True | |
560 elif arg == '-p' or arg == '--primary-suite-path': | |
561 global _primary_suite_path | |
562 _primary_suite_path = os.path.abspath(_get_argvalue(arg, args, i + 1)) | |
563 i = i + 1 | |
564 | |
565 global _src_suitemodel | |
566 _src_suitemodel = SuiteModel.set_suitemodel(src_suitemodel_arg, suitemap_arg) | |
567 global _dst_suitemodel | |
568 _dst_suitemodel = SuiteModel.set_suitemodel(dst_suitemodel_arg, suitemap_arg) | |
569 | |
570 | |
571 class SiblingSuiteModel(SuiteModel): | |
572 """All suites are siblings in the same parent directory, recorded as _suiteRootDir""" | |
573 def __init__(self, suiteRootDir, option, suitemap): | |
574 SuiteModel.__init__(self) | |
575 self._suiteRootDir = suiteRootDir | |
576 self._create_suitenamemap(option[len('sibling:'):], suitemap) | |
577 | |
578 def find_suite_dir(self, name): | |
579 return self._search_dir(self._suiteRootDir, self._mxDirName(name)) | |
580 | |
581 def set_primary_dir(self, d): | |
582 SuiteModel.set_primary_dir(self, d) | |
583 self._suiteRootDir = dirname(d) | |
584 | |
585 def importee_dir(self, importer_dir, suitename): | |
586 if self.suitenamemap.has_key(suitename): | |
587 suitename = self.suitenamemap[suitename] | |
588 return join(dirname(importer_dir), suitename) | |
589 | |
590 class NestedImportsSuiteModel(SuiteModel): | |
591 """Imported suites are all siblings in an 'imported_suites' directory of the primary suite""" | |
592 def _imported_suites_dirname(self): | |
593 return "imported_suites" | |
594 | |
595 def __init__(self, primaryDir, option, suitemap): | |
596 SuiteModel.__init__(self) | |
597 self._primaryDir = primaryDir | |
598 self._create_suitenamemap(option[len('nested:'):], suitemap) | |
599 | |
600 def find_suite_dir(self, name): | |
601 return self._search_dir(join(self._primaryDir, self._imported_suites_dirname()), self._mxDirName(name)) | |
602 | |
603 def importee_dir(self, importer_dir, suitename): | |
604 if self.suitenamemap.has_key(suitename): | |
605 suitename = self.suitenamemap[suitename] | |
606 if basename(importer_dir) == basename(self._primaryDir): | |
607 # primary is importer | |
608 this_imported_suites_dirname = join(importer_dir, self._imported_suites_dirname()) | |
609 if not exists(this_imported_suites_dirname): | |
610 os.mkdir(this_imported_suites_dirname) | |
611 return join(this_imported_suites_dirname, suitename) | |
612 else: | |
613 return join(dirname(importer_dir), suitename) | |
614 | |
615 def nestedsuites_dirname(self): | |
616 return self._imported_suites_dirname() | |
617 | |
618 class PathSuiteModel(SuiteModel): | |
619 """The most general model. Uses a map from suitename to URL/path provided by the user""" | |
620 def __init__(self, path): | |
621 SuiteModel.__init__(self) | |
622 paths = path.split(',') | |
623 self.suit_to_url = {} | |
624 for path in paths: | |
625 pair = path.split('=') | |
626 if len(pair) > 1: | |
627 suitename = pair[0] | |
628 suiteurl = pair[1] | |
629 else: | |
630 suitename = basename(pair[0]) | |
631 suiteurl = pair[0] | |
632 self.suit_to_url[suitename] = suiteurl | |
633 | |
634 def find_suite_dir(self, suitename): | |
635 if self.suit_to_url.has_key(suitename): | |
636 return self.suit_to_url[suitename] | |
637 else: | |
638 return None | |
639 | |
640 def importee_dir(self, importer_dir, suitename): | |
641 if suitename in self.suit_to_url: | |
642 return self.suit_to_url[suitename] | |
643 else: | |
644 abort('suite ' + suitename + ' not found') | |
645 | |
646 class SuiteImport: | |
647 def __init__(self, name, version): | |
648 self.name = name | |
649 self.version = version | |
650 | |
651 @staticmethod | |
652 def parse_specification(specification): | |
653 pair = specification.split(',') | |
654 name = pair[0] | |
655 if len(pair) > 1: | |
656 version = pair[1] | |
657 else: | |
658 version = None | |
659 return SuiteImport(name, version) | |
660 | |
661 @staticmethod | |
662 def tostring(name, version): | |
663 return name + ',' + version | |
664 | |
665 def __str__(self): | |
666 return self.name + ',' + self.version | |
667 | |
668 class Suite: | 442 class Suite: |
669 def __init__(self, mxDir, primary, load=True): | 443 def __init__(self, mxDir, primary, load=True): |
670 self.dir = dirname(mxDir) | 444 self.dir = dirname(mxDir) |
671 self.mxDir = mxDir | 445 self.mxDir = mxDir |
672 self.projects = [] | 446 self.projects = [] |
673 self.libs = [] | 447 self.libs = [] |
674 self.dists = [] | 448 self.dists = [] |
675 self.imports = [] | |
676 self.commands = None | 449 self.commands = None |
677 self.primary = primary | 450 self.primary = primary |
678 self.requiredMxVersion = None | 451 self.requiredMxVersion = None |
679 self.name = _suitename(mxDir) # validated in _load_projects | 452 self.name = _suitename(mxDir) # validated in _load_projects |
680 if load: | 453 if load: |
681 # load suites bottom up to make sure command overriding works properly | 454 # just check that there are no imports |
682 self._load_imports() | 455 self._load_imports() |
683 self._load_env() | 456 self._load_env() |
684 self._load_commands() | 457 self._load_commands() |
685 _suites[self.name] = self | 458 _suites[self.name] = self |
686 | 459 |
687 def __str__(self): | 460 def __str__(self): |
688 return self.name | 461 return self.name |
689 | |
690 def version(self, abortOnError=True): | |
691 # we do not cache the version | |
692 return _hg.tip(self.dir, abortOnError) | |
693 | 462 |
694 def _load_projects(self): | 463 def _load_projects(self): |
695 libsMap = dict() | 464 libsMap = dict() |
696 projsMap = dict() | 465 projsMap = dict() |
697 distsMap = dict() | 466 distsMap = dict() |
836 self.mx_post_parse_cmd_line = mod.mx_post_parse_cmd_line | 605 self.mx_post_parse_cmd_line = mod.mx_post_parse_cmd_line |
837 | 606 |
838 mod.mx_init(self) | 607 mod.mx_init(self) |
839 self.commands = mod | 608 self.commands = mod |
840 | 609 |
841 def _imports_file(self): | |
842 return join(self.mxDir, 'imports') | |
843 | |
844 def import_timestamp(self): | |
845 return TimeStampFile(self._imports_file()) | |
846 | |
847 def visit_imports(self, visitor, **extra_args): | |
848 """ | |
849 Visitor support for the imports file. | |
850 For each line of the imports file that specifies an import, the visitor function is | |
851 called with this suite, a SuiteImport instance created from the line and any extra args | |
852 passed to this call. In addition, if extra_args contains a key 'update_versions' that is True, | |
853 a StringIO value is added to extra_args with key 'updated_imports', and the visitor is responsible | |
854 for writing a (possibly) updated import line to the file, and the file is (possibly) updated after | |
855 all imports are processed. | |
856 N.B. There is no built-in support for avoiding visiting the same suite multiple times, | |
857 as this function only visits the imports of a single suite. If a (recursive) visitor function | |
858 wishes to visit a suite exactly once, it must manage that through extra_args. | |
859 """ | |
860 importsFile = self._imports_file() | |
861 if exists(importsFile): | |
862 update_versions = extra_args.has_key('update_versions') and extra_args['update_versions'] | |
863 out = StringIO.StringIO() if update_versions else None | |
864 extra_args['updated_imports'] = out | |
865 with open(importsFile) as f: | |
866 for line in f: | |
867 sline = line.strip() | |
868 if len(sline) == 0 or sline.startswith('#'): | |
869 if out is not None: | |
870 out.write(sline + '\n') | |
871 continue | |
872 suite_import = SuiteImport.parse_specification(line.strip()) | |
873 visitor(self, suite_import, **extra_args) | |
874 | |
875 if out is not None: | |
876 update_file(importsFile, out.getvalue()) | |
877 | |
878 @staticmethod | |
879 def _find_and_loadsuite(importing_suite, suite_import, **extra_args): | |
880 """visitor for the initial suite load""" | |
881 importMxDir = _src_suitemodel.find_suite_dir(suite_import.name) | |
882 if importMxDir is None: | |
883 abort('import ' + suite_import.name + ' not found') | |
884 importing_suite.imports.append(suite_import) | |
885 _loadSuite(importMxDir, False) | |
886 # we do not check at this stage whether the tip version of imported_suite | |
887 # matches that of the import, since during development, this can and will change | |
888 | |
889 def _load_imports(self): | 610 def _load_imports(self): |
890 self.visit_imports(self._find_and_loadsuite) | 611 if exists(join(self.mxDir, 'imports')): |
612 abort('multiple suites are not supported in this version of mx') | |
891 | 613 |
892 def _load_env(self): | 614 def _load_env(self): |
893 e = join(self.mxDir, 'env') | 615 e = join(self.mxDir, 'env') |
894 if exists(e): | 616 if exists(e): |
895 with open(e) as f: | 617 with open(e) as f: |
906 def _post_init(self, opts): | 628 def _post_init(self, opts): |
907 self._load_projects() | 629 self._load_projects() |
908 if self.requiredMxVersion is None: | 630 if self.requiredMxVersion is None: |
909 warn("This suite does not express any required mx version. Consider adding 'mxversion=<version>' to your projects file.") | 631 warn("This suite does not express any required mx version. Consider adding 'mxversion=<version>' to your projects file.") |
910 elif self.requiredMxVersion > version: | 632 elif self.requiredMxVersion > version: |
911 abort("This suite requires mx version " + str(self.requiredMxVersion) + " while your current mx verion is " + str(version) + ". Please update mx.") | 633 abort("This suite requires mx version " + str(self.requiredMxVersion) + " while your current mx version is " + str(version) + ". Please update mx.") |
912 # set the global data structures, checking for conflicts unless _check_global_structures is False | 634 # set the global data structures, checking for conflicts unless _check_global_structures is False |
913 for p in self.projects: | 635 for p in self.projects: |
914 existing = _projects.get(p.name) | 636 existing = _projects.get(p.name) |
915 if existing is not None and _check_global_structures: | 637 if existing is not None: |
916 abort('cannot override project ' + p.name + ' in ' + p.dir + " with project of the same name in " + existing.dir) | 638 abort('cannot override project ' + p.name + ' in ' + p.dir + " with project of the same name in " + existing.dir) |
917 if not p.name in _opts.ignored_projects: | 639 if not p.name in _opts.ignored_projects: |
918 _projects[p.name] = p | 640 _projects[p.name] = p |
919 for l in self.libs: | 641 for l in self.libs: |
920 existing = _libs.get(l.name) | 642 existing = _libs.get(l.name) |
921 # Check that suites that define same library are consistent | 643 # Check that suites that define same library are consistent |
922 if existing is not None and existing != l and _check_global_structures: | 644 if existing is not None and existing != l: |
923 abort('inconsistent library redefinition of ' + l.name + ' in ' + existing.suite.dir + ' and ' + l.suite.dir) | 645 abort('inconsistent library redefinition of ' + l.name + ' in ' + existing.suite.dir + ' and ' + l.suite.dir) |
924 _libs[l.name] = l | 646 _libs[l.name] = l |
925 for d in self.dists: | 647 for d in self.dists: |
926 existing = _dists.get(d.name) | 648 existing = _dists.get(d.name) |
927 if existing is not None and _check_global_structures: | 649 if existing is not None: |
928 # allow redefinition, so use path from existing | 650 # allow redefinition, so use path from existing |
929 # abort('cannot redefine distribution ' + d.name) | 651 # abort('cannot redefine distribution ' + d.name) |
930 warn('distribution ' + d.name + ' redefined') | 652 warn('distribution ' + d.name + ' redefined') |
931 d.path = existing.path | 653 d.path = existing.path |
932 _dists[d.name] = d | 654 _dists[d.name] = d |
1037 | 759 |
1038 def suites(opt_limit_to_suite=False): | 760 def suites(opt_limit_to_suite=False): |
1039 """ | 761 """ |
1040 Get the list of all loaded suites. | 762 Get the list of all loaded suites. |
1041 """ | 763 """ |
1042 if opt_limit_to_suite and _opts.specific_suites: | 764 return _suites.values() |
1043 result = [] | |
1044 for s in _suites.values(): | |
1045 if s.name in _opts.specific_suites: | |
1046 result.append(s) | |
1047 return result | |
1048 else: | |
1049 return _suites.values() | |
1050 | 765 |
1051 def suite(name, fatalIfMissing=True): | 766 def suite(name, fatalIfMissing=True): |
1052 """ | 767 """ |
1053 Get the suite for a given name. | 768 Get the suite for a given name. |
1054 """ | 769 """ |
1082 Get the list of all loaded projects optionally limited by --suite option | 797 Get the list of all loaded projects optionally limited by --suite option |
1083 """ | 798 """ |
1084 return projects(True) | 799 return projects(True) |
1085 | 800 |
1086 def _projects_opt_limit_to_suites(projects): | 801 def _projects_opt_limit_to_suites(projects): |
1087 if not _opts.specific_suites: | 802 return projects |
1088 return projects | |
1089 else: | |
1090 result = [] | |
1091 for p in projects: | |
1092 s = p.suite | |
1093 if s.name in _opts.specific_suites: | |
1094 result.append(p) | |
1095 return result | |
1096 | 803 |
1097 def annotation_processors(): | 804 def annotation_processors(): |
1098 """ | 805 """ |
1099 Get the list of all loaded projects that define an annotation processor. | 806 Get the list of all loaded projects that define an annotation processor. |
1100 """ | 807 """ |
1286 self.add_argument('--Jp', action='append', dest='java_args_pfx', help='prefix Java VM arguments (e.g. --Jp @-dsa)', metavar='@<args>', default=[]) | 993 self.add_argument('--Jp', action='append', dest='java_args_pfx', help='prefix Java VM arguments (e.g. --Jp @-dsa)', metavar='@<args>', default=[]) |
1287 self.add_argument('--Ja', action='append', dest='java_args_sfx', help='suffix Java VM arguments (e.g. --Ja @-dsa)', metavar='@<args>', default=[]) | 994 self.add_argument('--Ja', action='append', dest='java_args_sfx', help='suffix Java VM arguments (e.g. --Ja @-dsa)', metavar='@<args>', default=[]) |
1288 self.add_argument('--user-home', help='users home directory', metavar='<path>', default=os.path.expanduser('~')) | 995 self.add_argument('--user-home', help='users home directory', metavar='<path>', default=os.path.expanduser('~')) |
1289 self.add_argument('--java-home', help='bootstrap JDK installation directory (must be JDK 6 or later)', metavar='<path>') | 996 self.add_argument('--java-home', help='bootstrap JDK installation directory (must be JDK 6 or later)', metavar='<path>') |
1290 self.add_argument('--ignore-project', action='append', dest='ignored_projects', help='name of project to ignore', metavar='<name>', default=[]) | 997 self.add_argument('--ignore-project', action='append', dest='ignored_projects', help='name of project to ignore', metavar='<name>', default=[]) |
1291 self.add_argument('--suite', action='append', dest='specific_suites', help='limit command to given suite', default=[]) | |
1292 self.add_argument('--src-suitemodel', help='mechanism for locating imported suites', metavar='<arg>', default='sibling') | |
1293 self.add_argument('--dst-suitemodel', help='mechanism for placing cloned/pushed suites', metavar='<arg>', default='sibling') | |
1294 self.add_argument('--suitemap', help='explicit remapping of suite names', metavar='<args>') | |
1295 if get_os() != 'windows': | 998 if get_os() != 'windows': |
1296 # Time outs are (currently) implemented with Unix specific functionality | 999 # Time outs are (currently) implemented with Unix specific functionality |
1297 self.add_argument('--timeout', help='timeout (in seconds) for command', type=int, default=0, metavar='<secs>') | 1000 self.add_argument('--timeout', help='timeout (in seconds) for command', type=int, default=0, metavar='<secs>') |
1298 self.add_argument('--ptimeout', help='timeout (in seconds) for subprocesses', type=int, default=0, metavar='<secs>') | 1001 self.add_argument('--ptimeout', help='timeout (in seconds) for subprocesses', type=int, default=0, metavar='<secs>') |
1299 | 1002 |
2032 compliance = str(p.javaCompliance) if p.javaCompliance is not None else args.compliance | 1735 compliance = str(p.javaCompliance) if p.javaCompliance is not None else args.compliance |
2033 if jdtJar is None: | 1736 if jdtJar is None: |
2034 log('Compiling Java sources for {0} with javac...'.format(p.name)) | 1737 log('Compiling Java sources for {0} with javac...'.format(p.name)) |
2035 | 1738 |
2036 | 1739 |
2037 javacCmd = [java().javac, '-g', '-J-Xmx1g', '-encoding', 'UTF-8', '-source', compliance, '-target', compliance, '-classpath', cp, '-d', outputDir] | 1740 javacCmd = [java().javac, '-g', '-J-Xmx1g', '-source', compliance, '-target', compliance, '-classpath', cp, '-d', outputDir] |
2038 if java().debug_port is not None: | 1741 if java().debug_port is not None: |
2039 javacCmd += ['-J-Xdebug', '-J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(java().debug_port)] | 1742 javacCmd += ['-J-Xdebug', '-J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(java().debug_port)] |
2040 javacCmd += processorArgs | 1743 javacCmd += processorArgs |
2041 javacCmd += ['@' + argfile.name] | 1744 javacCmd += ['@' + argfile.name] |
2042 | 1745 |
2050 if java().debug_port is not None: | 1753 if java().debug_port is not None: |
2051 jdtArgs += ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(java().debug_port)] | 1754 jdtArgs += ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(java().debug_port)] |
2052 | 1755 |
2053 jdtArgs += [ '-jar', jdtJar, | 1756 jdtArgs += [ '-jar', jdtJar, |
2054 '-' + compliance, | 1757 '-' + compliance, |
2055 '-encoding', 'UTF-8', | |
2056 '-cp', cp, '-g', '-enableJavadoc', | 1758 '-cp', cp, '-g', '-enableJavadoc', |
2057 '-d', outputDir] | 1759 '-d', outputDir] |
2058 jdtArgs += processorArgs | 1760 jdtArgs += processorArgs |
2059 | 1761 |
2060 | 1762 |
2878 _eclipseinit_suite(args, s, buildProcessorJars, refreshOnly) | 2580 _eclipseinit_suite(args, s, buildProcessorJars, refreshOnly) |
2879 | 2581 |
2880 generate_eclipse_workingsets() | 2582 generate_eclipse_workingsets() |
2881 | 2583 |
2882 def _check_ide_timestamp(suite, configZip, ide): | 2584 def _check_ide_timestamp(suite, configZip, ide): |
2883 """return True if and only if the projects file, imports file, eclipse-settings files, and mx itself are all older than configZip""" | 2585 """return True if and only if the projects file, eclipse-settings files, and mx itself are all older than configZip""" |
2884 projectsFile = join(suite.mxDir, 'projects') | 2586 projectsFile = join(suite.mxDir, 'projects') |
2885 if configZip.isOlderThan(projectsFile): | 2587 if configZip.isOlderThan(projectsFile): |
2886 return False | |
2887 if configZip.isOlderThan(suite.import_timestamp()): | |
2888 return False | 2588 return False |
2889 # Assume that any mx change might imply changes to the generated IDE files | 2589 # Assume that any mx change might imply changes to the generated IDE files |
2890 if configZip.isOlderThan(__file__): | 2590 if configZip.isOlderThan(__file__): |
2891 return False | 2591 return False |
2892 | 2592 |
3653 for dirpath, dirnames, files in os.walk(suite.dir): | 3353 for dirpath, dirnames, files in os.walk(suite.dir): |
3654 if dirpath == suite.dir: | 3354 if dirpath == suite.dir: |
3655 # no point in traversing .hg | 3355 # no point in traversing .hg |
3656 if '.hg' in dirnames: | 3356 if '.hg' in dirnames: |
3657 dirnames.remove('.hg') | 3357 dirnames.remove('.hg') |
3658 # if there are nested suites must not scan those now, as they are not in projectDirs | |
3659 if _src_suitemodel.nestedsuites_dirname() in dirnames: | |
3660 dirnames.remove(_src_suitemodel.nestedsuites_dirname()) | |
3661 elif dirpath in projectDirs: | 3358 elif dirpath in projectDirs: |
3662 # don't traverse subdirs of an existing project in this suite | 3359 # don't traverse subdirs of an existing project in this suite |
3663 dirnames[:] = [] | 3360 dirnames[:] = [] |
3664 else: | 3361 else: |
3665 projectConfigFiles = frozenset(['.classpath', 'nbproject']) | 3362 projectConfigFiles = frozenset(['.classpath', 'nbproject']) |
4074 def _kwArg(kwargs): | 3771 def _kwArg(kwargs): |
4075 if len(kwargs) > 0: | 3772 if len(kwargs) > 0: |
4076 return kwargs.pop(0) | 3773 return kwargs.pop(0) |
4077 return None | 3774 return None |
4078 | 3775 |
4079 def sclone(args): | |
4080 """clone a suite repository, and its imported suites""" | |
4081 _hg.check() | |
4082 parser = ArgumentParser(prog='mx sclone') | |
4083 parser.add_argument('--source', help='url/path of repo containing suite', metavar='<url>') | |
4084 parser.add_argument('--dest', help='destination directory (default basename of source)', metavar='<path>') | |
4085 parser.add_argument("--no-imports", action='store_true', help='do not clone imported suites') | |
4086 parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...') | |
4087 args = parser.parse_args(args) | |
4088 # check for non keyword args | |
4089 if args.source is None: | |
4090 args.source = _kwArg(args.nonKWArgs) | |
4091 if args.dest is None: | |
4092 args.dest = _kwArg(args.nonKWArgs) | |
4093 if len(args.nonKWArgs) > 0: | |
4094 abort('unrecognized args: ' + ' '.join(args.nonKWArgs)) | |
4095 | |
4096 if args.source is None: | |
4097 # must be primary suite and dest is required | |
4098 if _primary_suite is None: | |
4099 abort('--source missing and no primary suite found') | |
4100 if args.dest is None: | |
4101 abort('--dest required when --source is not given') | |
4102 source = _primary_suite.dir | |
4103 else: | |
4104 source = args.source | |
4105 | |
4106 if args.dest is not None: | |
4107 dest = args.dest | |
4108 else: | |
4109 dest = basename(source) | |
4110 | |
4111 dest = os.path.abspath(dest) | |
4112 # We can now set the primary dir for the src/dst suitemodel | |
4113 _dst_suitemodel.set_primary_dir(dest) | |
4114 _src_suitemodel.set_primary_dir(source) | |
4115 | |
4116 _sclone(source, dest, None, args.no_imports) | |
4117 | |
4118 def _sclone(source, dest, version, no_imports): | |
4119 cmd = ['hg', 'clone'] | |
4120 if version is not None: | |
4121 cmd.append('-r') | |
4122 cmd.append(version) | |
4123 cmd.append(source) | |
4124 cmd.append(dest) | |
4125 | |
4126 run(cmd) | |
4127 | |
4128 mxDir = _is_suite_dir(dest) | |
4129 if mxDir is None: | |
4130 warn(source + ' is not an mx suite') | |
4131 return None | |
4132 | |
4133 # create a Suite (without loading) to enable imports visitor | |
4134 s = Suite(mxDir, False, load=False) | |
4135 if not no_imports: | |
4136 s.visit_imports(_scloneimports_visitor, source=source) | |
4137 return s | |
4138 | |
4139 def _scloneimports_visitor(s, suite_import, source, **extra_args): | |
4140 """ | |
4141 cloneimports visitor for Suite.visit_imports. | |
4142 The destination information is encapsulated by 's' | |
4143 """ | |
4144 _scloneimports(s, suite_import, source) | |
4145 | |
4146 def _scloneimports_suitehelper(sdir): | |
4147 mxDir = _is_suite_dir(sdir) | |
4148 if mxDir is None: | |
4149 abort(sdir + ' is not an mx suite') | |
4150 else: | |
4151 # create a Suite (without loading) to enable imports visitor | |
4152 return Suite(mxDir, False, load=False) | |
4153 | |
4154 def _scloneimports(s, suite_import, source): | |
4155 # clone first, then visit imports once we can locate them | |
4156 importee_source = _src_suitemodel.importee_dir(source, suite_import.name) | |
4157 importee_dest = _dst_suitemodel.importee_dir(s.dir, suite_import.name) | |
4158 if exists(importee_dest): | |
4159 # already exists in the suite model, but may be wrong version | |
4160 importee_suite = _scloneimports_suitehelper(importee_dest) | |
4161 if suite_import.version is not None and importee_suite.version() != suite_import.version: | |
4162 abort("imported version of " + suite_import.name + " in " + s.name + " does not match the version in already existing suite: " + importee_suite.dir) | |
4163 importee_suite.visit_imports(_scloneimports_visitor, source=importee_source) | |
4164 else: | |
4165 _sclone(importee_source, importee_dest, suite_import.version, False) | |
4166 # _clone handles the recursive visit of the new imports | |
4167 | |
4168 def scloneimports(args): | |
4169 """clone the imports of an existing suite""" | |
4170 _hg.check() | |
4171 parser = ArgumentParser(prog='mx scloneimports') | |
4172 parser.add_argument('--source', help='url/path of repo containing suite', metavar='<url>') | |
4173 parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...') | |
4174 args = parser.parse_args(args) | |
4175 # check for non keyword args | |
4176 if args.source is None: | |
4177 args.source = _kwArg(args.nonKWArgs) | |
4178 | |
4179 if not os.path.isdir(args.source): | |
4180 abort(args.source + ' is not a directory') | |
4181 | |
4182 s = _scloneimports_suitehelper(args.source) | |
4183 | |
4184 default_path = _hg.default_push(args.source) | |
4185 | |
4186 if default_path is None: | |
4187 abort('no default path in ' + join(args.source, '.hg', 'hgrc')) | |
4188 | |
4189 # We can now set the primary dir for the dst suitemodel | |
4190 # N.B. source is effectively the destination and the default_path is the (original) source | |
4191 _dst_suitemodel.set_primary_dir(args.source) | |
4192 | |
4193 s.visit_imports(_scloneimports_visitor, source=default_path) | |
4194 | |
4195 def _spush_import_visitor(s, suite_import, dest, checks, clonemissing, **extra_args): | |
4196 """push visitor for Suite.visit_imports""" | |
4197 if dest is not None: | |
4198 dest = _dst_suitemodel.importee_dir(dest, suite_import.name) | |
4199 _spush(suite(suite_import.name), suite_import, dest, checks, clonemissing) | |
4200 | |
4201 def _spush_check_import_visitor(s, suite_import, **extra_args): | |
4202 """push check visitor for Suite.visit_imports""" | |
4203 currentTip = suite(suite_import.name).version() | |
4204 if currentTip != suite_import.version: | |
4205 abort('imported version of ' + suite_import.name + ' in suite ' + s.name + ' does not match tip') | |
4206 | |
4207 def _spush(s, suite_import, dest, checks, clonemissing): | |
4208 if checks: | |
4209 if not _hg.can_push(s): | |
4210 abort('working directory ' + s.dir + ' contains uncommitted changes, push aborted') | |
4211 | |
4212 # check imports first | |
4213 if checks: | |
4214 s.visit_imports(_spush_check_import_visitor) | |
4215 | |
4216 # ok, push imports | |
4217 s.visit_imports(_spush_import_visitor, dest=dest, checks=checks, clonemissing=clonemissing) | |
4218 | |
4219 dest_exists = True | |
4220 | |
4221 if clonemissing: | |
4222 if not os.path.exists(dest): | |
4223 dest_exists = False | |
4224 | |
4225 def add_version(cmd, suite_import): | |
4226 if suite_import is not None and suite_import.version is not None: | |
4227 cmd.append('-r') | |
4228 cmd.append(suite_import.version) | |
4229 | |
4230 if dest_exists: | |
4231 cmd = ['hg', '-R', s.dir, 'push'] | |
4232 add_version(cmd, suite_import) | |
4233 if dest is not None: | |
4234 cmd.append(dest) | |
4235 rc = run(cmd, nonZeroIsFatal=False) | |
4236 if rc != 0: | |
4237 # rc of 1 not an error, means no changes | |
4238 if rc != 1: | |
4239 abort("push failed, exit code " + str(rc)) | |
4240 else: | |
4241 cmd = ['hg', 'clone'] | |
4242 add_version(cmd, suite_import) | |
4243 cmd.append(s.dir) | |
4244 cmd.append(dest) | |
4245 run(cmd) | |
4246 | |
4247 def spush(args): | |
4248 """push primary suite and all its imports""" | |
4249 _hg.check() | |
4250 parser = ArgumentParser(prog='mx spush') | |
4251 parser.add_argument('--dest', help='url/path of repo to push to (default as per hg push)', metavar='<path>') | |
4252 parser.add_argument('--no-checks', action='store_true', help='checks on status, versions are disabled') | |
4253 parser.add_argument('--clonemissing', action='store_true', help='clone missing imported repos at destination (forces --no-checks)') | |
4254 parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...') | |
4255 args = parser.parse_args(args) | |
4256 if args.dest is None: | |
4257 args.dest = _kwArg(args.nonKWArgs) | |
4258 if len(args.nonKWArgs) > 0: | |
4259 abort('unrecognized args: ' + ' '.join(args.nonKWArgs)) | |
4260 | |
4261 if args.dest is not None and not os.path.isdir(args.dest): | |
4262 abort('destination must be a directory') | |
4263 | |
4264 s = _check_primary_suite() | |
4265 | |
4266 if args.clonemissing: | |
4267 if args.dest is None: | |
4268 abort('--dest required with --clonemissing') | |
4269 args.nochecks = True | |
4270 | |
4271 if args.dest is not None: | |
4272 _dst_suitemodel.set_primary_dir(args.dest) | |
4273 | |
4274 _spush(s, None, args.dest, not args.no_checks, args.clonemissing) | |
4275 | |
4276 def _supdate_import_visitor(s, suite_import, **extra_args): | |
4277 _supdate(suite(suite_import.name), suite_import) | |
4278 | |
4279 def _supdate(s, suite_import): | |
4280 s.visit_imports(_supdate_import_visitor) | |
4281 | |
4282 run(['hg', '-R', s.dir, 'update']) | |
4283 | |
4284 def supdate(args): | |
4285 """update primary suite and all its imports""" | |
4286 | |
4287 _hg.check() | |
4288 s = _check_primary_suite() | |
4289 | |
4290 _supdate(s, None) | |
4291 | |
4292 def _scheck_imports_visitor(s, suite_import, update_versions, updated_imports): | |
4293 """scheckimports visitor for Suite.visit_imports""" | |
4294 _scheck_imports(s, suite(suite_import.name), suite_import, update_versions, updated_imports) | |
4295 | |
4296 def _scheck_imports(importing_suite, imported_suite, suite_import, update_versions, updated_imports): | |
4297 # check imports recursively | |
4298 imported_suite.visit_imports(_scheck_imports_visitor, update_versions=update_versions) | |
4299 | |
4300 currentTip = imported_suite.version() | |
4301 if currentTip != suite_import.version: | |
4302 print('imported version of ' + imported_suite.name + ' in ' + importing_suite.name + ' does not match tip' + (': updating' if update_versions else '')) | |
4303 | |
4304 if update_versions: | |
4305 suite_import.version = currentTip | |
4306 line = str(suite_import) | |
4307 updated_imports.write(line + '\n') | |
4308 | |
4309 def scheckimports(args): | |
4310 """check that suite import versions are up to date""" | |
4311 parser = ArgumentParser(prog='mx scheckimports') | |
4312 parser.add_argument('--update-versions', help='update imported version ids', action='store_true') | |
4313 args = parser.parse_args(args) | |
4314 _check_primary_suite().visit_imports(_scheck_imports_visitor, update_versions=args.update_versions) | |
4315 | |
4316 def _spull_import_visitor(s, suite_import, update_versions, updated_imports): | |
4317 """pull visitor for Suite.visit_imports""" | |
4318 _spull(suite(suite_import.name), update_versions, updated_imports) | |
4319 | |
4320 def _spull(s, update_versions, updated_imports): | |
4321 _hg.check() | |
4322 # pull imports first | |
4323 s.visit_imports(_spull_import_visitor, update_versions=update_versions) | |
4324 | |
4325 run(['hg', '-R', s.dir, 'pull', '-u']) | |
4326 if update_versions and updated_imports is not None: | |
4327 tip = s.version() | |
4328 updated_imports.write(SuiteImport.tostring(s.name, tip) + '\n') | |
4329 | |
4330 def spull(args): | |
4331 """pull primary suite and all its imports""" | |
4332 _hg.check() | |
4333 parser = ArgumentParser(prog='mx spull') | |
4334 parser.add_argument('--update-versions', action='store_true', help='update version ids of imported suites') | |
4335 args = parser.parse_args(args) | |
4336 | |
4337 _spull(_check_primary_suite(), args.update_versions, None) | |
4338 | |
4339 def findclass(args, logToConsole=True): | 3776 def findclass(args, logToConsole=True): |
4340 """find all classes matching a given substring""" | 3777 """find all classes matching a given substring""" |
4341 matches = [] | 3778 matches = [] |
4342 for entry, filename in classpath_walk(includeBootClasspath=True): | 3779 for entry, filename in classpath_walk(includeBootClasspath=True): |
4343 if filename.endswith('.class'): | 3780 if filename.endswith('.class'): |
4470 'help': [help_, '[command]'], | 3907 'help': [help_, '[command]'], |
4471 'ideclean': [ideclean, ''], | 3908 'ideclean': [ideclean, ''], |
4472 'ideinit': [ideinit, ''], | 3909 'ideinit': [ideinit, ''], |
4473 'archive': [archive, '[options]'], | 3910 'archive': [archive, '[options]'], |
4474 'projectgraph': [projectgraph, ''], | 3911 'projectgraph': [projectgraph, ''], |
4475 'sclone': [sclone, '[options]'], | |
4476 'scheckimports': [scheckimports, ''], | |
4477 'scloneimports': [scloneimports, '[options]'], | |
4478 'spull': [spull, '[options'], | |
4479 'spush': [spush, '[options'], | |
4480 'supdate': [supdate, ''], | |
4481 'pylint': [pylint, ''], | 3912 'pylint': [pylint, ''], |
4482 'javap': [javap, '<class name patterns>'], | 3913 'javap': [javap, '<class name patterns>'], |
4483 'javadoc': [javadoc, '[options]'], | 3914 'javadoc': [javadoc, '[options]'], |
4484 'site': [site, '[options]'], | 3915 'site': [site, '[options]'], |
4485 'netbeansinit': [netbeansinit, ''], | 3916 'netbeansinit': [netbeansinit, ''], |
4512 def _check_primary_suite(): | 3943 def _check_primary_suite(): |
4513 if _primary_suite is None: | 3944 if _primary_suite is None: |
4514 abort('no primary suite found') | 3945 abort('no primary suite found') |
4515 else: | 3946 else: |
4516 return _primary_suite | 3947 return _primary_suite |
4517 | |
4518 def _needs_primary_suite(command): | |
4519 return not command.startswith("sclone") | |
4520 | |
4521 def _needs_primary_suite_cl(): | |
4522 return not any("sclone" in s for s in sys.argv[1:]) | |
4523 | 3948 |
4524 def _findPrimarySuiteMxDirFrom(d): | 3949 def _findPrimarySuiteMxDirFrom(d): |
4525 """ search for a suite directory upwards from 'd' """ | 3950 """ search for a suite directory upwards from 'd' """ |
4526 while d: | 3951 while d: |
4527 mxDir = _is_suite_dir(d) | 3952 mxDir = _is_suite_dir(d) |
4549 return mxDir | 3974 return mxDir |
4550 # backwards compatibility: search from path of this file | 3975 # backwards compatibility: search from path of this file |
4551 return _findPrimarySuiteMxDirFrom(dirname(__file__)) | 3976 return _findPrimarySuiteMxDirFrom(dirname(__file__)) |
4552 | 3977 |
4553 def main(): | 3978 def main(): |
4554 SuiteModel.parse_options() | |
4555 | |
4556 global _hg | |
4557 _hg = HgConfig() | |
4558 | |
4559 primary_suite_error = 'no primary suite found' | |
4560 primarySuiteMxDir = _findPrimarySuiteMxDir() | 3979 primarySuiteMxDir = _findPrimarySuiteMxDir() |
4561 if primarySuiteMxDir: | 3980 if primarySuiteMxDir: |
4562 _src_suitemodel.set_primary_dir(dirname(primarySuiteMxDir)) | |
4563 global _primary_suite | 3981 global _primary_suite |
4564 _primary_suite = _loadSuite(primarySuiteMxDir, True) | 3982 _primary_suite = _loadSuite(primarySuiteMxDir, True) |
4565 else: | 3983 else: |
4566 # in general this is an error, except for the sclone/scloneimports commands, | 3984 abort('no primary suite found') |
4567 # and an extensions command will likely not parse in this case, as any extra arguments | |
4568 # will not have been added to _argParser. | |
4569 # If the command line does not contain a string matching one of the exceptions, we can safely abort, | |
4570 # but not otherwise, as we can't be sure the string isn't in a value for some other option. | |
4571 if _needs_primary_suite_cl(): | |
4572 abort(primary_suite_error) | |
4573 | 3985 |
4574 opts, commandAndArgs = _argParser._parse_cmd_line() | 3986 opts, commandAndArgs = _argParser._parse_cmd_line() |
4575 | |
4576 if primarySuiteMxDir is None: | |
4577 if len(commandAndArgs) > 0 and _needs_primary_suite(commandAndArgs[0]): | |
4578 abort(primary_suite_error) | |
4579 else: | |
4580 warn(primary_suite_error) | |
4581 | 3987 |
4582 global _opts, _java | 3988 global _opts, _java |
4583 _opts = opts | 3989 _opts = opts |
4584 _java = JavaConfig(opts) | 3990 _java = JavaConfig(opts) |
4585 | 3991 |