changeset 13855:bc32c9f5719b

remove multiple suite/repo support
author Mick Jordan <mick.jordan@oracle.com>
date Sat, 01 Feb 2014 10:47:12 -0800
parents 5d455591cfbd
children 2ba54e75b032
files mxtool/mx.py
diffstat 1 files changed, 14 insertions(+), 608 deletions(-) [+]
line wrap: on
line diff
--- a/mxtool/mx.py	Sat Feb 01 15:33:13 2014 +0000
+++ b/mxtool/mx.py	Sat Feb 01 10:47:12 2014 -0800
@@ -28,6 +28,8 @@
 r"""
 mx is a command line tool for managing the development of Java code organized as suites of projects.
 
+Version 1.x supports a single suite of projects.
+
 Full documentation can be found at https://wiki.openjdk.java.net/display/Graal/The+mx+Tool
 """
 
@@ -48,14 +50,9 @@
 _annotationProcessors = None
 _primary_suite_path = None
 _primary_suite = None
-_src_suitemodel = None
-_dst_suitemodel = None
 _opts = None
 _java = None
-_check_global_structures = True  # can be set False to allow suites with duplicate definitions to load without aborting
 _warn = False
-_hg = None
-
 
 """
 A distribution is a jar or zip file containing the output from one or more Java projects.
@@ -442,229 +439,6 @@
             else:
                 return None
 
-    def can_push(self, s, strict=True):
-        try:
-            output = subprocess.check_output(['hg', '-R', s.dir, 'status'])
-            # super strict
-            return output == ''
-        except OSError:
-            warn(self.missing)
-        except subprocess.CalledProcessError:
-            return False
-
-    def default_push(self, sdir):
-        with open(join(sdir, '.hg', 'hgrc')) as f:
-            for line in f:
-                line = line.rstrip()
-                if line.startswith('default = '):
-                    return line[len('default = '):]
-        return None
-
-class SuiteModel:
-    """
-    Defines how to locate a URL/path for a suite, including imported suites.
-    Conceptually a SuiteModel is defined by a primary suite URL/path and a
-    map from suite name to URL/path for imported suites.
-    Subclasses define a specfic implementation.
-    """
-    def __init__(self):
-        self.primaryDir = None
-        self.suitenamemap = {}
-
-    def find_suite_dir(self, suitename):
-        """locates the URL/path for suitename or None if not found"""
-        abort('find_suite_dir not implemented')
-
-    def set_primary_dir(self, d):
-        """informs that d is the primary suite directory"""
-        self._primaryDir = d
-
-    def importee_dir(self, importer_dir, suitename):
-        """returns the directory path for an import of suitename, given importer_dir"""
-        abort('importee_dir not implemented')
-
-    def nestedsuites_dirname(self):
-        """Returns the dirname that contains any nested suites if the model supports that"""
-        return None
-
-    def _mxDirName(self, name):
-        # temporary workaround until mx.graal exists
-        if name == 'graal':
-            return 'mx'
-        else:
-            return 'mx.' + name
-
-    def _search_dir(self, searchDir, mxDirName):
-        for dd in os.listdir(searchDir):
-            sd = _is_suite_dir(join(searchDir, dd), mxDirName)
-            if sd is not None:
-                return sd
-
-    def _create_suitenamemap(self, optionspec, suitemap):
-        """Three ways to specify a suite name mapping, in order of precedence:
-        1. Explicitly in optionspec.
-        2. In suitemap.
-        3. in MXSUITEMAP environment variable.
-        """
-        if optionspec != '':
-            spec = optionspec
-        elif suitemap is not None:
-            spec = suitemap
-        elif get_env('MXSUITEMAP') is not None:
-            spec = get_env('MXSUITEMAP')
-        else:
-            return
-        pairs = spec.split(',')
-        for pair in pairs:
-            mappair = pair.split('=')
-            self.suitenamemap[mappair[0]] = mappair[1]
-
-    @staticmethod
-    def set_suitemodel(option, suitemap):
-        if option.startswith('sibling'):
-            return SiblingSuiteModel(os.getcwd(), option, suitemap)
-        elif option.startswith('nested'):
-            return NestedImportsSuiteModel(os.getcwd(), option, suitemap)
-        elif option.startswith('path'):
-            return PathSuiteModel(option[len('path:'):])
-        else:
-            abort('unknown suitemodel type: ' + option)
-
-    @staticmethod
-    def parse_options():
-        # suite-specific args may match the known args so there is no way at this early stage
-        # to use ArgParser to handle the suite model global arguments, so we just do it manually.
-        def _get_argvalue(arg, args, i):
-            if i < len(args):
-                return args[i]
-            else:
-                abort('value expected with ' + arg)
-
-        args = sys.argv[1:]
-        src_suitemodel_arg = dst_suitemodel_arg = 'sibling'
-        suitemap_arg = None
-
-        i = 0
-        while i < len(args):
-            arg = args[i]
-            if arg == '--src-suitemodel':
-                src_suitemodel_arg = _get_argvalue(arg, args, i + 1)
-            elif arg == '--dst-suitemodel':
-                dst_suitemodel_arg = _get_argvalue(arg, args, i + 1)
-            elif arg == '--suitemap':
-                suitemap_arg = _get_argvalue(arg, args, i + 1)
-            elif arg == '-w':
-                # to get warnings on suite loading issues before command line is parsed
-                global _warn
-                _warn = True
-            elif arg == '-p' or arg == '--primary-suite-path':
-                global _primary_suite_path
-                _primary_suite_path = os.path.abspath(_get_argvalue(arg, args, i + 1))
-            i = i + 1
-
-        global _src_suitemodel
-        _src_suitemodel = SuiteModel.set_suitemodel(src_suitemodel_arg, suitemap_arg)
-        global _dst_suitemodel
-        _dst_suitemodel = SuiteModel.set_suitemodel(dst_suitemodel_arg, suitemap_arg)
-
-
-class SiblingSuiteModel(SuiteModel):
-    """All suites are siblings in the same parent directory, recorded as _suiteRootDir"""
-    def __init__(self, suiteRootDir, option, suitemap):
-        SuiteModel.__init__(self)
-        self._suiteRootDir = suiteRootDir
-        self._create_suitenamemap(option[len('sibling:'):], suitemap)
-
-    def find_suite_dir(self, name):
-        return self._search_dir(self._suiteRootDir, self._mxDirName(name))
-
-    def set_primary_dir(self, d):
-        SuiteModel.set_primary_dir(self, d)
-        self._suiteRootDir = dirname(d)
-
-    def importee_dir(self, importer_dir, suitename):
-        if self.suitenamemap.has_key(suitename):
-            suitename = self.suitenamemap[suitename]
-        return join(dirname(importer_dir), suitename)
-
-class NestedImportsSuiteModel(SuiteModel):
-    """Imported suites are all siblings in an 'imported_suites' directory of the primary suite"""
-    def _imported_suites_dirname(self):
-        return "imported_suites"
-
-    def __init__(self, primaryDir, option, suitemap):
-        SuiteModel.__init__(self)
-        self._primaryDir = primaryDir
-        self._create_suitenamemap(option[len('nested:'):], suitemap)
-
-    def find_suite_dir(self, name):
-        return self._search_dir(join(self._primaryDir, self._imported_suites_dirname()), self._mxDirName(name))
-
-    def importee_dir(self, importer_dir, suitename):
-        if self.suitenamemap.has_key(suitename):
-            suitename = self.suitenamemap[suitename]
-        if basename(importer_dir) == basename(self._primaryDir):
-            # primary is importer
-            this_imported_suites_dirname = join(importer_dir, self._imported_suites_dirname())
-            if not exists(this_imported_suites_dirname):
-                os.mkdir(this_imported_suites_dirname)
-            return join(this_imported_suites_dirname, suitename)
-        else:
-            return join(dirname(importer_dir), suitename)
-
-    def nestedsuites_dirname(self):
-        return self._imported_suites_dirname()
-
-class PathSuiteModel(SuiteModel):
-    """The most general model. Uses a map from suitename to URL/path provided by the user"""
-    def __init__(self, path):
-        SuiteModel.__init__(self)
-        paths = path.split(',')
-        self.suit_to_url = {}
-        for path in paths:
-            pair = path.split('=')
-            if len(pair) > 1:
-                suitename = pair[0]
-                suiteurl = pair[1]
-            else:
-                suitename = basename(pair[0])
-                suiteurl = pair[0]
-            self.suit_to_url[suitename] = suiteurl
-
-    def find_suite_dir(self, suitename):
-        if self.suit_to_url.has_key(suitename):
-            return self.suit_to_url[suitename]
-        else:
-            return None
-
-    def importee_dir(self, importer_dir, suitename):
-        if suitename in self.suit_to_url:
-            return self.suit_to_url[suitename]
-        else:
-            abort('suite ' + suitename + ' not found')
-
-class SuiteImport:
-    def __init__(self, name, version):
-        self.name = name
-        self.version = version
-
-    @staticmethod
-    def parse_specification(specification):
-        pair = specification.split(',')
-        name = pair[0]
-        if len(pair) > 1:
-            version = pair[1]
-        else:
-            version = None
-        return SuiteImport(name, version)
-
-    @staticmethod
-    def tostring(name, version):
-        return name + ',' + version
-
-    def __str__(self):
-        return self.name + ',' + self.version
-
 class Suite:
     def __init__(self, mxDir, primary, load=True):
         self.dir = dirname(mxDir)
@@ -672,13 +446,12 @@
         self.projects = []
         self.libs = []
         self.dists = []
-        self.imports = []
         self.commands = None
         self.primary = primary
         self.requiredMxVersion = None
         self.name = _suitename(mxDir)  # validated in _load_projects
         if load:
-            # load suites bottom up to make sure command overriding works properly
+            # just check that there are no imports
             self._load_imports()
             self._load_env()
             self._load_commands()
@@ -687,10 +460,6 @@
     def __str__(self):
         return self.name
 
-    def version(self, abortOnError=True):
-        # we do not cache the version
-        return _hg.tip(self.dir, abortOnError)
-
     def _load_projects(self):
         libsMap = dict()
         projsMap = dict()
@@ -838,56 +607,9 @@
             mod.mx_init(self)
             self.commands = mod
 
-    def _imports_file(self):
-        return join(self.mxDir, 'imports')
-
-    def import_timestamp(self):
-        return TimeStampFile(self._imports_file())
-
-    def visit_imports(self, visitor, **extra_args):
-        """
-        Visitor support for the imports file.
-        For each line of the imports file that specifies an import, the visitor function is
-        called with this suite, a SuiteImport instance created from the line and any extra args
-        passed to this call. In addition, if extra_args contains a key 'update_versions' that is True,
-        a StringIO value is added to extra_args with key 'updated_imports', and the visitor is responsible
-        for writing a (possibly) updated import line to the file, and the file is (possibly) updated after
-        all imports are processed.
-        N.B. There is no built-in support for avoiding visiting the same suite multiple times,
-        as this function only visits the imports of a single suite. If a (recursive) visitor function
-        wishes to visit a suite exactly once, it must manage that through extra_args.
-        """
-        importsFile = self._imports_file()
-        if exists(importsFile):
-            update_versions = extra_args.has_key('update_versions') and extra_args['update_versions']
-            out = StringIO.StringIO() if update_versions else None
-            extra_args['updated_imports'] = out
-            with open(importsFile) as f:
-                for line in f:
-                    sline = line.strip()
-                    if len(sline) == 0 or sline.startswith('#'):
-                        if out is not None:
-                            out.write(sline + '\n')
-                        continue
-                    suite_import = SuiteImport.parse_specification(line.strip())
-                    visitor(self, suite_import, **extra_args)
-
-            if out is not None:
-                update_file(importsFile, out.getvalue())
-
-    @staticmethod
-    def _find_and_loadsuite(importing_suite, suite_import, **extra_args):
-        """visitor for the initial suite load"""
-        importMxDir = _src_suitemodel.find_suite_dir(suite_import.name)
-        if importMxDir is None:
-            abort('import ' + suite_import.name + ' not found')
-        importing_suite.imports.append(suite_import)
-        _loadSuite(importMxDir, False)
-        # we do not check at this stage whether the tip version of imported_suite
-        # matches that of the import, since during development, this can and will change
-
     def _load_imports(self):
-        self.visit_imports(self._find_and_loadsuite)
+        if exists(join(self.mxDir, 'imports')):
+            abort('multiple suites are not supported in this version of mx')
 
     def _load_env(self):
         e = join(self.mxDir, 'env')
@@ -908,23 +630,23 @@
         if self.requiredMxVersion is None:
             warn("This suite does not express any required mx version. Consider adding 'mxversion=<version>' to your projects file.")
         elif self.requiredMxVersion > version:
-            abort("This suite requires mx version " + str(self.requiredMxVersion) + " while your current mx verion is " + str(version) + ". Please update mx.")
+            abort("This suite requires mx version " + str(self.requiredMxVersion) + " while your current mx version is " + str(version) + ". Please update mx.")
         # set the global data structures, checking for conflicts unless _check_global_structures is False
         for p in self.projects:
             existing = _projects.get(p.name)
-            if existing is not None and _check_global_structures:
+            if existing is not None:
                 abort('cannot override project  ' + p.name + ' in ' + p.dir + " with project of the same name in  " + existing.dir)
             if not p.name in _opts.ignored_projects:
                 _projects[p.name] = p
         for l in self.libs:
             existing = _libs.get(l.name)
             # Check that suites that define same library are consistent
-            if existing is not None and existing != l and _check_global_structures:
+            if existing is not None and existing != l:
                 abort('inconsistent library redefinition of ' + l.name + ' in ' + existing.suite.dir + ' and ' + l.suite.dir)
             _libs[l.name] = l
         for d in self.dists:
             existing = _dists.get(d.name)
-            if existing is not None and _check_global_structures:
+            if existing is not None:
                 # allow redefinition, so use path from existing
                 # abort('cannot redefine distribution  ' + d.name)
                 warn('distribution ' + d.name + ' redefined')
@@ -1039,14 +761,7 @@
     """
     Get the list of all loaded suites.
     """
-    if opt_limit_to_suite and _opts.specific_suites:
-        result = []
-        for s in _suites.values():
-            if s.name in _opts.specific_suites:
-                result.append(s)
-        return result
-    else:
-        return _suites.values()
+    return _suites.values()
 
 def suite(name, fatalIfMissing=True):
     """
