Mercurial > hg > truffle
comparison mxtool/mx.py @ 12387:aff825fef0fd
Merge.
author | Christian Humer <christian.humer@gmail.com> |
---|---|
date | Wed, 02 Oct 2013 13:26:31 +0200 |
parents | 039b133ded75 |
children | fbe1ee508936 |
comparison
equal
deleted
inserted
replaced
12386:91dbb0b7dc8b | 12387:aff825fef0fd |
---|---|
35 | 35 |
36 The organizing principle of mx is a project suite. A suite is a directory | 36 The organizing principle of mx is a project suite. A suite is a directory |
37 containing one or more projects. It's not coincidental that this closely | 37 containing one or more projects. It's not coincidental that this closely |
38 matches the layout of one or more projects in a Mercurial repository. | 38 matches the layout of one or more projects in a Mercurial repository. |
39 The configuration information for a suite lives in an 'mx' sub-directory | 39 The configuration information for a suite lives in an 'mx' sub-directory |
40 at the top level of the suite. | 40 at the top level of the suite. A suite is given a name by a 'suite=name' |
41 property in the 'mx/projects' file (if omitted the name is suite directory). | |
42 An 'mx' subdirectory can be named as plain 'mx' or 'mx.name', where | |
43 'name' is typically the name as the suite name. | |
44 The latter is useful to avoid clashes in IDE project names. | |
41 | 45 |
42 When launched, mx treats the current working directory as a suite. | 46 When launched, mx treats the current working directory as a suite. |
43 This is the primary suite. All other suites are called included suites. | 47 This is the primary suite. All other suites are called included suites. |
44 | 48 |
45 The configuration files (i.e. in the 'mx' sub-directory) of a suite are: | 49 The configuration files (i.e. in the 'mx' sub-directory) of a suite are: |
135 by extension commands. | 139 by extension commands. |
136 | 140 |
137 Property values can use environment variables with Bash syntax (e.g. ${HOME}). | 141 Property values can use environment variables with Bash syntax (e.g. ${HOME}). |
138 """ | 142 """ |
139 | 143 |
140 import sys, os, errno, time, subprocess, shlex, types, urllib2, contextlib, StringIO, zipfile, signal, xml.sax.saxutils, tempfile | 144 import sys, os, errno, time, subprocess, shlex, types, urllib2, contextlib, StringIO, zipfile, signal, xml.sax.saxutils, tempfile, fnmatch |
141 import textwrap | 145 import textwrap |
142 import xml.parsers.expat | 146 import xml.parsers.expat |
143 import shutil, re, xml.dom.minidom | 147 import shutil, re, xml.dom.minidom |
144 from collections import Callable | 148 from collections import Callable |
145 from threading import Thread | 149 from threading import Thread |
152 _suites = dict() | 156 _suites = dict() |
153 _annotationProcessors = None | 157 _annotationProcessors = None |
154 _mainSuite = None | 158 _mainSuite = None |
155 _opts = None | 159 _opts = None |
156 _java = None | 160 _java = None |
161 _check_global_structures = True # can be set False to allow suites with duplicate definitions to load without aborting | |
162 | |
157 | 163 |
158 """ | 164 """ |
159 A distribution is a jar or zip file containing the output from one or more Java projects. | 165 A distribution is a jar or zip file containing the output from one or more Java projects. |
160 """ | 166 """ |
161 class Distribution: | 167 class Distribution: |
433 self.sourceUrls = sourceUrls | 439 self.sourceUrls = sourceUrls |
434 for url in urls: | 440 for url in urls: |
435 if url.endswith('/') != self.path.endswith(os.sep): | 441 if url.endswith('/') != self.path.endswith(os.sep): |
436 abort('Path for dependency directory must have a URL ending with "/": path=' + self.path + ' url=' + url) | 442 abort('Path for dependency directory must have a URL ending with "/": path=' + self.path + ' url=' + url) |
437 | 443 |
444 def __eq__(self, other): | |
445 if isinstance(other, Library): | |
446 if len(self.urls) == 0: | |
447 return self.path == other.path | |
448 else: | |
449 return self.urls == other.urls | |
450 else: | |
451 return NotImplemented | |
452 | |
453 | |
454 def __ne__(self, other): | |
455 result = self.__eq__(other) | |
456 if result is NotImplemented: | |
457 return result | |
458 return not result | |
459 | |
460 | |
438 def get_path(self, resolve): | 461 def get_path(self, resolve): |
439 path = self.path | 462 path = self.path |
440 if not isabs(path): | 463 if not isabs(path): |
441 path = join(self.suite.dir, path) | 464 path = join(self.suite.dir, path) |
465 includedInJDK = getattr(self, 'includedInJDK', None) | |
466 if includedInJDK and java().javaCompliance >= JavaCompliance(includedInJDK): | |
467 return None | |
442 if resolve and self.mustExist and not exists(path): | 468 if resolve and self.mustExist and not exists(path): |
443 assert not len(self.urls) == 0, 'cannot find required library ' + self.name + ' ' + path | 469 assert not len(self.urls) == 0, 'cannot find required library ' + self.name + ' ' + path |
444 print('Downloading ' + self.name + ' from ' + str(self.urls)) | 470 print('Downloading ' + self.name + ' from ' + str(self.urls)) |
445 download(path, self.urls) | 471 download(path, self.urls) |
446 return path | 472 return path |
456 download(path, self.sourceUrls) | 482 download(path, self.sourceUrls) |
457 return path | 483 return path |
458 | 484 |
459 def append_to_classpath(self, cp, resolve): | 485 def append_to_classpath(self, cp, resolve): |
460 path = self.get_path(resolve) | 486 path = self.get_path(resolve) |
461 if exists(path) or not resolve: | 487 if path and (exists(path) or not resolve): |
462 cp.append(path) | 488 cp.append(path) |
463 | 489 |
464 def all_deps(self, deps, includeLibs, includeSelf=True, includeAnnotationProcessors=False): | 490 def all_deps(self, deps, includeLibs, includeSelf=True, includeAnnotationProcessors=False): |
465 if not includeLibs or not includeSelf: | 491 if not includeLibs or not includeSelf: |
466 return deps | 492 return deps |
467 deps.append(self) | 493 deps.append(self) |
468 return deps | 494 return deps |
469 | 495 |
470 class Suite: | 496 class Suite: |
471 def __init__(self, d, primary): | 497 def __init__(self, d, mxDir, primary): |
472 self.dir = d | 498 self.dir = d |
499 self.mxDir = mxDir | |
473 self.projects = [] | 500 self.projects = [] |
474 self.libs = [] | 501 self.libs = [] |
475 self.dists = [] | 502 self.dists = [] |
476 self.includes = [] | 503 self.includes = [] |
477 self.commands = None | 504 self.commands = None |
478 self.primary = primary | 505 self.primary = primary |
479 mxDir = join(d, 'mx') | |
480 self._load_env(mxDir) | 506 self._load_env(mxDir) |
481 self._load_commands(mxDir) | 507 self._load_commands(mxDir) |
482 self._load_includes(mxDir) | 508 self._load_includes(mxDir) |
483 self.name = d # re-initialized in _load_projects | 509 self.name = d # re-initialized in _load_projects |
484 | 510 |
490 projsMap = dict() | 516 projsMap = dict() |
491 distsMap = dict() | 517 distsMap = dict() |
492 projectsFile = join(mxDir, 'projects') | 518 projectsFile = join(mxDir, 'projects') |
493 if not exists(projectsFile): | 519 if not exists(projectsFile): |
494 return | 520 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 | |
495 with open(projectsFile) as f: | 528 with open(projectsFile) as f: |
496 for line in f: | 529 for line in f: |
497 line = line.strip() | 530 line = line.strip() |
498 if len(line) != 0 and line[0] != '#': | 531 if len(line) != 0 and line[0] != '#': |
499 key, value = line.split('=', 1) | 532 key, value = line.split('=', 1) |
501 parts = key.split('@') | 534 parts = key.split('@') |
502 | 535 |
503 if len(parts) == 1: | 536 if len(parts) == 1: |
504 if parts[0] != 'suite': | 537 if parts[0] != 'suite': |
505 abort('Single part property must be "suite": ' + key) | 538 abort('Single part property must be "suite": ' + key) |
506 self.name = value | 539 if self.name != value: |
540 currentKey = _find_suite_key() | |
541 _suites.pop(currentKey) | |
542 self.name = value | |
543 _suites[value] = self | |
507 continue | 544 continue |
508 if len(parts) != 3: | 545 if len(parts) != 3: |
509 abort('Property name does not have 3 parts separated by "@": ' + key) | 546 abort('Property name does not have 3 parts separated by "@": ' + key) |
510 kind, name, attr = parts | 547 kind, name, attr = parts |
511 if kind == 'project': | 548 if kind == 'project': |
588 if not hasattr(mod, 'mx_init'): | 625 if not hasattr(mod, 'mx_init'): |
589 abort(commandsPath + ' must define an mx_init(env) function') | 626 abort(commandsPath + ' must define an mx_init(env) function') |
590 if hasattr(mod, 'mx_post_parse_cmd_line'): | 627 if hasattr(mod, 'mx_post_parse_cmd_line'): |
591 self.mx_post_parse_cmd_line = mod.mx_post_parse_cmd_line | 628 self.mx_post_parse_cmd_line = mod.mx_post_parse_cmd_line |
592 | 629 |
593 mod.mx_init() | 630 mod.mx_init(self) |
594 self.commands = mod | 631 self.commands = mod |
595 | 632 |
596 def _load_includes(self, mxDir): | 633 def _load_includes(self, mxDir): |
597 includes = join(mxDir, 'includes') | 634 includes = join(mxDir, 'includes') |
598 if exists(includes): | 635 if exists(includes): |
599 with open(includes) as f: | 636 with open(includes) as f: |
600 for line in f: | 637 for line in f: |
601 include = expandvars_in_property(line.strip()) | 638 include = expandvars_in_property(line.strip()) |
602 self.includes.append(include) | 639 self.includes.append(include) |
603 _loadSuite(include, False) | 640 _loadSuite(os.path.abspath(include), False) |
604 | 641 |
605 def _load_env(self, mxDir): | 642 def _load_env(self, mxDir): |
606 e = join(mxDir, 'env') | 643 e = join(mxDir, 'env') |
607 if exists(e): | 644 if exists(e): |
608 with open(e) as f: | 645 with open(e) as f: |
613 if len(line) != 0 and line[0] != '#': | 650 if len(line) != 0 and line[0] != '#': |
614 if not '=' in line: | 651 if not '=' in line: |
615 abort(e + ':' + str(lineNum) + ': line does not match pattern "key=value"') | 652 abort(e + ':' + str(lineNum) + ': line does not match pattern "key=value"') |
616 key, value = line.split('=', 1) | 653 key, value = line.split('=', 1) |
617 os.environ[key.strip()] = expandvars_in_property(value.strip()) | 654 os.environ[key.strip()] = expandvars_in_property(value.strip()) |
655 | |
618 def _post_init(self, opts): | 656 def _post_init(self, opts): |
619 mxDir = join(self.dir, 'mx') | 657 self._load_projects(self.mxDir) |
620 self._load_projects(mxDir) | 658 # set the global data structures, checking for conflicts unless _global_structures is False |
621 for p in self.projects: | 659 for p in self.projects: |
622 existing = _projects.get(p.name) | 660 existing = _projects.get(p.name) |
623 if existing is not None: | 661 if existing is not None and _check_global_structures: |
624 abort('cannot override project ' + p.name + ' in ' + p.dir + " with project of the same name in " + existing.dir) | 662 abort('cannot override project ' + p.name + ' in ' + p.dir + " with project of the same name in " + existing.dir) |
625 if not p.name in _opts.ignored_projects: | 663 if not p.name in _opts.ignored_projects: |
626 _projects[p.name] = p | 664 _projects[p.name] = p |
627 for l in self.libs: | 665 for l in self.libs: |
628 existing = _libs.get(l.name) | 666 existing = _libs.get(l.name) |
629 if existing is not None: | 667 # Check that suites that define same library are consistent |
630 abort('cannot redefine library ' + l.name) | 668 if existing is not None and existing != l and _check_global_structures: |
669 abort('inconsistent library redefinition of ' + l.name + ' in ' + existing.suite.dir + ' and ' + l.suite.dir) | |
631 _libs[l.name] = l | 670 _libs[l.name] = l |
632 for d in self.dists: | 671 for d in self.dists: |
633 existing = _dists.get(d.name) | 672 existing = _dists.get(d.name) |
634 if existing is not None: | 673 if existing is not None and _check_global_structures: |
635 abort('cannot redefine distribution ' + d.name) | 674 # allow redefinition, so use path from existing |
675 # abort('cannot redefine distribution ' + d.name) | |
676 print('WARNING: distribution ' + d.name + ' redefined') | |
677 d.path = existing.path | |
636 _dists[d.name] = d | 678 _dists[d.name] = d |
637 if hasattr(self, 'mx_post_parse_cmd_line'): | 679 if hasattr(self, 'mx_post_parse_cmd_line'): |
638 self.mx_post_parse_cmd_line(opts) | 680 self.mx_post_parse_cmd_line(opts) |
639 | 681 |
640 class XMLElement(xml.dom.minidom.Element): | 682 class XMLElement(xml.dom.minidom.Element): |
727 return 'windows' | 769 return 'windows' |
728 else: | 770 else: |
729 abort('Unknown operating system ' + sys.platform) | 771 abort('Unknown operating system ' + sys.platform) |
730 | 772 |
731 def _loadSuite(d, primary=False): | 773 def _loadSuite(d, primary=False): |
732 mxDir = join(d, 'mx') | 774 """ |
733 if not exists(mxDir) or not isdir(mxDir): | 775 Load a suite from the 'mx' or 'mx.bbb' subdirectory of d, where 'bbb' is basename of d |
776 """ | |
777 mxDefaultDir = join(d, 'mx') | |
778 name = os.path.basename(d) | |
779 mxTaggedDir = mxDefaultDir + '.' + name | |
780 mxDir = None | |
781 if exists(mxTaggedDir) and isdir(mxTaggedDir): | |
782 mxDir = mxTaggedDir | |
783 else: | |
784 if exists(mxDefaultDir) and isdir(mxDefaultDir): | |
785 mxDir = mxDefaultDir | |
786 | |
787 | |
788 if mxDir is None: | |
734 return None | 789 return None |
735 if len([s for s in _suites.itervalues() if s.dir == d]) == 0: | 790 if len([s for s in _suites.itervalues() if s.dir == d]) == 0: |
736 s = Suite(d, primary) | 791 s = Suite(d, mxDir, primary) |
737 _suites[s.name] = s | 792 # N.B. this will be updated once the projects file has been read |
793 _suites[name] = s | |
738 return s | 794 return s |
739 | 795 |
740 def suites(): | 796 def suites(opt_limit_to_suite=False): |
741 """ | 797 """ |
742 Get the list of all loaded suites. | 798 Get the list of all loaded suites. |
743 """ | 799 """ |
744 return _suites.values() | 800 if opt_limit_to_suite and _opts.specific_suites: |
801 result = [] | |
802 for s in _suites.values(): | |
803 if s.name in _opts.specific_suites: | |
804 result.append(s) | |
805 return result | |
806 else: | |
807 return _suites.values() | |
745 | 808 |
746 def suite(name, fatalIfMissing=True): | 809 def suite(name, fatalIfMissing=True): |
747 """ | 810 """ |
748 Get the suite for a given name. | 811 Get the suite for a given name. |
749 """ | 812 """ |
750 s = _suites.get(name) | 813 s = _suites.get(name) |
751 if s is None and fatalIfMissing: | 814 if s is None and fatalIfMissing: |
752 abort('suite named ' + name + ' not found') | 815 abort('suite named ' + name + ' not found') |
753 return s | 816 return s |
754 | 817 |
755 def projects(): | 818 def projects_from_names(projectNames): |
756 """ | 819 """ |
757 Get the list of all loaded projects. | 820 Get the list of projects corresponding to projectNames; all projects if None |
758 """ | 821 """ |
759 return _projects.values() | 822 if projectNames is None: |
823 return projects() | |
824 else: | |
825 return [project(name) for name in projectNames] | |
826 | |
827 def projects(opt_limit_to_suite=False): | |
828 """ | |
829 Get the list of all loaded projects limited by --suite option if opt_limit_to_suite == True | |
830 """ | |
831 | |
832 if opt_limit_to_suite: | |
833 return _projects_opt_limit_to_suites(_projects.values()) | |
834 else: | |
835 return _projects.values() | |
836 | |
837 def projects_opt_limit_to_suites(): | |
838 """ | |
839 Get the list of all loaded projects optionally limited by --suite option | |
840 """ | |
841 return projects(True) | |
842 | |
843 def _projects_opt_limit_to_suites(projects): | |
844 if not _opts.specific_suites: | |
845 return projects | |
846 else: | |
847 result = [] | |
848 for p in projects: | |
849 s = p.suite | |
850 if s.name in _opts.specific_suites: | |
851 result.append(p) | |
852 return result | |
760 | 853 |
761 def annotation_processors(): | 854 def annotation_processors(): |
762 """ | 855 """ |
763 Get the list of all loaded projects that define an annotation processor. | 856 Get the list of all loaded projects that define an annotation processor. |
764 """ | 857 """ |
875 """ | 968 """ |
876 Gets projects and libraries sorted such that dependencies | 969 Gets projects and libraries sorted such that dependencies |
877 are before the projects that depend on them. Unless 'includeLibs' is | 970 are before the projects that depend on them. Unless 'includeLibs' is |
878 true, libraries are omitted from the result. | 971 true, libraries are omitted from the result. |
879 """ | 972 """ |
973 projects = projects_from_names(projectNames) | |
974 | |
975 return sorted_project_deps(projects, includeLibs=includeLibs, includeAnnotationProcessors=includeAnnotationProcessors) | |
976 | |
977 def sorted_project_deps(projects, includeLibs=False, includeAnnotationProcessors=False): | |
880 deps = [] | 978 deps = [] |
881 if projectNames is None: | |
882 projects = _projects.values() | |
883 else: | |
884 projects = [project(name) for name in projectNames] | |
885 | |
886 for p in projects: | 979 for p in projects: |
887 p.all_deps(deps, includeLibs=includeLibs, includeAnnotationProcessors=includeAnnotationProcessors) | 980 p.all_deps(deps, includeLibs=includeLibs, includeAnnotationProcessors=includeAnnotationProcessors) |
888 return deps | 981 return deps |
889 | 982 |
890 def _handle_missing_java_home(): | 983 def _handle_missing_java_home(): |
934 return ArgumentParser.format_help(self) + _format_commands() | 1027 return ArgumentParser.format_help(self) + _format_commands() |
935 | 1028 |
936 | 1029 |
937 def __init__(self): | 1030 def __init__(self): |
938 self.java_initialized = False | 1031 self.java_initialized = False |
939 ArgumentParser.__init__(self, prog='mx') | 1032 # this doesn't resolve the right way, but can't figure out how to override _handle_conflict_resolve in _ActionsContainer |
1033 ArgumentParser.__init__(self, prog='mx', conflict_handler='resolve') | |
940 | 1034 |
941 self.add_argument('-v', action='store_true', dest='verbose', help='enable verbose output') | 1035 self.add_argument('-v', action='store_true', dest='verbose', help='enable verbose output') |
942 self.add_argument('-V', action='store_true', dest='very_verbose', help='enable very verbose output') | 1036 self.add_argument('-V', action='store_true', dest='very_verbose', help='enable very verbose output') |
943 self.add_argument('--dbg', type=int, dest='java_dbg_port', help='make Java processes wait on <port> for a debugger', metavar='<port>') | 1037 self.add_argument('--dbg', type=int, dest='java_dbg_port', help='make Java processes wait on <port> for a debugger', metavar='<port>') |
944 self.add_argument('-d', action='store_const', const=8000, dest='java_dbg_port', help='alias for "-dbg 8000"') | 1038 self.add_argument('-d', action='store_const', const=8000, dest='java_dbg_port', help='alias for "-dbg 8000"') |
948 self.add_argument('--Jp', action='append', dest='java_args_pfx', help='prefix Java VM arguments (e.g. --Jp @-dsa)', metavar='@<args>', default=[]) | 1042 self.add_argument('--Jp', action='append', dest='java_args_pfx', help='prefix Java VM arguments (e.g. --Jp @-dsa)', metavar='@<args>', default=[]) |
949 self.add_argument('--Ja', action='append', dest='java_args_sfx', help='suffix Java VM arguments (e.g. --Ja @-dsa)', metavar='@<args>', default=[]) | 1043 self.add_argument('--Ja', action='append', dest='java_args_sfx', help='suffix Java VM arguments (e.g. --Ja @-dsa)', metavar='@<args>', default=[]) |
950 self.add_argument('--user-home', help='users home directory', metavar='<path>', default=os.path.expanduser('~')) | 1044 self.add_argument('--user-home', help='users home directory', metavar='<path>', default=os.path.expanduser('~')) |
951 self.add_argument('--java-home', help='bootstrap JDK installation directory (must be JDK 6 or later)', metavar='<path>') | 1045 self.add_argument('--java-home', help='bootstrap JDK installation directory (must be JDK 6 or later)', metavar='<path>') |
952 self.add_argument('--ignore-project', action='append', dest='ignored_projects', help='name of project to ignore', metavar='<name>', default=[]) | 1046 self.add_argument('--ignore-project', action='append', dest='ignored_projects', help='name of project to ignore', metavar='<name>', default=[]) |
1047 self.add_argument('--suite', action='append', dest='specific_suites', help='limit command to given suite', default=[]) | |
953 if get_os() != 'windows': | 1048 if get_os() != 'windows': |
954 # Time outs are (currently) implemented with Unix specific functionality | 1049 # Time outs are (currently) implemented with Unix specific functionality |
955 self.add_argument('--timeout', help='timeout (in seconds) for command', type=int, default=0, metavar='<secs>') | 1050 self.add_argument('--timeout', help='timeout (in seconds) for command', type=int, default=0, metavar='<secs>') |
956 self.add_argument('--ptimeout', help='timeout (in seconds) for subprocesses', type=int, default=0, metavar='<secs>') | 1051 self.add_argument('--ptimeout', help='timeout (in seconds) for subprocesses', type=int, default=0, metavar='<secs>') |
957 | 1052 |
984 | 1079 |
985 opts.ignored_projects = opts.ignored_projects + os.environ.get('IGNORED_PROJECTS', '').split(',') | 1080 opts.ignored_projects = opts.ignored_projects + os.environ.get('IGNORED_PROJECTS', '').split(',') |
986 | 1081 |
987 commandAndArgs = opts.__dict__.pop('commandAndArgs') | 1082 commandAndArgs = opts.__dict__.pop('commandAndArgs') |
988 return opts, commandAndArgs | 1083 return opts, commandAndArgs |
1084 | |
1085 def _handle_conflict_resolve(self, action, conflicting_actions): | |
1086 self._handle_conflict_error(action, conflicting_actions) | |
989 | 1087 |
990 def _format_commands(): | 1088 def _format_commands(): |
991 msg = '\navailable commands:\n\n' | 1089 msg = '\navailable commands:\n\n' |
992 for cmd in sorted(_commands.iterkeys()): | 1090 for cmd in sorted(_commands.iterkeys()): |
993 c, _ = _commands[cmd][:2] | 1091 c, _ = _commands[cmd][:2] |
1498 # Silently ignore JDT if default location is used but not ecj.jar exists there | 1596 # Silently ignore JDT if default location is used but not ecj.jar exists there |
1499 jdtJar = None | 1597 jdtJar = None |
1500 | 1598 |
1501 built = set() | 1599 built = set() |
1502 | 1600 |
1503 projects = None | |
1504 if args.projects is not None: | |
1505 projects = args.projects.split(',') | |
1506 | |
1507 if args.only is not None: | 1601 if args.only is not None: |
1602 # N.B. This build will not include dependencies including annotation processor dependencies | |
1508 sortedProjects = [project(name) for name in args.only.split(',')] | 1603 sortedProjects = [project(name) for name in args.only.split(',')] |
1509 else: | 1604 else: |
1510 sortedProjects = sorted_deps(projects, includeAnnotationProcessors=True) | 1605 if args.projects is not None: |
1606 projectNames = args.projects.split(',') | |
1607 else: | |
1608 projectNames = None | |
1609 | |
1610 projects = _projects_opt_limit_to_suites(projects_from_names(projectNames)) | |
1611 # N.B. Limiting to a suite only affects the starting set of projects. Dependencies in other suites will still be compiled | |
1612 sortedProjects = sorted_project_deps(projects, includeAnnotationProcessors=True) | |
1613 | |
1614 if args.java: | |
1615 ideinit([], refreshOnly=True, buildProcessorJars=False) | |
1511 | 1616 |
1512 for p in sortedProjects: | 1617 for p in sortedProjects: |
1513 if p.native: | 1618 if p.native: |
1514 if args.native: | 1619 if args.native: |
1515 log('Calling GNU make {0}...'.format(p.dir)) | 1620 log('Calling GNU make {0}...'.format(p.dir)) |
1805 | 1910 |
1806 pnames = [p.name for p in projs] | 1911 pnames = [p.name for p in projs] |
1807 build(['--projects', ",".join(pnames)]) | 1912 build(['--projects', ",".join(pnames)]) |
1808 archive(pnames) | 1913 archive(pnames) |
1809 | 1914 |
1915 def pylint(args): | |
1916 """run pylint (if available) over Python source files (found by 'hg locate' or by tree walk with -walk)""" | |
1917 | |
1918 parser = ArgumentParser(prog='mx pylint') | |
1919 parser.add_argument('--walk', action='store_true', help='use tree walk find .py files') | |
1920 args = parser.parse_args(args) | |
1921 | |
1922 rcfile = join(dirname(__file__), '.pylintrc') | |
1923 if not exists(rcfile): | |
1924 log('pylint configuration file does not exist: ' + rcfile) | |
1925 return | |
1926 | |
1927 try: | |
1928 output = subprocess.check_output(['pylint', '--version'], stderr=subprocess.STDOUT) | |
1929 m = re.match(r'.*pylint (\d+)\.(\d+)\.(\d+).*', output, re.DOTALL) | |
1930 if not m: | |
1931 log('could not determine pylint version from ' + output) | |
1932 return | |
1933 major, minor, micro = (int(m.group(1)), int(m.group(2)), int(m.group(3))) | |
1934 if major < 1: | |
1935 log('require pylint version >= 1 (got {0}.{1}.{2})'.format(major, minor, micro)) | |
1936 return | |
1937 except BaseException: | |
1938 log('pylint is not available') | |
1939 return | |
1940 | |
1941 def findfiles_by_walk(): | |
1942 result = [] | |
1943 for suite in suites(True): | |
1944 for root, dirs, files in os.walk(suite.dir): | |
1945 for f in files: | |
1946 if f.endswith('.py'): | |
1947 pyfile = join(root, f) | |
1948 result.append(pyfile) | |
1949 if 'bin' in dirs: | |
1950 dirs.remove('bin') | |
1951 if 'lib' in dirs: | |
1952 # avoids downloaded .py files | |
1953 dirs.remove('lib') | |
1954 return result | |
1955 | |
1956 def findfiles_by_hg(): | |
1957 result = [] | |
1958 for suite in suites(True): | |
1959 versioned = subprocess.check_output(['hg', 'locate', '-f'], stderr=subprocess.STDOUT, cwd=suite.dir).split(os.linesep) | |
1960 for f in versioned: | |
1961 if f.endswith('.py') and exists(f): | |
1962 result.append(f) | |
1963 return result | |
1964 | |
1965 # Perhaps we should just look in suite.mxDir directories for .py files? | |
1966 if args.walk: | |
1967 pyfiles = findfiles_by_walk() | |
1968 else: | |
1969 pyfiles = findfiles_by_hg() | |
1970 | |
1971 env = os.environ.copy() | |
1972 | |
1973 pythonpath = dirname(__file__) | |
1974 for suite in suites(True): | |
1975 pythonpath = os.pathsep.join([pythonpath, suite.mxDir]) | |
1976 | |
1977 env['PYTHONPATH'] = pythonpath | |
1978 | |
1979 for pyfile in pyfiles: | |
1980 log('Running pylint on ' + pyfile + '...') | |
1981 run(['pylint', '--reports=n', '--rcfile=' + rcfile, pyfile], env=env, nonZeroIsFatal=False) | |
1982 | |
1810 def archive(args): | 1983 def archive(args): |
1811 """create jar files for projects and distributions""" | 1984 """create jar files for projects and distributions""" |
1812 parser = ArgumentParser(prog='mx archive') | 1985 parser = ArgumentParser(prog='mx archive') |
1813 parser.add_argument('names', nargs=REMAINDER, metavar='[<project>|@<distribution>]...') | 1986 parser.add_argument('names', nargs=REMAINDER, metavar='[<project>|@<distribution>]...') |
1814 args = parser.parse_args(args) | 1987 args = parser.parse_args(args) |
1830 if dep.isLibrary(): | 2003 if dep.isLibrary(): |
1831 l = dep | 2004 l = dep |
1832 # merge library jar into distribution jar | 2005 # merge library jar into distribution jar |
1833 logv('[' + d.path + ': adding library ' + l.name + ']') | 2006 logv('[' + d.path + ': adding library ' + l.name + ']') |
1834 lpath = l.get_path(resolve=True) | 2007 lpath = l.get_path(resolve=True) |
1835 with zipfile.ZipFile(lpath, 'r') as lp: | 2008 if lpath: |
1836 for arcname in lp.namelist(): | 2009 with zipfile.ZipFile(lpath, 'r') as lp: |
1837 if arcname.startswith('META-INF/services/'): | 2010 for arcname in lp.namelist(): |
1838 f = arcname[len('META-INF/services/'):].replace('/', os.sep) | 2011 if arcname.startswith('META-INF/services/') and not arcname == 'META-INF/services/': |
1839 with open(join(services, f), 'a') as outfile: | 2012 f = arcname[len('META-INF/services/'):].replace('/', os.sep) |
1840 for line in lp.read(arcname).splitlines(): | 2013 with open(join(services, f), 'a') as outfile: |
1841 outfile.write(line) | 2014 for line in lp.read(arcname).splitlines(): |
1842 else: | 2015 outfile.write(line) |
1843 overwriteCheck(zf, arcname, lpath + '!' + arcname) | 2016 else: |
1844 zf.writestr(arcname, lp.read(arcname)) | 2017 overwriteCheck(zf, arcname, lpath + '!' + arcname) |
2018 zf.writestr(arcname, lp.read(arcname)) | |
1845 else: | 2019 else: |
1846 p = dep | 2020 p = dep |
1847 # skip a Java project if its Java compliance level is "higher" than the configured JDK | 2021 # skip a Java project if its Java compliance level is "higher" than the configured JDK |
1848 if java().javaCompliance < p.javaCompliance: | 2022 if java().javaCompliance < p.javaCompliance: |
1849 log('Excluding {0} from {2} (Java compliance level {1} required)'.format(p.name, p.javaCompliance, d.path)) | 2023 log('Excluding {0} from {2} (Java compliance level {1} required)'.format(p.name, p.javaCompliance, d.path)) |
1910 | 2084 |
1911 The exit code of this command reflects how many files were updated.""" | 2085 The exit code of this command reflects how many files were updated.""" |
1912 | 2086 |
1913 changedFiles = 0 | 2087 changedFiles = 0 |
1914 for s in suites(): | 2088 for s in suites(): |
1915 projectsFile = join(s.dir, 'mx', 'projects') | 2089 projectsFile = join(s.mxDir, 'projects') |
1916 if not exists(projectsFile): | 2090 if not exists(projectsFile): |
1917 continue | 2091 continue |
1918 with open(projectsFile) as f: | 2092 with open(projectsFile) as f: |
1919 out = StringIO.StringIO() | 2093 out = StringIO.StringIO() |
1920 pattern = re.compile('project@([^@]+)@dependencies=.*') | 2094 pattern = re.compile('project@([^@]+)@dependencies=.*') |
1961 content = out.getvalue() | 2135 content = out.getvalue() |
1962 if update_file(projectsFile, content): | 2136 if update_file(projectsFile, content): |
1963 changedFiles += 1 | 2137 changedFiles += 1 |
1964 return changedFiles | 2138 return changedFiles |
1965 | 2139 |
2140 class TimeStampFile: | |
2141 def __init__(self, path): | |
2142 self.path = path | |
2143 self.timestamp = os.path.getmtime(path) if exists(path) else None | |
2144 | |
2145 def outOfDate(self, arg): | |
2146 if not self.timestamp: | |
2147 return True | |
2148 if isinstance(arg, types.ListType): | |
2149 files = arg | |
2150 else: | |
2151 files = [arg] | |
2152 for f in files: | |
2153 if os.path.getmtime(f) > self.timestamp: | |
2154 return True | |
2155 return False | |
2156 | |
2157 def exists(self): | |
2158 return exists(self.path) | |
2159 | |
2160 def touch(self): | |
2161 if exists(self.path): | |
2162 os.utime(self.path, None) | |
2163 else: | |
2164 if not isdir(dirname(self.path)): | |
2165 os.makedirs(dirname(self.path)) | |
2166 file(self.path, 'a') | |
2167 | |
1966 def checkstyle(args): | 2168 def checkstyle(args): |
1967 """run Checkstyle on the Java sources | 2169 """run Checkstyle on the Java sources |
1968 | 2170 |
1969 Run Checkstyle over the Java sources. Any errors or warnings | 2171 Run Checkstyle over the Java sources. Any errors or warnings |
1970 produced by Checkstyle result in a non-zero exit code.""" | 2172 produced by Checkstyle result in a non-zero exit code.""" |
1973 | 2175 |
1974 parser.add_argument('-f', action='store_true', dest='force', help='force checking (disables timestamp checking)') | 2176 parser.add_argument('-f', action='store_true', dest='force', help='force checking (disables timestamp checking)') |
1975 args = parser.parse_args(args) | 2177 args = parser.parse_args(args) |
1976 | 2178 |
1977 totalErrors = 0 | 2179 totalErrors = 0 |
1978 for p in sorted_deps(): | 2180 for p in projects_opt_limit_to_suites(): |
1979 if p.native: | 2181 if p.native: |
1980 continue | 2182 continue |
1981 sourceDirs = p.source_dirs() | 2183 sourceDirs = p.source_dirs() |
1982 dotCheckstyle = join(p.dir, '.checkstyle') | 2184 dotCheckstyle = join(p.dir, '.checkstyle') |
1983 | 2185 |
1995 javafilelist += [join(root, name) for name in files if name.endswith('.java') and name != 'package-info.java'] | 2197 javafilelist += [join(root, name) for name in files if name.endswith('.java') and name != 'package-info.java'] |
1996 if len(javafilelist) == 0: | 2198 if len(javafilelist) == 0: |
1997 logv('[no Java sources in {0} - skipping]'.format(sourceDir)) | 2199 logv('[no Java sources in {0} - skipping]'.format(sourceDir)) |
1998 continue | 2200 continue |
1999 | 2201 |
2000 timestampFile = join(p.suite.dir, 'mx', 'checkstyle-timestamps', sourceDir[len(p.suite.dir) + 1:].replace(os.sep, '_') + '.timestamp') | 2202 timestamp = TimeStampFile(join(p.suite.mxDir, 'checkstyle-timestamps', sourceDir[len(p.suite.dir) + 1:].replace(os.sep, '_') + '.timestamp')) |
2001 if not exists(dirname(timestampFile)): | |
2002 os.makedirs(dirname(timestampFile)) | |
2003 mustCheck = False | 2203 mustCheck = False |
2004 if not args.force and exists(timestampFile): | 2204 if not args.force and timestamp.exists(): |
2005 timestamp = os.path.getmtime(timestampFile) | 2205 mustCheck = timestamp.outOfDate(javafilelist) |
2006 for f in javafilelist: | |
2007 if os.path.getmtime(f) > timestamp: | |
2008 mustCheck = True | |
2009 break | |
2010 else: | 2206 else: |
2011 mustCheck = True | 2207 mustCheck = True |
2012 | 2208 |
2013 if not mustCheck: | 2209 if not mustCheck: |
2014 if _opts.verbose: | 2210 if _opts.verbose: |
2081 if name == 'file': | 2277 if name == 'file': |
2082 source[0] = attrs['name'] | 2278 source[0] = attrs['name'] |
2083 elif name == 'error': | 2279 elif name == 'error': |
2084 errors.append('{}:{}: {}'.format(source[0], attrs['line'], attrs['message'])) | 2280 errors.append('{}:{}: {}'.format(source[0], attrs['line'], attrs['message'])) |
2085 | 2281 |
2086 p = xml.parsers.expat.ParserCreate() | 2282 xp = xml.parsers.expat.ParserCreate() |
2087 p.StartElementHandler = start_element | 2283 xp.StartElementHandler = start_element |
2088 with open(auditfileName) as fp: | 2284 with open(auditfileName) as fp: |
2089 p.ParseFile(fp) | 2285 xp.ParseFile(fp) |
2090 if len(errors) != 0: | 2286 if len(errors) != 0: |
2091 map(log, errors) | 2287 map(log, errors) |
2092 totalErrors = totalErrors + len(errors) | 2288 totalErrors = totalErrors + len(errors) |
2093 else: | 2289 else: |
2094 if exists(timestampFile): | 2290 timestamp.touch() |
2095 os.utime(timestampFile, None) | |
2096 else: | |
2097 file(timestampFile, 'a') | |
2098 finally: | 2291 finally: |
2099 if exists(auditfileName): | 2292 if exists(auditfileName): |
2100 os.unlink(auditfileName) | 2293 os.unlink(auditfileName) |
2101 return totalErrors | 2294 return totalErrors |
2102 | 2295 |
2113 parser.add_argument('--no-native', action='store_false', dest='native', help='do not clean native projects') | 2306 parser.add_argument('--no-native', action='store_false', dest='native', help='do not clean native projects') |
2114 parser.add_argument('--no-java', action='store_false', dest='java', help='do not clean Java projects') | 2307 parser.add_argument('--no-java', action='store_false', dest='java', help='do not clean Java projects') |
2115 | 2308 |
2116 args = parser.parse_args(args) | 2309 args = parser.parse_args(args) |
2117 | 2310 |
2118 for p in projects(): | 2311 for p in projects_opt_limit_to_suites(): |
2119 if p.native: | 2312 if p.native: |
2120 if args.native: | 2313 if args.native: |
2121 run([gmake_cmd(), '-C', p.dir, 'clean']) | 2314 run([gmake_cmd(), '-C', p.dir, 'clean']) |
2122 else: | 2315 else: |
2123 if args.java: | 2316 if args.java: |
2205 | 2398 |
2206 slm.close('sourceContainers') | 2399 slm.close('sourceContainers') |
2207 slm.close('sourceLookupDirector') | 2400 slm.close('sourceLookupDirector') |
2208 return slm | 2401 return slm |
2209 | 2402 |
2210 def make_eclipse_attach(hostname, port, name=None, deps=None): | 2403 def make_eclipse_attach(suite, hostname, port, name=None, deps=None): |
2211 """ | 2404 """ |
2212 Creates an Eclipse launch configuration file for attaching to a Java process. | 2405 Creates an Eclipse launch configuration file for attaching to a Java process. |
2213 """ | 2406 """ |
2214 if deps is None: | 2407 if deps is None: |
2215 deps = [] | 2408 deps = [] |
2227 launch.element('stringAttribute', {'key' : 'org.eclipse.jdt.launching.VM_CONNECTOR_ID', 'value' : 'org.eclipse.jdt.launching.socketAttachConnector'}) | 2420 launch.element('stringAttribute', {'key' : 'org.eclipse.jdt.launching.VM_CONNECTOR_ID', 'value' : 'org.eclipse.jdt.launching.socketAttachConnector'}) |
2228 launch.close('launchConfiguration') | 2421 launch.close('launchConfiguration') |
2229 launch = launch.xml(newl='\n', standalone='no') % slm.xml(escape=True, standalone='no') | 2422 launch = launch.xml(newl='\n', standalone='no') % slm.xml(escape=True, standalone='no') |
2230 | 2423 |
2231 if name is None: | 2424 if name is None: |
2232 name = 'attach-' + hostname + '-' + port | 2425 if len(suites()) == 1: |
2233 eclipseLaunches = join('mx', 'eclipse-launches') | 2426 suitePrefix = '' |
2427 else: | |
2428 suitePrefix = suite.name + '-' | |
2429 name = suitePrefix + 'attach-' + hostname + '-' + port | |
2430 eclipseLaunches = join(suite.mxDir, 'eclipse-launches') | |
2234 if not exists(eclipseLaunches): | 2431 if not exists(eclipseLaunches): |
2235 os.makedirs(eclipseLaunches) | 2432 os.makedirs(eclipseLaunches) |
2236 return update_file(join(eclipseLaunches, name + '.launch'), launch) | 2433 return update_file(join(eclipseLaunches, name + '.launch'), launch) |
2237 | 2434 |
2238 def make_eclipse_launch(javaArgs, jre, name=None, deps=None): | 2435 def make_eclipse_launch(javaArgs, jre, name=None, deps=None): |
2300 eclipseLaunches = join('mx', 'eclipse-launches') | 2497 eclipseLaunches = join('mx', 'eclipse-launches') |
2301 if not exists(eclipseLaunches): | 2498 if not exists(eclipseLaunches): |
2302 os.makedirs(eclipseLaunches) | 2499 os.makedirs(eclipseLaunches) |
2303 return update_file(join(eclipseLaunches, name + '.launch'), launch) | 2500 return update_file(join(eclipseLaunches, name + '.launch'), launch) |
2304 | 2501 |
2305 def eclipseinit(args, suite=None, buildProcessorJars=True): | 2502 def eclipseinit(args, buildProcessorJars=True, refreshOnly=False): |
2306 """(re)generate Eclipse project configurations and working sets""" | 2503 """(re)generate Eclipse project configurations and working sets""" |
2307 | 2504 for s in suites(True): |
2308 if suite is None: | 2505 _eclipseinit_suite(args, s, buildProcessorJars, refreshOnly) |
2309 suite = _mainSuite | 2506 |
2507 generate_eclipse_workingsets() | |
2508 | |
2509 | |
2510 def _eclipseinit_suite(args, suite, buildProcessorJars=True, refreshOnly=False): | |
2511 projectsFile = join(suite.mxDir, 'projects') | |
2512 timestamp = TimeStampFile(join(suite.mxDir, 'eclipseinit.timestamp')) | |
2513 if refreshOnly and not timestamp.exists(): | |
2514 return | |
2515 | |
2516 if not timestamp.outOfDate(projectsFile): | |
2517 logv('[Eclipse configurations are up to date - skipping]') | |
2518 return | |
2310 | 2519 |
2311 if buildProcessorJars: | 2520 if buildProcessorJars: |
2521 ## todo suite specific | |
2312 processorjars() | 2522 processorjars() |
2313 | 2523 |
2314 projToDist = dict() | 2524 projToDist = dict() |
2315 for dist in _dists.values(): | 2525 for dist in _dists.values(): |
2316 distDeps = sorted_deps(dist.deps) | 2526 distDeps = sorted_deps(dist.deps) |
2317 for p in distDeps: | 2527 for p in distDeps: |
2318 projToDist[p.name] = (dist, [dep.name for dep in distDeps]) | 2528 projToDist[p.name] = (dist, [dep.name for dep in distDeps]) |
2319 | 2529 |
2320 for p in projects(): | 2530 for p in suite.projects: |
2321 if p.native: | 2531 if p.native: |
2322 continue | 2532 continue |
2323 | 2533 |
2324 if not exists(p.dir): | 2534 if not exists(p.dir): |
2325 os.makedirs(p.dir) | 2535 os.makedirs(p.dir) |
2354 out.element('classpathentry', {'exported' : 'true', 'kind' : 'con', 'path' : getattr(dep, 'eclipse.container')}) | 2564 out.element('classpathentry', {'exported' : 'true', 'kind' : 'con', 'path' : getattr(dep, 'eclipse.container')}) |
2355 elif hasattr(dep, 'eclipse.project'): | 2565 elif hasattr(dep, 'eclipse.project'): |
2356 out.element('classpathentry', {'combineaccessrules' : 'false', 'exported' : 'true', 'kind' : 'src', 'path' : '/' + getattr(dep, 'eclipse.project')}) | 2566 out.element('classpathentry', {'combineaccessrules' : 'false', 'exported' : 'true', 'kind' : 'src', 'path' : '/' + getattr(dep, 'eclipse.project')}) |
2357 else: | 2567 else: |
2358 path = dep.path | 2568 path = dep.path |
2359 if dep.mustExist: | 2569 dep.get_path(resolve=True) |
2360 dep.get_path(resolve=True) | 2570 if not path or (not exists(path) and not dep.mustExist): |
2361 if not isabs(path): | 2571 continue |
2362 # Relative paths for "lib" class path entries have various semantics depending on the Eclipse | 2572 |
2363 # version being used (e.g. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=274737) so it's | 2573 if not isabs(path): |
2364 # safest to simply use absolute paths. | 2574 # Relative paths for "lib" class path entries have various semantics depending on the Eclipse |
2365 path = join(suite.dir, path) | 2575 # version being used (e.g. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=274737) so it's |
2366 | 2576 # safest to simply use absolute paths. |
2367 attributes = {'exported' : 'true', 'kind' : 'lib', 'path' : path} | 2577 path = join(p.suite.dir, path) |
2368 | 2578 |
2369 sourcePath = dep.get_source_path(resolve=True) | 2579 attributes = {'exported' : 'true', 'kind' : 'lib', 'path' : path} |
2370 if sourcePath is not None: | 2580 |
2371 attributes['sourcepath'] = sourcePath | 2581 sourcePath = dep.get_source_path(resolve=True) |
2372 out.element('classpathentry', attributes) | 2582 if sourcePath is not None: |
2583 attributes['sourcepath'] = sourcePath | |
2584 out.element('classpathentry', attributes) | |
2373 else: | 2585 else: |
2374 out.element('classpathentry', {'combineaccessrules' : 'false', 'exported' : 'true', 'kind' : 'src', 'path' : '/' + dep.name}) | 2586 out.element('classpathentry', {'combineaccessrules' : 'false', 'exported' : 'true', 'kind' : 'src', 'path' : '/' + dep.name}) |
2375 | 2587 |
2376 out.element('classpathentry', {'kind' : 'output', 'path' : getattr(p, 'eclipse.output', 'bin')}) | 2588 out.element('classpathentry', {'kind' : 'output', 'path' : getattr(p, 'eclipse.output', 'bin')}) |
2377 out.close('classpath') | 2589 out.close('classpath') |
2452 | 2664 |
2453 settingsDir = join(p.dir, ".settings") | 2665 settingsDir = join(p.dir, ".settings") |
2454 if not exists(settingsDir): | 2666 if not exists(settingsDir): |
2455 os.mkdir(settingsDir) | 2667 os.mkdir(settingsDir) |
2456 | 2668 |
2457 eclipseSettingsDir = join(suite.dir, 'mx', 'eclipse-settings') | 2669 eclipseSettingsDir = join(p.suite.mxDir, 'eclipse-settings') |
2458 if exists(eclipseSettingsDir): | 2670 if exists(eclipseSettingsDir): |
2459 for name in os.listdir(eclipseSettingsDir): | 2671 for name in os.listdir(eclipseSettingsDir): |
2460 if name == "org.eclipse.jdt.apt.core.prefs" and not len(p.annotation_processors()) > 0: | 2672 if name == "org.eclipse.jdt.apt.core.prefs" and not len(p.annotation_processors()) > 0: |
2461 continue | 2673 continue |
2462 path = join(eclipseSettingsDir, name) | 2674 path = join(eclipseSettingsDir, name) |
2476 for dep in dependency(ap).all_deps([], True): | 2688 for dep in dependency(ap).all_deps([], True): |
2477 if dep.isLibrary(): | 2689 if dep.isLibrary(): |
2478 if not hasattr(dep, 'eclipse.container') and not hasattr(dep, 'eclipse.project'): | 2690 if not hasattr(dep, 'eclipse.container') and not hasattr(dep, 'eclipse.project'): |
2479 if dep.mustExist: | 2691 if dep.mustExist: |
2480 path = dep.get_path(resolve=True) | 2692 path = dep.get_path(resolve=True) |
2481 if not isabs(path): | 2693 if path: |
2482 # Relative paths for "lib" class path entries have various semantics depending on the Eclipse | 2694 if not isabs(path): |
2483 # version being used (e.g. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=274737) so it's | 2695 # Relative paths for "lib" class path entries have various semantics depending on the Eclipse |
2484 # safest to simply use absolute paths. | 2696 # version being used (e.g. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=274737) so it's |
2485 path = join(suite.dir, path) | 2697 # safest to simply use absolute paths. |
2486 out.element('factorypathentry', {'kind' : 'EXTJAR', 'id' : path, 'enabled' : 'true', 'runInBatchMode' : 'false'}) | 2698 path = join(p.suite.dir, path) |
2699 out.element('factorypathentry', {'kind' : 'EXTJAR', 'id' : path, 'enabled' : 'true', 'runInBatchMode' : 'false'}) | |
2487 else: | 2700 else: |
2488 out.element('factorypathentry', {'kind' : 'WKSPJAR', 'id' : '/' + dep.name + '/' + dep.name + '.jar', 'enabled' : 'true', 'runInBatchMode' : 'false'}) | 2701 out.element('factorypathentry', {'kind' : 'WKSPJAR', 'id' : '/' + dep.name + '/' + dep.name + '.jar', 'enabled' : 'true', 'runInBatchMode' : 'false'}) |
2489 out.close('factorypath') | 2702 out.close('factorypath') |
2490 update_file(join(p.dir, '.factorypath'), out.xml(indent='\t', newl='\n')) | 2703 update_file(join(p.dir, '.factorypath'), out.xml(indent='\t', newl='\n')) |
2491 | 2704 |
2492 make_eclipse_attach('localhost', '8000', deps=projects()) | 2705 make_eclipse_attach(suite, 'localhost', '8000', deps=projects()) |
2493 generate_eclipse_workingsets(suite) | 2706 timestamp.touch() |
2494 | |
2495 | 2707 |
2496 def _isAnnotationProcessorDependency(p): | 2708 def _isAnnotationProcessorDependency(p): |
2497 """ | 2709 """ |
2498 Determines if a given project is part of an annotation processor. | 2710 Determines if a given project is part of an annotation processor. |
2499 """ | 2711 """ |
2546 dotProjectDoc.element('value', data='true') | 2758 dotProjectDoc.element('value', data='true') |
2547 dotProjectDoc.close('dictionary') | 2759 dotProjectDoc.close('dictionary') |
2548 dotProjectDoc.close('arguments') | 2760 dotProjectDoc.close('arguments') |
2549 dotProjectDoc.close('buildCommand') | 2761 dotProjectDoc.close('buildCommand') |
2550 | 2762 |
2551 def generate_eclipse_workingsets(suite): | 2763 def generate_eclipse_workingsets(): |
2552 """ | 2764 """ |
2553 Populate the workspace's working set configuration with working sets generated from project data. | 2765 Populate the workspace's working set configuration with working sets generated from project data for the primary suite |
2554 If the workspace already contains working set definitions, the existing ones will be retained and extended. | 2766 If the workspace already contains working set definitions, the existing ones will be retained and extended. |
2555 In case mx/env does not contain a WORKSPACE definition pointing to the workspace root directory, the Graal project root directory will be assumed. | 2767 In case mx/env does not contain a WORKSPACE definition pointing to the workspace root directory, a parent search from the primary suite directory is performed. |
2556 If no workspace root directory can be identified, the Graal project root directory is used and the user has to place the workingsets.xml file by hand. | 2768 If no workspace root directory can be identified, the primary suite directory is used and the user has to place the workingsets.xml file by hand. |
2557 """ | 2769 """ |
2558 | 2770 |
2559 # identify the location where to look for workingsets.xml | 2771 # identify the location where to look for workingsets.xml |
2560 wsfilename = 'workingsets.xml' | 2772 wsfilename = 'workingsets.xml' |
2561 wsroot = suite.dir | 2773 wsloc = '.metadata/.plugins/org.eclipse.ui.workbench' |
2562 if os.environ.has_key('WORKSPACE'): | 2774 if os.environ.has_key('WORKSPACE'): |
2563 wsroot = os.environ['WORKSPACE'] | 2775 expected_wsroot = os.environ['WORKSPACE'] |
2564 wsdir = join(wsroot, '.metadata/.plugins/org.eclipse.ui.workbench') | 2776 else: |
2777 expected_wsroot = _mainSuite.dir | |
2778 | |
2779 wsroot = _find_eclipse_wsroot(expected_wsroot) | |
2780 if wsroot is None: | |
2781 # failed to find it | |
2782 wsroot = expected_wsroot | |
2783 | |
2784 wsdir = join(wsroot, wsloc) | |
2565 if not exists(wsdir): | 2785 if not exists(wsdir): |
2566 wsdir = wsroot | 2786 wsdir = wsroot |
2787 log('Could not find Eclipse metadata directory. Please place ' + wsfilename + ' in ' + wsloc + ' manually.') | |
2567 wspath = join(wsdir, wsfilename) | 2788 wspath = join(wsdir, wsfilename) |
2568 | 2789 |
2569 # gather working set info from project data | 2790 # gather working set info from project data |
2570 workingSets = dict() | 2791 workingSets = dict() |
2571 for p in projects(): | 2792 for p in projects(): |
2581 wsdoc = _copy_workingset_xml(wspath, workingSets) | 2802 wsdoc = _copy_workingset_xml(wspath, workingSets) |
2582 else: | 2803 else: |
2583 wsdoc = _make_workingset_xml(workingSets) | 2804 wsdoc = _make_workingset_xml(workingSets) |
2584 | 2805 |
2585 update_file(wspath, wsdoc.xml(newl='\n')) | 2806 update_file(wspath, wsdoc.xml(newl='\n')) |
2807 | |
2808 def _find_eclipse_wsroot(wsdir): | |
2809 md = join(wsdir, '.metadata') | |
2810 if exists(md): | |
2811 return wsdir | |
2812 split = os.path.split(wsdir) | |
2813 if split[0] == wsdir: # root directory | |
2814 return None | |
2815 else: | |
2816 return _find_eclipse_wsroot(split[0]) | |
2817 | |
2818 def _foobar(val): | |
2819 print(val) | |
2586 | 2820 |
2587 def _make_workingset_xml(workingSets): | 2821 def _make_workingset_xml(workingSets): |
2588 wsdoc = XMLDoc() | 2822 wsdoc = XMLDoc() |
2589 wsdoc.open('workingSetManager') | 2823 wsdoc.open('workingSetManager') |
2590 | 2824 |
2664 wsdoc.open('workingSet', {'editPageID': 'org.eclipse.jdt.ui.JavaWorkingSetPage', 'factoryID': 'org.eclipse.ui.internal.WorkingSetFactory', 'id': 'wsid_' + ws, 'label': ws, 'name': ws}) | 2898 wsdoc.open('workingSet', {'editPageID': 'org.eclipse.jdt.ui.JavaWorkingSetPage', 'factoryID': 'org.eclipse.ui.internal.WorkingSetFactory', 'id': 'wsid_' + ws, 'label': ws, 'name': ws}) |
2665 | 2899 |
2666 def _workingset_element(wsdoc, p): | 2900 def _workingset_element(wsdoc, p): |
2667 wsdoc.element('item', {'elementID': '=' + p, 'factoryID': 'org.eclipse.jdt.ui.PersistableJavaElementFactory'}) | 2901 wsdoc.element('item', {'elementID': '=' + p, 'factoryID': 'org.eclipse.jdt.ui.PersistableJavaElementFactory'}) |
2668 | 2902 |
2669 def netbeansinit(args, suite=None): | 2903 def netbeansinit(args, refreshOnly=False, buildProcessorJars=True): |
2670 """(re)generate NetBeans project configurations""" | 2904 """(re)generate NetBeans project configurations""" |
2671 | 2905 |
2672 if suite is None: | 2906 for suite in suites(True): |
2673 suite = _mainSuite | 2907 _netbeansinit_suite(args, suite, refreshOnly, buildProcessorJars) |
2908 | |
2909 def _netbeansinit_suite(args, suite, refreshOnly=False, buildProcessorJars=True): | |
2910 projectsFile = join(suite.mxDir, 'projects') | |
2911 timestamp = TimeStampFile(join(suite.mxDir, 'netbeansinit.timestamp')) | |
2912 if refreshOnly and not timestamp.exists(): | |
2913 return | |
2914 | |
2915 if not timestamp.outOfDate(projectsFile): | |
2916 logv('[NetBeans configurations are up to date - skipping]') | |
2917 return | |
2674 | 2918 |
2675 updated = False | 2919 updated = False |
2676 for p in projects(): | 2920 for p in suite.projects: |
2677 if p.native: | 2921 if p.native: |
2678 continue | 2922 continue |
2679 | 2923 |
2680 if exists(join(p.dir, 'plugin.xml')): # eclipse plugin project | 2924 if exists(join(p.dir, 'plugin.xml')): # eclipse plugin project |
2681 continue | 2925 continue |
2854 | 3098 |
2855 if dep.isLibrary(): | 3099 if dep.isLibrary(): |
2856 if not dep.mustExist: | 3100 if not dep.mustExist: |
2857 continue | 3101 continue |
2858 path = dep.get_path(resolve=True) | 3102 path = dep.get_path(resolve=True) |
2859 if os.sep == '\\': | 3103 if path: |
2860 path = path.replace('\\', '\\\\') | 3104 if os.sep == '\\': |
2861 ref = 'file.reference.' + dep.name + '-bin' | 3105 path = path.replace('\\', '\\\\') |
2862 print >> out, ref + '=' + path | 3106 ref = 'file.reference.' + dep.name + '-bin' |
3107 print >> out, ref + '=' + path | |
2863 | 3108 |
2864 else: | 3109 else: |
2865 n = dep.name.replace('.', '_') | 3110 n = dep.name.replace('.', '_') |
2866 relDepPath = os.path.relpath(dep.dir, p.dir).replace(os.sep, '/') | 3111 relDepPath = os.path.relpath(dep.dir, p.dir).replace(os.sep, '/') |
2867 ref = 'reference.' + n + '.jar' | 3112 ref = 'reference.' + n + '.jar' |
2884 if updated: | 3129 if updated: |
2885 log('If using NetBeans:') | 3130 log('If using NetBeans:') |
2886 log(' 1. Ensure that a platform named "JDK_' + str(java().version) + '" is defined (Tools -> Java Platforms)') | 3131 log(' 1. Ensure that a platform named "JDK_' + str(java().version) + '" is defined (Tools -> Java Platforms)') |
2887 log(' 2. Open/create a Project Group for the directory containing the projects (File -> Project Group -> New Group... -> Folder of Projects)') | 3132 log(' 2. Open/create a Project Group for the directory containing the projects (File -> Project Group -> New Group... -> Folder of Projects)') |
2888 | 3133 |
2889 def ideclean(args, suite=None): | 3134 timestamp.touch() |
3135 | |
3136 def ideclean(args): | |
2890 """remove all Eclipse and NetBeans project configurations""" | 3137 """remove all Eclipse and NetBeans project configurations""" |
2891 def rm(path): | 3138 def rm(path): |
2892 if exists(path): | 3139 if exists(path): |
2893 os.remove(path) | 3140 os.remove(path) |
3141 | |
3142 for s in suites(): | |
3143 rm(join(s.mxDir, 'eclipseinit.timestamp')) | |
3144 rm(join(s.mxDir, 'netbeansinit.timestamp')) | |
2894 | 3145 |
2895 for p in projects(): | 3146 for p in projects(): |
2896 if p.native: | 3147 if p.native: |
2897 continue | 3148 continue |
2898 | 3149 |
2908 rm(join(p.dir, p.name + '.jar')) | 3159 rm(join(p.dir, p.name + '.jar')) |
2909 except: | 3160 except: |
2910 log("Error removing {0}".format(p.name + '.jar')) | 3161 log("Error removing {0}".format(p.name + '.jar')) |
2911 | 3162 |
2912 | 3163 |
2913 def ideinit(args, suite=None): | 3164 def ideinit(args, refreshOnly=False, buildProcessorJars=True): |
2914 """(re)generate Eclipse and NetBeans project configurations""" | 3165 """(re)generate Eclipse and NetBeans project configurations""" |
2915 eclipseinit(args, suite) | 3166 eclipseinit(args, refreshOnly=refreshOnly, buildProcessorJars=buildProcessorJars) |
2916 netbeansinit(args, suite) | 3167 netbeansinit(args, refreshOnly=refreshOnly, buildProcessorJars=buildProcessorJars) |
2917 fsckprojects([]) | 3168 if not refreshOnly: |
3169 fsckprojects([]) | |
2918 | 3170 |
2919 def fsckprojects(args): | 3171 def fsckprojects(args): |
2920 """find directories corresponding to deleted Java projects and delete them""" | 3172 """find directories corresponding to deleted Java projects and delete them""" |
2921 for suite in suites(): | 3173 for suite in suites(True): |
2922 projectDirs = [p.dir for p in suite.projects] | 3174 projectDirs = [p.dir for p in suite.projects] |
2923 for root, dirnames, files in os.walk(suite.dir): | 3175 for root, dirnames, files in os.walk(suite.dir): |
2924 currentDir = join(suite.dir, root) | 3176 currentDir = join(suite.dir, root) |
2925 if currentDir in projectDirs: | 3177 if currentDir in projectDirs: |
2926 # don't traverse subdirs of an existing project | 3178 # don't traverse subdirs of an existing project |
2931 if len(indicators) != 0: | 3183 if len(indicators) != 0: |
2932 if not sys.stdout.isatty() or ask_yes_no(currentDir + ' looks like a removed project -- delete it', 'n'): | 3184 if not sys.stdout.isatty() or ask_yes_no(currentDir + ' looks like a removed project -- delete it', 'n'): |
2933 shutil.rmtree(currentDir) | 3185 shutil.rmtree(currentDir) |
2934 log('Deleted ' + currentDir) | 3186 log('Deleted ' + currentDir) |
2935 | 3187 |
2936 def javadoc(args, parser=None, docDir='javadoc', includeDeps=True): | 3188 def javadoc(args, parser=None, docDir='javadoc', includeDeps=True, stdDoclet=True): |
2937 """generate javadoc for some/all Java projects""" | 3189 """generate javadoc for some/all Java projects""" |
2938 | 3190 |
2939 parser = ArgumentParser(prog='mx javadoc') if parser is None else parser | 3191 parser = ArgumentParser(prog='mx javadoc') if parser is None else parser |
2940 parser.add_argument('-d', '--base', action='store', help='base directory for output') | 3192 parser.add_argument('-d', '--base', action='store', help='base directory for output') |
2941 parser.add_argument('--unified', action='store_true', help='put javadoc in a single directory instead of one per project') | 3193 parser.add_argument('--unified', action='store_true', help='put javadoc in a single directory instead of one per project') |
2949 parser.add_argument('--exclude-packages', action='store', help='comma separated packages to exclude') | 3201 parser.add_argument('--exclude-packages', action='store', help='comma separated packages to exclude') |
2950 | 3202 |
2951 args = parser.parse_args(args) | 3203 args = parser.parse_args(args) |
2952 | 3204 |
2953 # build list of projects to be processed | 3205 # build list of projects to be processed |
2954 candidates = sorted_deps() | |
2955 if args.projects is not None: | 3206 if args.projects is not None: |
2956 candidates = [project(name) for name in args.projects.split(',')] | 3207 candidates = [project(name) for name in args.projects.split(',')] |
3208 else: | |
3209 candidates = projects_opt_limit_to_suites() | |
2957 | 3210 |
2958 # optionally restrict packages within a project | 3211 # optionally restrict packages within a project |
2959 packages = [] | 3212 packages = [] |
2960 if args.packages is not None: | 3213 if args.packages is not None: |
2961 packages = [name for name in args.packages.split(',')] | 3214 packages = [name for name in args.packages.split(',')] |
3033 print >> fp, '<html><body>Documentation for the <code>' + p.name + '</code> project.</body></html>' | 3286 print >> fp, '<html><body>Documentation for the <code>' + p.name + '</code> project.</body></html>' |
3034 delOverviewFile = True | 3287 delOverviewFile = True |
3035 nowarnAPI = [] | 3288 nowarnAPI = [] |
3036 if not args.warnAPI: | 3289 if not args.warnAPI: |
3037 nowarnAPI.append('-XDignore.symbol.file') | 3290 nowarnAPI.append('-XDignore.symbol.file') |
3291 | |
3292 # windowTitle onloy applies to the standard doclet processor | |
3293 windowTitle = [] | |
3294 if stdDoclet: | |
3295 windowTitle = ['-windowtitle', p.name + ' javadoc'] | |
3038 try: | 3296 try: |
3039 log('Generating {2} for {0} in {1}'.format(p.name, out, docDir)) | 3297 log('Generating {2} for {0} in {1}'.format(p.name, out, docDir)) |
3040 run([java().javadoc, memory, | 3298 run([java().javadoc, memory, |
3041 '-windowtitle', p.name + ' javadoc', | |
3042 '-XDignore.symbol.file', | 3299 '-XDignore.symbol.file', |
3043 '-classpath', cp, | 3300 '-classpath', cp, |
3044 '-quiet', | 3301 '-quiet', |
3045 '-d', out, | 3302 '-d', out, |
3046 '-overview', overviewFile, | 3303 '-overview', overviewFile, |
3047 '-sourcepath', sp] + | 3304 '-sourcepath', sp] + |
3048 links + | 3305 links + |
3049 extraArgs + | 3306 extraArgs + |
3050 nowarnAPI + | 3307 nowarnAPI + |
3308 windowTitle + | |
3051 list(pkgs)) | 3309 list(pkgs)) |
3052 log('Generated {2} for {0} in {1}'.format(p.name, out, docDir)) | 3310 log('Generated {2} for {0} in {1}'.format(p.name, out, docDir)) |
3053 finally: | 3311 finally: |
3054 if delOverviewFile: | 3312 if delOverviewFile: |
3055 os.remove(overviewFile) | 3313 os.remove(overviewFile) |
3405 run([javapExe, '-private', '-verbose', '-classpath', classpath()] + selection) | 3663 run([javapExe, '-private', '-verbose', '-classpath', classpath()] + selection) |
3406 | 3664 |
3407 def show_projects(args): | 3665 def show_projects(args): |
3408 """show all loaded projects""" | 3666 """show all loaded projects""" |
3409 for s in suites(): | 3667 for s in suites(): |
3410 projectsFile = join(s.dir, 'mx', 'projects') | 3668 projectsFile = join(s.mxDir, 'projects') |
3411 if exists(projectsFile): | 3669 if exists(projectsFile): |
3412 log(projectsFile) | 3670 log(projectsFile) |
3413 for p in s.projects: | 3671 for p in s.projects: |
3414 log('\t' + p.name) | 3672 log('\t' + p.name) |
3415 | 3673 |
3433 """ | 3691 """ |
3434 Define how a single command-line argument. | 3692 Define how a single command-line argument. |
3435 """ | 3693 """ |
3436 assert _argParser is not None | 3694 assert _argParser is not None |
3437 _argParser.add_argument(*args, **kwargs) | 3695 _argParser.add_argument(*args, **kwargs) |
3696 | |
3697 def update_commands(suite, new_commands): | |
3698 for key, value in new_commands.iteritems(): | |
3699 if _commands.has_key(key) and not suite.primary: | |
3700 pass | |
3701 # print("WARNING: attempt to redefine command '" + key + "' in suite " + suite.dir) | |
3702 else: | |
3703 _commands[key] = value | |
3438 | 3704 |
3439 # Table of commands in alphabetical order. | 3705 # Table of commands in alphabetical order. |
3440 # Keys are command names, value are lists: [<function>, <usage msg>, <format args to doc string of function>...] | 3706 # Keys are command names, value are lists: [<function>, <usage msg>, <format args to doc string of function>...] |
3441 # If any of the format args are instances of Callable, then they are called with an 'env' are before being | 3707 # If any of the format args are instances of Callable, then they are called with an 'env' are before being |
3442 # used in the call to str.format(). | 3708 # used in the call to str.format(). |
3454 'help': [help_, '[command]'], | 3720 'help': [help_, '[command]'], |
3455 'ideclean': [ideclean, ''], | 3721 'ideclean': [ideclean, ''], |
3456 'ideinit': [ideinit, ''], | 3722 'ideinit': [ideinit, ''], |
3457 'archive': [archive, '[options]'], | 3723 'archive': [archive, '[options]'], |
3458 'projectgraph': [projectgraph, ''], | 3724 'projectgraph': [projectgraph, ''], |
3725 'pylint': [pylint, ''], | |
3459 'javap': [javap, '<class name patterns>'], | 3726 'javap': [javap, '<class name patterns>'], |
3460 'javadoc': [javadoc, '[options]'], | 3727 'javadoc': [javadoc, '[options]'], |
3461 'site': [site, '[options]'], | 3728 'site': [site, '[options]'], |
3462 'netbeansinit': [netbeansinit, ''], | 3729 'netbeansinit': [netbeansinit, ''], |
3463 'projects': [show_projects, ''], | 3730 'projects': [show_projects, ''], |
3465 | 3732 |
3466 _argParser = ArgParser() | 3733 _argParser = ArgParser() |
3467 | 3734 |
3468 def _findPrimarySuite(): | 3735 def _findPrimarySuite(): |
3469 def is_suite_dir(d): | 3736 def is_suite_dir(d): |
3470 mxDir = join(d, 'mx') | 3737 for f in os.listdir(d): |
3471 if exists(mxDir) and isdir(mxDir) and exists(join(mxDir, 'projects')): | 3738 if f == 'mx' or fnmatch.fnmatch(f, 'mx.*'): |
3472 return dirname(mxDir) | 3739 mxDir = join(d, f) |
3740 if exists(mxDir) and isdir(mxDir) and exists(join(mxDir, 'projects')): | |
3741 return dirname(mxDir) | |
3742 | |
3473 | 3743 |
3474 # try current working directory first | 3744 # try current working directory first |
3475 if is_suite_dir(os.getcwd()): | 3745 if is_suite_dir(os.getcwd()): |
3476 return os.getcwd() | 3746 return os.getcwd() |
3477 | 3747 |