DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Cliff has posted 1 posts at DZone. View Full User Profile

A Pure-Java Alternative To 'make' Or 'ANT'

04.15.2009
| 3740 views |
  • submit to reddit
        
import java.io.*;
import java.util.*;
import java.util.concurrent.*;

/**
 * A simple project build routine.

 * I hate makefiles, and ANT uses the horrid XML so I'm doing this hack
 * instead.  Actually, I've used this technique before to great success.  I
 * ought to make a Real Project out of it someday.
 *
 * What you see is the 'guts' of make/ANT PLUS the project file dependencies
 * at the end - all in pure Java.  Thus I can add new features to the make
 * (parallel distributed make, shared caching, etc) or add new dependencies.
 * Especially for the dependencies, there is no obtuse format to describe
 * things - it's all pure Java.
 *
 * TO DO LIST:
 * 
 * Parallize Yea Olde Above Priority Queue across the Build Farm
 * FindBugz nitpicky
 *
 * Priorities -

 * 1) Things that failed for syntax errors last time; under the assumption
 *    you're working in a tight edit/compile cycle anything that failed in the
 *    last go-round should be compiled first this go-round - because you
 *    probably don't have all the syntax errors out yet.  This is very useful
 *    when hacking top-level includes like thread.hpp, because each touch of
 *    thread.hpp will force the World to recompile but likely there's exactly
 *    .cpp file you care about (thread.cpp?) and it's usually very late in the
 *    build.  One easy way to do this is to notice that failed build steps
 *    always nuke their target, so a missing target is most likely came from a
 *    syntax error on the last edit/compile cycle.
 *
 * 2) Jobs that might make more work - such as the ADLC or JVMTI or reading
 *    the foo.P files; each of these steps might possibly make more targets to
 *    build.  Thus the priority queue has to be dynamic; some targets appear
 *    only after some build-steps have happened.
 *
 * 3) Slow jobs; waiting to the end of a 200-file compile to START compiling
 *    'type.cpp' just lengthens the build cycle.  type.cpp probably takes more
 *    than 60sec to compile whereas most jobs compile less than 5sec.  We want
 *    to overlap the compile of type.cpp with many other compiles.
 *
 * @since 1.6
 * @author Cliff Click
 */
class build { 
  static int _verbose;     // Be noisy; 0 (quiet), 1 (noisy) or 2 (very noisy)
  static boolean _justprint;    // Print, do not do any building
  static boolean _clean;        // Remove target instead of building it
  static boolean _keepontrucking; // Do not stop at errors
  // Top-level project directory
  static File TOP;              // File for the project directory
  static String TOP_PATH;       // Path to top-level
  static String TOP_PATH_SLASH;
  // Where the results go
  static String SANDBOX;
  // Pick up JAVA_HOME from the java property "java.home"
  static String JAVA_HOME;

  // --- main ----------------------------------------------------------------
  static public void main( final String args[] ) throws IOException, InterruptedException {
    // --- First up: find build.java!
    // Where we exec'd java.exe from
    TOP = new File(".");
    TOP_PATH = TOP.getCanonicalPath();
    File f = new File(TOP,"build/build.java");
    while( !f.exists() ) {
      File p2 = new File(TOP,"..");
      if( p2.getCanonicalPath().equals(TOP_PATH) )
        throw new Error("build/build.java not found; build/build.java marks top of project hierarchy");
      TOP = p2;
      TOP_PATH = TOP.getCanonicalPath();
      f = new File(TOP,"build/build.java");
    }

    TOP_PATH_SLASH = TOP_PATH.replaceAll("\\\\","\\\\\\\\");

    // --- Track down default sandbox
    File sand = TOP;
    f = new File(sand,"sandbox");
    while( !f.exists() ) {
      File p2 = new File(sand,"..");
      String spath = sand.getCanonicalPath();
      if( p2.getCanonicalPath().equals(spath) )
        break;                  // No sandbox found
      sand = p2;
      f = new File(sand,"sandbox");
    }
    if( f.exists() ) SANDBOX = f.getCanonicalPath();

    JAVA_HOME = System.getProperty("java.home");
    if (JAVA_HOME.endsWith("/jre")) {
      JAVA_HOME = JAVA_HOME.substring(0, JAVA_HOME.lastIndexOf('/'));
    }

    // --- Strip out any flags; sanity check all targets before doing any of them
    int j = 0;
    boolean error = false;
    String coreOrJIT = "jit";
    String targetPlatform = null;
    String buildvariant = null;
    String g_suffix = "";
    for( int i=0; i<args.length; i++ ) {
      final String arg = args[i];

      if( arg.charAt(0) == '-' ) {
        if( false ) ;
        else if( arg.equals("-v"    ) ) _verbose = 1;
        else if( arg.equals("-vv"   ) ) _verbose = 2;
        else if( arg.equals("-n"    ) ) _justprint = true;
        else if( arg.equals("-k"    ) ) _keepontrucking = true;
        else if( arg.equals("-clean") ) _clean = true;
        else if( arg.equals("-s"    ) ) SANDBOX = args[++i];
        else if( arg.equals("-t"    ) ) targetPlatform = args[++i];
        else if( arg.equals("-core" ) ) coreOrJIT = "core";
        else if( arg.equals("-jit"  ) ) coreOrJIT = "jit";
        else {
          error = true;
          System.out.println("Unknown flag "+arg);
        }
      } else if( arg.equals("debug") || arg.equals("fastdebug") ) {
        buildvariant = arg;
        g_suffix = "_g";
      } else if( arg.equals("optimized") || arg.equals("product") ) {
        buildvariant = arg;
      } else {
        args[j++] = arg;        // Compact out flags from target list
      }
    }

    if (targetPlatform != null) {
      if (targetPlatform.equals("aztek-vega2")) {
        args[j++] = "sandbox/obj/hotspot6/aztek_vega2_" + coreOrJIT + "1.6/" + buildvariant + "/libjvm" + g_suffix + ".a";
      } else if (targetPlatform.equals("acapulco-nxu")) {
        args[j++] = "sandbox/obj/hotspot6/acapulco_nxu_" + coreOrJIT + "1.6/" + buildvariant + "/libjvm" + g_suffix + ".a";
      } else {
        throw new Error("Unknown target platform specified with -t");
      }

      if (_verbose > 0) {
        System.out.println("Target is " + args[j-1]);
      }
    }

    try {
      if( error ) {
        System.out.println("");
        System.out.println("Usage: build [-v|-vv] [-n] [-k] [-clean] [-s SANDBOX] [-t PLATFORM] [-core|-jit] [debug|fastdebug|optimized|product]");
        System.out.println("\t -v          : verbose output, overrides -vv option");
        System.out.println("\t -vv         : super verbose output, overrides -v option");
        System.out.println("\t -n          : do nothing, just print");
        System.out.println("\t -k          : don't stop on errors");
        System.out.println("\t -clean      : delete build output");
        System.out.println("\t -s SANDBOX  : set SANDBOX directory");
        System.out.println("\t -t PLATFORM : set target platform");
        System.out.println("\t -core       : build the \"core\" target, overrides -jit option");
        System.out.println("\t -jit        : build the \"jit\" target, overrides -core option");
        System.out.println("");
        throw new Error("Command line errors");
      }
      if( _verbose > 0 ) 
        System.out.println("Building in "+TOP.getCanonicalPath());
      if( SANDBOX == null )
        throw new Error("No sandbox");
      if( !SANDBOX.endsWith("/sandbox") )
        throw new Error("Sandbox does not end with /sandbox");
      if( !new File(SANDBOX).exists() )
        throw new Error("Sandbox does not exist: "+SANDBOX);

      // Make SANDBOX a relative path, if it's shorter.  Much easier to read the output.
      String t=TOP_PATH;
      String up="";
      while( !SANDBOX.startsWith(t) ) {
        up = up+"../";
        t = t.substring(0,t.lastIndexOf('/'));
      }
      SANDBOX = up+SANDBOX.substring(t.length()+1);

      if( _verbose > 0 ) 
        System.out.println("Sandbox is "+SANDBOX);

      // Command-line targets starting with "sandbox/" are essentially
      // shortcuts for "$SANDBOX/", but for our chosen SANDBOX not some OS
      // environment variable.
      for( int i=0; i<j; i++ )
        if( args[i].startsWith("sandbox/") )
          args[i] = SANDBOX+args[i].substring(7);

      // --- Put all named targets into a bogus top-level target
      if( j == 0 ) throw new Error("No default target and no targets specified; did you mean to build nothing?");
    } finally {
      // --- All Done!
      System.out.flush();
      System.err.flush();
    }

    build0.main0(Arrays.copyOf(args,j));
  }
}

