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