@@ -1084,15 +799,7 @@
     return projects(True)
 
 def _projects_opt_limit_to_suites(projects):
-    if not _opts.specific_suites:
-        return projects
-    else:
-        result = []
-        for p in projects:
-            s = p.suite
-            if s.name in _opts.specific_suites:
-                result.append(p)
-        return result
+    return projects
 
 def annotation_processors():
     """
@@ -1288,10 +995,6 @@
         self.add_argument('--user-home', help='users home directory', metavar='<path>', default=os.path.expanduser('~'))
         self.add_argument('--java-home', help='bootstrap JDK installation directory (must be JDK 6 or later)', metavar='<path>')
         self.add_argument('--ignore-project', action='append', dest='ignored_projects', help='name of project to ignore', metavar='<name>', default=[])
-        self.add_argument('--suite', action='append', dest='specific_suites', help='limit command to given suite', default=[])
-        self.add_argument('--src-suitemodel', help='mechanism for locating imported suites', metavar='<arg>', default='sibling')
-        self.add_argument('--dst-suitemodel', help='mechanism for placing cloned/pushed suites', metavar='<arg>', default='sibling')
-        self.add_argument('--suitemap', help='explicit remapping of suite names', metavar='<args>')
         if get_os() != 'windows':
             # Time outs are (currently) implemented with Unix specific functionality
             self.add_argument('--timeout', help='timeout (in seconds) for command', type=int, default=0, metavar='<secs>')
@@ -2034,7 +1737,7 @@
                 log('Compiling Java sources for {0} with javac...'.format(p.name))
 
 
-                javacCmd = [java().javac, '-g', '-J-Xmx1g', '-encoding', 'UTF-8', '-source', compliance, '-target', compliance, '-classpath', cp, '-d', outputDir]
+                javacCmd = [java().javac, '-g', '-J-Xmx1g', '-source', compliance, '-target', compliance, '-classpath', cp, '-d', outputDir]
                 if java().debug_port is not None:
                     javacCmd += ['-J-Xdebug', '-J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(java().debug_port)]
                 javacCmd += processorArgs
@@ -2052,7 +1755,6 @@
 
                 jdtArgs += [ '-jar', jdtJar,
                          '-' + compliance,
-                         '-encoding', 'UTF-8',
                          '-cp', cp, '-g', '-enableJavadoc',
                          '-d', outputDir]
                 jdtArgs += processorArgs
@@ -2880,12 +2582,10 @@
     generate_eclipse_workingsets()
 
 def _check_ide_timestamp(suite, configZip, ide):
-    """return True if and only if the projects file, imports file, eclipse-settings files, and mx itself are all older than configZip"""
+    """return True if and only if the projects file, eclipse-settings files, and mx itself are all older than configZip"""
     projectsFile = join(suite.mxDir, 'projects')
     if configZip.isOlderThan(projectsFile):
         return False
-    if configZip.isOlderThan(suite.import_timestamp()):
-        return False
     # Assume that any mx change might imply changes to the generated IDE files
     if configZip.isOlderThan(__file__):
         return False
@@ -3655,9 +3355,6 @@
                 # no point in traversing .hg
                 if '.hg' in dirnames:
                     dirnames.remove('.hg')
-                # if there are nested suites must not scan those now, as they are not in projectDirs
-                if _src_suitemodel.nestedsuites_dirname() in dirnames:
-                    dirnames.remove(_src_suitemodel.nestedsuites_dirname())
             elif dirpath in projectDirs:
                 # don't traverse subdirs of an existing project in this suite
                 dirnames[:] = []
@@ -4076,266 +3773,6 @@
         return kwargs.pop(0)
     return None
 
-def sclone(args):
-    """clone a suite repository, and its imported suites"""
-    _hg.check()
-    parser = ArgumentParser(prog='mx sclone')
-    parser.add_argument('--source', help='url/path of repo containing suite', metavar='<url>')
-    parser.add_argument('--dest', help='destination directory (default basename of source)', metavar='<path>')
-    parser.add_argument("--no-imports", action='store_true', help='do not clone imported suites')
-    parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...')
-    args = parser.parse_args(args)
-    # check for non keyword args
-    if args.source is None:
-        args.source = _kwArg(args.nonKWArgs)
-    if args.dest is None:
-        args.dest = _kwArg(args.nonKWArgs)
-    if len(args.nonKWArgs) > 0:
-        abort('unrecognized args: ' + ' '.join(args.nonKWArgs))
-
-    if args.source is None:
-        # must be primary suite and dest is required
-        if _primary_suite is None:
-            abort('--source missing and no primary suite found')
-        if args.dest is None:
-            abort('--dest required when --source is not given')
-        source = _primary_suite.dir
-    else:
-        source = args.source
-
-    if args.dest is not None:
-        dest = args.dest
-    else:
-        dest = basename(source)
-
-    dest = os.path.abspath(dest)
-    # We can now set the primary dir for the src/dst suitemodel
-    _dst_suitemodel.set_primary_dir(dest)
-    _src_suitemodel.set_primary_dir(source)
-
-    _sclone(source, dest, None, args.no_imports)
-
-def _sclone(source, dest, version, no_imports):
-    cmd = ['hg', 'clone']
-    if version is not None:
-        cmd.append('-r')
-        cmd.append(version)
-    cmd.append(source)
-    cmd.append(dest)
-
-    run(cmd)
-
-    mxDir = _is_suite_dir(dest)
-    if mxDir is None:
-        warn(source + ' is not an mx suite')
-        return None
-
-    # create a Suite (without loading) to enable imports visitor
-    s = Suite(mxDir, False, load=False)
-    if not no_imports:
-        s.visit_imports(_scloneimports_visitor, source=source)
-    return s
-
-def _scloneimports_visitor(s, suite_import, source, **extra_args):
-    """
-    cloneimports visitor for Suite.visit_imports.
-    The destination information is encapsulated by 's'
-    """
-    _scloneimports(s, suite_import, source)
-
-def _scloneimports_suitehelper(sdir):
-    mxDir = _is_suite_dir(sdir)
-    if mxDir is None:
-        abort(sdir + ' is not an mx suite')
-    else:
-        # create a Suite (without loading) to enable imports visitor
-        return Suite(mxDir, False, load=False)
-
-def _scloneimports(s, suite_import, source):
-    # clone first, then visit imports once we can locate them
-    importee_source = _src_suitemodel.importee_dir(source, suite_import.name)
-    importee_dest = _dst_suitemodel.importee_dir(s.dir, suite_import.name)
-    if exists(importee_dest):
-        # already exists in the suite model, but may be wrong version
-        importee_suite = _scloneimports_suitehelper(importee_dest)
-        if suite_import.version is not None and importee_suite.version() != suite_import.version:
-            abort("imported version of " + suite_import.name + " in " + s.name + " does not match the version in already existing suite: " + importee_suite.dir)
-        importee_suite.visit_imports(_scloneimports_visitor, source=importee_source)
-    else:
-        _sclone(importee_source, importee_dest, suite_import.version, False)
-        # _clone handles the recursive visit of the new imports
-
-def scloneimports(args):
-    """clone the imports of an existing suite"""
-    _hg.check()
-    parser = ArgumentParser(prog='mx scloneimports')
-    parser.add_argument('--source', help='url/path of repo containing suite', metavar='<url>')
-    parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...')
-    args = parser.parse_args(args)
-    # check for non keyword args
-    if args.source is None:
-        args.source = _kwArg(args.nonKWArgs)
-
-    if not os.path.isdir(args.source):
-        abort(args.source + ' is not a directory')
-
-    s = _scloneimports_suitehelper(args.source)
-
-    default_path = _hg.default_push(args.source)
-
-    if default_path is None:
-        abort('no default path in ' + join(args.source, '.hg', 'hgrc'))
-
-    # We can now set the primary dir for the dst suitemodel
-    # N.B. source is effectively the destination and the default_path is the (original) source
-    _dst_suitemodel.set_primary_dir(args.source)
-
-    s.visit_imports(_scloneimports_visitor, source=default_path)
-
-def _spush_import_visitor(s, suite_import, dest, checks, clonemissing, **extra_args):
-    """push visitor for Suite.visit_imports"""
-    if dest is not None:
-        dest = _dst_suitemodel.importee_dir(dest, suite_import.name)
-    _spush(suite(suite_import.name), suite_import, dest, checks, clonemissing)
-
-def _spush_check_import_visitor(s, suite_import, **extra_args):
-    """push check visitor for Suite.visit_imports"""
-    currentTip = suite(suite_import.name).version()
-    if currentTip != suite_import.version:
-        abort('imported version of ' + suite_import.name + ' in suite ' + s.name + ' does not match tip')
-
-def _spush(s, suite_import, dest, checks, clonemissing):
-    if checks:
-        if not _hg.can_push(s):
-            abort('working directory ' + s.dir + ' contains uncommitted changes, push aborted')
-
-    # check imports first
-    if checks:
-        s.visit_imports(_spush_check_import_visitor)
-
-    # ok, push imports
-    s.visit_imports(_spush_import_visitor, dest=dest, checks=checks, clonemissing=clonemissing)
-
-    dest_exists = True
-
-    if clonemissing:
-        if not os.path.exists(dest):
-            dest_exists = False
-
-    def add_version(cmd, suite_import):
-        if suite_import is not None and suite_import.version is not None:
-            cmd.append('-r')
-            cmd.append(suite_import.version)
-
-    if dest_exists:
-        cmd = ['hg', '-R', s.dir, 'push']
-        add_version(cmd, suite_import)
-        if dest is not None:
-            cmd.append(dest)
-        rc = run(cmd, nonZeroIsFatal=False)
-        if rc != 0:
-            # rc of 1 not an error,  means no changes
-            if rc != 1:
-                abort("push failed, exit code " + str(rc))
-    else:
-        cmd = ['hg', 'clone']
-        add_version(cmd, suite_import)
-        cmd.append(s.dir)
-        cmd.append(dest)
-        run(cmd)
-
-def spush(args):
-    """push primary suite and all its imports"""
-    _hg.check()
-    parser = ArgumentParser(prog='mx spush')
-    parser.add_argument('--dest', help='url/path of repo to push to (default as per hg push)', metavar='<path>')
-    parser.add_argument('--no-checks', action='store_true', help='checks on status, versions are disabled')
-    parser.add_argument('--clonemissing', action='store_true', help='clone missing imported repos at destination (forces --no-checks)')
-    parser.add_argument('nonKWArgs', nargs=REMAINDER, metavar='source [dest]...')
-    args = parser.parse_args(args)
-    if args.dest is None:
-        args.dest = _kwArg(args.nonKWArgs)
-    if len(args.nonKWArgs) > 0:
-        abort('unrecognized args: ' + ' '.join(args.nonKWArgs))
-
-    if args.dest is not None and not os.path.isdir(args.dest):
-        abort('destination must be a directory')
-
-    s = _check_primary_suite()
-
-    if args.clonemissing:
-        if args.dest is None:
-            abort('--dest required with --clonemissing')
-        args.nochecks = True
-
-    if args.dest is not None:
-        _dst_suitemodel.set_primary_dir(args.dest)
-
-    _spush(s, None, args.dest, not args.no_checks, args.clonemissing)
-
-def _supdate_import_visitor(s, suite_import, **extra_args):
-    _supdate(suite(suite_import.name), suite_import)
-
-def _supdate(s, suite_import):
-    s.visit_imports(_supdate_import_visitor)
-
-    run(['hg', '-R', s.dir, 'update'])
-
-def supdate(args):
-    """update primary suite and all its imports"""
-
-    _hg.check()
-    s = _check_primary_suite()
-
-    _supdate(s, None)
-
-def _scheck_imports_visitor(s, suite_import, update_versions, updated_imports):
-    """scheckimports visitor for Suite.visit_imports"""
-    _scheck_imports(s, suite(suite_import.name), suite_import, update_versions, updated_imports)
-
-def _scheck_imports(importing_suite, imported_suite, suite_import, update_versions, updated_imports):
-    # check imports recursively
-    imported_suite.visit_imports(_scheck_imports_visitor, update_versions=update_versions)
-
-    currentTip = imported_suite.version()
-    if currentTip != suite_import.version:
-        print('imported version of ' + imported_suite.name + ' in ' + importing_suite.name + ' does not match tip' + (': updating' if update_versions else ''))
-
-    if update_versions:
-        suite_import.version = currentTip
-        line = str(suite_import)
-        updated_imports.write(line + '\n')
-
-def scheckimports(args):
-    """check that suite import versions are up to date"""
-    parser = ArgumentParser(prog='mx scheckimports')
-    parser.add_argument('--update-versions', help='update imported version ids', action='store_true')
-    args = parser.parse_args(args)
-    _check_primary_suite().visit_imports(_scheck_imports_visitor, update_versions=args.update_versions)
-
-def _spull_import_visitor(s, suite_import, update_versions, updated_imports):
-    """pull visitor for Suite.visit_imports"""
-    _spull(suite(suite_import.name), update_versions, updated_imports)
-
-def _spull(s, update_versions, updated_imports):
-    _hg.check()
-    # pull imports first
-    s.visit_imports(_spull_import_visitor, update_versions=update_versions)
-
-    run(['hg', '-R', s.dir, 'pull', '-u'])
-    if update_versions and updated_imports is not None:
-        tip = s.version()
-        updated_imports.write(SuiteImport.tostring(s.name, tip) + '\n')
-
-def spull(args):
-    """pull primary suite and all its imports"""
-    _hg.check()
-    parser = ArgumentParser(prog='mx spull')
-    parser.add_argument('--update-versions', action='store_true', help='update version ids of imported suites')
-    args = parser.parse_args(args)
-
-    _spull(_check_primary_suite(), args.update_versions, None)
-
 def findclass(args, logToConsole=True):
     """find all classes matching a given substring"""
     matches = []
@@ -4472,12 +3909,6 @@
     'ideinit': [ideinit, ''],
     'archive': [archive, '[options]'],
     'projectgraph': [projectgraph, ''],
-    'sclone': [sclone, '[options]'],
-    'scheckimports': [scheckimports, ''],
-    'scloneimports': [scloneimports, '[options]'],
-    'spull': [spull, '[options'],
-    'spush': [spush, '[options'],
-    'supdate': [supdate, ''],
     'pylint': [pylint, ''],
     'javap': [javap, '<class name patterns>'],
     'javadoc': [javadoc, '[options]'],
@@ -4515,12 +3946,6 @@
     else:
         return _primary_suite
 
-def _needs_primary_suite(command):
-    return not command.startswith("sclone")
-
-def _needs_primary_suite_cl():
-    return not any("sclone" in s for s in sys.argv[1:])
-
 def _findPrimarySuiteMxDirFrom(d):
     """ search for a suite directory upwards from 'd' """
     while d:
@@ -4551,34 +3976,15 @@
     return _findPrimarySuiteMxDirFrom(dirname(__file__))
 
 def main():
-    SuiteModel.parse_options()
-
-    global _hg
-    _hg = HgConfig()
-
-    primary_suite_error = 'no primary suite found'
     primarySuiteMxDir = _findPrimarySuiteMxDir()
     if primarySuiteMxDir:
-        _src_suitemodel.set_primary_dir(dirname(primarySuiteMxDir))
         global _primary_suite
         _primary_suite = _loadSuite(primarySuiteMxDir, True)
     else:
-        # in general this is an error, except for the sclone/scloneimports commands,
-        # and an extensions command will likely not parse in this case, as any extra arguments
-        # will not have been added to _argParser.
-        # If the command line does not contain a string matching one of the exceptions, we can safely abort,
-        # but not otherwise, as we can't be sure the string isn't in a value for some other option.
-        if _needs_primary_suite_cl():
-            abort(primary_suite_error)
+        abort('no primary suite found')
 
     opts, commandAndArgs = _argParser._parse_cmd_line()
 
-    if primarySuiteMxDir is None:
-        if len(commandAndArgs) > 0 and _needs_primary_suite(commandAndArgs[0]):
-            abort(primary_suite_error)
-        else:
-            warn(primary_suite_error)
-
     global _opts, _java
     _opts = opts
     _java = JavaConfig(opts)