// --- build0 -------------------------------------------------------------------
class build0 { 
  static private class BuildError extends Error { BuildError( String s ) { super(s); } }

  final static int _verbose = build._verbose; // Be noisy; 0 (quiet), 1 (noisy) or 2 (very noisy)
  final static boolean _justprint = build._justprint; // Print, do not do any building
  final static boolean _clean = build._clean; // Remove target instead of building it
  final static boolean _keepontrucking = build._keepontrucking; // Do not stop at errors
  final static boolean _allow_timestamp_ties = true;
  // Top-level project directory
  final static File TOP = build.TOP; // File for the project directory
  final static String TOP_PATH = build.TOP_PATH; // Path to top-level
  final static String TOP_PATH_SLASH = build.TOP_PATH_SLASH; // Canonical path to top level
  // Where the results go
  final static String SANDBOX = build.SANDBOX;

  // Some common strings
  static final String JAVA_HOME = build.JAVA_HOME;
  static final String JAVAC     = JAVA_HOME + "/bin/javac";
  static final String JAVA      = JAVA_HOME + "/bin/java";

  // A work-queue for the Main thread.  Tasks on this list need to have their
  // dependencies inspected.  This is normally once per task, but foo.P files
  // run an "extra_check" which expands out a list of source .h file
  // dependencies.
  static final LinkedBlockingQueue<Q> _FindDeps = new LinkedBlockingQueue<Q>(); 
  static final Q ALL_DONE_SENTINEL = new Q("no such target");

  // The work queue
  static final Runtime RUN = Runtime.getRuntime();
  static final int numCPUs = RUN.availableProcessors();
  static final int numWorkers = (numCPUs<3) ? (numCPUs+1) : ((numCPUs<8)?(numCPUs+2):(numCPUs+(numCPUs>>2)));

  static final ThreadPoolExecutor TPE = 
    new ThreadPoolExecutor( numCPUs/*starting thread count*/, numWorkers/*max threads*/, 1L, TimeUnit.SECONDS, 
                            new PriorityBlockingQueue<Runnable>());


  // --- main0 ---------------------------------------------------------------
  static public void main0( final String args[] ) throws IOException, InterruptedException {
    // --- Next up: always re-make self as needed
    // _build_c - is the class file for build.java (this file!)
    _build_c.find_deps();
    _FindDeps.take().find_deps();
    assert _FindDeps.size() == 0 ;
    long bc_time = _build_c._modtime;
    _build_c.build_step(0);
    _FindDeps.take();
    if( _build_c._modtime != bc_time ) {
      // Since we remade ourself, launch & run self in a nested process to do
      // the actual 'build' using the new version of self.
      TPE.shutdown();
      String a = JAVA + " -cp build build ";
      for( String arg : args )
        a += arg+" ";
      sys_exec(a,true).writeTo(System.out);
      System.out.flush();
      System.err.flush();
      System.exit(0);
    }

    try {
      Q qs[] = new Q[args.length];
      boolean error = false;
      for( int i=0; i<args.length; i++ ) { // For all targets
        qs[i] = Q.FILES.get(args[i]);
        if( qs[i] == null ) {
          error = true;
          System.err.println("Unknown target "+args[i]);
        }
      }
      if( error ) throw new BuildError("Command line errors");

      // When the top-level targets 'do_it' runs, it will do nothing but put the
      // end-of-lazily-found deps on the _FindDeps queue, which will break the
      // main thread back out and start shutting down the thread pools.
      _FindDeps.put(new Q("targets",' ',qs));
      
      // --- Now spin on the _FindDeps queue in the Main thread (only),
      // processing tasks to produce any dependent tasks.  Tasks can be added by
      // worker threads asynchronously.
      Thread.currentThread().setPriority(Thread.currentThread().getPriority()+1);
      while( true ) {
        final Q q = _FindDeps.take(); // Blocking call to get tasks.
        if( q == ALL_DONE_SENTINEL )
          break;
        q.find_deps();
      }
    } catch( RejectedExecutionException e ) {
    } finally {
      // --- All Done!
      TPE.shutdown();
      System.out.flush();
      System.err.flush();
    }
    // End of main
  }

  // --- StreamEater ---------------------------------------------------------
  // Used to 'eat' the output stream from a remote process and store it in a
  // local ByteArrayOutputStream.
  static private class StreamEater extends Thread {
    final private InputStream _is;
    final public ByteArrayOutputStream _buf = new ByteArrayOutputStream();
    private IOException _e;
    StreamEater( InputStream is ) { _is = is; start(); }
    public void run() {
      byte[] buf = new byte[1024];
      try {
        int len;
        while( (len=_is.read(buf)) != -1 ) {
          _buf.write(buf,0,len);
        }
      } catch( IOException e ) {
        _e = e;                 // Catch it for later, we're in the wrong thread
      }
    }
    public void close() throws IOException, InterruptedException {
      // called from the main thread on the StreamEater object, but not in the
      // StreamEater thread.
      join();
      if( _e != null ) throw _e; // Rethrow any exception in the main thread
    }
  }

  // --- sys_exec ------------------------------------------------------------
  // Run the command string as a new system process.  Throw an error if the
  // return value is not zero, or any number of other errors happen.  On an
  // error, all process output is dumped to System.out (stdout).  On success
  // the output is buffered and the caller can decide how to dump.
  static ByteArrayOutputStream sys_exec( String exec, boolean die_on_fail ) {
    if( exec.length() == 0 ) return null; // Vacuously works for empty commands

    // The standard 'exec' call does not handle quoted strings very
    // nicely; this makes it hard to e.g. pass a quoted string to a
    // 'sh' shell thus allowing multiple commands in a single step.
    String[] execs = null;
    int x = exec.indexOf('"');
    if( x != -1 && exec.charAt(x-1)==' ' ) { // exec String contains quotes?
      // nothing yet
      ArrayList<String> toks = new ArrayList<String>();
      int tok=0;
      for( int i=0; i<exec.length(); i++ ) {
        char c = exec.charAt(i);
        if( c == ' ' || c == '"' ) {
          if( tok<i ) toks.add(exec.substring(tok,i));
          tok=i+1;              // next token starts after this whitespace
        } 
        if( c == '"' ) {
          i++;
          while( exec.charAt(i) != '"' ) i++;
          toks.add(exec.substring(tok,i));
          tok = i+1;
        }
      }
      if( tok<exec.length() ) toks.add(exec.substring(tok,exec.length()));
      execs = new String[toks.size()];
      toks.toArray(execs);
      System.out.println("split into chunks: "+execs.length);
      System.out.println(exec);
    }

    // Now run the command string in a seperate process.  Buffer all output,
    // so that error output from parallel processes can be dumped out without
    // interleaving.  Also if the command finishes without any errors and we
    // are not running very-verbose then we don't dump the commands output.
    StreamEater err = null, out = null;
    // This try/catch block will dump any output from the process before make dies
    try {
      Process p = null;
      // This try/catch block will catch any I/O errors and turn them into BuildErrors
      try {
        // Run the 'exec' String in a seperate process.
        p = execs == null ? RUN.exec(exec) : RUN.exec(execs);
        err = new StreamEater(p.getErrorStream()); // Start StreamEater threads
        out = new StreamEater(p.getInputStream());
        final int status = p.waitFor();

        if( status != 0 )
          throw new BuildError("Status "+status+" from "+exec);
        out.close();            // catch runaway StreamEater thread
        err.close();            // catch runaway StreamEater thread
      } catch( IOException e ) {
        if( err == null ) System.out.println(""+e);
        throw new BuildError("IOException from "+exec);
      } catch( InterruptedException e ) {
        throw new BuildError("Interrupted while waiting on "+exec);
      } finally {
        if( p != null ) p.destroy(); // catch runaway process
      }
    } catch( BuildError be ) {
      // Build-step choked.  Dump any output
      if( out != null ) try { out._buf.writeTo(System.out); } catch( IOException e ) { throw new BuildError(e.toString()); }
      if( err != null ) try { err._buf.writeTo(System.out); } catch( IOException e ) { throw new BuildError(e.toString()); }
      if( die_on_fail )  {      // For nested builds
        System.out.flush();
        System.err.flush();
        System.exit(0); 
      }
      throw be;                 // Rethrow after dumping output
    }
    return out._buf;           // No errors?  Then here is the buffered output
  }

