OSDN Git Service

import 0.9.4
[handbrake-jp/handbrake-jp.git] / make / configure.py
diff --git a/make/configure.py b/make/configure.py
new file mode 100644 (file)
index 0000000..a12d380
--- /dev/null
@@ -0,0 +1,1409 @@
+###############################################################################
+##
+## This script is coded for minimum version of Python 2.4 .
+## Pyhthon3 is incompatible.
+##
+## Authors: konablend
+##
+###############################################################################
+
+import fnmatch
+import optparse
+import os
+import platform
+import re
+import subprocess
+import sys
+import time
+
+from optparse import OptionGroup
+from optparse import OptionGroup
+from optparse import OptionParser
+from sys import stderr
+from sys import stdout
+
+class AbortError( Exception ):
+    def __init__( self, format, *args ):
+        self.value = format % args
+    def __str__( self ):
+        return self.value
+
+###############################################################################
+##
+## Main configure object.
+##
+## dir = containing this configure script
+## cwd = current working dir at time of script launch
+##
+class Configure( object ):
+    OUT_QUIET   = 0
+    OUT_INFO    = 1
+    OUT_VERBOSE = 2
+
+    def __init__( self, verbose ):
+        self._log_info    = []
+        self._log_verbose = []
+        self._record      = False
+
+        self.verbose = verbose
+        self.dir = os.path.dirname( sys.argv[0] )
+        self.cwd = os.getcwd()
+
+        self.build_dir = '.'
+
+        ## compute src dir which is 2 dirs up from this script
+        self.src_dir = os.path.normpath( sys.argv[0] )
+        for i in range( 2 ):
+            self.src_dir = os.path.dirname( self.src_dir )
+        if len( self.src_dir ) == 0:
+            self.src_dir = os.curdir
+
+    def _final_dir( self, chdir, dir ):
+        dir = os.path.normpath( dir )
+        if not os.path.isabs( dir ):
+            if os.path.isabs( chdir ):
+                dir = os.path.normpath( os.path.abspath(dir ))
+            else:
+                dir = os.path.normpath( self.relpath( dir, chdir ))
+        return dir
+
+    ## output functions
+    def errln( self, format, *args ):
+        s = (format % args)
+        if re.match( '^.*[!?:;.]$', s ):
+            stderr.write( 'ERROR: %s configure stop.\n' % (s) )
+        else:
+            stderr.write( 'ERROR: %s; configure stop.\n' % (s) )
+        self.record_log()
+        sys.exit( 1 )
+    def infof( self, format, *args ):
+        line = format % args
+        self._log_verbose.append( line )
+        if cfg.verbose >= Configure.OUT_INFO:
+            self._log_info.append( line )
+            stdout.write( line )
+    def verbosef( self, format, *args ):
+        line = format % args
+        self._log_verbose.append( line )
+        if cfg.verbose >= Configure.OUT_VERBOSE:
+            stdout.write( line )
+
+    ## doc is ready to be populated
+    def doc_ready( self ):
+        ## compute final paths as they are after chdir into build
+        self.build_final  = os.curdir
+        self.src_final    = self._final_dir( self.build_dir, self.src_dir )
+        self.prefix_final = self._final_dir( self.build_dir, self.prefix_dir )
+
+        cfg.infof( 'compute: makevar SRC/    = %s\n', self.src_final )
+        cfg.infof( 'compute: makevar BUILD/  = %s\n', self.build_final )
+        cfg.infof( 'compute: makevar PREFIX/ = %s\n', self.prefix_final )
+
+        ## xcode does a chdir so we need appropriate values
+        macosx = os.path.join( self.src_dir, 'macosx' )
+        self.xcode_x_src    = self._final_dir( macosx, self.src_dir )
+        self.xcode_x_build  = self._final_dir( macosx, self.build_dir )
+        self.xcode_x_prefix = self._final_dir( macosx, self.prefix_dir )
+
+    ## perform chdir and enable log recording
+    def chdir( self ):
+        if os.path.abspath( self.build_dir ) == os.path.abspath( self.src_dir ):
+            cfg.errln( 'build (scratch) directory must not be the same as top-level source root!' )
+
+        if self.build_dir != os.curdir:
+            if os.path.exists( self.build_dir ):
+                if not options.force:
+                    self.errln( 'build directory already exists: %s (use --force to overwrite)', self.build_dir )
+            else:
+                self.mkdirs( self.build_dir )
+            self.infof( 'chdir: %s\n', self.build_dir )
+            os.chdir( self.build_dir )
+
+        ## enable logging
+        self._record = True
+
+    def mkdirs( self, dir ):
+        if len(dir) and not os.path.exists( dir ):
+            self.infof( 'mkdir: %s\n', dir )
+            os.makedirs( dir )
+
+    def open( self, *args ):
+        dir = os.path.dirname( args[0] )
+        if len(args) > 1 and args[1].find('w') != -1:
+            self.mkdirs( dir )
+        m = re.match( '^(.*)\.tmp$', args[0] )
+        if m:
+            self.infof( 'write: %s\n', m.group(1) )
+        else:
+            self.infof( 'write: %s\n', args[0] )
+
+        try:
+            return open( *args )
+        except Exception, x:
+            cfg.errln( 'open failure: %s', x )
+
+    def record_log( self ):
+        if not self._record:
+            return
+        self._record = False
+        self.verbose = Configure.OUT_QUIET
+        file = cfg.open( 'log/config.info.txt', 'w' )
+        for line in self._log_info:
+            file.write( line )
+        file.close()
+        file = cfg.open( 'log/config.verbose.txt', 'w' )
+        for line in self._log_verbose:
+            file.write( line )
+        file.close()
+
+    ## Find executable by searching path.
+    ## On success, returns full pathname of executable.
+    ## On fail, returns None.
+    def findExecutable( self, name ):
+        if len( os.path.split(name)[0] ):
+            if os.access( name, os.X_OK ):
+                return name
+            return None
+        
+        if not os.environ.has_key( 'PATH' ) or os.environ[ 'PATH' ] == '':
+            path = os.defpath
+        else:
+            path = os.environ['PATH']
+        
+        for dir in path.split( os.pathsep ):
+            f = os.path.join( dir, name )
+            if os.access( f, os.X_OK ):
+                return f
+        return None
+
+    ## taken from python2.6 -- we need it
+    def relpath( self, path, start=os.curdir ):
+        """Return a relative version of a path"""
+
+        if not path:
+            raise ValueError("no path specified")
+
+        start_list = os.path.abspath(start).split(os.sep)
+        path_list = os.path.abspath(path).split(os.sep)
+
+        # Work out how much of the filepath is shared by start and path.
+        i = len(os.path.commonprefix([start_list, path_list]))
+
+        rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:]
+        if not rel_list:
+            return os.curdir
+        return os.path.join(*rel_list)
+
+    ## update with parsed cli options
+    def update_cli( self, options ):
+        self.src_dir    = os.path.normpath( options.src )
+        self.build_dir  = os.path.normpath( options.build )
+        self.prefix_dir = os.path.normpath( options.prefix )
+        if options.sysroot != None:
+                self.sysroot_dir = os.path.normpath( options.sysroot )
+        else:
+                self.sysroot_dir = ""
+
+        if options.minver != None:
+                self.minver = options.minver
+        else:
+                self.minver = ""
+
+        ## special case if src == build: add build subdir
+        if os.path.abspath( self.src_dir ) == os.path.abspath( self.build_dir ):
+            self.build_dir = os.path.join( self.build_dir, 'build' )
+
+###############################################################################
+##
+## abstract action
+##
+## pretext = text which immediately follows 'probe:' output prefix
+## abort   = if true configure will exit on probe fail
+## head    = if true probe session is stripped of all but first line
+## session = output from command, including stderr
+## fail    = true if probe failed
+##
+class Action( object ):
+    actions = []
+
+    def __init__( self, category, pretext='unknown', abort=False, head=False ):
+        if self not in Action.actions:
+            Action.actions.append( self )
+
+        self.category = category
+        self.pretext  = pretext
+        self.abort    = abort
+        self.head     = head
+        self.session  = None
+
+        self.run_done = False
+        self.fail     = True
+        self.msg_fail = 'fail'
+        self.msg_pass = 'pass'
+        self.msg_end  = 'end'
+
+    def _actionBegin( self ):
+        cfg.infof( '%s: %s...', self.category, self.pretext )
+
+    def _actionEnd( self ):
+        if self.fail:
+            cfg.infof( '(%s) %s\n', self.msg_fail, self.msg_end )
+            if self.abort:
+                self._dumpSession( cfg.infof )
+                cfg.errln( 'unable to continue' )
+            self._dumpSession( cfg.verbosef )
+        else:
+            cfg.infof( '(%s) %s\n', self.msg_pass, self.msg_end )
+            self._dumpSession( cfg.verbosef )
+
+    def _dumpSession( self, printf ):
+        if self.session and len(self.session):
+            for line in self.session:
+                printf( '  : %s\n', line )
+        else:
+            printf( '  : <NO-OUTPUT>\n' )
+
+    def _parseSession( self ):
+        pass
+
+    def run( self ):
+        if self.run_done:
+            return
+        self.run_done = True
+        self._actionBegin()
+        self._action()
+        if not self.fail:
+            self._parseSession()
+        self._actionEnd()
+
+###############################################################################
+##
+## base probe: anything which runs in shell.
+##
+## pretext = text which immediately follows 'probe:' output prefix
+## command = full command and arguments to pipe
+## abort   = if true configure will exit on probe fail
+## head    = if true probe session is stripped of all but first line
+## session = output from command, including stderr
+## fail    = true if probe failed
+##
+class ShellProbe( Action ):
+    def __init__( self, pretext, command, abort=False, head=False ):
+        super( ShellProbe, self ).__init__( 'probe', pretext, abort, head )
+        self.command = command
+
+    def _action( self ):
+        ## pipe and redirect stderr to stdout; effects communicate result
+        pipe = subprocess.Popen( self.command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
+
+        ## read data into memory buffers, only first element (stdout) data is used
+        data = pipe.communicate()
+        self.fail = pipe.returncode != 0
+
+        if data[0]:
+            self.session = data[0].splitlines()
+        else:
+            self.session = []
+
+        if pipe.returncode:
+            self.msg_end = 'code %d' % (pipe.returncode)
+
+    def _dumpSession( self, printf ):
+        printf( '  + %s\n', self.command )
+        super( ShellProbe, self )._dumpSession( printf )
+
+###############################################################################
+##
+## GNU host tuple probe: determine canonical platform type
+##
+## example results from various platforms:
+##
+##   i386-apple-darwin9.6.0     (Mac OS X 10.5.6 Intel)
+##   powerpc-apple-darwin9.6.0  (Mac OS X 10.5.6 PPC)
+##   i686-pc-cygwin             (Cygwin, Microsoft Vista)
+##   x86_64-unknown-linux-gnu   (Linux, Fedora 10 x86_64)
+##
+class HostTupleProbe( ShellProbe, list ):
+    GNU_TUPLE_RE = '([^-]+)-?([^-]*)-([^0-9-]+)([^-]*)-?([^-]*)'
+
+    def __init__( self ):
+        super( HostTupleProbe, self ).__init__( 'host tuple', '%s/config.guess' % (cfg.dir), abort=True, head=True )
+
+    def _parseSession( self ):
+        if len(self.session):
+            self.spec = self.session[0]
+        else:
+            self.spec = ''
+
+        ## grok GNU host tuples
+        m = re.match( HostTupleProbe.GNU_TUPLE_RE, self.spec )
+        if not m:
+            self.fail = True
+            self.msg_end = 'invalid host tuple: %s' % (self.spec)
+            return
+
+        self.msg_end = self.spec
+
+        ## assign tuple from regex
+        self[:] = m.groups()
+
+        ## for clarity
+        self.machine = self[0]
+        self.vendor  = self[1]
+        self.system  = self[2]
+        self.release = self[3]
+        self.extra   = self[4]
+
+        ## nice formal name for 'system'
+        self.systemf = platform.system()
+
+        if self.match( '*-*-cygwin*' ):
+            self.systemf = self[2][0].upper() + self[2][1:]
+            
+    ## glob-match against spec
+    def match( self, *specs ):
+        for spec in specs:
+            if fnmatch.fnmatch( self.spec, spec ):
+                return True
+        return False
+
+###############################################################################
+
+class BuildAction( Action, list ):
+    def __init__( self ):
+        super( BuildAction, self ).__init__( 'compute', 'build tuple', abort=True )
+
+    def _action( self ):
+        ## check if --cross spec was used; must maintain 5-tuple compatibility with regex
+        if options.cross:
+            self.spec = os.path.basename( options.cross ).rstrip( '-' )
+        else:
+            self.spec = arch.mode[arch.mode.mode]
+
+        ## grok GNU host tuples
+        m = re.match( HostTupleProbe.GNU_TUPLE_RE, self.spec )
+        if not m:
+            self.msg_end = 'invalid host tuple: %s' % (self.spec)
+            return
+
+        self.msg_end = self.spec
+
+        ## assign tuple from regex
+        self[:] = m.groups()
+
+        ## for clarity
+        self.machine = self[0]
+        self.vendor  = self[1]
+        self.system  = self[2]
+        self.release = self[3]
+        self.extra   = self[4]
+        self.systemf = host.systemf
+
+        ## when cross we need switch for platforms
+        if options.cross:
+            if self.match( '*mingw*' ):
+                self.systemf = 'MinGW'
+            elif self.systemf:
+                self.systemf[0] = self.systemf[0].upper()
+            self.title = '%s %s' % (build.systemf,self.machine)
+        else:
+            self.title = '%s %s' % (build.systemf,arch.mode.mode)
+        self.fail = False
+
+    ## glob-match against spec
+    def match( self, *specs ):
+        for spec in specs:
+            if fnmatch.fnmatch( self.spec, spec ):
+                return True
+        return False
+
+###############################################################################
+##
+## value wrapper; value is accepted only if one of host specs matcheds
+## otherwise it is None (or a keyword-supplied val)
+##
+## result is attribute 'value'
+##
+class IfHost( object ):
+    def __init__( self, value, *specs, **kwargs ):
+        self.value = kwargs.get('none',None)
+        for spec in specs:
+            if host.match( spec ):
+                self.value = value
+                break
+
+    def __nonzero__( self ):
+        return self.value != None
+        
+    def __str__( self ):
+        return self.value
+
+
+###############################################################################
+##
+## platform conditional value; loops through list of tuples comparing
+## to first host match and sets value accordingly; the first value is
+## always default.
+##
+class ForHost( object ):
+    def __init__( self, default, *tuples ):
+        self.value = default
+        for tuple in tuples:
+            if host.match( tuple[1] ):
+                self.value = tuple[0]
+                break
+
+    def __str__( self ):
+        return self.value
+
+###############################################################################
+
+class ArchAction( Action ):
+    def __init__( self ):
+        super( ArchAction, self ).__init__( 'compute', 'available architectures', abort=True )
+        self.mode = SelectMode( 'architecture', (host.machine,host.spec) )
+
+    def _action( self ):
+        self.fail = False
+
+        ## some match on system should be made here; otherwise we signal a warning. 
+        if host.match( '*-*-cygwin*' ):
+            pass
+        elif host.match( '*-*-darwin*' ):
+            self.mode['i386']   = 'i386-apple-darwin%s'      % (host.release)
+            self.mode['x86_64'] = 'x86_64-apple-darwin%s'    % (host.release)
+            self.mode['ppc']    = 'powerpc-apple-darwin%s'   % (host.release)
+            self.mode['ppc64']  = 'powerpc64-apple-darwin%s' % (host.release)
+
+            ## special cases in that powerpc does not match gcc -arch value
+            ## which we like to use; so it has to be removed.
+            ## note: we don't know if apple will release Ssnow Leopad/ppc64 yet; just a guess.
+            if 'powerpc' in self.mode:
+                del self.mode['powerpc']
+                self.mode.mode = 'ppc'
+            elif 'powerpc64' in self.mode:
+                del self.mode['powerpc64']
+                self.mode.mode = 'ppc64'
+        elif host.match( '*-*-linux*' ):
+            pass
+        else:
+            self.msg_pass = 'WARNING'
+
+        self.msg_end = self.mode.toString()
+
+    ## glob-match against spec
+    def match( self, spec ):
+        return fnmatch.fnmatch( self.spec, spec )
+
+###############################################################################
+
+class CoreProbe( Action ):
+    def __init__( self ):
+        super( CoreProbe, self ).__init__( 'probe', 'number of CPU cores' )
+        self.count = 1
+
+    def _action( self ):
+        if self.fail:
+            ## good for darwin9.6.0 and linux
+            try:
+                self.count = os.sysconf( 'SC_NPROCESSORS_ONLN' )
+                if self.count < 1:
+                    self.count = 1
+                self.fail = False
+            except:
+                pass
+
+        if self.fail:
+            ## windows
+            try:
+                self.count = int( os.environ['NUMBER_OF_PROCESSORS'] )
+                if self.count < 1:
+                    self.count = 1
+                self.fail = False
+            except:
+                pass
+
+        ## clamp
+        if self.count < 1:
+            self.count = 1
+        elif self.count > 32:
+            self.count = 32
+
+        if options.launch:
+            if options.launch_jobs == 0:
+                self.jobs = core.count
+            else:
+                self.jobs = options.launch_jobs
+        else:
+            self.jobs = core.count
+
+        self.msg_end = str(self.count)
+
+###############################################################################
+
+class SelectMode( dict ):
+    def __init__( self, descr, *modes, **kwargs ):
+        super( SelectMode, self ).__init__( modes )
+        self.descr    = descr
+        self.modes    = modes
+        self.default  = kwargs.get('default',modes[0][0])
+        self.mode     = self.default
+
+    def cli_add_option( self, parser, option ):
+        parser.add_option( option, default=self.mode, metavar='MODE',
+            help='select %s mode: %s' % (self.descr,self.toString()),
+            action='callback', callback=self.cli_callback, type='str' )
+
+    def cli_callback( self, option, opt_str, value, parser, *args, **kwargs ):
+        if value not in self:
+            raise optparse.OptionValueError( 'invalid %s mode: %s (choose from %s)'
+                % (self.descr,value,self.toString( True )) )
+        self.mode = value
+
+    def toString( self, nodefault=False ):
+        keys = self.keys()
+        keys.sort()
+        if len(self) == 1:
+            value = self.mode
+        elif nodefault:
+            value = ' '.join( keys )
+        else:
+            value = '%s [%s]' % (' '.join( keys ), self.mode )
+        return value
+
+###############################################################################
+##
+## Repository object.
+## Holds information gleaned from subversion working dir.
+##
+## Builds are classed into one of the following types:
+##
+##  release
+##      must be built from official svn with '/tags/' in the url
+##  developer
+##      must be built from official svn but is not a release
+##  unofficial
+##      all other builds
+##
+class RepoProbe( ShellProbe ):
+    def __init__( self ):
+        super( RepoProbe, self ).__init__( 'svn info', 'svn info %s' % (cfg.src_dir) )
+
+        self.url       = 'svn://nowhere.com/project/unknown'
+        self.root      = 'svn://nowhere.com/project'
+        self.branch    = 'unknown'
+        self.uuid      = '00000000-0000-0000-0000-000000000000';
+        self.rev       = 0
+        self.date      = '0000-00-00 00:00:00 -0000'
+        self.official  = 0
+        self.type      = 'unofficial'
+
+    def _parseSession( self ):
+        for line in self.session:
+            ## grok fields
+            m = re.match( '([^:]+):\\s+(.+)', line )
+            if not m:
+                continue
+
+            (name,value) = m.groups()
+            if name == 'URL':
+                self.url = value
+            elif name == 'Repository Root':
+                self.root = value
+            elif name == 'Repository UUID':
+                self.uuid = value
+            elif name == 'Revision':
+                self.rev = int( value )
+            elif name == 'Last Changed Date':
+                # strip chars in parens
+                if value.find( ' (' ):
+                    self.date = value[0:value.find(' (')]
+                else:
+                    self.date = value
+
+        ## grok branch
+        i = self.url.rfind( '/' )
+        if i != -1 and i < len(self.url)-1:
+            self.branch = self.url[i+1:]
+
+        # type-classification via repository UUID
+        if self.uuid == 'b64f7644-9d1e-0410-96f1-a4d463321fa5':
+            self.official = 1
+            m = re.match( '([^:]+)://([^/]+)/(.+)', self.url )
+            if m and re.match( '.*tags/.*', m.group( 3 )):
+                self.type = 'release'
+            else:
+                self.type = 'developer'
+
+        self.msg_end = self.url
+
+###############################################################################
+##
+## project object.
+##
+## Contains manually updated version numbers consistent with HB releases
+## and other project metadata.
+##
+class Project( Action ):
+    def __init__( self ):
+        super( Project, self ).__init__( 'compute', 'project data' )
+
+        self.name          = 'HandBrake'
+        self.acro_lower    = 'hb'
+        self.acro_upper    = 'HB'
+        self.url_website   = 'http://handbrake.fr'
+        self.url_community = 'http://forum.handbrake.fr'
+        self.url_irc       = 'irc://irc.freenode.net/handbrake'
+
+        self.name_lower = self.name.lower()
+        self.name_upper = self.name.upper()
+
+        self.vmajor = 0
+        self.vminor = 9
+        self.vpoint = 4
+
+    def _action( self ):
+        ## add architecture to URL only for Mac
+        if fnmatch.fnmatch( build.spec, '*-*-darwin*' ):
+            url_arch = '.%s' % (arch.mode.mode)
+        else:
+            url_arch = ''
+
+        if repo.type == 'release':
+            self.version = '%d.%d.%d' % (self.vmajor,self.vminor,self.vpoint)
+            url_ctype = ''
+            url_ntype = 'stable'
+            self.build = time.strftime('%Y%m%d') + '00'
+            self.title = '%s %s (%s)' % (self.name,self.version,self.build)
+        elif repo.type == 'developer':
+            self.version = 'svn%d' % (repo.rev)
+            url_ctype = '_unstable'
+            url_ntype = 'unstable'
+            self.build = time.strftime('%Y%m%d') + '01'
+            self.title = '%s svn%d (%s)' % (self.name,repo.rev,self.build)
+        else:
+            self.version = 'rev%d' % (repo.rev)
+            url_ctype = '_unofficial'
+            url_ntype = 'unofficial'
+            self.build = time.strftime('%Y%m%d') + '99'
+            self.title = '%s rev%d (%s)' % (self.name,repo.rev,self.build)
+
+        self.url_appcast = 'http://handbrake.fr/appcast%s%s.xml' % (url_ctype,url_arch)
+        self.url_appnote = 'http://handbrake.fr/appcast/%s.html' % (url_ntype)
+
+        self.msg_end = '%s (%s)' % (self.name,repo.type)
+        self.fail = False
+
+###############################################################################
+
+class ToolProbe( Action ):
+    tools = []
+
+    def __init__( self, var, *names, **kwargs ):
+        super( ToolProbe, self ).__init__( 'find', abort=kwargs.get('abort',True) )
+        if not self in ToolProbe.tools:
+            ToolProbe.tools.append( self )
+        self.var    = var
+        self.names  = []
+        self.kwargs = kwargs
+        for name in names:
+            if name:
+                self.names.append( str(name) )
+        self.name = self.names[0]
+        self.pretext = self.name
+        self.pathname = self.names[0]
+
+    def _action( self ):
+        self.session = []
+        for i,name in enumerate(self.names):
+            self.session.append( 'name[%d] = %s' % (i,name) )
+        for name in self.names:
+            f = cfg.findExecutable( name )
+            if f:
+                self.pathname = f
+                self.fail = False
+                self.msg_end = f
+                break
+        if self.fail:
+            self.msg_end = 'not found'
+
+    def cli_add_option( self, parser ):
+        parser.add_option( '--'+self.name, metavar='PROG',
+            help='[%s]' % (self.pathname),
+            action='callback', callback=self.cli_callback, type='str' )
+
+    def cli_callback( self, option, opt_str, value, parser, *args, **kwargs ):
+        self.__init__( self.var, value, **self.kwargs )
+        self.run()
+
+    def doc_add( self, doc ):
+        doc.add( self.var, self.pathname )
+
+###############################################################################
+
+class SelectTool( Action ):
+    selects = []
+
+    def __init__( self, var, name, *pool, **kwargs ):
+        super( SelectTool, self ).__init__( 'select', abort=kwargs.get('abort',True) )
+        self.pretext = name
+        if not self in SelectTool.selects:
+            SelectTool.selects.append( self )
+        self.var      = var
+        self.name     = name
+        self.pool     = pool
+        self.kwargs   = kwargs
+
+    def _action( self ):
+        self.session = []
+        for i,(name,tool) in enumerate(self.pool):
+            self.session.append( 'tool[%d] = %s (%s)' % (i,name,tool.pathname) )
+        for (name,tool) in self.pool:
+            if not tool.fail:
+                self.selected = name
+                self.fail = False
+                self.msg_end = '%s (%s)' % (name,tool.pathname)
+                break
+        if self.fail:
+            self.msg_end = 'not found'
+
+    def cli_add_option( self, parser ):
+        parser.add_option( '--'+self.name, metavar='MODE',
+            help='select %s mode: %s' % (self.name,self.toString()),
+            action='callback', callback=self.cli_callback, type='str' )
+
+    def cli_callback( self, option, opt_str, value, parser, *args, **kwargs ):
+        found = False
+        for (name,tool) in self.pool:
+            if name == value:
+                found = True
+                self.__init__( self.var, self.name, [name,tool], **kwargs )
+                self.run()
+                break
+        if not found:
+            raise optparse.OptionValueError( 'invalid %s mode: %s (choose from %s)'
+                % (self.name,value,self.toString( True )) )
+
+    def doc_add( self, doc ):
+        doc.add( self.var, self.selected )
+
+    def toString( self, nodefault=False ):
+        if len(self.pool) == 1:
+            value = self.pool[0][0]
+        else:
+            s = ''
+            for key,value in self.pool:
+                s += ' ' + key
+            if nodefault:
+                value = s[1:]
+            else:
+                value = '%s [%s]' % (s[1:], self.selected )
+        return value
+
+###############################################################################
+##
+## config object used to output gnu-make or gnu-m4 output.
+##
+## - add() to add NAME/VALUE pairs suitable for both make/m4.
+## - addBlank() to add a linefeed for both make/m4.
+## - addMake() to add a make-specific line.
+## - addM4() to add a m4-specific line.
+##
+class ConfigDocument:
+    def __init__( self ):
+        self._elements = []
+
+    def _outputMake( self, file, namelen, name, value, append ):
+        if append:
+            if value == None or len(str(value)) == 0:
+                file.write( '%-*s +=\n' % (namelen, name) )
+            else:
+                file.write( '%-*s += %s\n' % (namelen, name, value) )
+        else:
+            if value == None or len(str(value)) == 0:
+                file.write( '%-*s  =\n' % (namelen, name) )
+            else:
+                file.write( '%-*s  = %s\n' % (namelen, name, value) )
+
+    def _outputM4( self, file, namelen, name, value ):
+        namelen += 7
+        name = '<<__%s>>,' % name.replace( '.', '_' )
+        file.write( 'define(%-*s  <<%s>>)dnl\n' % (namelen, name, value ))
+
+    def add( self, name, value, append=False ):
+        self._elements.append( [name,value,append] )
+
+    def addBlank( self ):
+        self._elements.append( None )
+
+    def addComment( self, format, *args ):
+        self.addMake( '## ' + format % args )
+        self.addM4( 'dnl ' + format % args )
+
+    def addMake( self, line ):
+        self._elements.append( ('?make',line) )
+
+    def addM4( self, line ):
+        self._elements.append( ('?m4',line) )
+
+    def output( self, file, type ):
+        namelen = 0
+        for item in self._elements:
+            if item == None or item[0].find( '?' ) == 0:
+                continue
+            if len(item[0]) > namelen:
+                namelen = len(item[0])
+        for item in self._elements:
+            if item == None:
+                if type == 'm4':
+                    file.write( 'dnl\n' )
+                else:
+                    file.write( '\n' )
+                continue
+            if item[0].find( '?' ) == 0:
+                if item[0].find( type, 1 ) == 1:
+                    file.write( '%s\n' % (item[1]) )
+                continue
+
+            if type == 'm4':
+                self._outputM4( file, namelen, item[0], item[1] )
+            else:
+                self._outputMake( file, namelen, item[0], item[1], item[2] )
+
+    def update( self, name, value ):
+        for item in self._elements:
+            if item == None:
+                continue
+            if item[0] == name:
+                item[1] = value
+                return
+        raise ValueError( 'element not found: %s' % (name) )
+
+    def write( self, type ):
+        if type == 'make':
+            fname = 'GNUmakefile'
+        elif type == 'm4':
+            fname = os.path.join( 'project', project.name_lower + '.m4' )
+        else:
+            raise ValueError, 'unknown file type: ' + type
+
+        ftmp  = fname + '.tmp'
+        try:
+            try:
+                file = cfg.open( ftmp, 'w' )
+                self.output( file, type )
+            finally:
+                try:
+                    file.close()
+                except:
+                    pass
+        except Exception, x:
+            try:
+                os.remove( ftmp )
+            except Exception, x:
+                pass
+            cfg.errln( 'failed writing to %s\n%s', ftmp, x )
+
+        try:
+            os.rename( ftmp, fname )
+        except Exception, x:
+            cfg.errln( 'failed writing to %s\n%s', fname, x )
+
+###############################################################################
+##
+## create cli parser
+##
+
+## class to hook options and create CONF.args list
+class Option( optparse.Option ):
+    conf_args = []
+
+    def _conf_record( self, opt, value ):
+        ## skip conf,force,launch
+        if re.match( '^--(conf|force|launch).*$', opt ):
+            return
+
+        ## remove duplicates (last duplicate wins)
+        for i,arg in enumerate( Option.conf_args ):
+            if opt == arg[0]:
+                del Option.conf_args[i]
+                break
+
+        if value:
+            Option.conf_args.append( [opt,'%s=%s' % (opt,value)] )
+        else:
+            Option.conf_args.append( [opt,'%s' % (opt)] )
+
+    def take_action( self, action, dest, opt, value, values, parser ):
+        self._conf_record( opt, value )
+        return optparse.Option.take_action( self, action, dest, opt, value, values, parser )
+
+def createCLI():
+    cli = OptionParser( 'usage: %prog [OPTIONS...] [TARGETS...]' )
+    cli.option_class = Option
+
+    cli.description = ''
+    cli.description += 'Configure %s build system.' % (project.name)
+
+    ## add hidden options
+    cli.add_option( '--conf-method', default='terminal', action='store', help=optparse.SUPPRESS_HELP )
+    cli.add_option( '--force', default=False, action='store_true', help='overwrite existing build config' )
+    cli.add_option( '--verbose', default=False, action='store_true', help='increase verbosity' )
+
+    ## add install options
+    grp = OptionGroup( cli, 'Directory Locations' )
+    h = IfHost( 'specify sysroot (e.g. for Leopard builds from Snow Leapard)', '*-*-darwin*', none=optparse.SUPPRESS_HELP ).value
+    grp.add_option( '--sysroot', default=None, action='store', metavar='DIR',
+        help=h )
+    grp.add_option( '--src', default=cfg.src_dir, action='store', metavar='DIR',
+        help='specify top-level source dir [%s]' % (cfg.src_dir) )
+    grp.add_option( '--build', default=cfg.build_dir, action='store', metavar='DIR',
+        help='specify build scratch/output dir [%s]' % (cfg.build_dir) )
+    grp.add_option( '--prefix', default=cfg.prefix_dir, action='store', metavar='DIR',
+        help='specify install dir for products [%s]' % (cfg.prefix_dir) )
+    cli.add_option_group( grp )
+
+    ## add feature options
+    grp = OptionGroup( cli, 'Feature Options' )
+
+    h = IfHost( 'enable assembly code in non-contrib modules', 'NOMATCH*-*-darwin*', 'NOMATCH*-*-linux*', none=optparse.SUPPRESS_HELP ).value
+    grp.add_option( '--enable-asm', default=False, action='store_true', help=h )
+
+    h = IfHost( 'disable GTK GUI', '*-*-linux*', none=optparse.SUPPRESS_HELP ).value
+    grp.add_option( '--disable-gtk', default=False, action='store_true', help=h )
+    h = IfHost( 'enable GTK GUI (mingw)', '*-*-mingw*', none=optparse.SUPPRESS_HELP ).value
+    grp.add_option( '--enable-gtk-mingw', default=False, action='store_true', help=h )
+
+    h = IfHost( 'disable Xcode', '*-*-darwin*', none=optparse.SUPPRESS_HELP ).value
+    grp.add_option( '--disable-xcode', default=False, action='store_true', help=h )
+
+    cli.add_option_group( grp )
+
+    ## add launch options
+    grp = OptionGroup( cli, 'Launch Options' )
+    grp.add_option( '--launch', default=False, action='store_true',
+        help='launch build, capture log and wait for completion' )
+    grp.add_option( '--launch-jobs', default=1, action='store', metavar='N', type='int',
+        help='allow N jobs at once; 0 to match CPU count [1]' )
+    grp.add_option( '--launch-args', default=None, action='store', metavar='ARGS',
+        help='specify additional ARGS for launch command' )
+    grp.add_option( '--launch-quiet', default=False, action='store_true',
+        help='do not echo build output while waiting' )
+    cli.add_option_group( grp )
+
+    ## add compile options
+    grp = OptionGroup( cli, 'Compiler Options' )
+    debugMode.cli_add_option( grp, '--debug' )
+    optimizeMode.cli_add_option( grp, '--optimize' )
+    arch.mode.cli_add_option( grp, '--arch' )
+    grp.add_option( '--cross', default=None, action='store', metavar='SPEC',
+        help='specify GCC cross-compilation spec' )
+    h = IfHost( 'Min OS X Version', '*-*-darwin*', none=optparse.SUPPRESS_HELP ).value
+    grp.add_option( '--minver', default=None, action='store', metavar='VER',
+        help=h )
+    cli.add_option_group( grp )
+
+    ## add tool locations
+    grp = OptionGroup( cli, 'Tool Basenames and Locations' )
+    for tool in ToolProbe.tools:
+        tool.cli_add_option( grp )
+    cli.add_option_group( grp )
+
+    ## add tool modes
+    grp = OptionGroup( cli, 'Tool Options' )
+    for select in SelectTool.selects:
+        select.cli_add_option( grp )
+    cli.add_option_group( grp )
+    return cli
+
+###############################################################################
+##
+## launcher - used for QuickStart method; launch; build and capture log.
+##
+class Launcher:
+    def __init__( self, targets ):
+        # open build logfile
+        self._file = cfg.open( 'log/build.txt', 'w' )
+
+        cmd = '%s -j%d' % (Tools.gmake.pathname,core.jobs)
+        if options.launch_args:
+            cmd += ' ' + options.launch_args
+        if len(targets):
+            cmd += ' ' + ' '.join(targets)
+
+        ## record begin
+        timeBegin = time.time()
+        self.infof( 'time begin: %s\n', time.asctime() )
+        self.infof( 'launch: %s\n', cmd )
+        if options.launch_quiet:
+            stdout.write( 'building to %s ...\n' % (os.path.abspath( cfg.build_final )))
+        else:
+            stdout.write( '%s\n' % ('-' * 79) )
+
+        ## launch/pipe
+        try:
+            pipe = subprocess.Popen( cmd, shell=True, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
+        except Exception, x:
+            cfg.errln( 'launch failure: %s', x )
+        for line in pipe.stdout:
+            self.echof( '%s', line )
+        pipe.wait()
+
+        ## record end
+        timeEnd = time.time()
+        elapsed = timeEnd - timeBegin
+
+        if pipe.returncode:
+            result = 'FAILURE (code %d)' % pipe.returncode
+        else:
+            result = 'SUCCESS'
+
+        ## present duration in decent format
+        seconds = elapsed
+        hours = int(seconds / 3600)
+        seconds -= hours * 3600
+        minutes = int(seconds / 60)
+        seconds -= minutes * 60
+
+        segs = []
+        duration = ''
+
+        if hours == 1:
+            segs.append( '%d hour' % hours )
+        elif hours > 1:
+            segs.append( '%d hours' % hours )
+
+        if len(segs) or minutes == 1:
+            segs.append( '%d minute' % minutes )
+        elif len(segs) or  minutes > 1:
+            segs.append( '%d minutes' % minutes )
+
+        if seconds == 1:
+            segs.append( '%d second' % seconds )
+        else:
+            segs.append( '%d seconds' % seconds )
+
+        if not options.launch_quiet:
+            stdout.write( '%s\n' % ('-' * 79) )
+        self.infof( 'time end: %s\n', time.asctime() )
+        self.infof( 'duration: %s (%.2fs)\n', ', '.join(segs), elapsed )
+        self.infof( 'result: %s\n', result )
+
+        ## cleanup
+        self._file.close()
+
+    def echof( self, format, *args ):
+        line = format % args
+        self._file.write( line )
+        if not options.launch_quiet:
+            stdout.write( '  : %s' % line )
+            stdout.flush()
+
+    def infof( self, format, *args ):
+        line = format % args
+        self._file.write( line )
+        cfg.infof( '%s', line )
+
+###############################################################################
+##
+## main program
+##
+try:
+    ## we need to pre-check argv for -h or --help or --verbose to deal with
+    ## initializing Configure correctly.
+    verbose = Configure.OUT_INFO
+    for arg in sys.argv:
+        if arg == '-h' or arg == '--help':
+            verbose = Configure.OUT_QUIET
+            break
+        if arg == '--verbose':
+            verbose = Configure.OUT_VERBOSE
+
+    ## create main objects; actions/probes run() is delayed.
+    ## if any actions must be run earlier (eg: for configure --help purposes)
+    ## then run() must be invoked earlier. subequent run() invocations
+    ## are ignored.
+    cfg   = Configure( verbose )
+    host  = HostTupleProbe(); host.run()
+
+    cfg.prefix_dir = ForHost( '/usr/local', ['/Applications','*-*-darwin*'] ).value
+
+    build = BuildAction()
+    arch  = ArchAction(); arch.run()
+
+    ## create remaining main objects
+    core    = CoreProbe()
+    repo    = RepoProbe()
+    project = Project()
+
+    ## create tools in a scope
+    class Tools:
+        ar    = ToolProbe( 'AR.exe',    'ar' )
+        cp    = ToolProbe( 'CP.exe',    'cp' )
+        curl  = ToolProbe( 'CURL.exe',  'curl', abort=False )
+        gcc   = ToolProbe( 'GCC.gcc',   'gcc', IfHost( 'gcc-4', '*-*-cygwin*' ))
+
+        if host.match( '*-*-darwin*' ):
+            gmake = ToolProbe( 'GMAKE.exe', 'make', 'gmake' )
+        else:
+            gmake = ToolProbe( 'GMAKE.exe', 'gmake', 'make' )
+
+        m4     = ToolProbe( 'M4.exe',     'm4' )
+        mkdir  = ToolProbe( 'MKDIR.exe',  'mkdir' )
+        patch  = ToolProbe( 'PATCH.exe',  'gpatch', 'patch' )
+        rm     = ToolProbe( 'RM.exe',     'rm' )
+        ranlib = ToolProbe( 'RANLIB.exe', 'ranlib' )
+        strip  = ToolProbe( 'STRIP.exe',  'strip' )
+        tar    = ToolProbe( 'TAR.exe',    'gtar', 'tar' )
+        wget   = ToolProbe( 'WGET.exe',   'wget', abort=False )
+        yasm   = ToolProbe( 'YASM.exe',   'yasm', abort=False )
+
+        xcodebuild = ToolProbe( 'XCODEBUILD.exe', 'xcodebuild', abort=False )
+        lipo       = ToolProbe( 'LIPO.exe',       'lipo', abort=False )
+
+        fetch = SelectTool( 'FETCH.select', 'fetch', ['wget',wget], ['curl',curl] )
+
+    ## run tool probes
+    for tool in ToolProbe.tools:
+        tool.run()
+    for select in SelectTool.selects:
+        select.run()
+
+    debugMode = SelectMode( 'debug', ('none','none'), ('min','min'), ('std','std'), ('max','max') )
+    optimizeMode = SelectMode( 'optimize', ('none','none'), ('speed','speed'), ('size','size'), default='speed' )
+
+    ## create CLI and parse
+    cli = createCLI()
+    (options,args) = cli.parse_args()
+
+    ## update cfg with cli directory locations
+    cfg.update_cli( options )
+
+    ## prepare list of targets and NAME=VALUE args to pass to make
+    targets = []
+    exports = []
+    rx_exports = re.compile( '([^=]+)=(.*)' )
+    for arg in args:
+        m = rx_exports.match( arg )
+        if m:
+            exports.append( m.groups() )
+        else:
+            targets.append( arg )
+
+    ## re-run tools with cross-compilation needs
+    if options.cross:
+        for tool in ( Tools.ar, Tools.gcc, Tools.ranlib, Tools.strip ):
+            tool.__init__( tool.var, '%s-%s' % (options.cross,tool.name), **tool.kwargs )
+            tool.run()
+
+    ## run delayed actions
+    for action in Action.actions:
+        action.run()
+
+    ## cfg hook before doc prep
+    cfg.doc_ready()
+
+    ## create document object
+    doc = ConfigDocument()
+    doc.addComment( 'generated by configure on %s', time.strftime( '%c' ))
+
+    ## add configure line for reconfigure purposes
+    doc.addBlank()
+    args = []
+    for arg in Option.conf_args:
+        args.append( arg[1] )
+    doc.add( 'CONF.args', ' '.join( args ))
+
+    doc.addBlank()
+    doc.add( 'HB.title',       project.title )
+    doc.add( 'HB.name',        project.name )
+    doc.add( 'HB.name.lower',  project.name_lower )
+    doc.add( 'HB.name.upper',  project.name_upper )
+    doc.add( 'HB.acro.lower',  project.acro_lower )
+    doc.add( 'HB.acro.upper',  project.acro_upper )
+
+    doc.add( 'HB.url.website',    project.url_website )
+    doc.add( 'HB.url.community',  project.url_community )
+    doc.add( 'HB.url.irc',        project.url_irc )
+    doc.add( 'HB.url.appcast',    project.url_appcast )
+    doc.add( 'HB.url.appnote',    project.url_appnote )
+
+    doc.add( 'HB.version.major',  project.vmajor )
+    doc.add( 'HB.version.minor',  project.vminor )
+    doc.add( 'HB.version.point',  project.vpoint )
+    doc.add( 'HB.version',        project.version )
+    doc.add( 'HB.version.hex',    '%04x%02x%02x%08x' % (project.vmajor,project.vminor,project.vpoint,repo.rev) )
+
+    doc.add( 'HB.build', project.build )
+
+    doc.add( 'HB.repo.url',       repo.url )
+    doc.add( 'HB.repo.root',      repo.root )
+    doc.add( 'HB.repo.branch',    repo.branch )
+    doc.add( 'HB.repo.uuid',      repo.uuid )
+    doc.add( 'HB.repo.rev',       repo.rev )
+    doc.add( 'HB.repo.date',      repo.date )
+    doc.add( 'HB.repo.official',  repo.official )
+    doc.add( 'HB.repo.type',      repo.type )
+
+    doc.addBlank()
+    doc.add( 'HOST.spec',    host.spec )
+    doc.add( 'HOST.machine', host.machine )
+    doc.add( 'HOST.vendor',  host.vendor )
+    doc.add( 'HOST.system',  host.system )
+    doc.add( 'HOST.systemf', host.systemf )
+    doc.add( 'HOST.release', host.release )
+    doc.add( 'HOST.extra',   host.extra )
+    doc.add( 'HOST.title',   '%s %s' % (host.systemf,arch.mode.default) )
+    doc.add( 'HOST.ncpu',    core.count )
+
+    doc.addBlank()
+    doc.add( 'BUILD.spec',    build.spec )
+    doc.add( 'BUILD.machine', build.machine )
+    doc.add( 'BUILD.vendor',  build.vendor )
+    doc.add( 'BUILD.system',  build.system )
+    doc.add( 'BUILD.systemf', build.systemf )
+    doc.add( 'BUILD.release', build.release )
+    doc.add( 'BUILD.extra',   build.extra )
+    doc.add( 'BUILD.title',   build.title )
+    doc.add( 'BUILD.ncpu',    core.count )
+    doc.add( 'BUILD.jobs',    core.jobs )
+
+    doc.add( 'BUILD.cross',        int(options.cross != None or arch.mode.mode != arch.mode.default) )
+    if options.cross:
+        doc.add( 'BUILD.cross.prefix', '%s-' % (options.cross) )
+    else:
+        doc.add( 'BUILD.cross.prefix', '' )
+
+    doc.add( 'BUILD.method', 'terminal' )
+    doc.add( 'BUILD.date',   time.strftime('%c') )
+    doc.add( 'BUILD.arch',   arch.mode.mode )
+
+    doc.addBlank()
+    doc.add( 'CONF.method', options.conf_method )
+
+    doc.addBlank()
+    doc.add( 'SRC',     cfg.src_final )
+    doc.add( 'SRC/',    cfg.src_final + os.sep )
+    doc.add( 'BUILD',   cfg.build_final )
+    doc.add( 'BUILD/',  cfg.build_final + os.sep )
+    doc.add( 'PREFIX',  cfg.prefix_final )
+    doc.add( 'PREFIX/', cfg.prefix_final + os.sep )
+    
+    doc.addBlank()
+    doc.add( 'FEATURE.asm',   'disabled' )
+    doc.add( 'FEATURE.gtk',   int( not options.disable_gtk ))
+    doc.add( 'FEATURE.gtk.mingw',   int( options.enable_gtk_mingw ))
+    doc.add( 'FEATURE.xcode', int( not (Tools.xcodebuild.fail or options.disable_xcode or options.cross) ))
+
+    if not Tools.xcodebuild.fail and not options.disable_xcode:
+        doc.addBlank()
+        doc.add( 'XCODE.external.src',    cfg.xcode_x_src )
+        doc.add( 'XCODE.external.build',  cfg.xcode_x_build )
+        doc.add( 'XCODE.external.prefix', cfg.xcode_x_prefix )
+
+    doc.addMake( '' )
+    doc.addMake( '## include definitions' )
+    doc.addMake( 'include $(SRC/)make/include/main.defs' )
+
+    doc.addBlank()
+    for tool in ToolProbe.tools:
+        tool.doc_add( doc )
+
+    doc.addBlank()
+    for select in SelectTool.selects:
+        select.doc_add( doc )
+
+    doc.addBlank()
+    if build.match( '*-*-darwin*' ):
+        doc.add( 'GCC.archs', arch.mode.mode )
+        doc.add( 'GCC.sysroot', cfg.sysroot_dir )
+        doc.add( 'GCC.minver', cfg.minver )
+    else:
+        doc.add( 'GCC.archs', '' )
+        doc.add( 'GCC.sysroot', '' )
+        doc.add( 'GCC.minver', '' )
+    doc.add( 'GCC.ldsysroot', '$(GCC.sysroot)' )
+    doc.add( 'GCC.ldminver', '$(GCC.minver)' )
+    doc.add( 'GCC.g', debugMode.mode )
+    doc.add( 'GCC.O', optimizeMode.mode )
+
+    if options.enable_asm and not Tools.yasm.fail:
+        asm = ''
+        if build.match( 'i?86-*' ):
+            asm = 'x86'
+            doc.add( 'LIBHB.GCC.D', 'HAVE_MMX', append=True )
+            doc.add( 'LIBHB.YASM.D', 'ARCH_X86', append=True )
+            if build.match( '*-*-darwin*' ):
+                doc.add( 'LIBHB.YASM.f', 'macho32' )
+            else:
+                doc.add( 'LIBHB.YASM.f', 'elf32' )
+            doc.add( 'LIBHB.YASM.m', 'x86' )
+        elif build.match( 'x86_64-*' ):
+            asm = 'x86'
+            doc.add( 'LIBHB.GCC.D', 'HAVE_MMX ARCH_X86_64', append=True )
+            if build.match( '*-*-darwin*' ):
+                doc.add( 'LIBHB.YASM.D', 'ARCH_X86_64 PIC', append=True )
+                doc.add( 'LIBHB.YASM.f', 'macho64' )
+            else:
+                doc.add( 'LIBHB.YASM.D', 'ARCH_X86_64', append=True )
+                doc.add( 'LIBHB.YASM.f', 'elf64' )
+            doc.add( 'LIBHB.YASM.m', 'amd64' )
+        doc.update( 'FEATURE.asm', asm )
+
+    ## add exports to make
+    if len(exports):
+        doc.addBlank()
+        doc.addComment( 'overrides via VARIABLE=VALUE on command-line' )
+        for nv in exports:
+            doc.add( nv[0], nv[1] )
+
+    doc.addMake( '' )
+    doc.addMake( '## include custom definitions' )
+    doc.addMake( '-include $(SRC/)custom.defs' )
+    doc.addMake( '-include $(BUILD/)GNUmakefile.custom.defs' )
+
+    doc.addMake( '' )
+    doc.addMake( '## include rules' )
+    doc.addMake( 'include $(SRC/)make/include/main.rules' )
+    doc.addMake( '-include $(SRC/)custom.rules' )
+    doc.addMake( '-include $(BUILD/)GNUmakefile.custom.rules' )
+
+    ## chdir
+    cfg.chdir()
+
+    ## perform
+    doc.write( 'make' )
+    doc.write( 'm4' )
+    if options.launch:
+        Launcher( targets )
+
+    cfg.record_log()
+
+    if os.path.normpath( cfg.build_dir ) == os.curdir:
+        nocd = True
+    else:
+        nocd = False
+
+    stdout.write( '%s\n' % ('-' * 79) )
+    if options.launch:
+        stdout.write( 'Build is finished!\n' )
+        if nocd:
+            stdout.write( 'You may now examine the output.\n' )
+        else:
+            stdout.write( 'You may now cd into %s and examine the output.\n' % (cfg.build_dir) )
+    else:
+        stdout.write( 'Build is configured!\n' )
+        if nocd:
+            stdout.write( 'You may now run make (%s).\n' % (Tools.gmake.pathname) )
+        else:
+            stdout.write( 'You may now cd into %s and run make (%s).\n' % (cfg.build_dir,Tools.gmake.pathname) )
+
+except AbortError, x:
+    stderr.write( 'ERROR: %s\n' % (x) )
+    try:
+        cfg.record_log()
+    except:
+        pass        
+    sys.exit( 1 )    
+
+sys.exit( 0 )