comparison mxtool/mx.py @ 13855:bc32c9f5719b

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