  // Compute the base file name for a CPP file.
  // Silently chokes for non-CPP files.
  static final String basename( final String name ) {
    final String cppname = name.substring(name.lastIndexOf('/')+1);
    final String basename = cppname.substring(0,cppname.length()-4);
    return basename;
  }

  // --- A dependency --------------------------------------------------------
  static private class Q implements Comparable, Runnable {

    // A table of all dependencies & target files in the world
    static ConcurrentHashMap<String,Q> FILES = new ConcurrentHashMap<String,Q>();

    // Basic definition of a dependency
    final String _target;       // Target file name
    final char _src_sep;        // Standard string seperator used between dependent file names
    // I assume that calling lastModified() is a modestly expensive OS call.
    // I *know* that some OS's report file timestamps rounded down badly, to
    // the nearest 1 second on linux.
    long _modtime;           // Cache of _dst.lastModified() (or System.CTM for -n builds)

    int _priority;              // hint for build-order speed
    int _cum_prior;             // Cumlative priority from here to root

    // These next 5 fields need to be atomically updated.  
    // They are updated and read concurrently by many threads.

    // The _srcs array is updated by 'extra_check' for ".o" files - we read
    // the matching ".P" file for a list of dependent ".hpp" files.  It never
    // otherwise changes.  No other thread should be reading it until the
    // sync'd extra_check returns - and then it should bounce over to the main
    // thread for a re-check of dependencies.

    Q[] _srcs; // Array of dependent files; not final because HPP dependencies get auto-added later
    
    // Cleared by _extra_check so that main knows to re-inspect hpp lists.
    File _dst;                  // Actual OS file; set once during find_deps, cleared by extra_check

    // Count of _ready_children.  This field changes when
    // (1) the _srcs change, hence changing the number of children
    // (2) any child changes state
    int _ready_children; // Count of ready children 
    // Valid states are- 
    // - null - never inspected
    // - "done" - all children are "done" and this build-step fired if needed
    // - "extra_check" - extra deps added; do not attempt to add again
    // - "failed"
    String _state;
    
    // Parent dependences of this dep.  Changed by a child extra_step.
    final Vector<Q> _parents = new Vector<Q>();

    // --- Constructor for a list of source dependencies
    Q( String target, char src_sep, Q[] srcs ) {
      _target = target;
      _src_sep = src_sep;
      _srcs = srcs;

      // Basic sanity checking
      if( target.indexOf('%') != -1 ) 
        throw new IllegalArgumentException("dependency target has a '%': "+target);
      
      // Install the target/dependency mapping in a global flat table
      Q old = FILES.put(_target,this);
      if( old != null ) 
        throw new IllegalArgumentException("More than one dependency for target "+
                                           target+((_srcs.length > 0) ? (" with source "+_srcs[0]._target):""));

      // Set any childs' parent-pointers to me
      for( Q src : _srcs ) {
        synchronized(src) {
          src._parents.add(this);
        }
      }
    }

    // --- Constructor for a root file; no dependencies
    static final private Q[] NONE = new Q[0];
    Q( String target ) {
      this(target,' ',NONE);
    }

    // --- Factory for a single dependency
    static Q Q1( String target, Q src ) {
      Q qs[] = new Q[1];
      qs[0] = src;
      return new Q(target,' ',qs);
    }

    // Special version for dynamically added targets with no deps (ala system
    // include files): does a putIfAbsent into FILES.
    private Q( String target, boolean f ) { _target = target; _src_sep = ' '; _srcs = NONE; }
    static Q new_dynamic( String target ) {
      Q q = FILES.get(target);
      if( q != null ) return q; // Quick cutout
      q = new Q(target,false);
      FILES.putIfAbsent(target,q); // returns OLD value in table
      return FILES.get(target);    // Return current table contents
    }

    // --- addDep
    // A handful of files need some #include deps "hand added" instead of using
    // the auto-discovery mechanism.
    synchronized Q addDep( Q ... deps ) {
      Q[] srcs = new Q[_srcs.length+deps.length];
      System.arraycopy(_srcs,0,srcs,0,_srcs.length);
      System.arraycopy( deps,0,srcs,_srcs.length,deps.length);
      _srcs = srcs;
      for( Q dep : deps )
        synchronized(dep) { dep._parents.add(this); }
      return this;
    }

    // --- find_deps
    // One-shot per Q, make sure each _src has _dst File.
    // Sync'd to cover setting _dst & reading _ready_children count
    synchronized void find_deps() throws InterruptedException {
      if( _dst == null ) {      // First time thru only
        if( this instanceof QLinkerOption ) {
          _dst = new File(TOP,"");
          _modtime = 1;
        } else {
          _dst = _target.charAt(0)=='/' // Absolute path?
            ? new File(_target)         // Then use it absolutely
            : new File(TOP,_target);    // Else assume TOP relative
          _modtime = _dst.lastModified(); // Cache OS time
          // Zero-length files are almost always errors sometimes caused by
          // hitting ^C at a bad moment.  Just assume they need to be built.
          if( _dst.length() == 0 ) _modtime = 0; // Pretend zero-length files don't exist
        }
        _priority = 0;
        if( _modtime==0 )               // if file does not exist
          _priority += 99999999;        // do it first
        if( _target.endsWith("P") )     // If file creates more work
          _priority += 59999999;        // do it 2nd
        _priority += _dst.length();     // Otherwise do slower (bigger) files before faster files
        _cum_prior = _priority;         // Add in your parent's priority also
        for( Q p : _parents )           // Also include all parents' priorities
          _cum_prior += p._cum_prior;
        if( _modtime == 0 && _srcs.length == 0 ) 
          throw new BuildError("Source file "+_target+" is missing, used by at least "+_parents.get(0)._target);
      } else if( _state != "extra_check" ) { // Tossed onto finddeps extra times?
        // _dst already set, so already ran finddeps
        return;                     // Nothing to do here
      }

      // Put children on as well
      for( Q src : _srcs ) {
        synchronized(src) {
          if( src._dst == null || src._state == "extra_check" )
            _FindDeps.put(src);
          else
            src._cum_prior += _cum_prior;
        }
      }
      // Put trivially ready things on the WorkQueue
      if( _state == null && _ready_children == _srcs.length )
        TPE.execute(this);
    }


    // Make a single String with all sources, pre-pended with the top-level
    // path and seperated by the 'sep' char.
    String flat_src( char sep ) {
      String s = "";
      if( _srcs.length==0 ) return s;
      for( int i=0; i<_srcs.length-1; i++ ) {
        if( _srcs[i]._target.charAt(0)!='/' &&
            !(_srcs[i] instanceof QLinkerOption) )
          s += TOP_PATH_SLASH+"/";
        s += _srcs[i]._target+sep;
      }
      if( _srcs[_srcs.length-1]._target.charAt(0)!='/' )
        s += TOP_PATH_SLASH+"/";
      s += _srcs[_srcs.length-1]._target;
      return s;
    }

    // The void 'do_it' function, with no output.  Override this to do
    // a real build-step.  Also print a 1-liner on what the step is
    // doing, WITH printing a newline in a single output write.
    protected ByteArrayOutputStream do_it( ) {
      return null;
    }

    // Override if your build-step adds any dependencies
    // Return 1 - added child deps that need to be checked
    // Return 2 - force build step to happen
    protected int extra_check() {
      return 0;
    }

    // Building priorities encoded here
    // 1- missing files (probably a failed prior build-step with syntax errors
    // 2- jobs making more work (.P files, ADLC, or JVMTI XML)
    // 3- big jobs
    // Lower priorities are pulled from the work queue first
    public int compareTo(Object o) {
      return ((Q)o)._cum_prior - _cum_prior;
    }

