changeset 4144:34527a24bcf8

Adopted mxtool changes.
author Doug Simon <doug.simon@oracle.com>
date Mon, 19 Dec 2011 18:31:09 +0100
parents df24df51bffd
children 9aee7df31417
files .hgignore graal/com.oracle.max.asmdis/.checkstyle.exclude graal/com.oracle.max.graal.graph/.checkstyle_checks.xml mx/.project mx/.pydevproject mx/commands.py mx/projects mxtool/mx.py
diffstat 8 files changed, 893 insertions(+), 639 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Sun Dec 18 13:55:25 2011 +0100
+++ b/.hgignore	Mon Dec 19 18:31:09 2011 +0100
@@ -7,7 +7,8 @@
 ^jdk1.7.0/`
 ^java64/
 ^work/
-\.checkstyle
+\.checkstyle$
+\.checkstyle.*.timestamp
 \.classpath
 \.project
 \.settings/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.max.asmdis/.checkstyle.exclude	Mon Dec 19 18:31:09 2011 +0100
@@ -0,0 +1,2 @@
+src/com/sun/max/asm/amd64/complete
+src/com/sun/max/asm/ia32/complete
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.max.graal.graph/.checkstyle_checks.xml	Mon Dec 19 18:31:09 2011 +0100
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+
+<!--
+    This configuration file was written by the eclipse-cs plugin configuration editor
+-->
+<!--
+    Checkstyle-Configuration: Maxine Checks
+    Description: none
+-->
+<module name="Checker">
+  <property name="severity" value="warning"/>
+  <module name="TreeWalker">
+    <property name="tabWidth" value="4"/>
+    <module name="FileContentsHolder"/>
+    <module name="JavadocStyle">
+      <property name="checkHtml" value="false"/>
+    </module>
+    <module name="LocalFinalVariableName"/>
+    <module name="LocalVariableName"/>
+    <module name="MemberName">
+      <property name="format" value="^(([a-z][a-zA-Z0-9]*$)|(_[A-Z][a-zA-Z0-9]*_[a-z][a-zA-Z0-9]*$))"/>
+    </module>
+    <module name="MethodName">
+      <property name="format" value="^[a-z][a-z_A-Z0-9]*$"/>
+    </module>
+    <module name="PackageName"/>
+    <module name="ParameterName"/>
+    <module name="TypeName">
+      <property name="format" value="^[A-Z][_a-zA-Z0-9]*$"/>
+    </module>
+    <module name="RedundantImport"/>
+    <module name="LineLength">
+      <property name="max" value="250"/>
+    </module>
+    <module name="MethodParamPad"/>
+    <module name="NoWhitespaceAfter">
+      <property name="tokens" value="ARRAY_INIT,BNOT,DEC,DOT,INC,LNOT,UNARY_MINUS,UNARY_PLUS"/>
+    </module>
+    <module name="NoWhitespaceBefore">
+      <property name="tokens" value="SEMI,DOT,POST_DEC,POST_INC"/>
+    </module>
+    <module name="ParenPad"/>
+    <module name="TypecastParenPad">
+      <property name="tokens" value="RPAREN,TYPECAST"/>
+    </module>
+    <module name="WhitespaceAfter"/>
+    <module name="WhitespaceAround">
+      <property name="tokens" value="ASSIGN,BAND,BAND_ASSIGN,BOR,BOR_ASSIGN,BSR,BSR_ASSIGN,BXOR,BXOR_ASSIGN,COLON,DIV,DIV_ASSIGN,EQUAL,GE,GT,LAND,LE,LITERAL_ASSERT,LITERAL_CATCH,LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_FOR,LITERAL_IF,LITERAL_RETURN,LITERAL_SYNCHRONIZED,LITERAL_TRY,LITERAL_WHILE,LOR,LT,MINUS,MINUS_ASSIGN,MOD,MOD_ASSIGN,NOT_EQUAL,PLUS,PLUS_ASSIGN,QUESTION,SL,SLIST,SL_ASSIGN,SR,SR_ASSIGN,STAR,STAR_ASSIGN,LITERAL_ASSERT,TYPE_EXTENSION_AND"/>
+    </module>
+    <module name="RedundantModifier"/>
+    <module name="AvoidNestedBlocks">
+      <property name="allowInSwitchCase" value="true"/>
+    </module>
+    <module name="EmptyBlock">
+      <property name="option" value="text"/>
+      <property name="tokens" value="LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_IF,LITERAL_TRY,LITERAL_WHILE,STATIC_INIT"/>
+    </module>
+    <module name="LeftCurly"/>
+    <module name="NeedBraces"/>
+    <module name="RightCurly"/>
+    <module name="DoubleCheckedLocking">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="EmptyStatement"/>
+    <module name="HiddenField">
+      <property name="severity" value="ignore"/>
+      <property name="ignoreConstructorParameter" value="true"/>
+      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+    </module>
+    <module name="FinalClass"/>
+    <module name="HideUtilityClassConstructor">
+      <property name="severity" value="ignore"/>
+      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+    </module>
+    <module name="ArrayTypeStyle"/>
+    <module name="UpperEll"/>
+    <module name="FallThrough"/>
+    <module name="FinalLocalVariable">
+      <property name="severity" value="ignore"/>
+      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+    </module>
+    <module name="MultipleVariableDeclarations"/>
+    <module name="StringLiteralEquality">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="SuperFinalize"/>
+    <module name="UnnecessaryParentheses">
+      <property name="severity" value="ignore"/>
+      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+    </module>
+    <module name="Indentation">
+      <property name="severity" value="ignore"/>
+      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+    </module>
+    <module name="StaticVariableName">
+      <property name="format" value="^[A-Za-z][a-zA-Z0-9]*$"/>
+    </module>
+    <module name="EmptyForInitializerPad"/>
+    <module name="EmptyForIteratorPad"/>
+    <module name="ModifierOrder"/>
+    <module name="DefaultComesLast"/>
+    <module name="InnerAssignment">
+      <property name="severity" value="ignore"/>
+      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+    </module>
+    <module name="JUnitTestCase"/>
+    <module name="ModifiedControlVariable"/>
+    <module name="MutableException">
+      <property name="severity" value="ignore"/>
+      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+    </module>
+    <module name="ParameterAssignment">
+      <property name="severity" value="ignore"/>
+      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+    </module>
+    <module name="RegexpSinglelineJava">
+      <metadata name="net.sf.eclipsecs.core.comment" value="Illegal trailing whitespace(s) at the end of the line."/>
+      <property name="format" value="\s$"/>
+      <property name="message" value="Illegal trailing whitespace(s) at the end of the line."/>
+      <property name="ignoreComments" value="true"/>
+      <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Checks for trailing spaces at the end of a line"/>
+    </module>
+    <module name="RegexpSinglelineJava">
+      <metadata name="net.sf.eclipsecs.core.comment" value="illegal space before a comma"/>
+      <property name="format" value=" ,"/>
+      <property name="message" value="illegal space before a comma"/>
+      <property name="ignoreComments" value="true"/>
+      <metadata name="com.atlassw.tools.eclipse.checkstyle.customMessage" value="Illegal whitespace before a comma."/>
+      <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Checks for whitespace before a comma."/>
+    </module>
+  </module>
+  <module name="RegexpHeader">
+    <property name="header" value="/\*\n \* Copyright \(c\) (20[0-9][0-9], )?20[0-9][0-9], Oracle and/or its affiliates. All rights reserved.\n \* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n \*\n \* This code is free software; you can redistribute it and/or modify it\n \* under the terms of the GNU General Public License version 2 only, as\n \* published by the Free Software Foundation.\n \*\n \* This code is distributed in the hope that it will be useful, but WITHOUT\n \* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n \* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n \* version 2 for more details \(a copy is included in the LICENSE file that\n \* accompanied this code\).\n \*\n \* You should have received a copy of the GNU General Public License version\n \* 2 along with this work; if not, write to the Free Software Foundation,\n \* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n \*\n \* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA\n \* or visit www.oracle.com if you need additional information or have any\n \* questions.\n \*/\n"/>
+  </module>
+  <module name="FileTabCharacter">
+    <property name="severity" value="error"/>
+  </module>
+  <module name="NewlineAtEndOfFile">
+    <property name="lineSeparator" value="lf"/>
+  </module>
+  <module name="Translation"/>
+  <module name="SuppressionCommentFilter">
+    <property name="offCommentFormat" value="Checkstyle: stop constant name check"/>
+    <property name="onCommentFormat" value="Checkstyle: resume constant name check"/>
+    <property name="checkFormat" value="ConstantNameCheck"/>
+    <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Allow non-conforming constant names"/>
+  </module>
+  <module name="SuppressionCommentFilter">
+    <property name="offCommentFormat" value="Checkstyle: stop method name check"/>
+    <property name="onCommentFormat" value="Checkstyle: resume method name check"/>
+    <property name="checkFormat" value="MethodName"/>
+    <property name="checkC" value="false"/>
+    <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable method name checks"/>
+  </module>
+  <module name="SuppressionCommentFilter">
+    <property name="offCommentFormat" value="CheckStyle: stop parameter assignment check"/>
+    <property name="onCommentFormat" value="CheckStyle: resume parameter assignment check"/>
+    <property name="checkFormat" value="ParameterAssignment"/>
+    <property name="checkC" value="false"/>
+    <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable Parameter Assignment"/>
+  </module>
+  <module name="SuppressionCommentFilter">
+    <property name="offCommentFormat" value="Checkstyle: stop final variable check"/>
+    <property name="onCommentFormat" value="Checkstyle: resume final variable check"/>
+    <property name="checkFormat" value="FinalLocalVariable"/>
+    <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable final variable checks"/>
+  </module>
+  <module name="SuppressionCommentFilter">
+    <property name="offCommentFormat" value="Checkstyle: stop"/>
+    <property name="onCommentFormat" value="Checkstyle: resume"/>
+    <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable all checks"/>
+  </module>
+  <module name="SuppressionCommentFilter">
+    <property name="offCommentFormat" value="// START GENERATED RAW ASSEMBLER METHODS"/>
+    <property name="onCommentFormat" value="// END GENERATED RAW ASSEMBLER METHODS"/>
+    <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable all checks for generated raw assembler methods"/>
+  </module>
+  <module name="SuppressionCommentFilter">
+    <property name="offCommentFormat" value="// START GENERATED LABEL ASSEMBLER METHODS"/>
+    <property name="onCommentFormat" value="// END GENERATED LABEL ASSEMBLER METHODS"/>
+    <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable all checks for generated label assembler methods"/>
+  </module>
+  <module name="SuppressionCommentFilter">
+    <property name="offCommentFormat" value="CheckStyle: stop inner assignment check"/>
+    <property name="onCommentFormat" value="CheckStyle: resume inner assignment check"/>
+    <property name="checkFormat" value="InnerAssignment"/>
+    <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable inner assignment checks"/>
+  </module>
+  <module name="SuppressionCommentFilter">
+    <property name="offCommentFormat" value="Checkstyle: stop field name check"/>
+    <property name="onCommentFormat" value="Checkstyle: resume field name check"/>
+    <property name="checkFormat" value="MemberName"/>
+    <property name="checkC" value="false"/>
+    <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable field name checks"/>
+  </module>
+  <module name="RegexpMultiline">
+    <metadata name="net.sf.eclipsecs.core.comment" value="illegal Windows line ending"/>
+    <property name="format" value="\r\n"/>
+    <property name="message" value="illegal Windows line ending"/>
+  </module>
+</module>
--- a/mx/.project	Sun Dec 18 13:55:25 2011 +0100
+++ b/mx/.project	Mon Dec 19 18:31:09 2011 +0100
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>mx.graalvm</name>
+	<name>mx</name>
 	<comment></comment>
 	<projects>
 	</projects>
--- a/mx/.pydevproject	Sun Dec 18 13:55:25 2011 +0100
+++ b/mx/.pydevproject	Mon Dec 19 18:31:09 2011 +0100
@@ -5,7 +5,7 @@
 <pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
 <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
 <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
-<path>/mx.graalvm</path>
+<path>/mx</path>
 </pydev_pathproperty>
 
 </pydev_project>
--- a/mx/commands.py	Sun Dec 18 13:55:25 2011 +0100
+++ b/mx/commands.py	Mon Dec 19 18:31:09 2011 +0100
@@ -27,35 +27,37 @@
 # ----------------------------------------------------------------------------------------------------
 
 import os, sys, shutil, tarfile, StringIO
-from os.path import join, exists, dirname, isfile, isdir
+from os.path import join, exists, dirname, isfile, isdir, isabs
+import mx
 
-graal_home = dirname(dirname(__file__))
+_graal_home = dirname(dirname(__file__))
+_vmbuild = 'product'
 
-def clean(env, args):
+def clean(args):
     """cleans the GraalVM source tree"""
     os.environ.update(ARCH_DATA_MODEL='64', LANG='C', HOTSPOT_BUILD_JOBS='16')
-    env.run([env.gmake_cmd(), 'clean'], cwd=join(graal_home, 'make'))
+    mx.run([mx.gmake_cmd(), 'clean'], cwd=join(_graal_home, 'make'))
 
-def example(env, args):
+def example(args):
     """run some or all Graal examples"""
     examples = {
         'safeadd': ['com.oracle.max.graal.examples.safeadd', 'com.oracle.max.graal.examples.safeadd.Main'],
         'vectorlib': ['com.oracle.max.graal.examples.vectorlib', 'com.oracle.max.graal.examples.vectorlib.Main'],
     }
 
-    def run_example(env, verbose, project, mainClass):
-        cp = env.pdb.classpath(project)
+    def run_example(verbose, project, mainClass):
+        cp = mx.classpath(project)
         sharedArgs = ['-Xcomp', '-XX:CompileOnly=Main', mainClass]
         
         res = []
-        env.log("=== Server VM ===")
+        mx.log("=== Server VM ===")
         printArg = '-XX:+PrintCompilation' if verbose else '-XX:-PrintCompilation'
-        res.append(vm(env, ['-cp', cp, printArg] + sharedArgs, vm="-server"))
-        env.log("=== Graal VM ===")
+        res.append(vm(['-cp', cp, printArg] + sharedArgs, vm="-server"))
+        mx.log("=== Graal VM ===")
         printArg = '-G:+PrintCompilation' if verbose else '-G:-PrintCompilation'
-        res.append(vm(env, ['-cp', cp, printArg, '-G:-Extend', '-G:-Inline'] + sharedArgs))
-        env.log("=== Graal VM with extensions ===")
-        res.append(vm(env, ['-cp', cp, printArg, '-G:+Extend', '-G:-Inline'] + sharedArgs))
+        res.append(vm(['-cp', cp, printArg, '-G:-Extend', '-G:-Inline'] + sharedArgs))
+        mx.log("=== Graal VM with extensions ===")
+        res.append(vm(['-cp', cp, printArg, '-G:+Extend', '-G:-Inline'] + sharedArgs))
         
         if len([x for x in res if x != 0]) != 0:
             return 1
@@ -71,13 +73,13 @@
     for a in args:
         config = examples.get(a)
         if config is None:
-            env.log('unknown example: ' + a + '  {available examples = ' + str(examples.keys()) + '}')
+            mx.log('unknown example: ' + a + '  {available examples = ' + str(examples.keys()) + '}')
         else:
-            env.log('--------- ' + a + ' ------------')
+            mx.log('--------- ' + a + ' ------------')
             project, mainClass = config
-            run_example(env, verbose, project, mainClass)
+            run_example(verbose, project, mainClass)
 
-def dacapo(env, args):
+def dacapo(args):
     """run one or all DaCapo benchmarks"""
     
     benchmarks = {
@@ -97,9 +99,9 @@
         'xalan': ['-n', '5'],
     }
     
-    dacapo = env.check_get_env('DACAPO_CP')
+    dacapo = mx.check_get_env('DACAPO_CP')
     if not isfile(dacapo) or not dacapo.endswith('.jar'):
-        env.abort('Specified DaCapo jar file does not exist or is not a jar file: ' + dacapo)
+        mx.abort('Specified DaCapo jar file does not exist or is not a jar file: ' + dacapo)
             
     vmOpts = ['-Xms1g', '-Xmx2g', '-esa', '-cp', dacapo]
 
@@ -109,7 +111,7 @@
         del args[0]
         config = benchmarks.get(bm) 
         if (config is None):
-            env.abort('unknown benchmark: ' + bm + '\nselect one of: ' + str(benchmarks.keys()))
+            mx.abort('unknown benchmark: ' + bm + '\nselect one of: ' + str(benchmarks.keys()))
         runs[bm] = config
     
     if len(runs) == 0:
@@ -118,16 +120,16 @@
     vmOpts += args
     for bm in runs:
         config = benchmarks.get(bm)
-        vm(env, vmOpts + ['Harness'] + config + [bm])
+        vm(vmOpts + ['Harness'] + config + [bm])
     
-def tests(env, args):
+def tests(args):
     """run a selection of the Maxine JTT tests in Graal"""
     
-    maxine = env.check_get_env('MAXINE_HOME')
+    maxine = mx.check_get_env('MAXINE_HOME')
     def jtt(name):
         return join(maxine, 'com.oracle.max.vm', 'test', 'jtt', name)
     
-    return vm(env, ['-ea', '-esa', '-Xcomp', '-XX:+PrintCompilation', '-XX:CompileOnly=jtt'] + args +
+    return vm(['-ea', '-esa', '-Xcomp', '-XX:+PrintCompilation', '-XX:CompileOnly=jtt'] + args +
                        ['-Xbootclasspath/p:' + join(maxine, 'com.oracle.max.vm', 'bin'), 
                         '-Xbootclasspath/p:' + join(maxine, 'com.oracle.max.base', 'bin'),
                         'test.com.sun.max.vm.compiler.JavaTester',
@@ -146,42 +148,42 @@
                         jtt('hotspot')])
 
 
-def _download_and_extract_targz_jdk7(env, url, dst):
+def _download_and_extract_targz_jdk7(url, dst):
     assert url.endswith('.tar.gz')
-    dl = join(graal_home, 'jdk7.tar.gz')
+    dl = join(_graal_home, 'jdk7.tar.gz')
     try:
         if not exists(dl):
-            env.download(dl, [url])
-        tmp = join(graal_home, 'tmp')
+            mx.download(dl, [url])
+        tmp = join(_graal_home, 'tmp')
         if not exists(tmp):
             os.mkdir(tmp)
         with tarfile.open(dl, mode='r:gz') as f:
-            env.log('Extracting ' + dl)
+            mx.log('Extracting ' + dl)
             f.extractall(path=tmp)
         jdk = os.listdir(tmp)[0]
         shutil.move(join(tmp, jdk), dst)
         os.rmdir(tmp)
         os.remove(dl)
     except SystemExit:
-        env.abort('Could not download JDK7 from http://www.oracle.com/technetwork/java/javase/downloads/index.html.\n' + 
+        mx.abort('Could not download JDK7 from http://www.oracle.com/technetwork/java/javase/downloads/index.html.\n' + 
                   'Please do this manually and install it at ' + dst + ' or set the JDK7 environment variable to the install location.')
     
 
-def _jdk7(env, build='product', create=False):
+def _jdk7(build='product', create=False):
     jdk7 = os.environ.get('JDK7')
     if jdk7 is None:
-        jdk7 = join(graal_home, 'jdk7')
+        jdk7 = join(_graal_home, 'jdk7')
         if not exists(jdk7):
             # Try to download it
-            if env.os == 'linux':
-                _download_and_extract_targz_jdk7(env, 'http://download.oracle.com/otn-pub/java/jdk/7u2-b13/jdk-7u2-linux-x64.tar.gz', jdk7)
+            if mx.get_os() == 'linux':
+                _download_and_extract_targz_jdk7('http://download.oracle.com/otn-pub/java/jdk/7u2-b13/jdk-7u2-linux-x64.tar.gz', jdk7)
             else:
-                env.abort('Download JDK7 from http://www.oracle.com/technetwork/java/javase/downloads/index.html\n' + 
+                mx.abort('Download JDK7 from http://www.oracle.com/technetwork/java/javase/downloads/index.html\n' + 
                           'and install it at ' + jdk7 + ' or set the JDK7 environment variable to the JDK7 install location.')
         
     jre = join(jdk7, 'jre')
     if not exists(jre) or not isdir(jre):
-        env.abort(jdk7 + ' does not appear to be a valid JDK directory ("jre" sub-directory is missing)')
+        mx.abort(jdk7 + ' does not appear to be a valid JDK directory ("jre" sub-directory is missing)')
     
     if build == 'product':
         return jdk7
@@ -189,25 +191,25 @@
         res = join(jdk7, build)
         if not exists(res):
             if not create:
-                env.abort('The ' + build + ' VM has not been created - run \'mx clean; mx make ' + build + '\'') 
-            env.log('[creating ' + res + '...]')
+                mx.abort('The ' + build + ' VM has not been created - run \'mx clean; mx make ' + build + '\'') 
+            mx.log('[creating ' + res + '...]')
             os.mkdir(res)
             for d in ['jre', 'lib', 'bin', 'include']:
                 shutil.copytree(join(jdk7, d), join(res, d))
         return res
     else:
-        env.abort('Unknown build type: ' + build)
+        mx.abort('Unknown build type: ' + build)
     
-def make(env, args):
+def make(args):
     """builds the GraalVM binary
     
     The optional argument specifies what type of VM to build."""
 
-    def fix_jvm_cfg(env, jdk):
+    def fix_jvm_cfg(jdk):
         jvmCfg = join(jdk, 'jre', 'lib', 'amd64', 'jvm.cfg')
         found = False
         if not exists(jvmCfg):
-            env.abort(jvmCfg + ' does not exist')
+            mx.abort(jvmCfg + ' does not exist')
             
         with open(jvmCfg) as f:
             for line in f:
@@ -215,20 +217,20 @@
                     found = True
                     break
         if not found:
-            env.log('Appending "-graal KNOWN" to ' + jvmCfg)
+            mx.log('Appending "-graal KNOWN" to ' + jvmCfg)
             with open(jvmCfg, 'a') as f:
                 f.write('-graal KNOWN\n')
 
     build = 'product' if len(args) == 0 else args[0]
-    jdk7 = _jdk7(env, build, True)
+    jdk7 = _jdk7(build, True)
     if build == 'debug':
         build = 'jvmg'
     
-    fix_jvm_cfg(env, jdk7)
+    fix_jvm_cfg(jdk7)
 
     graalVmDir = join(jdk7, 'jre', 'lib', 'amd64', 'graal')
     if not exists(graalVmDir):
-        env.log('Creating Graal directory in JDK7: ' + graalVmDir)
+        mx.log('Creating Graal directory in JDK7: ' + graalVmDir)
         os.makedirs(graalVmDir)
 
     def filterXusage(line):
@@ -236,34 +238,33 @@
             sys.stderr.write(line + os.linesep)
             
     os.environ.update(ARCH_DATA_MODEL='64', LANG='C', HOTSPOT_BUILD_JOBS='3', ALT_BOOTDIR=jdk7, INSTALL='y')
-    env.run([env.gmake_cmd(), build + 'graal'], cwd=join(graal_home, 'make'), err=filterXusage)
+    mx.run([mx.gmake_cmd(), build + 'graal'], cwd=join(_graal_home, 'make'), err=filterXusage)
     
-def vm(env, args, vm='-graal'):
+def vm(args, vm='-graal'):
     """run the GraalVM"""
   
-    build = env.vmbuild
-    if env.java_dbg:
+    build = mx.vmbuild
+    if mx.java_dbg:
         args = ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000'] + args
-    os.environ['GRAAL'] = join(env.check_get_env('GRAAL_HOME'), 'graal')
-    exe = join(_jdk7(env, build), 'bin', env.exe_suffix('java'))
-    return env.run([exe, vm] + args)
+    os.environ['GRAAL'] = join(mx.check_get_env('GRAAL_HOME'), 'graal')
+    exe = join(_jdk7(build), 'bin', mx.exe_suffix('java'))
+    return mx.run([exe, vm] + args)
 
-def eclipseprojects(env, args):
+def eclipseprojects(args):
     """(re)generate Eclipse project configurations
 
     The exit code of this command reflects how many files were updated."""
 
+
     def println(out, obj):
         out.write(str(obj) + '\n')
         
-    pdb = env.pdb
-    for p in pdb.projects.values():
+    for p in mx.projects():
         if p.native:
             continue
         
-        d = join(p.baseDir, p.name)
-        if not exists(d):
-            os.makedirs(d)
+        if not exists(p.dir):
+            os.makedirs(p.dir)
 
         changedFiles = 0
 
@@ -272,7 +273,7 @@
         println(out, '<?xml version="1.0" encoding="UTF-8"?>')
         println(out, '<classpath>')
         for src in p.srcDirs:
-            srcDir = join(d, src)
+            srcDir = join(p.dir, src)
             if not exists(srcDir):
                 os.mkdir(srcDir)
             println(out, '\t<classpathentry kind="src" path="' + src + '"/>')
@@ -280,7 +281,7 @@
         # Every Java program depends on the JRE
         println(out, '\t<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>')
         
-        for dep in p.all_deps([], pdb, True):
+        for dep in p.all_deps([], True):
             if dep == p:
                 continue;
             
@@ -292,7 +293,7 @@
                 else:
                     path = dep.path
                     if dep.mustExist:
-                        if os.path.isabs(path):
+                        if isabs(path):
                             println(out, '\t<classpathentry exported="true" kind="lib" path="' + path + '"/>')
                         else:
                             println(out, '\t<classpathentry exported="true" kind="lib" path="/' + path + '"/>')
@@ -302,44 +303,44 @@
         println(out, '\t<classpathentry kind="output" path="' + getattr(p, 'eclipse.output', 'bin') + '"/>')
         println(out, '</classpath>')
         
-        if env.update_file(join(p.baseDir, p.name, '.classpath'), out.getvalue()):
+        if mx.update_file(join(p.dir, '.classpath'), out.getvalue()):
             changedFiles += 1
             
         out.close()
 
-        csConfig = join(p.baseDir, p.checkstyleProj, '.checkstyle_checks.xml')
+        csConfig = join(mx.project(p.checkstyleProj).dir, '.checkstyle_checks.xml')
         if exists(csConfig):
             out = StringIO.StringIO()
             
-            dotCheckstyle = join(d, ".checkstyle")
+            dotCheckstyle = join(p.dir, ".checkstyle")
             checkstyleConfigPath = '/' + p.checkstyleProj + '/.checkstyle_checks.xml'
             println(out, '<?xml version="1.0" encoding="UTF-8"?>')
             println(out, '<fileset-config file-format-version="1.2.0" simple-config="true">')
-            println(out, '\t<local-check-config name="Maxine Checks" location="' + checkstyleConfigPath + '" type="project" description="">')
+            println(out, '\t<local-check-config name="Graal Checks" location="' + checkstyleConfigPath + '" type="project" description="">')
             println(out, '\t\t<additional-data name="protect-config-file" value="false"/>')
             println(out, '\t</local-check-config>')
-            println(out, '\t<fileset name="all" enabled="true" check-config-name="Maxine Checks" local="true">')
+            println(out, '\t<fileset name="all" enabled="true" check-config-name="Graal Checks" local="true">')
             println(out, '\t\t<file-match-pattern match-pattern="." include-pattern="true"/>')
             println(out, '\t</fileset>')
             println(out, '\t<filter name="FileTypesFilter" enabled="true">')
             println(out, '\t\t<filter-data value="java"/>')
             println(out, '\t</filter>')
 
-            exclude = join(d, '.checkstyle.exclude')
+            exclude = join(p.dir, '.checkstyle.exclude')
             if exists(exclude):
                 println(out, '\t<filter name="FilesFromPackage" enabled="true">')
                 with open(exclude) as f:
                     for line in f:
                         if not line.startswith('#'):
                             line = line.strip()
-                            exclDir = join(d, line)
+                            exclDir = join(p.dir, line)
                             assert isdir(exclDir), 'excluded source directory listed in ' + exclude + ' does not exist or is not a directory: ' + exclDir
                         println(out, '\t\t<filter-data value="' + line + '"/>')
                 println(out, '\t</filter>')
                         
             println(out, '</fileset-config>')
             
-            if env.update_file(dotCheckstyle, out.getvalue()):
+            if mx.update_file(dotCheckstyle, out.getvalue()):
                 changedFiles += 1
                 
             out.close()
@@ -373,14 +374,14 @@
         println(out, '\t</natures>')
         println(out, '</projectDescription>')
         
-        if env.update_file(join(d, '.project'), out.getvalue()):
+        if mx.update_file(join(p.dir, '.project'), out.getvalue()):
             changedFiles += 1
             
         out.close()
 
         out = StringIO.StringIO()
         
-        settingsDir = join(d, ".settings")
+        settingsDir = join(p.dir, ".settings")
         if not exists(settingsDir):
             os.mkdir(settingsDir)
 
@@ -388,23 +389,23 @@
         
         with open(join(myDir, 'org.eclipse.jdt.core.prefs')) as f:
             content = f.read()
-        if env.update_file(join(settingsDir, 'org.eclipse.jdt.core.prefs'), content):
+        if mx.update_file(join(settingsDir, 'org.eclipse.jdt.core.prefs'), content):
             changedFiles += 1
             
         with open(join(myDir, 'org.eclipse.jdt.ui.prefs')) as f:
             content = f.read()
-        if env.update_file(join(settingsDir, 'org.eclipse.jdt.ui.prefs'), content):
+        if mx.update_file(join(settingsDir, 'org.eclipse.jdt.ui.prefs'), content):
             changedFiles += 1
         
     if changedFiles != 0:
-        env.abort(changedFiles)
+        mx.abort(changedFiles)
 
-def mx_init(env):
-    env.vmbuild = 'product'
-    env.add_argument('--product', action='store_const', dest='vmbuild', const='product', help='select the product VM')
-    env.add_argument('--debug', action='store_const', dest='vmbuild', const='debug', help='select the debug VM')
-    env.add_argument('--fastdebug', action='store_const', dest='vmbuild', const='fastdebug', help='select the fast debug VM')
-    env.add_argument('--optimized', action='store_const', dest='vmbuild', const='optimized', help='select the optimized VM')
+def mx_init():
+    _vmbuild = 'product'
+    mx.add_argument('--product', action='store_const', dest='vmbuild', const='product', help='select the product VM')
+    mx.add_argument('--debug', action='store_const', dest='vmbuild', const='debug', help='select the debug VM')
+    mx.add_argument('--fastdebug', action='store_const', dest='vmbuild', const='fastdebug', help='select the fast debug VM')
+    mx.add_argument('--optimized', action='store_const', dest='vmbuild', const='optimized', help='select the optimized VM')
     commands = {
         'dacapo': [dacapo, '[benchmark] [VM options]'],
         'example': [example, '[-v] example names...'],
@@ -412,6 +413,11 @@
         'make': [make, '[product|debug|fastdebug|optimized]'],
         'tests': [tests, ''],
         'vm': [vm, '[-options] class [args...]'],
-	'eclipseprojects': [eclipseprojects, ''],
+	    'eclipseprojects': [eclipseprojects, ''],
     }
-    env.commands.update(commands)
+    mx.commands.update(commands)
+
+def mx_post_parse_cmd_line(opts):
+    global _vmbuild
+    if not opts.vmbuild is None:
+        _vmbuild = opts.vmbuild
--- a/mx/projects	Sun Dec 18 13:55:25 2011 +0100
+++ b/mx/projects	Mon Dec 19 18:31:09 2011 +0100
@@ -34,8 +34,8 @@
 library@JUNIT@urls=http://repo1.maven.org/maven2/junit/junit/4.8/junit-4.8.jar
 library@JUNIT@eclipse.container=org.eclipse.jdt.junit.JUNIT_CONTAINER/4
 
-library@CHECKSTYLE@path=lib/checkstyle-5.4-all.jar
-library@CHECKSTYLE@urls=jar:http://sourceforge.net/projects/checkstyle/files/checkstyle/5.4/checkstyle-5.4-bin.zip/download!/checkstyle-5.4/checkstyle-5.4-all.jar
+library@CHECKSTYLE@path=lib/checkstyle-5.5-all.jar
+library@CHECKSTYLE@urls=jar:http://sourceforge.net/projects/checkstyle/files/checkstyle/5.5/checkstyle-5.5-bin.zip/download!/checkstyle-5.5/checkstyle-5.5-all.jar
 
 # graal.hotspot
 project@com.oracle.max.graal.hotspot@subDir=graal
--- a/mxtool/mx.py	Sun Dec 18 13:55:25 2011 +0100
+++ b/mxtool/mx.py	Mon Dec 19 18:31:09 2011 +0100
@@ -102,11 +102,23 @@
 
 DEFAULT_JAVA_ARGS = '-ea -Xss2m -Xmx1g'
 
+_projects = dict()
+_libs = dict()
+_suites = dict()
+
+""" Options parsed from the command line. """
+_opts = None
+
+""" Prefix for a Java command. """
+_java = None
+
+"""
+A dependency is a library or project specified in a suite.
+"""
 class Dependency:
-    def __init__(self, name, baseDir):
+    def __init__(self, suite, name):
         self.name = name
-        self.baseDir = baseDir
-        self.env = None
+        self.suite = suite
         
     def __str__(self):
         return self.name
@@ -124,45 +136,48 @@
         return isinstance(self, Library)
     
 class Project(Dependency):
-    def __init__(self, baseDir, name, srcDirs, deps):
-        Dependency.__init__(self, name, baseDir)
+    def __init__(self, suite, name, srcDirs, deps, dir):
+        Dependency.__init__(self, suite, name)
         self.srcDirs = srcDirs
         self.deps = deps
         self.checkstyleProj = name
-        self.dir = join(baseDir, name)
         self.native = False
+        self.dir = dir
         
-    def all_deps(self, deps, pdb, includeLibs):
+    def all_deps(self, deps, includeLibs):
         if self in deps:
             return deps
         for name in self.deps:
             assert name != self.name
-            dep = pdb.libs.get(name, None)
+            dep = _libs.get(name, None)
             if dep is not None:
                 if includeLibs and not dep in deps:
                     deps.append(dep)
             else:
-                dep = pdb.project(name)
+                dep = project(name)
                 if not dep in deps:
-                    dep.all_deps(deps, pdb, includeLibs)
+                    dep.all_deps(deps, includeLibs)
         if not self in deps:
             deps.append(self)
         return deps
     
-    def _compute_max_dep_distances(self, name, distances, dist, pdb):
+    def _compute_max_dep_distances(self, name, distances, dist):
         currentDist = distances.get(name);
         if currentDist is None or currentDist < dist:
             distances[name] = dist
-            if pdb.projects.has_key(name):
-                p = pdb.project(name)
+            p = project(name, False)
+            if p is not None:
                 for dep in p.deps:
-                    self._compute_max_dep_distances(dep, distances, dist + 1, pdb)
+                    self._compute_max_dep_distances(dep, distances, dist + 1)
                 
-
-    def canonical_deps(self, env, pdb):
+    def canonical_deps(self):
+        """
+        Get the dependencies of this project that are not recursive (i.e. cannot be reached
+        via other dependencies).
+        """
         distances = dict()
         result = set()
-        self._compute_max_dep_distances(self.name, distances, 0, pdb)
+        self._compute_max_dep_distances(self.name, distances, 0)
         for n,d in distances.iteritems():
             assert d > 0 or n == self.name
             if d == 1:
@@ -175,50 +190,54 @@
     
 
     def source_dirs(self):
-        return [join(self.baseDir, self.name, s) for s in self.srcDirs]
+        """
+        Get the directories in whihc the sources of this project are found.
+        """
+        return [join(self.dir, s) for s in self.srcDirs]
         
     def output_dir(self):
-        return join(self.baseDir, self.name, 'bin')
+        """
+        Get the directory in which the class files of this project are found.
+        """
+        if self.native:
+            return None
+        return join(self.dir, 'bin')
 
-    def classpath(self, resolve, env):
-        classesDir = join(self.baseDir, 'classes')
-        if exists(classesDir):
-            return [self.output_dir(), classesDir]
-        return [self.output_dir()]
-    
-
+    def append_to_classpath(self, cp, resolve):
+        if not self.native:
+            cp.append(self.output_dir())
 
 class Library(Dependency):
-    def __init__(self, baseDir, name, path, mustExist, urls):
-        Dependency.__init__(self, name, baseDir)
+    def __init__(self, suite, name, path, mustExist, urls):
+        Dependency.__init__(self, suite, name)
         self.path = path
         self.urls = urls
         self.mustExist = mustExist
     
-    def classpath(self, resolve, env):
+    def get_path(self, resolve):
         path = self.path
         if not isabs(path):
-            path = join(self.baseDir, path)
+            path = join(self.suite.dir, path)
         if resolve and self.mustExist and not exists(path):
             assert not len(self.urls) == 0, 'cannot find required library  ' + self.name + " " + path;
-            env.download(path, self.urls)
-
+            download(path, self.urls)
+        return path
+        
+    def append_to_classpath(self, cp, resolve):
+        path = self.get_path(resolve)
         if exists(path) or not resolve:
-            return [path]
-        return []
-    
-class ProjectsDB():
+            cp.append(path)
     
-    def __init__(self, env):
-        self.env = env
-        self.projects = dict()
-        self.libs = dict()
-        self.commandModules = dict()
-        self.baseDirs = []
-        self.primary = ''
+class Suite:
+    def __init__(self, dir, primary):
+        self.dir = dir
+        self.projects = []
+        self.libs = []
+        self.includes = []
+        self.commands = None
+        self._load(join(dir, 'mx'), primary=primary)
 
-    def _load_projects(self, mxDir, baseDir):
-        env = self.env
+    def _load_projects(self, mxDir):
         libsMap = dict()
         projsMap = dict() 
         projectsFile = join(mxDir, 'projects')
@@ -231,21 +250,24 @@
                     key, value = line.split('=', 1)
                     
                     parts = key.split('@')
+                    
+                    if len(parts) == 2:
+                        pass
                     if len(parts) != 3:
-                        env.abort('Property name does not have 3 parts separated by "@": ' + key)
+                        abort('Property name does not have 3 parts separated by "@": ' + key)
                     kind, name, attr = parts
                     if kind == 'project':
                         m = projsMap
                     elif kind == 'library':
                         m = libsMap
                     else:
-                        env.abort('Property name does not start with "project@" or "library@": ' + key)
+                        abort('Property name does not start with "project@" or "library@": ' + key)
                         
                     attrs = m.get(name)
                     if attrs is None:
                         attrs = dict()
                         m[name] = attrs
-                    value = env.expandvars_in_property(value)
+                    value = expandvars_in_property(value)
                     attrs[attr] = value
                         
         def pop_list(attrs, name):
@@ -255,30 +277,28 @@
             return [n.strip() for n in v.split(',')]
         
         for name, attrs in projsMap.iteritems():
-            if self.projects.has_key(name):
-                env.abort('cannot override project  ' + name + ' in ' + self.project(name).baseDir + " with project of the same name in  " + mxDir)
             srcDirs = pop_list(attrs, 'sourceDirs')
             deps = pop_list(attrs, 'dependencies')
-            subDir = attrs.pop('subDir', '');
-            p = Project(join(baseDir, subDir), name, srcDirs, deps)
+            subDir = attrs.pop('subDir', None);
+            if subDir is None:
+                dir = join(self.dir, name)
+            else:
+                dir = join(self.dir, subDir, name)
+            p = Project(self, name, srcDirs, deps, dir)
             p.checkstyleProj = attrs.pop('checkstyle', name)
             p.native = attrs.pop('native', '') == 'true'
             p.__dict__.update(attrs)
-            self.projects[name] = p
+            self.projects.append(p)
 
         for name, attrs in libsMap.iteritems():
-            if self.libs.has_key(name):
-                env.abort('cannot redefine library ' + name)
-            
             path = attrs['path']
             mustExist = attrs.pop('optional', 'false') != 'true'
             urls = pop_list(attrs, 'urls')
-            l = Library(baseDir, name, path, mustExist, urls)
+            l = Library(self, name, path, mustExist, urls)
             l.__dict__.update(attrs)
-            self.libs[name] = l
+            self.libs.append(l)
         
-    def _load_commands(self, mxDir, baseDir):
-        env = self.env
+    def _load_commands(self, mxDir):
         commands = join(mxDir, 'commands.py')
         if exists(commands):
             # temporarily extend the Python path
@@ -290,115 +310,142 @@
             del sys.path[0]
 
             if not hasattr(mod, 'mx_init'):
-                env.abort(commands + ' must define an mx_init(env) function')
-                
-            mod.mx_init(env)
+                abort(commands + ' must define an mx_init(env) function')
                 
-            name = baseDir + '.commands'
-            sfx = 1
-            while sys.modules.has_key(name):
-                name = baseDir + str(sfx) + '.commands'
-                sfx += 1
-            
-            sys.modules[name] = sys.modules.pop('commands')
-            self.commandModules[name] = mod
+            mod.mx_init()
+            self.commands = mod
                 
-    def _load_includes(self, mxDir, baseDir):
+    def _load_includes(self, mxDir):
         includes = join(mxDir, 'includes')
         if exists(includes):
             with open(includes) as f:
                 for line in f:
-                    includeMxDir = join(self.env.expandvars_in_property(line.strip()), 'mx')
-                    self.load(includeMxDir)
+                    self.includes.append(expandvars_in_property(line.strip()))
         
-    def _load_env(self, mxDir, baseDir):
-        env = join(mxDir, 'env')
-        if exists(env):
-            with open(env) as f:
+    def _load_env(self, mxDir):
+        e = join(mxDir, 'env')
+        if exists(e):
+            with open(e) as f:
                 for line in f:
                     line = line.strip()
                     if len(line) != 0 and line[0] != '#':
                         key, value = line.split('=', 1)
-                        os.environ[key.strip()] = self.env.expandvars_in_property(value.strip())
+                        os.environ[key.strip()] = expandvars_in_property(value.strip())
         
-    def load(self, mxDir, primary=False):
-        """ loads the mx data from a given directory """
-        if not exists(mxDir) or not isdir(mxDir):
-            self.env.abort('Directory does not exist: ' + mxDir)
-        baseDir = dirname(mxDir)
+    def _load(self, mxDir, primary):
+        self._load_includes(mxDir)
+        self._load_projects(mxDir)
+        self._load_env(mxDir)
         if primary:
-            self.primary = baseDir
-        if not baseDir in self.baseDirs:
-            self.baseDirs.append(baseDir)
-            self._load_includes(mxDir, baseDir)
-            self._load_projects(mxDir, baseDir)
-            self._load_env(mxDir, baseDir)
-            if primary:
-                self._load_commands(mxDir, baseDir)
-
-    def project_names(self):
-        return ' '.join(self.projects.keys())
+            self._load_commands(mxDir)
         
-    def project(self, name, fatalIfMissing=True):
-        p = self.projects.get(name)
-        if p is None:
-            self.env.abort('project named ' + name + ' not found')
-        return p
-    
-    def library(self, name):
-        l = self.libs.get(name)
-        if l is None:
-            self.env.abort('library named ' + name + ' not found')
-        return l
+def get_os():
+    if sys.platform.startswith('darwin'):
+        return 'darwin'
+    elif sys.platform.startswith('linux'):
+        return 'linux'
+    elif sys.platform.startswith('sunos'):
+        return 'solaris'
+    elif sys.platform.startswith('win32') or sys.platform.startswith('cygwin'):
+        return 'windows'
+    else:
+        abort('Unknown operating system ' + sys.platform)
+
+def _loadSuite(dir, primary=False):
+    mxDir = join(dir, 'mx')
+    if not exists(mxDir) or not isdir(mxDir):
+        return
+    if not _suites.has_key(dir):
+        suite = Suite(dir, primary)
+        _suites[dir] = suite 
+        for p in suite.projects:
+            existing = _projects.get(p.name)
+            if existing is not None:
+                abort('cannot override project  ' + p.name + ' in ' + p.dir + " with project of the same name in  " + existing.dir)
+            _projects[p.name] = p
+        for l in suite.libs:
+            existing = _libs.get(l.name)
+            if existing is not None:
+                abort('cannot redefine library  ' + l.name)
+            _libs[l.name] = l
+
+def suites():
+    """
+    Get the list of all loaded suites.
+    """
+    return _suites.values()
 
-    def _as_classpath(self, deps, resolve):
-        cp = []
-        if self.env.cp_prefix is not None:
-            cp = [self.env.cp_prefix]
-        for d in deps:
-            cp += d.classpath(resolve, self.env)
-        if self.env.cp_suffix is not None:
-            cp += [self.env.cp_suffix]
-        return os.pathsep.join(cp)
+def projects():
+    """
+    Get the list of all loaded projects.
+    """
+    return _projects.values()
+    
+def project(name, fatalIfMissing=True):
+    """
+    Get the project for a given name. This will abort if the named project does
+    not exist and 'fatalIfMissing' is true.
+    """
+    p = _projects.get(name)
+    if p is None and fatalIfMissing:
+        abort('project named ' + name + ' not found')
+    return p
+
+def library(name, fatalIfMissing=True):
+    """
+    Gets the library for a given name. This will abort if the named library does
+    not exist and 'fatalIfMissing' is true.
+    """
+    l = _libs.get(name)
+    if l is None and fatalIfMissing:
+        abort('library named ' + name + ' not found')
+    return l
 
-    def classpath(self, names=None, resolve=True):
-        if names is None:
-            return self._as_classpath(self.sorted_deps(True), resolve)
-        deps = []
-        if isinstance(names, types.StringTypes):
-            self.project(names).all_deps(deps, self, True)
-        else:
-            for n in names:
-                self.project(n).all_deps(deps, self, True)
-        return self._as_classpath(deps, resolve)
-        
-    def sorted_deps(self, includeLibs=False):
-        deps = []
-        for p in self.projects.itervalues():
-            p.all_deps(deps, self, includeLibs)
-        return deps
+def _as_classpath(deps, resolve):
+    cp = []
+    if _opts.cp_prefix is not None:
+        cp = [_opts.cp_prefix]
+    for d in deps:
+        d.append_to_classpath(cp, resolve)
+    if _opts.cp_suffix is not None:
+        cp += [_opts.cp_suffix]
+    return os.pathsep.join(cp)
 
-class Env(ArgumentParser):
+def classpath(names=None, resolve=True):
+    """
+    Get the class path for a list of given projects, resolving each entry in the
+    path (e.g. downloading a missing library) if 'resolve' is true.
+    """
+    if names is None:
+        return _as_classpath(sorted_deps(True), resolve)
+    deps = []
+    if isinstance(names, types.StringTypes):
+        project(names).all_deps(deps, True)
+    else:
+        for n in names:
+            project(n).all_deps(deps, True)
+    return _as_classpath(deps, resolve)
+    
+def sorted_deps(includeLibs=False):
+    """
+    Gets the loaded projects and libraries sorted such that dependencies
+    are before the projects that depend on them. Unless 'includeLibs' is
+    true, libraries are omitted from the result.
+    """
+    deps = []
+    for p in _projects.itervalues():
+        p.all_deps(deps, includeLibs)
+    return deps
 
-    def format_commands(self):
-        msg = '\navailable commands:\n\n'
-        for cmd in sorted(self.commands.iterkeys()):
-            c, _ = self.commands[cmd][:2]
-            doc = c.__doc__
-            if doc is None:
-                doc = ''
-            msg += ' {0:<20} {1}\n'.format(cmd, doc.split('\n', 1)[0])
-        return msg + '\n'
-    
+class ArgParser(ArgumentParser):
+
     # Override parent to append the list of available commands
     def format_help(self):
-        return ArgumentParser.format_help(self) + self.format_commands()
+        return ArgumentParser.format_help(self) + _format_commands()
     
     
     def __init__(self):
         self.java_initialized = False
-        self.pdb = ProjectsDB(self)
-        self.commands = dict()
         ArgumentParser.__init__(self, prog='mx')
     
         self.add_argument('-v', action='store_true', dest='verbose', help='enable verbose output')
@@ -409,319 +456,317 @@
         self.add_argument('--Jp', action='append', dest='java_args_pfx', help='prefix Java VM arguments (e.g. --Jp @-dsa)', metavar='@<args>', default=[])
         self.add_argument('--Ja', action='append', dest='java_args_sfx', help='suffix Java VM arguments (e.g. --Ja @-dsa)', metavar='@<args>', default=[])
         self.add_argument('--user-home', help='users home directory', metavar='<path>', default=os.path.expanduser('~'))
-        self.add_argument('--java-home', help='JDK installation directory (must be JDK 6 or later)', metavar='<path>', default=self.default_java_home())
-        self.add_argument('--java', help='Java VM executable (default: bin/java under $JAVA_HOME)', metavar='<path>')
-        self.add_argument('--os', dest='os', help='operating system override')
+        self.add_argument('--java-home', help='JDK installation directory (must be JDK 6 or later)', metavar='<path>', default=_default_java_home())
         
     def _parse_cmd_line(self, args=None):
         if args is None:
             args = sys.argv[1:]
-        
+
         self.add_argument('commandAndArgs', nargs=REMAINDER, metavar='command args...')
         
-        self.parse_args(namespace=self)
+        opts = self.parse_args()
+        
+        if opts.java_home is None or opts.java_home == '':
+            abort('Could not find Java home. Use --java-home option or ensure JAVA_HOME environment variable is set.')
+
+        if opts.user_home is None or opts.user_home == '':
+            abort('Could not find user home. Use --user-home option or ensure HOME environment variable is set.')
+    
+        os.environ['JAVA_HOME'] = opts.java_home
+        os.environ['HOME'] = opts.user_home
+ 
+        opts.java = join(opts.java_home, 'bin', 'java')
+        opts.javac = join(opts.java_home, 'bin', 'javac')
+        opts.javap = join(opts.java_home, 'bin', 'javap')
+        
+        commandAndArgs = opts.__dict__.pop('commandAndArgs')
+        
+        for s in suites():
+            mod = s.commands
+            if hasattr(mod is not None and mod, 'mx_post_parse_cmd_line'):
+                mod.mx_post_parse_cmd_line(opts)
+                
+        return opts, commandAndArgs
+    
+def _format_commands():
+    msg = '\navailable commands:\n\n'
+    for cmd in sorted(commands.iterkeys()):
+        c, _ = commands[cmd][:2]
+        doc = c.__doc__
+        if doc is None:
+            doc = ''
+        msg += ' {0:<20} {1}\n'.format(cmd, doc.split('\n', 1)[0])
+    return msg + '\n'
+
+def java_home():
+    return _opts.java_home
+
+def java_exe():
+    return exe_suffix(_opts.java)
+
+def java_args():
+    init_java()
+    return _opts.java_args
 
-        if self.java_home is None or self.java_home == '':
-            self.abort('Could not find Java home. Use --java-home option or ensure JAVA_HOME environment variable is set.')
+def format_java_cmd(args):
+    init_java()
+    return _java + args
+    
+def run_java(args, nonZeroIsFatal=True, out=None, err=None, cwd=None):
+    return run(format_java_cmd(args), nonZeroIsFatal=nonZeroIsFatal, out=out, err=err, cwd=cwd)
 
-        if self.user_home is None or self.user_home == '':
-            self.abort('Could not find user home. Use --user-home option or ensure HOME environment variable is set.')
+def run(args, nonZeroIsFatal=True, out=None, err=None, cwd=None):
+    """
+    Run a command in a subprocess, wait for it to complete and return the exit status of the process.
+    If the exit status is non-zero and `nonZeroIsFatal` is true, then the program is exited with
+    the same exit status.
+    Each line of the standard output and error streams of the subprocess are redirected to the
+    provided out and err functions if they are not None.
+    """
+    
+    assert isinstance(args, types.ListType), "'args' must be a list: " + str(args)
+    for arg in args:
+        assert isinstance(arg, types.StringTypes), 'argument is not a string: ' + str(arg)
+    
+    if _opts.verbose:
+        log(' '.join(args))
+        
+    try:
+        if out is None and err is None:
+            retcode = subprocess.call(args, cwd=cwd)
+        else:
+            def redirect(stream, f):
+                for line in iter(stream.readline, ''):
+                    f(line)
+                stream.close()
+            p = subprocess.Popen(args, cwd=cwd, stdout=None if out is None else subprocess.PIPE, stderr=None if err is None else subprocess.PIPE)
+            if out is not None:
+                t = Thread(target=redirect, args=(p.stdout, out))
+                t.daemon = True # thread dies with the program
+                t.start()
+            if err is not None:
+                t = Thread(target=redirect, args=(p.stderr, err))
+                t.daemon = True # thread dies with the program
+                t.start()
+            retcode = p.wait()
+    except OSError as e:
+        log('Error executing \'' + ' '.join(args) + '\': ' + str(e))
+        if _opts.verbose:
+            raise e
+        abort(e.errno)
+    
+
+    if retcode and nonZeroIsFatal:
+        if _opts.verbose:
+            raise subprocess.CalledProcessError(retcode, ' '.join(args))
+        abort(retcode)
+        
+    return retcode
 
-        if self.os is None:
-            self.remote = False
-            if sys.platform.startswith('darwin'):
-                self.os = 'darwin'
-            elif sys.platform.startswith('linux'):
-                self.os = 'linux'
-            elif sys.platform.startswith('sunos'):
-                self.os = 'solaris'
-            elif sys.platform.startswith('win32') or sys.platform.startswith('cygwin'):
-                self.os = 'windows'
-            else:
-                print 'Supported operating system could not be derived from', sys.platform, '- use --os option explicitly.'
-                sys.exit(1)
+def exe_suffix(name):
+    """
+    Gets the platform specific suffix for an executable 
+    """
+    if os == 'windows':
+        return name + '.exe'
+    return name
+
+def init_java():
+    """
+    Lazy initialization and preprocessing of this object's fields before running a Java command.
+    """
+    global _java
+    if _java is not None:
+        return
+
+    def delAtAndSplit(s):
+        return shlex.split(s.lstrip('@'))
+
+    _opts.java_args = delAtAndSplit(_opts.java_args)
+    _opts.java_args_pfx = sum(map(delAtAndSplit, _opts.java_args_pfx), [])
+    _opts.java_args_sfx = sum(map(delAtAndSplit, _opts.java_args_sfx), [])
+    
+    # Prepend the -d64 VM option only if the java command supports it
+    output = ''
+    java = _opts.java
+    try:
+        
+        output = subprocess.check_output([java, '-d64', '-version'], stderr=subprocess.STDOUT)
+        _opts.java_args = ['-d64'] + _opts.java_args
+    except subprocess.CalledProcessError as e:
+        try:
+            output = subprocess.check_output([java, '-version'], stderr=subprocess.STDOUT)
+        except subprocess.CalledProcessError as e:
+            print e.output
+            abort(e.returncode)
+
+    output = output.split()
+    assert output[0] == 'java' or output[0] == 'openjdk'
+    assert output[1] == 'version'
+    version = output[2]
+    if not version.startswith('"1.6') and not version.startswith('"1.7'):
+        abort('Requires Java version 1.6 or 1.7, got version ' + version)
+
+    if _opts.java_dbg:
+        _opts.java_args += ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000']
+        
+    _java = [java] + _opts.java_args_pfx + _opts.java_args + _opts.java_args_sfx 
+    
+def _default_java_home():
+    javaHome = os.getenv('JAVA_HOME')
+    if javaHome is None:
+        if exists('/usr/lib/java/java-6-sun'):
+            javaHome = '/usr/lib/java/java-6-sun'
+        elif exists('/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home'):
+            javaHome = '/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home'
+        elif exists('/usr/jdk/latest'):
+            javaHome = '/usr/jdk/latest'
+    return javaHome
+
+def check_get_env(key):
+    """
+    Gets an environment variable, aborting with a useful message if it is not set.
+    """
+    value = os.environ.get(key)
+    if value is None:
+        abort('Required environment variable ' + key + ' must be set')
+    return value
+
+def log(msg=None):
+    """
+    Write a message to the console.
+    All script output goes through this method thus allowing a subclass
+    to redirect it. 
+    """
+    if msg is None:
+        print
+    else:
+        print msg
+
+def expand_project_in_class_path_arg(cpArg):
+    cp = []
+    for part in cpArg.split(os.pathsep):
+        if part.startswith('@'):
+            cp += classpath(part[1:]).split(os.pathsep)
         else:
-            self.java_args += ' -Dmax.os=' + self.os 
-            self.remote = True 
-    
-        if self.java is None:
-            self.java = join(self.java_home, 'bin', 'java')
+            cp.append(part)
+    return os.pathsep.join(cp)
     
-        os.environ['JAVA_HOME'] = self.java_home
-        os.environ['HOME'] = self.user_home
- 
-        self.javac = join(self.java_home, 'bin', 'javac')
-        
-        for mod in self.pdb.commandModules.itervalues():
-            if hasattr(mod, 'mx_post_parse_cmd_line'):
-                mod.mx_post_parse_cmd_line(self)
+def expand_project_in_args(args):
+    for i in range(len(args)):
+        if args[i] == '-cp' or args[i] == '-classpath':
+            if i + 1 < len(args):
+                args[i + 1] = expand_project_in_class_path_arg(args[i + 1])
+            return
+
 
-    def expandvars_in_property(self, value):
+def gmake_cmd():
+    for a in ['make', 'gmake', 'gnumake']:
+        try:
+            output = subprocess.check_output([a, '--version'])
+            if 'GNU' in output:
+                return a;
+        except:
+            pass
+    abort('Could not find a GNU make executable on the current path.')
+
+def expandvars_in_property(value):
         result = expandvars(value)
         if '$' in result or '%' in result:
-            self.abort('Property contains an undefined environment variable: ' + value)
+            abort('Property contains an undefined environment variable: ' + value)
         return result
-        
-
-    def load_config_file(self, configFile, override=False):
-        """ adds attributes to this object from a file containing key=value lines """
-        if exists(configFile):
-            with open(configFile) as f:
-                for line in f:
-                    k, v = line.split('=', 1)
-                    k = k.strip().lower()
-                    if (override or not hasattr(self, k)):
-                        setattr(self, k, self.expandvars_in_property(v.strip()))
-                        
-    def format_java_cmd(self, args):
-        self.init_java()
-        return [self.java] + self.java_args_pfx + self.java_args + self.java_args_sfx + args
-        
-    def run_java(self, args, nonZeroIsFatal=True, out=None, err=None, cwd=None):
-        return self.run(self.format_java_cmd(args), nonZeroIsFatal=nonZeroIsFatal, out=out, err=err, cwd=cwd)
-    
-    def run(self, args, nonZeroIsFatal=True, out=None, err=None, cwd=None):
-        """
-        Run a command in a subprocess, wait for it to complete and return the exit status of the process.
-        If the exit status is non-zero and `nonZeroIsFatal` is true, then the program is exited with
-        the same exit status.
-        Each line of the standard output and error streams of the subprocess are redirected to the
-        provided out and err functions if they are not None.
-        """
-        
-        assert isinstance(args, types.ListType), "'args' must be a list: " + str(args)
-        for arg in args:
-            assert isinstance(arg, types.StringTypes), 'argument is not a string: ' + str(arg)
-        
-        if self.verbose:
-            self.log(' '.join(args))
-            
-        try:
-            if out is None and err is None:
-                retcode = subprocess.call(args, cwd=cwd)
-            else:
-                def redirect(stream, f):
-                    for line in iter(stream.readline, ''):
-                        f(line)
-                    stream.close()
-                p = subprocess.Popen(args, cwd=cwd, stdout=None if out is None else subprocess.PIPE, stderr=None if err is None else subprocess.PIPE)
-                if out is not None:
-                    t = Thread(target=redirect, args=(p.stdout, out))
-                    t.daemon = True # thread dies with the program
-                    t.start()
-                if err is not None:
-                    t = Thread(target=redirect, args=(p.stderr, err))
-                    t.daemon = True # thread dies with the program
-                    t.start()
-                retcode = p.wait()
-        except OSError as e:
-            self.log('Error executing \'' + ' '.join(args) + '\': ' + str(e))
-            if self.verbose:
-                raise e
-            self.abort(e.errno)
-        
-
-        if retcode and nonZeroIsFatal:
-            if self.verbose:
-                raise subprocess.CalledProcessError(retcode, ' '.join(args))
-            self.abort(retcode)
-            
-        return retcode
-
-    def check_get_env(self, key):
-        """
-        Gets an environment variable, aborting with a useful message if it is not set.
-        """
-        value = os.environ.get(key)
-        if value is None:
-            self.abort('Required environment variable ' + key + ' must be set (e.g. in ' + join(self.pdb.primary, 'env') + ')')
-        return value
-
-    def exe_suffix(self, name):
-        """
-        Gets the platform specific suffix for an executable 
-        """
-        if self.os == 'windows':
-            return name + '.exe'
-        return name
-    
-    def log(self, msg=None):
-        """
-        Write a message to the console.
-        All script output goes through this method thus allowing a subclass
-        to redirect it. 
-        """
-        if msg is None:
-            print
-        else:
-            print msg
-
-    def expand_project_in_class_path_arg(self, cpArg):
-        cp = []
-        for part in cpArg.split(os.pathsep):
-            if part.startswith('@'):
-                cp += self.pdb.classpath(part[1:]).split(os.pathsep)
-            else:
-                cp.append(part)
-        return os.pathsep.join(cp)
-        
-    def expand_project_in_args(self, args):
-        for i in range(len(args)):
-            if args[i] == '-cp' or args[i] == '-classpath':
-                if i + 1 < len(args):
-                    args[i + 1] = self.expand_project_in_class_path_arg(args[i + 1])
-                return
-    
-
-    def init_java(self):
-        """
-        Lazy initialization and preprocessing of this object's fields before running a Java command.
-        """
-        if self.java_initialized:
-            return
-
-        def delAtAndSplit(s):
-            return shlex.split(s.lstrip('@'))
-
-        self.java_args = delAtAndSplit(self.java_args)
-        self.java_args_pfx = sum(map(delAtAndSplit, self.java_args_pfx), [])
-        self.java_args_sfx = sum(map(delAtAndSplit, self.java_args_sfx), [])
-        
-        # Prepend the -d64 VM option only if the java command supports it
-        output = ''
-        try:
-            output = subprocess.check_output([self.java, '-d64', '-version'], stderr=subprocess.STDOUT)
-            self.java_args = ['-d64'] + self.java_args
-        except subprocess.CalledProcessError as e:
-            try:
-                output = subprocess.check_output([self.java, '-version'], stderr=subprocess.STDOUT)
-            except subprocess.CalledProcessError as e:
-                print e.output
-                self.abort(e.returncode)
-
-        output = output.split()
-        assert output[0] == 'java' or output[0] == 'openjdk'
-        assert output[1] == 'version'
-        version = output[2]
-        if not version.startswith('"1.6') and not version.startswith('"1.7'):
-            self.abort('Requires Java version 1.6 or 1.7, got version ' + version)
-
-        if self.java_dbg:
-            self.java_args += ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000']
-            
-        self.java_initialized = True
-    
-    def default_java_home(self):
-        javaHome = os.getenv('JAVA_HOME')
-        if javaHome is None:
-            if exists('/usr/lib/java/java-6-sun'):
-                javaHome = '/usr/lib/java/java-6-sun'
-            elif exists('/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home'):
-                javaHome = '/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home'
-            elif exists('/usr/jdk/latest'):
-                javaHome = '/usr/jdk/latest'
-        return javaHome
-
-    def gmake_cmd(self):
-        for a in ['make', 'gmake', 'gnumake']:
-            try:
-                output = subprocess.check_output([a, '--version'])
-                if 'GNU' in output:
-                    return a;
-            except:
-                pass
-        self.abort('Could not find a GNU make executable on the current path.')
 
            
-    def abort(self, codeOrMessage):
-        """
-        Aborts the program with a SystemExit exception.
-        If 'codeOrMessage' is a plain integer, it specifies the system exit status;
-        if it is None, the exit status is zero; if it has another type (such as a string),
-        the object's value is printed and the exit status is one.
-        """
-        raise SystemExit(codeOrMessage)
+def abort(codeOrMessage):
+    """
+    Aborts the program with a SystemExit exception.
+    If 'codeOrMessage' is a plain integer, it specifies the system exit status;
+    if it is None, the exit status is zero; if it has another type (such as a string),
+    the object's value is printed and the exit status is one.
+    """
+    raise SystemExit(codeOrMessage)
 
-    def download(self, path, urls):
-        """
-        Attempts to downloads content for each URL in a list, stopping after the first successful download.
-        If the content cannot be retrieved from any URL, the program is aborted. The downloaded content
-        is written to the file indicated by 'path'.
-        """
-        d = dirname(path)
-        if d != '' and not exists(d):
-            os.makedirs(d)
-            
-        def url_open(url):
-            userAgent = 'Mozilla/5.0 (compatible)'
-            headers = { 'User-Agent' : userAgent }
-            req = urllib2.Request(url, headers=headers)
-            return urllib2.urlopen(req);
+def download(path, urls, verbose=False):
+    """
+    Attempts to downloads content for each URL in a list, stopping after the first successful download.
+    If the content cannot be retrieved from any URL, the program is aborted. The downloaded content
+    is written to the file indicated by 'path'.
+    """
+    d = dirname(path)
+    if d != '' and not exists(d):
+        os.makedirs(d)
+        
+    def url_open(url):
+        userAgent = 'Mozilla/5.0 (compatible)'
+        headers = { 'User-Agent' : userAgent }
+        req = urllib2.Request(url, headers=headers)
+        return urllib2.urlopen(req);
+        
+    for url in urls:
+        try:
+            if (verbose):
+                log('Downloading ' + url + ' to ' + path)
+            if url.startswith('zip:') or url.startswith('jar:'):
+                i = url.find('!/')
+                if i == -1:
+                    abort('Zip or jar URL does not contain "!/": ' + url)
+                url, _, entry = url[len('zip:'):].partition('!/')
+                with contextlib.closing(url_open(url)) as f:
+                    data = f.read()
+                    zipdata = StringIO.StringIO(f.read())
             
-        for url in urls:
-            try:
-                self.log('Downloading ' + url + ' to ' + path)
-                if url.startswith('zip:') or url.startswith('jar:'):
-                    i = url.find('!/')
-                    if i == -1:
-                        self.abort('Zip or jar URL does not contain "!/": ' + url)
-                    url, _, entry = url[len('zip:'):].partition('!/')
-                    with contextlib.closing(url_open(url)) as f:
-                        data = f.read()
-                        zipdata = StringIO.StringIO(f.read())
-                
-                    zf = zipfile.ZipFile(zipdata, 'r')
-                    data = zf.read(entry)
-                    with open(path, 'w') as f:
-                        f.write(data)
-                else:
-                    with contextlib.closing(url_open(url)) as f:
-                        data = f.read()
-                    with open(path, 'w') as f:
-                        f.write(data)
-                return
-            except IOError as e:
-                self.log('Error reading from ' + url + ': ' + str(e))
-            except zipfile.BadZipfile as e:
-                self.log('Error in zip file downloaded from ' + url + ': ' + str(e))
-                
-        # now try it with Java - urllib2 does not handle meta refreshes which are used by Sourceforge
-        myDir = dirname(__file__)
+                zf = zipfile.ZipFile(zipdata, 'r')
+                data = zf.read(entry)
+                with open(path, 'w') as f:
+                    f.write(data)
+            else:
+                with contextlib.closing(url_open(url)) as f:
+                    data = f.read()
+                with open(path, 'w') as f:
+                    f.write(data)
+            return
+        except IOError as e:
+            log('Error reading from ' + url + ': ' + str(e))
+        except zipfile.BadZipfile as e:
+            log('Error in zip file downloaded from ' + url + ': ' + str(e))
+            
+    # now try it with Java - urllib2 does not handle meta refreshes which are used by Sourceforge
+    myDir = dirname(__file__)
+    
+    javaSource = join(myDir, 'URLConnectionDownload.java')
+    javaClass = join(myDir, 'URLConnectionDownload.class')
+    if not exists(javaClass) or getmtime(javaClass) < getmtime(javaSource):
+        subprocess.check_call([_opts.javac, '-d', myDir, javaSource])
+    if run([_opts.java, '-cp', myDir, 'URLConnectionDownload', path] + urls) != 0:
+        abort('Could not download to ' + path + ' from any of the following URLs:\n\n    ' +
+                  '\n    '.join(urls) + '\n\nPlease use a web browser to do the download manually')
+
+def update_file(path, content):
+    """
+    Updates a file with some given content if the content differs from what's in
+    the file already. The return value indicates if the file was updated.
+    """
+    existed = exists(path)
+    try:
+        old = None
+        if existed:
+            with open(path, 'rb') as f:
+                old = f.read()
         
-        javaSource = join(myDir, 'URLConnectionDownload.java')
-        javaClass = join(myDir, 'URLConnectionDownload.class')
-        if not exists(javaClass) or getmtime(javaClass) < getmtime(javaSource):
-            subprocess.check_call([self.javac, '-d', myDir, javaSource])
-        if self.run([self.java, '-cp', myDir, 'URLConnectionDownload', path] + urls) != 0:
-            self.abort('Could not download to ' + path + ' from any of the following URLs:\n\n    ' +
-                      '\n    '.join(urls) + '\n\nPlease use a web browser to do the download manually')
-
-    def update_file(self, path, content):
-        """
-        Updates a file with some given content if the content differs from what's in
-        the file already. The return value indicates if the file was updated.
-        """
-        existed = exists(path)
-        try:
-            old = None
-            if existed:
-                with open(path, 'rb') as f:
-                    old = f.read()
+        if old == content:
+            return False
             
-            if old == content:
-                return False
-                
-            with open(path, 'wb') as f:
-                f.write(content)
-                
-            self.log(('modified ' if existed else 'created ') + path)
-            return True;
-        except IOError as e:
-            self.abort('Error while writing to ' + path + ': ' + str(e));
+        with open(path, 'wb') as f:
+            f.write(content)
+            
+        log(('modified ' if existed else 'created ') + path)
+        return True;
+    except IOError as e:
+        abort('Error while writing to ' + path + ': ' + str(e));
 
 # Builtin commands
             
-def build(env, args):
+def build(args):
     """compile the Java and C sources, linking the latter
 
     Compile all the Java source code using the appropriate compilers
@@ -745,40 +790,33 @@
             if len(choices) != 0:
                 jdtJar = join(plugins, sorted(choices, reverse=True)[0])
 
-    projects = [p.name for p in env.pdb.sorted_deps()]
     built = set()
-    for project in projects:
-        p = env.pdb.project(project)
-        projectDir = join(p.baseDir, project)
+    for p in sorted_deps():
         
         if p.native:
-            if env.os == 'windows':
-                env.log('Skipping C compilation on Windows until it is supported')
-                pass
-            
-            env.log('Compiling C sources in {0}...'.format(projectDir))
+            log('Compiling C sources in {0}...'.format(p.dir))
 
             if args.clean:
-                env.run([env.gmake_cmd(), 'clean'], cwd=projectDir)
+                run([gmake_cmd(), 'clean'], cwd=p.dir)
                 
-            env.run([env.gmake_cmd()], cwd=projectDir)
-            built.add(project)
+            run([gmake_cmd()], cwd=p.dir)
+            built.add(p.name)
             continue
         
         outputDir = p.output_dir()
         if exists(outputDir):
             if args.clean:
-                env.log('Cleaning {0}...'.format(outputDir))
+                log('Cleaning {0}...'.format(outputDir))
                 shutil.rmtree(outputDir)
                 os.mkdir(outputDir)
         else:
             os.mkdir(outputDir)
 
-        classpath = env.pdb.classpath(project)
-        sourceDirs = env.pdb.project(project).source_dirs()
+        cp = classpath(p.name)
+        sourceDirs = p.source_dirs()
         mustBuild = args.force
         if not mustBuild:
-            for dep in p.all_deps([], env.pdb, False):
+            for dep in p.all_deps([], False):
                 if dep.name in built:
                     mustBuild = True
             
@@ -797,23 +835,23 @@
                             break
                 
             if not mustBuild:
-                env.log('[all class files in {0} are up to date - skipping]'.format(sourceDir))
+                log('[all class files in {0} are up to date - skipping]'.format(sourceDir))
                 continue
                 
             if len(javafilelist) == 0:
-                env.log('[no Java sources in {0} - skipping]'.format(sourceDir))
+                log('[no Java sources in {0} - skipping]'.format(sourceDir))
                 continue
 
-            built.add(project)
+            built.add(p.name)
 
-            argfileName = join(projectDir, 'javafilelist.txt')
+            argfileName = join(p.dir, 'javafilelist.txt')
             argfile = open(argfileName, 'w')
             argfile.write('\n'.join(javafilelist))
             argfile.close()
             
             try:
                 if jdtJar is None:
-                    env.log('Compiling Java sources in {0} with javac...'.format(sourceDir))
+                    log('Compiling Java sources in {0} with javac...'.format(sourceDir))
                     
                     class Filter:
                         """
@@ -832,13 +870,13 @@
                             else:
                                 print line.rstrip()
                         
-                    env.run([env.javac, '-g', '-J-Xmx1g', '-classpath', classpath, '-d', outputDir, '@' + argfile.name], err=Filter().eat)
+                    run([_opts.javac, '-g', '-J-Xmx1g', '-classpath', cp, '-d', outputDir, '@' + argfile.name], err=Filter().eat)
                 else:
-                    env.log('Compiling Java sources in {0} with JDT...'.format(sourceDir))
-                    jdtProperties = join(projectDir, '.settings', 'org.eclipse.jdt.core.prefs')
+                    log('Compiling Java sources in {0} with JDT...'.format(sourceDir))
+                    jdtProperties = join(p.dir, '.settings', 'org.eclipse.jdt.core.prefs')
                     if not exists(jdtProperties):
                         raise SystemError('JDT properties file {0} not found'.format(jdtProperties))
-                    env.run([env.java, '-Xmx1g', '-jar', jdtJar, '-1.6', '-cp', classpath, '-g',
+                    run([_opts.java, '-Xmx1g', '-jar', jdtJar, '-1.6', '-cp', cp, '-g',
                              '-properties', jdtProperties, 
                              '-warn:-unusedImport,-unchecked',
                              '-d', outputDir, '@' + argfile.name])
@@ -851,15 +889,14 @@
                 if exists(dirname(dst)):
                     shutil.copyfile(name, dst)
 
-def canonicalizeprojects(env, args):
+def canonicalizeprojects(args):
     """process all project files to canonicalize the dependencies
 
     The exit code of this command reflects how many files were updated."""
     
     changedFiles = 0
-    pdb = env.pdb
-    for d in pdb.baseDirs:
-        projectsFile = join(d, 'mx', 'projects')
+    for s in suites():
+        projectsFile = join(s.dir, 'mx', 'projects')
         if not exists(projectsFile):
             continue
         with open(projectsFile) as f:
@@ -871,14 +908,14 @@
                 if m is None:
                     out.write(line + '\n')
                 else:
-                    p = pdb.project(m.group(1))
-                    out.write('project@' + m.group(1) + '@dependencies=' + ','.join(p.canonical_deps(env, pdb)) + '\n')
+                    p = project(m.group(1))
+                    out.write('project@' + m.group(1) + '@dependencies=' + ','.join(p.canonical_deps()) + '\n')
             content = out.getvalue()
-        if env.update_file(projectsFile, content):
+        if update_file(projectsFile, content):
             changedFiles += 1
     return changedFiles;
     
-def checkstyle(env, args):
+def checkstyle(args):
     """run Checkstyle on the Java sources
 
    Run Checkstyle over the Java sources. Any errors or warnings
@@ -886,20 +923,11 @@
 
 If no projects are given, then all Java projects are checked."""
     
-    allProjects = [p.name for p in env.pdb.sorted_deps()]
-    if len(args) == 0:
-        projects = allProjects
-    else:
-        projects = args
-        unknown = set(projects).difference(allProjects)
-        if len(unknown) != 0:
-            env.error('unknown projects: ' + ', '.join(unknown))
-        
-    for project in projects:
-        p = env.pdb.project(project)
-        projectDir = join(p.baseDir, project)
-        sourceDirs = env.pdb.project(project).source_dirs()
-        dotCheckstyle = join(projectDir, '.checkstyle')
+    for p in sorted_deps():
+        if p.native:
+            continue
+        sourceDirs = p.source_dirs()
+        dotCheckstyle = join(p.dir, '.checkstyle')
         
         if not exists(dotCheckstyle):
             continue
@@ -909,10 +937,10 @@
             for root, _, files in os.walk(sourceDir):
                 javafilelist += [join(root, name) for name in files if name.endswith('.java') and name != 'package-info.java']
             if len(javafilelist) == 0:
-                env.log('[no Java sources in {0} - skipping]'.format(sourceDir))
+                log('[no Java sources in {0} - skipping]'.format(sourceDir))
                 continue
 
-            timestampFile = join(p.baseDir, 'mx', '.checkstyle' + sourceDir[len(p.baseDir):].replace(os.sep, '_') + '.timestamp')
+            timestampFile = join(p.suite.dir, 'mx', '.checkstyle' + sourceDir[len(p.suite.dir):].replace(os.sep, '_') + '.timestamp')
             mustCheck = False
             if exists(timestampFile):
                 timestamp = os.path.getmtime(timestampFile)
@@ -924,7 +952,7 @@
                 mustCheck = True
             
             if not mustCheck:
-                env.log('[all Java sources in {0} already checked - skipping]'.format(sourceDir))
+                log('[all Java sources in {0} already checked - skipping]'.format(sourceDir))
                 continue
 
             if exists(timestampFile):                
@@ -935,15 +963,22 @@
             dotCheckstyleXML = xml.dom.minidom.parse(dotCheckstyle)
             localCheckConfig = dotCheckstyleXML.getElementsByTagName('local-check-config')[0]
             configLocation = localCheckConfig.getAttribute('location')
-            if configLocation.startswith('/'):
-                config = join(p.baseDir, configLocation.lstrip('/'))
+            configType = localCheckConfig.getAttribute('type')
+            if configType == 'project':
+                # Eclipse plugin "Project Relative Configuration" format:
+                #
+                #  '/<project_name>/<suffix>'
+                #
+                if configLocation.startswith('/'):
+                    name, _, suffix = configLocation.lstrip('/').partition('/')
+                    config = join(project(name).dir, suffix)
+                else:
+                    config = join(p.dir, configLocation)
             else:
-                config = join(projectDir, configLocation)
+                log('[unknown Checkstyle configuration type "' + configType + '" in {0} - skipping]'.format(sourceDir))
+                continue
                 
-            exclude = join(projectDir, '.checkstyle.exclude')
-                
-                
-            
+            exclude = join(p.dir, '.checkstyle.exclude')
             
             if exists(exclude):
                 with open(exclude) as f:
@@ -952,14 +987,14 @@
                 def match(name):
                     for p in patterns:
                         if p in name:
-                            env.log('excluding: ' + name)
+                            log('excluding: ' + name)
                             return True
                     return False
                     
                 javafilelist = [name for name in javafilelist if not match(name)]
             
-            auditfileName = join(projectDir, 'checkstyleOutput.txt')
-            env.log('Running Checkstyle on {0} using {1}...'.format(sourceDir, config))
+            auditfileName = join(p.dir, 'checkstyleOutput.txt')
+            log('Running Checkstyle on {0} using {1}...'.format(sourceDir, config))
             
             try:
 
@@ -981,52 +1016,50 @@
                     batch = javafilelist[:i]
                     javafilelist = javafilelist[i:]
                     try:
-                        env.run_java(['-Xmx1g', '-jar', env.pdb.library('CHECKSTYLE').classpath(True, env)[0], '-c', config, '-o', auditfileName] + batch)
+                        run_java(['-Xmx1g', '-jar', library('CHECKSTYLE').get_path(True), '-c', config, '-o', auditfileName] + batch)
                     finally:
                         if exists(auditfileName):
                             with open(auditfileName) as f:
                                 warnings = [line.strip() for line in f if 'warning:' in line]
                                 if len(warnings) != 0:
-                                    map(env.log, warnings)
+                                    map(log, warnings)
                                     return 1
             finally:
                 if exists(auditfileName):
                     os.unlink(auditfileName)
     return 0
 
-def clean(env, args):
+def clean(args):
     """remove all class files, images, and executables
 
     Removes all files created by a build, including Java class files, executables, and
     generated images.
     """
     
-    projects = env.pdb.projects.keys()
-    for project in projects:
-        p = env.pdb.project(project)
+    for p in projects():
         if p.native:
-            env.run([env.gmake_cmd(), '-C', p.dir, 'clean'])
+            run([gmake_cmd(), '-C', p.dir, 'clean'])
         else:
             outputDir = p.output_dir()
             if outputDir != '' and exists(outputDir):
-                env.log('Removing {0}...'.format(outputDir))
+                log('Removing {0}...'.format(outputDir))
                 shutil.rmtree(outputDir)
     
-def help_(env, args):
+def help_(args):
     """show help for a given command
 
 With no arguments, print a list of commands and short help for each command.
 
 Given a command name, print help for that command."""
     if len(args) == 0:
-        env.print_help()
+        _argParser.print_help()
         return
     
     name = args[0]
-    if not env.commands.has_key(name):
-        env.error('unknown command: ' + name)
+    if not commands.has_key(name):
+        _argParser.error('unknown command: ' + name)
     
-    value = env.commands[name]
+    value = commands[name]
     (func, usage) = value[:2]
     doc = func.__doc__
     if len(value) > 2:
@@ -1034,7 +1067,7 @@
         fmtArgs = []
         for d in docArgs:
             if isinstance(d, Callable):
-                fmtArgs += [d(env)]
+                fmtArgs += [d()]
             else:
                 fmtArgs += [str(d)]
         doc = doc.format(*fmtArgs)
@@ -1043,79 +1076,89 @@
 
 # Commands are in alphabetical order in this file.
 
-def javap(env, args):
+def javap(args):
     """launch javap with a -classpath option denoting all available classes
 
     Run the JDK javap class file disassembler with the following prepended options:
 
         -private -verbose -classpath <path to project classes>"""
         
-    javap = join(env.java_home, 'bin', 'javap')
+    javap = _opts.javap
     if not exists(javap):
-        env.abort('The javap executable does not exists: ' + javap)
+        abort('The javap executable does not exists: ' + javap)
     else:
-        env.run([javap, '-private', '-verbose', '-classpath', env.pdb.classpath()] + args)
+        run([javap, '-private', '-verbose', '-classpath', classpath()] + args)
 
-def projects(env, args):
+def show_projects(args):
     """show all loaded projects"""
-    pdb = env.pdb
-    for d in pdb.baseDirs:
-        projectsFile = join(d, 'mx', 'projects')
+    for s in suites():
+        projectsFile = join(s.dir, 'mx', 'projects')
         if exists(projectsFile):
-            env.log('# file:  ' + projectsFile)
-            for p in pdb.projects.values():
-                if p.baseDir == d:
-                    env.log(p.name)
-
+            log(projectsFile)
+            for p in s.projects:
+                log('\t' + p.name)
 
-def main(env):    
+def add_argument(*args, **kwargs):
+    """
+    Define how a single command-line argument.
+    """
+    assert _argParser is not None
+    _argParser.add_argument(*args, **kwargs)
+    
+# Table of commands in alphabetical order.
+# Keys are command names, value are lists: [<function>, <usage msg>, <format args to doc string of function>...]
+# If any of the format args are instances of Callable, then they are called with an 'env' are before being
+# used in the call to str.format().  
+# Extensions should update this table directly
+commands = {
+    'build': [build, '[options] projects...'],
+    'checkstyle': [checkstyle, 'projects...'],
+    'canonicalizeprojects': [canonicalizeprojects, ''],
+    'clean': [clean, ''],
+    'help': [help_, '[command]'],
+    'javap': [javap, ''],
+    'projects': [show_projects, ''],
+}
 
-    # Table of commands in alphabetical order.
-    # Keys are command names, value are lists: [<function>, <usage msg>, <format args to doc string of function>...]
-    # If any of the format args are instances of Callable, then they are called with an 'env' are before being
-    # used in the call to str.format().  
-    # Extensions should update this table directly
-    env.commands = {
-        'build': [build, '[options] projects...'],
-        'checkstyle': [checkstyle, 'projects...'],
-        'canonicalizeprojects': [canonicalizeprojects, ''],
-        'clean': [clean, ''],
-        'help': [help_, '[command]'],
-        'javap': [javap, ''],
-        'projects': [projects, ''],
-    }
-    
+_argParser = ArgParser()
+
+def main():    
     MX_INCLUDES = os.environ.get('MX_INCLUDES', None)
     if MX_INCLUDES is not None:
         for path in MX_INCLUDES.split(os.pathsep):
             d = join(path, 'mx')
             if exists(d) and isdir(d):
-                env.pdb.load(d)
+                _loadSuite(path)
                 
     cwdMxDir = join(os.getcwd(), 'mx')
     if exists(cwdMxDir) and isdir(cwdMxDir):
-        env.pdb.load(cwdMxDir, primary=True)
+        _loadSuite(os.getcwd(), True)
             
-    env._parse_cmd_line()
+    opts, commandAndArgs = _argParser._parse_cmd_line()
+    global _opts
+    _opts = opts
     
-    if len(env.commandAndArgs) == 0:
-        env.print_help()
+    if len(commandAndArgs) == 0:
+        _argParser.print_help()
         return
     
-    env.command = env.commandAndArgs[0]
-    env.command_args = env.commandAndArgs[1:]
+    command = commandAndArgs[0]
+    command_args = commandAndArgs[1:]
     
-    if not env.commands.has_key(env.command):
-        env.abort('mx: unknown command \'{0}\'\n{1}use "mx help" for more options'.format(env.command, env.format_commands()))
+    if not commands.has_key(command):
+        abort('mx: unknown command \'{0}\'\n{1}use "mx help" for more options'.format(command, _format_commands()))
         
-    c, _ = env.commands[env.command][:2]
+    c, _ = commands[command][:2]
     try:
-        retcode = c(env, env.command_args)
+        retcode = c(command_args)
         if retcode is not None and retcode != 0:
-            env.abort(retcode)
+            abort(retcode)
     except KeyboardInterrupt:
         # no need to show the stack trace when the user presses CTRL-C
-        env.abort(1)
+        abort(1)
 
 if __name__ == '__main__':
-    main(Env())
+    # rename this module as 'mx' so it is not imported twice by the commands.py modules
+    sys.modules['mx'] = sys.modules.pop('__main__')
+    
+    main()