    public void findDeps_queue() {
      try {
        _FindDeps.put(this); // Find'em all...
      } catch( InterruptedException e ) {
        throw new Error(e); // Rethrow as an unchecked exception - we're dead
      }
    }

    // --- Do the build-step, as needed
    public String build_step( final int xc ) {
      // Cleaning?  Nuke target and return
      if( _clean ) {
        if( _srcs.length == 0 ) return "done"; // Do not remove source files
        if( !_dst.exists() ) return "done";    // Already removed
        if( this == _build_c ) return "done";  // Highly annoying to delete own class file
        System.out.println("rm "+_target);
        if( !_justprint ) _dst.delete();
        return "done";
      }
        
      // See if all is up-to-date.  Collect out-of-date filenames.  Collect
      // youngest source file (output file produced by this build step should
      // end up newer than the youngest source file).
      long last_src = 0;
      String newer = null;
      for( Q src : _srcs ) {
        if( last_src < src._modtime )
          last_src = src._modtime;      // Latest source time
        if( _modtime < src._modtime ) { // Out of date?
          if( newer == null ) {         // No filenames yet?
            newer = src._target;        // Collect 1st filename
          } else {                      // 2nd or later filename
            newer += " "+src._target;   // Collect filenames
          }
        }
      }
      if( xc <= 1 && newer == null ) { // Found no out-of-date source files?
        if( _verbose > 1 ) {
          if( _srcs.length != 0 ) {
            System.out.println("-- " + _target + " > { "+flat_src(' ')+" } : already up to date");
          } else {
            System.out.println("-- " + _target + " is a source file : already up to date");
          }
        }
        return "done";
      }

      // Else out-of-date and must build
      // ---
      // Files out of date; must do this build step
      if( _verbose > 0 ) {
        if( _modtime == 0 ) System.out.println("-- " + _target+ " is missing");
        else                System.out.println("-- " + _target+ " <= { "+newer+" }"+
                                               (newer==null?", forced build step because of new dependencies":""));
      }
      
      // About to attempt to make the file; force any needed directories
      File target_file = new File(_target);
      File parent_dir = target_file.getParentFile();
      if (parent_dir != null) parent_dir.mkdirs();
      
      try {
        // Do the build step.  Capture (and currently ignore) and standard
        // output from a successful build step.  Failed build-steps always
        // dump their stdout+stderr.
        ByteArrayOutputStream buf = do_it();
      } catch( BuildError be ) { // Catch obvious build failures
        // Failed; make sure target is deleted (except for build.class)
        if( this != _build_c ) // Highly annoying to delete own class file
          _dst.delete(); 
        if( !_keepontrucking ) { // single error is fatal
          try { Thread.sleep(1000); } catch( InterruptedException e ) { }
          TPE.shutdown();
          System.out.flush();
          System.err.flush();
          System.exit(-1);
        }
        return "failed";
      }

      if( _justprint ) {        // No expectation of any change
        _modtime = last_src+1;  // Force modtime update          
        return "done";
      }
          
      // Double-check that the source files were not modified by the
      // build-step.  It's a fairly common error if file-names get swapped,
      // etc.  This exception is uncaught, so it aborts everything.  It
      // basically indicate a broken build file - the build-step is changing
      // the wrong file.
      for( Q src : _srcs )
        if( !(src instanceof QLinkerOption) &&
            src._modtime != src._dst.lastModified() )
          throw new IllegalArgumentException("Timestamp for source file "+src._target+
                                             " apparently changed by building "+_target+
                                             " last recorded time="+src._modtime+
                                             " and now the filesystem reports="+src._dst.lastModified());
      
      // Double-check that this step made progress.  Again, failure here is
      // likely a build-step failure to modify the correct file.
      long x = _dst.lastModified();
      int sleep=100;
      while( _modtime == x && sleep >0 ) {
        System.out.println("Timestamp for "+_target+" not changed by building; time="+x);
        try { Thread.sleep(1); } catch( InterruptedException e ) { };
        sleep--;
      }
      _modtime = x;
      long now = System.currentTimeMillis();
      while( now < _modtime ) {
        System.out.println("Timestamp for "+_target+" moved "+(_modtime-now)+"ms into the future by building; sleeping until the Future is Now");
        try { Thread.sleep(_modtime-now); } catch( InterruptedException e ) { };
        now = System.currentTimeMillis();
      }
      if( _modtime < last_src )
        throw new IllegalArgumentException("Timestamp for "+_target+" not changed by building "+_target);
      // Invariant: last_src <= _modtime <= now
        
        
      // For very fast build-steps, the target may be updated to a time equal
      // to the input file times after rounding to the file-system's time
      // precision - which might be as bad as 1 whole second.  Assume the
      // build step worked, but give the result an apparent time just past the
      // src file times to make later steps more obvious.
      if( !_allow_timestamp_ties ) {
        while( x == last_src ) {     // Aaahh, we have 'tied' in the timestamps!!!
          // Sleep/spin until the OS's version of a rounded timestamp shows real progress
          try { Thread.sleep(1); } catch( InterruptedException e ) { };
          now = System.currentTimeMillis();
          _dst.setLastModified(now); // Pretend file was made 'right NOW!'
          x = _dst.lastModified(); // Reload, after OS rounding
        }
        if( _modtime == last_src )
          System.out.println("Yawners... had to sleep "+(System.currentTimeMillis()-_modtime)+" msec to get timestamp to advance");
      }
      _modtime = x;             // Record apparent mod-time
      return "done";
    }

    // --- What happens 'FutureTask.get' is called on a Q?
    public void run() {
      if( _parents.size() == 0 ) { // Ready to build the top-level fake target?
        _state = "done";
        ALL_DONE_SENTINEL.findDeps_queue(); 
        return;
      }
      
      // Assert we never been run before, or only run thru 'extra_check'
      String st;
      synchronized(this) { st = _state; }
      assert st == "extra_check" || st == null : "1Running "+_target+" with st="+st+" and rdy="+_ready_children+":"+_srcs.length;
      assert _ready_children == _srcs.length   : "2Running "+_target+" with st="+st+" and rdy="+_ready_children+":"+_srcs.length;

      int xc = 0;               // Is build-step forced?  (generally by a missing file?)
      if( st == null ) {        // Not ready extra-step 
        try {
          xc = extra_check();   // Producing extra dependencies?
        } catch( Exception e ) {
          System.err.println(e.getMessage());
          TPE.shutdown();
          System.out.flush();
          System.err.flush();
          System.exit(-1);
        }
        if( xc == 1 ) return; // No more work here, until deps are all found & built
      }
        
      // See if any child failed
      String state = null;
      for( Q src : _srcs ) {     // Assert all children already done
        String cstate;
        synchronized(src) { cstate= src._state;}
        assert cstate == "done" || cstate == "failed" : "Running "+_target+" but child "+src._target+" has state="+cstate;
        if( cstate == "failed" )
          state = "failed";
      }

      // Do build step (if we aint dead yet)
      if( state == null )       // No failed children?
        state = build_step(xc); // Get new state from build_step

      // Change state under lock, but also clone parent list.  When the lock
      // releases, other parents may be added and they need to handle the
      // exposed _state (i.e., they just added a 'done' child so they better
      // update their ready_children count.
      Vector<Q> pclone;
      synchronized(this) {
        _state = state;
        pclone = new Vector<Q>(_parents);
      }

      // Inform all parents that this child is ready 
      for( Q p : pclone )
        p.child_is_ready(this);
    }

    // --- child_is_ready
    // Some child completed successfully.
    // Check _dst under lock; 
    synchronized void child_is_ready(Q c) {
      assert _ready_children < _srcs.length : " "+_target+" "+_ready_children+"<"+_srcs.length+ " child="+c._target;
      _ready_children++;

      // Happens on parents (with ready children) not involved in any
      // top-level build target - they become ready to build, but no target
      // needs them.
      if( _dst == null ) return; 

      if( _ready_children == _srcs.length ) {
        try {
          TPE.execute(this);
        } catch( RejectedExecutionException ree ) {
          // ignore if shutting down
        }
      }
    }

  };

  // --- A dependency with an exec string ------------------------------------
  // Mostly just a normal dependency; it "execs" a String to do the build-step.
  static private class QS extends Q {
    final String _exec;
    String _parsed_exec;

    // --- Constructor for a list of source dependencies
    QS( String target, String exec, char src_sep, Q ... srcs ) {
      super(target, src_sep,srcs);
      _exec = exec;
      for( int i=_exec.indexOf('%'); i!= -1; i = _exec.indexOf('%',i+1) ) {
        if( false ) {
        } else if( _exec.startsWith("dst",i+1) ) { 
        } else if( _exec.startsWith("src",i+1) ) { 
        } else if( _exec.startsWith("top",i+1) ) { 
        } else
          throw new IllegalArgumentException("dependency exec has unknown pattern: "+_exec.substring(i));
      }
    }

    QS( String target, String exec, Q src ) {
      this(target,exec,' ',src);
    }

    // --- parse_exec
    // The _exec String contains normal text, plus '%src' and '%dst' strings.
    String parse_exec() {
      if( _parsed_exec == null ) {
        if( _srcs.length > 0 ) _parsed_exec =        _exec.replaceAll("%src0",_srcs[0]._target);
        if( _srcs.length > 1 ) _parsed_exec = _parsed_exec.replaceAll("%src1",_srcs[1]._target);
        if( _srcs.length > 2 ) _parsed_exec = _parsed_exec.replaceAll("%src2",_srcs[2]._target);
        _parsed_exec = _parsed_exec
          .replaceAll("%dst",_target)
          .replaceAll("%src",flat_src(_src_sep))
          .replaceAll("%top",TOP_PATH_SLASH);
      }
      return _parsed_exec;
    }

    protected ByteArrayOutputStream do_it( ) {
      final String exec = parse_exec();
      System.out.println(exec);   // Print 1-liner on what the step is doing
      return _justprint ? null : sys_exec(exec, false);
    }
  }

  // --- A dependency for a C++ compile --------------------------------------
  // The source is a foo.P file; read it for more dependencies.
  // Add the dependencies in 'extra_check'.
  // The 'doit' step just changes the default printout from QS - otherwise
  // it just does a normal string exec.
  static private class QC extends QS {
    // Given a path to a C++ file (and a compile string), build a foo.o
    // dependency to build from a foo.P file.
    static QC gen( String tpath, String flavor, String exec, String basename, String ext, Q ... extras ) { 
      final String target = tpath+flavor+basename+".o";
      final String Pname = tpath+"incls/"+basename+ext;
      final Q src = FILES.get(Pname);
      if( src == null ) throw new IllegalArgumentException("Missing dep for "+Pname+"\n");
      if( extras!=null && extras.length>0 )
        src.addDep(extras);
      return new QC(target,exec,src);
    }
    private QC( String target, String exec, Q     src  ) { super(target,exec,src ); }

    // Given a list of C++ files (and a compile string), build an array of foo.o
    // dependencies to build, from the list of C++ files.
    static Q[] gen_all( Q[] cs, Q[] cps, Q[] cos, String tpath, String flavor, String exec, Q ... extras ) { 
      final Q os[] = new Q[cs.length+cps.length+cos.length];
      int j=0;
      for( int i=0; i<cs.length; i++ )
        os[j+i] = gen( tpath, flavor, exec+" "+cs [i]._target, basename(cs [i]._target), ".PP", extras );
      j = cs.length;
      for( int i=0; i<cps.length; i++ )
        os[j+i] = gen( tpath, flavor, exec+" "+cps[i]._target, basename(cps[i]._target), ".PP", extras );
      j = cs.length+cps.length;
      for( int i=0; i<cos.length; i++ )
        os[j+i] = gen( tpath, flavor, exec+" "+cos[i]._target, basename(cos[i]._target), ".PP", extras );
      return os;
    }

    // Override the default non-verbose printout.
    protected ByteArrayOutputStream do_it( ) {
      final String exec = parse_exec();
      if( _verbose > 0 ) System.out.println(exec); // Print 1-liner on what the step is doing
      else System.out.println("Compiling "+_target);
      return _justprint ? null : sys_exec(exec, false);
    }
    
    // Read the .P file for extra dependencies
    protected int extra_check() {
      Q pfile=null;
      for( Q x : _srcs ) {
        if( x._target.endsWith("P") ) {
          pfile=x;
          break;
        }
      }
      if( pfile==null ) throw new BuildError("Missing P file dep for "+_target);
      try {
        // Read & parse the deps file
        final File deps = new File(pfile._target);
        final int len = (int)deps.length();
        final char[] cbuf = new char[len];
        if( len != new FileReader(deps).read(cbuf,0,len) )
          throw new IOException("Unexpected short read");
        // Split the string based on:
        // ' ' - blank
        // '\' - Backslash.  Must be encoded as 4 backslashes in the split string
        // '\n' - newline.
        final String[] ss = new String(cbuf).split("[ \\\\\n:]+");
        // The first String in ss should be of the form "foo.o".
        if( ss.length < 1 || !_target.endsWith(ss[0]) )
          throw new IllegalArgumentException("Expected first dep of "+pfile._target+" to refer to "+_target+"; badly formed .PP file; please delete it");
        // The 2nd String should be of the form TOP+".../foo.cpp"
        String srcname="";
        if( ss.length < 2 || !(srcname=(TOP_PATH_SLASH+"/"+pfile._srcs[0]._target)).endsWith(ss[1]) )
          throw new IllegalArgumentException("Expected second dep to refer to "+srcname+" but found "+ss[1]+"; badly formed .PP file; please delete "+pfile._target);

        // Copy the initial _srcs into a HashMap, to remove dup strings from
        // the dependency file
        final HashMap<String,Q> srcs = new HashMap<String,Q>();
        for( Q s : _srcs )
          srcs.put(s._target,s);

        // The remaining Strings will be proper dependencies.  They either:
        // -  start with 'src' and are a relative name and must be in the global FILES list, or
        // -  start with TOP_PATH_SLASH, then 'src' and are treated as above, or 
        // -  they start with '/' and refer to a system include file, or 
        // -  start with 'acapulco_port' and refer to a ported OS include file,
        // -  start with 'sandbox' and refer to an OS include file,
        // -  start with 'j2se6' and refer to a mini-JRE include file,
        for( int i=2; i<ss.length; i++ ) {
          String s = ss[i];
          if( s.startsWith("/") || s.startsWith("acapulco_port/") || s.startsWith("../") || s.startsWith("j2se6/") ) {
            srcs.put(s,Q.new_dynamic(s)); // sandbox or system .h files are just assumed valid
          } else if( s.startsWith("src/") ) { // these should exist in build.java, for some kind of safety
            Q dep = Q.FILES.get(s);
            String fname = TOP_PATH_SLASH+"/"+s;
            boolean file_found = new File(fname).canRead();
            if( dep == null ) { // Source file not mentioned in build.java
              if( !file_found ) { // File not found?
                if( _verbose > 0 )
                  System.out.println("--- File "+fname+" not found, forcing rebuild");
                // File not found, and not in build.java - so dep file is
                // wrong; force a recompile of the C++ program.  It should
                // fail (after all an include file is missing).  If the C++
                // file is edited to remove the missing include file, that
                // should trigger a rebuild of the dep file - which will no
                // long mention the missing include file.
                return 2;       // force build step
              }
              // Dep file mentions a name that is not in build.java, but the
              // file still exists - assume it is a missing dependence in the
              // build file.
              throw new IllegalArgumentException("build.java does not have a dependence for file "+s);
            } else if( !file_found ) {
              // Dep file and build.java both mention a file that does not exist.
              // Probably the file was removed and build.java should be cleaned up.
              throw new IllegalArgumentException("build.java has a dependence for missing file "+fname);
            }
            srcs.put(s,dep);
          } else {
            throw new IllegalArgumentException("Dependency filename does not start with '/' or 'sandbox' or 'src' or 'j2se6' "+s);
          }
        }

        // Anything change?
        if( srcs.size() == _srcs.length ) return 0;

        final Q qsrcs[] = srcs.values().toArray(_srcs); // Update _srcs list; more deps found so it grew
        // Source list changed; update it, and the parent/child relations and counts
        // 'this' is locked
        synchronized(this) {    // Lock self; no updating _ready_children
          _state = "extra_check"; // Change state: we'll need an extra go'round in find_deps
          _srcs = qsrcs;        // Change list of children
          _ready_children = 0; // Recompute readiness; some children are ready and some are not
          for( Q src : qsrcs ) {
            synchronized(src) {
              src._parents.add(this);
              if( src._state == "done" || src._state == "failed" )
                _ready_children++;
            }
          }
          if( _ready_children == qsrcs.length )
            return 0;           // All new children all ready to go!
          findDeps_queue();     // Else must FindDep'em all...
          return 1;             // Added new deps!!!
        }

      } catch( FileNotFoundException e ) {
        if( _justprint || _clean ) return 0; // Just printing - so no new deps discovered
        throw new IllegalArgumentException("make is busted: did not spot missing file: "+e);
      } catch( IOException e ) { // Some I/O issue?
        throw new BuildError(e.toString()); // Rethrow as a BuildError
      }

    } // extra_check

  }

  // --- A dependency for an archive -----------------------------------------
  static private class QA extends QS {
    QA( String target, String exec, char src_sep, Q ... srcs ) { super(target,exec,src_sep,srcs); }
    // Override the default non-verbose printout.
    protected ByteArrayOutputStream do_it( ) {
      final String exec = parse_exec();
      if( _verbose > 0 ) System.out.println(exec); // Print 1-liner on what the step is doing
      else System.out.println("Archiving "+_target);
      return _justprint ? null : sys_exec(exec, false);
    }
  }
    

  // --- A dependency for a link ---------------------------------------------
  static private class QL extends QS {
    QL( String target, String exec, char src_sep, Object ... srcs ) { 
      super(target,exec,src_sep,flatten_to_Q(srcs)); 
    }
    static Q[] flatten_to_Q( Object[] srcs ) {
      int sz=0;
      for( Object o : srcs )
        sz += (o instanceof Q[]) ? ((Q[])o).length : 1;
      Q qs[] = new Q[sz];
      int i=0;
      for( Object o : srcs )
        if( o instanceof Q[] ) {
          Q[] sqs = (Q[])o;
          System.arraycopy(sqs,0,qs,i,sqs.length);
          i += sqs.length;
        } else {
          qs[i++] = (Q)o;
        }
      return qs;
    }
    // Override the default non-verbose printout.
    protected ByteArrayOutputStream do_it( ) {
      final String exec = parse_exec();
      if( _verbose > 0 ) System.out.println(exec); // Print 1-liner on what the step is doing
      else System.out.println("Linking   "+_target);
      return _justprint ? null : sys_exec(exec, false);
    }
  }
    
  // --- A bogus dependency for adding a linker option in the middle ---------
  static private class QLinkerOption extends Q {
    QLinkerOption( String opt ) { super(opt); }
    public String build_step( final int xc ) { return "done"; }
  }

  // --- Strip and Sign a Binary, in 1 step ---------
  static private class QStripSign extends Q {
    private QStripSign( String dst, Q[] srcs ) { super(dst,' ',srcs); }
    public static QStripSign make( String dst, Q src ) { Q[] qs = {src}; return new QStripSign(dst,qs); }
    
    protected ByteArrayOutputStream do_it( ) {
      final String exec0 = AZSTRIP+" -d -o "+_target+" "+_srcs[0]._target;
      if( _verbose > 0 ) System.out.println(exec0); // Print 1-liner on what the step is doing
      else System.out.println("Stripping "+_target);
      ByteArrayOutputStream bas0 = _justprint ? null : sys_exec(exec0, false);

      final String exec1 = ELFSIGN+" "+_target;
      if( _verbose > 0 ) System.out.println(exec1); // Print 1-liner on what the step is doing
      else System.out.println("Signing   "+_target);
      ByteArrayOutputStream bas1 = _justprint ? null : sys_exec(exec1, false);
      return bas1;
    }
  }

  // --- Construct foo.P dependencies from all foo.cpp files -----------------
  // Find all 'cpp' files in the FILES list.  For each src/.../foo.cpp, create
  // a 'path/incls/foo.P' dependency.  The dependency will cause the foo.P
  // file to be made from the 'bld' string if the foo.cpp file changes.  A
  // foo.P file lists all the other '#include' files used by foo.cpp.
  static private class QP extends QS {
    private QP( final String target, final String bld, char src_sep, final Q ... srcs ) { super(target,bld,src_sep,srcs); }
    private QP( final String target, final String bld, final Q src ) { super(target,bld,src); }
    static void build_P_deps( String path, String bld ) {
      for( String key : Q.FILES.keySet() ) {
        if( key.endsWith("cpp") ) {
          final String basename = basename(key);
          final String Pname = basename+".PP";
          Q pp = new QP(path + "incls/"+Pname,bld,' ',FILES.get(key));
        }
      }
    }
    // Override the default non-verbose printout.
    protected ByteArrayOutputStream do_it( ) {
      final String exec = parse_exec();
      if( _verbose > 1 ) System.out.println(exec); // Print 1-liner on what the step is doing
      else System.out.println("Depending "+_target);
      return _justprint ? null : sys_exec(exec, false);
    }
  }

  // --- A dependency, just 'touch'ing the target ----------------------------
  // Mostly just a normal dependency
  static private class Q_touch extends Q {
    Q_touch( final String target, final Q ... srcs ) { super(target,' ',srcs); }
    protected ByteArrayOutputStream do_it( ) {
      System.out.println("touch "+_target); // 1-liner of build-step
      if( _justprint ) return null;
      File f = new File(TOP_PATH_SLASH+"/"+_target);
      try { 
        f.delete();
        f.createNewFile(); 
        // You would think that to delete & create the file would update the
        // lastMod time accurately, but on linux at least it appears it can be
        // created at least 1 msec in the past.
        long t = System.currentTimeMillis();
        f.setLastModified(t);
      } catch( IOException e ) {
        throw new BuildError("Unable to make file "+_target+": "+e.toString());
      }
      return null;              // No output from a 'touch'
    }
  }


  // =========================================================================
  // --- The Dependencies ----------------------------------------------------
  // =========================================================================


  // The build-self dependency every project needs
  static final Q _build_j = new Q("build/build.java");
  static final Q _build_c = new QS("build/build.class", JAVAC + " -cp build %src",_build_j);

  // --- Some tools
  static final String AZ_SWTOOLS_GCC_TXU_DIR = "/home/swrelease/swtools/1.04.0422/linux/bin";
  static final String AZ_SWTOOLS_GCC_NXU_DIR = "/home/buildmaster/sw/gcc/4.3.0/linux/nxu_64/bin";

  static final String GCC_DEFINES = "-DJDK_VERSION_STR=\"1.6.0_99999\" ";
  static final String GCC_FLAGS = "-fno-rtti -fno-exceptions -pipe -fmessage-length=0 " + GCC_DEFINES;

  //static final String GCC_NXU = "/home/buildmaster/gcc4.3.2/build.gcc/nxu_64-linux-gnu/bin/g++ -Di86pc " + GCC_FLAGS;
  //static final String GC_NXU  = "/home/buildmaster/gcc4.3.2/build.gcc/nxu_64-linux-gnu/bin/gcc -pipe -fmessage-length=0 -Wall -Di86pc " + GCC_DEFINES;
  static final String GCC_NXU = AZ_SWTOOLS_GCC_NXU_DIR + "/g++ -fno-strict-aliasing -Di86pc -DAZPROF_NO_OPENSSL -DAZPROF_NO_EVENTS " + GCC_FLAGS;
  static final String GC_NXU  = AZ_SWTOOLS_GCC_NXU_DIR + "/gcc -fno-strict-aliasing -pipe -fmessage-length=0 -Wall -Di86pc " + GCC_DEFINES;

  static final String GCC_TXU = AZ_SWTOOLS_GCC_TXU_DIR + "/azg++ -Wa,-xarch=vega2 " + GCC_FLAGS;
  static final String GC_TXU  = AZ_SWTOOLS_GCC_TXU_DIR + "/azgcc -Wa,-xarch=vega2 " + GCC_FLAGS;
  static final String AZSTRIP = AZ_SWTOOLS_GCC_TXU_DIR + "/azstrip";
  static final String AZAR    = AZ_SWTOOLS_GCC_TXU_DIR + "/azar";
  static final String ELFSIGN = SANDBOX+"/linux/bin/i686/elfsign -k "+SANDBOX+"/linux/keys.private/binpriXX.pem ";

  // --- Some HotSpot CPU-specific source files
  static final String CPU_TXU                    = "src/cpu/txu/vm/";
  static final Q _assembler_txu_cpp              = new Q(CPU_TXU+"assembler_txu.cpp");
  static final Q _assembler_txu_hpp              = new Q(CPU_TXU+"assembler_pd.hpp");
  static final Q _bytecodes_txu_cpp              = new Q(CPU_TXU+"bytecodes_txu.cpp");
  static final Q _bytecodes_txu_hpp              = new Q(CPU_TXU+"bytecodes_pd.hpp");
  static final Q _bytes_txu_hpp                  = new Q(CPU_TXU+"bytes_pd.hpp");
  static final Q _c1_globals_txu_hpp             = new Q(CPU_TXU+"c1_globals_pd.hpp");
  static final Q _c2_globals_txu_hpp             = new Q(CPU_TXU+"c2_globals_pd.hpp");
  static final Q _constants_txu_hpp              = new Q(CPU_TXU+"constants_pd.hpp");
  static final Q _copy_txu_cpp                   = new Q(CPU_TXU+"copy_txu.cpp");
  static final Q _copy_txu_hpp                   = new Q(CPU_TXU+"copy_pd.hpp");
  static final Q _debug_txu_cpp                  = new Q(CPU_TXU+"debug_txu.cpp");
  static final Q _disassembler_txu_cpp           = new Q(CPU_TXU+"disassembler_txu.cpp");
  static final Q _disassembler_txu_hpp           = new Q(CPU_TXU+"disassembler_pd.hpp");
  static final Q _frame_txu_cpp                  = new Q(CPU_TXU+"frame_txu.cpp");
  static final Q _frame_txu_hpp                  = new Q(CPU_TXU+"frame_pd.hpp");
  static final Q _frame_txu_inline_hpp           = new Q(CPU_TXU+"frame_pd.inline.hpp");
  static final Q _global_defs_txu_hpp            = new Q(CPU_TXU+"globalDefinitions_pd.hpp");
  static final Q _globals_txu_hpp                = new Q(CPU_TXU+"globals_pd.hpp");
  static final Q _gpgc_traps_txu_hpp             = new Q(CPU_TXU+"gpgc_traps_pd.hpp");
  static final Q _heapRef_txu_hpp                = new Q(CPU_TXU+"heapRef_pd.hpp");
  static final Q _heapRef_txu_inline_hpp         = new Q(CPU_TXU+"heapRef_pd.inline.hpp");
  static final Q _icache_txu_cpp                 = new Q(CPU_TXU+"icache_txu.cpp");
  static final Q _icache_txu_hpp                 = new Q(CPU_TXU+"icache_pd.hpp");
  static final Q _interp_masm_txu_cpp            = new Q(CPU_TXU+"interp_masm_txu.cpp");
  static final Q _interp_masm_txu_hpp            = new Q(CPU_TXU+"interp_masm_pd.hpp");
  static final Q _interpreterRT_txu_cpp          = new Q(CPU_TXU+"interpreterRT_txu.cpp");
  static final Q _interpreterRT_txu_hpp          = new Q(CPU_TXU+"interpreterRT_pd.hpp");
  static final Q _interpreter_txu_cpp            = new Q(CPU_TXU+"interpreter_txu.cpp");
  static final Q _interpreter_txu_hpp            = new Q(CPU_TXU+"interpreter_pd.hpp");
  static final Q _javaFrameAnchor_txu_hpp        = new Q(CPU_TXU+"javaFrameAnchor_pd.hpp");
  static final Q _jniTypes_txu_hpp               = new Q(CPU_TXU+"jniTypes_pd.hpp");
  static final Q _lvb_txu_hpp                    = new Q(CPU_TXU+"lvb_pd.hpp");
  static final Q _nativeInst_txu_hpp             = new Q(CPU_TXU+"nativeInst_pd.hpp");
  static final Q _nativeInst_txu_cpp             = new Q(CPU_TXU+"nativeInst_txu.cpp");
  static final Q _objectRef_txu_cpp              = new Q(CPU_TXU+"objectRef_txu.cpp");
  static final Q _objectRef_txu_hpp              = new Q(CPU_TXU+"objectRef_pd.hpp");
  static final Q _objectRef_txu_inline_hpp       = new Q(CPU_TXU+"objectRef_pd.inline.hpp");
  static final Q _pauselessTraps_txu_hpp         = new Q(CPU_TXU+"pauselessTraps_pd.hpp");
  static final Q _refsHierarchy_txu_hpp          = new Q(CPU_TXU+"refsHierarchy_pd.hpp");
  static final Q _register_txu_cpp               = new Q(CPU_TXU+"register_txu.cpp");
  static final Q _register_txu_hpp               = new Q(CPU_TXU+"register_pd.hpp");
  static final Q _relocInfo_txu_hpp              = new Q(CPU_TXU+"relocInfo_pd.hpp");
  static final Q _shared_txu_cpp                 = new Q(CPU_TXU+"shared_txu.cpp");
  static final Q _sharedRuntime_txu_cpp          = new Q(CPU_TXU+"sharedRuntime_txu.cpp");
  static final Q _stackRef_txu_cpp               = new Q(CPU_TXU+"stackRef_txu.cpp");
  static final Q _stackRef_txu_hpp               = new Q(CPU_TXU+"stackRef_pd.hpp");
  static final Q _stackRef_txu_inline_hpp        = new Q(CPU_TXU+"stackRef_pd.inline.hpp");
  static final Q _stubGenerator_txu_cpp          = new Q(CPU_TXU+"stubGenerator_txu.cpp");
  static final Q _stubRoutines_txu_cpp           = new Q(CPU_TXU+"stubRoutines_txu.cpp");
  static final Q _stubRoutines_txu_hpp           = new Q(CPU_TXU+"stubRoutines_pd.hpp");
  static final Q _templateTable_txu_cpp          = new Q(CPU_TXU+"templateTable_txu.cpp");
  static final Q _templateTable_txu_hpp          = new Q(CPU_TXU+"templateTable_pd.hpp");
  static final Q _tickProfiler_txu_cpp           = new Q(CPU_TXU+"tickProfiler_txu.cpp");
  static final Q _thread_txu_hpp                 = new Q(CPU_TXU+"thread_pd.hpp");
  static final Q _vm_version_txu_cpp             = new Q(CPU_TXU+"vm_version_txu.cpp");
  static final Q _vm_version_txu_hpp             = new Q(CPU_TXU+"vm_version_pd.hpp");
  static final Q _vtableStubs_txu_cpp            = new Q(CPU_TXU+"vtableStubs_txu.cpp");

  static final String CPU_NXU                    = "src/cpu/nxu/vm/";
  static final Q _assembler_nxu_cpp              = new Q(CPU_NXU+"assembler_nxu.cpp");
  static final Q _assembler_nxu_hpp              = new Q(CPU_NXU+"assembler_pd.hpp");
  static final Q _bytecodes_nxu_cpp              = new Q(CPU_NXU+"bytecodes_nxu.cpp");
  static final Q _bytecodes_nxu_hpp              = new Q(CPU_NXU+"bytecodes_pd.hpp");
  static final Q _bytes_nxu_hpp                  = new Q(CPU_NXU+"bytes_pd.hpp");
  static final Q _c1_globals_nxu_hpp             = new Q(CPU_NXU+"c1_globals_pd.hpp");
  static final Q _c2_globals_nxu_hpp             = new Q(CPU_NXU+"c2_globals_pd.hpp");
  static final Q _constants_nxu_hpp              = new Q(CPU_NXU+"constants_pd.hpp");
  static final Q _copy_nxu_cpp                   = new Q(CPU_NXU+"copy_nxu.cpp");
  static final Q _copy_nxu_hpp                   = new Q(CPU_NXU+"copy_pd.hpp");
  static final Q _debug_nxu_cpp                  = new Q(CPU_NXU+"debug_nxu.cpp");
  static final Q _disassembler_nxu_cpp           = new Q(CPU_NXU+"disassembler_nxu.cpp");
  static final Q _disassembler_nxu_hpp           = new Q(CPU_NXU+"disassembler_pd.hpp");
  static final Q _frame_nxu_cpp                  = new Q(CPU_NXU+"frame_nxu.cpp");
  static final Q _frame_nxu_hpp                  = new Q(CPU_NXU+"frame_pd.hpp");
  static final Q _frame_nxu_inline_hpp           = new Q(CPU_NXU+"frame_pd.inline.hpp");
  static final Q _global_defs_nxu_hpp            = new Q(CPU_NXU+"globalDefinitions_pd.hpp");
  static final Q _globals_nxu_hpp                = new Q(CPU_NXU+"globals_pd.hpp");
  static final Q _gpgc_traps_nxu_hpp             = new Q(CPU_NXU+"gpgc_traps_pd.hpp");
  static final Q _heapRef_nxu_hpp                = new Q(CPU_NXU+"heapRef_pd.hpp");
  static final Q _heapRef_nxu_inline_hpp         = new Q(CPU_NXU+"heapRef_pd.inline.hpp");
  static final Q _icache_nxu_cpp                 = new Q(CPU_NXU+"icache_nxu.cpp");
  static final Q _icache_nxu_hpp                 = new Q(CPU_NXU+"icache_pd.hpp");
  static final Q _interp_masm_nxu_cpp            = new Q(CPU_NXU+"interp_masm_nxu.cpp");
  static final Q _interp_masm_nxu_hpp            = new Q(CPU_NXU+"interp_masm_pd.hpp");
  static final Q _interpreterRT_nxu_cpp          = new Q(CPU_NXU+"interpreterRT_nxu.cpp");
  static final Q _interpreterRT_nxu_hpp          = new Q(CPU_NXU+"interpreterRT_pd.hpp");
  static final Q _interpreter_nxu_cpp            = new Q(CPU_NXU+"interpreter_nxu.cpp");
  static final Q _interpreter_nxu_hpp            = new Q(CPU_NXU+"interpreter_pd.hpp");
  static final Q _javaFrameAnchor_nxu_hpp        = new Q(CPU_NXU+"javaFrameAnchor_pd.hpp");
  static final Q _jniTypes_nxu_hpp               = new Q(CPU_NXU+"jniTypes_pd.hpp");
  static final Q _lvb_nxu_hpp                    = new Q(CPU_NXU+"lvb_pd.hpp");
  static final Q _nativeInst_nxu_hpp             = new Q(CPU_NXU+"nativeInst_pd.hpp");
  static final Q _nativeInst_nxu_cpp             = new Q(CPU_NXU+"nativeInst_nxu.cpp");
  static final Q _objectRef_nxu_cpp              = new Q(CPU_NXU+"objectRef_nxu.cpp");
  static final Q _objectRef_nxu_hpp              = new Q(CPU_NXU+"objectRef_pd.hpp");
  static final Q _objectRef_nxu_inline_hpp       = new Q(CPU_NXU+"objectRef_pd.inline.hpp");
  static final Q _pauselessTraps_nxu_hpp         = new Q(CPU_NXU+"pauselessTraps_pd.hpp");
  static final Q _refsHierarchy_nxu_hpp          = new Q(CPU_NXU+"refsHierarchy_pd.hpp");
  static final Q _register_nxu_cpp               = new Q(CPU_NXU+"register_nxu.cpp");
  static final Q _register_nxu_hpp               = new Q(CPU_NXU+"register_pd.hpp");
  static final Q _relocInfo_nxu_hpp              = new Q(CPU_NXU+"relocInfo_pd.hpp");
  static final Q _shared_nxu_cpp                 = new Q(CPU_NXU+"shared_nxu.cpp");
  static final Q _sharedRuntime_nxu_cpp          = new Q(CPU_NXU+"sharedRuntime_nxu.cpp");
  static final Q _stackRef_nxu_cpp               = new Q(CPU_NXU+"stackRef_nxu.cpp");
  static final Q _stackRef_nxu_hpp               = new Q(CPU_NXU+"stackRef_pd.hpp");
  static final Q _stackRef_nxu_inline_hpp        = new Q(CPU_NXU+"stackRef_pd.inline.hpp");
  static final Q _stubGenerator_nxu_cpp          = new Q(CPU_NXU+"stubGenerator_nxu.cpp");
  static final Q _stubRoutines_nxu_cpp           = new Q(CPU_NXU+"stubRoutines_nxu.cpp");
  static final Q _stubRoutines_nxu_hpp           = new Q(CPU_NXU+"stubRoutines_pd.hpp");
  static final Q _templateTable_nxu_cpp          = new Q(CPU_NXU+"templateTable_nxu.cpp");
  static final Q _templateTable_nxu_hpp          = new Q(CPU_NXU+"templateTable_pd.hpp");
  static final Q _tickProfiler_nxu_cpp           = new Q(CPU_NXU+"tickProfiler_nxu.cpp");
  static final Q _thread_nxu_hpp                 = new Q(CPU_NXU+"thread_pd.hpp");
  static final Q _vm_version_nxu_cpp             = new Q(CPU_NXU+"vm_version_nxu.cpp");
  static final Q _vm_version_nxu_hpp             = new Q(CPU_NXU+"vm_version_pd.hpp");
  static final Q _vtableStubs_nxu_cpp            = new Q(CPU_NXU+"vtableStubs_nxu.cpp");

  // --- Some HotSpot OS-specific source files

  static final String INCLUDES_AZTEK             = " -I "+SANDBOX+"/aztek/include/";
  static final String OS_AZTEK                   = "src/os/aztek/vm/";
  static final Q _attachListener_aztek_cpp       = new Q(OS_AZTEK+"attachListener_aztek.cpp");
  static final Q _c1_globals_aztek_hpp           = new Q(OS_AZTEK+"c1_globals_os.hpp");
  static final Q _c2_globals_aztek_hpp           = new Q(OS_AZTEK+"c2_globals_os.hpp");
  static final Q _externalProfiler_aztek_cpp     = new Q(OS_AZTEK+"externalProfiler_aztek.cpp");
  static final Q _global_defs_aztek_hpp          = new Q(OS_AZTEK+"globalDefinitions_os.hpp");
  static final Q _globals_aztek_hpp              = new Q(OS_AZTEK+"globals_os.hpp");
  static final Q _hpi_aztek_cpp                  = new Q(OS_AZTEK+"hpi_aztek.cpp");
  static final Q _hpi_aztek_hpp                  = new Q(OS_AZTEK+"hpi_os.hpp");
  static final Q _jvm_aztek_cpp                  = new Q(OS_AZTEK+"jvm_aztek.cpp");
  static final Q _jvm_aztek_h                    = new Q(OS_AZTEK+"jvm_os.h");
  static final Q _mutex_aztek_cpp                = new Q(OS_AZTEK+"mutex_aztek.cpp");
  static final Q _osThread_aztek_cpp             = new Q(OS_AZTEK+"osThread_aztek.cpp");
  static final Q _osThread_aztek_hpp             = new Q(OS_AZTEK+"osThread_os.hpp");
  static final Q _os_aztek_cpp                   = new Q(OS_AZTEK+"os_aztek.cpp");
  static final Q _os_aztek_hpp                   = new Q(OS_AZTEK+"os_os.hpp");
  static final Q _os_aztek_inline_hpp            = new Q(OS_AZTEK+"os_os.inline.hpp");
  static final Q _thread_aztek_inline_hpp        = new Q(OS_AZTEK+"thread_os.inline.hpp");
  static final Q _vmError_aztek_cpp              = new Q(OS_AZTEK+"vmError_aztek.cpp");

  static final String INCLUDES_ACAPULCO_PORT     = " -I acapulco_port/acapulco/include/";
  static final String INCLUDES_ACAPULCO          = " -I "+SANDBOX+"/aztek/include/";
  static final String OS_ACAPULCO                = "src/os/acapulco/vm/";
  static final Q _attachListener_acapulco_cpp    = new Q(OS_ACAPULCO+"attachListener_acapulco.cpp");
  static final Q _c1_globals_acapulco_hpp        = new Q(OS_ACAPULCO+"c1_globals_os.hpp");
  static final Q _c2_globals_acapulco_hpp        = new Q(OS_ACAPULCO+"c2_globals_os.hpp");
  static final Q _externalProfiler_acapulc