OSDN Git Service

This patch adds mingw32 cross-compilation support to HandBrake trunk to
[handbrake-jp/handbrake-jp-git.git] / make / configure.py
1 ###############################################################################
2 ##
3 ## This script is coded for minimum version of Python 2.4 .
4 ## Pyhthon3 is incompatible.
5 ##
6 ## Authors: konablend
7 ##
8 ###############################################################################
9
10 import fnmatch
11 import optparse
12 import os
13 import platform
14 import re
15 import subprocess
16 import sys
17 import time
18
19 from optparse import OptionGroup
20 from optparse import OptionGroup
21 from optparse import OptionParser
22 from sys import stderr
23 from sys import stdout
24
25 class AbortError( Exception ):
26     def __init__( self, format, *args ):
27         self.value = format % args
28     def __str__( self ):
29         return self.value
30
31 ###############################################################################
32 ##
33 ## Main configure object.
34 ##
35 ## dir = containing this configure script
36 ## cwd = current working dir at time of script launch
37 ##
38 class Configure( object ):
39     OUT_QUIET   = 0
40     OUT_INFO    = 1
41     OUT_VERBOSE = 2
42
43     def __init__( self, verbose ):
44         self._log_info    = []
45         self._log_verbose = []
46         self._record      = False
47
48         self.verbose = verbose
49         self.dir = os.path.dirname( sys.argv[0] )
50         self.cwd = os.getcwd()
51
52         self.build_dir = '.'
53
54         ## compute src dir which is 2 dirs up from this script
55         self.src_dir = os.path.normpath( sys.argv[0] )
56         for i in range( 2 ):
57             self.src_dir = os.path.dirname( self.src_dir )
58         if len( self.src_dir ) == 0:
59             self.src_dir = os.curdir
60
61     def _final_dir( self, chdir, dir ):
62         dir = os.path.normpath( dir )
63         if not os.path.isabs( dir ):
64             if os.path.isabs( chdir ):
65                 dir = os.path.normpath( os.path.abspath(dir ))
66             else:
67                 dir = os.path.normpath( self.relpath( dir, chdir ))
68         return dir
69
70     ## output functions
71     def errln( self, format, *args ):
72         s = (format % args)
73         if re.match( '^.*[!?:;.]$', s ):
74             stderr.write( 'ERROR: %s configure stop.\n' % (s) )
75         else:
76             stderr.write( 'ERROR: %s; configure stop.\n' % (s) )
77         self.record_log()
78         sys.exit( 1 )
79     def infof( self, format, *args ):
80         line = format % args
81         self._log_verbose.append( line )
82         if cfg.verbose >= Configure.OUT_INFO:
83             self._log_info.append( line )
84             stdout.write( line )
85     def verbosef( self, format, *args ):
86         line = format % args
87         self._log_verbose.append( line )
88         if cfg.verbose >= Configure.OUT_VERBOSE:
89             stdout.write( line )
90
91     ## doc is ready to be populated
92     def doc_ready( self ):
93         ## compute final paths as they are after chdir into build
94         self.build_final  = os.curdir
95         self.src_final    = self._final_dir( self.build_dir, self.src_dir )
96         self.prefix_final = self._final_dir( self.build_dir, self.prefix_dir )
97
98         cfg.infof( 'compute: makevar SRC/    = %s\n', self.src_final )
99         cfg.infof( 'compute: makevar BUILD/  = %s\n', self.build_final )
100         cfg.infof( 'compute: makevar PREFIX/ = %s\n', self.prefix_final )
101
102         ## xcode does a chdir so we need appropriate values
103         macosx = os.path.join( self.src_dir, 'macosx' )
104         self.xcode_x_src    = self._final_dir( macosx, self.src_dir )
105         self.xcode_x_build  = self._final_dir( macosx, self.build_dir )
106         self.xcode_x_prefix = self._final_dir( macosx, self.prefix_dir )
107
108     ## perform chdir and enable log recording
109     def chdir( self ):
110         if os.path.abspath( self.build_dir ) == os.path.abspath( self.src_dir ):
111             cfg.errln( 'build (scratch) directory must not be the same as top-level source root!' )
112
113         if self.build_dir != os.curdir:
114             if os.path.exists( self.build_dir ):
115                 if not options.force:
116                     self.errln( 'build directory already exists: %s (use --force to overwrite)', self.build_dir )
117             else:
118                 self.mkdirs( self.build_dir )
119             self.infof( 'chdir: %s\n', self.build_dir )
120             os.chdir( self.build_dir )
121
122         ## enable logging
123         self._record = True
124
125     def mkdirs( self, dir ):
126         if len(dir) and not os.path.exists( dir ):
127             self.infof( 'mkdir: %s\n', dir )
128             os.makedirs( dir )
129
130     def open( self, *args ):
131         dir = os.path.dirname( args[0] )
132         if len(args) > 1 and args[1].find('w') != -1:
133             self.mkdirs( dir )
134         m = re.match( '^(.*)\.tmp$', args[0] )
135         if m:
136             self.infof( 'write: %s\n', m.group(1) )
137         else:
138             self.infof( 'write: %s\n', args[0] )
139
140         try:
141             return open( *args )
142         except Exception, x:
143             cfg.errln( 'open failure: %s', x )
144
145     def record_log( self ):
146         if not self._record:
147             return
148         self._record = False
149         self.verbose = Configure.OUT_QUIET
150         file = cfg.open( 'log/config.info.txt', 'w' )
151         for line in self._log_info:
152             file.write( line )
153         file.close()
154         file = cfg.open( 'log/config.verbose.txt', 'w' )
155         for line in self._log_verbose:
156             file.write( line )
157         file.close()
158
159     ## Find executable by searching path.
160     ## On success, returns full pathname of executable.
161     ## On fail, returns None.
162     def findExecutable( self, name ):
163         if len( os.path.split(name)[0] ):
164             if os.access( name, os.X_OK ):
165                 return name
166             return None
167         
168         if not os.environ.has_key( 'PATH' ) or os.environ[ 'PATH' ] == '':
169             path = os.defpath
170         else:
171             path = os.environ['PATH']
172         
173         for dir in path.split( os.pathsep ):
174             f = os.path.join( dir, name )
175             if os.access( f, os.X_OK ):
176                 return f
177         return None
178
179     ## taken from python2.6 -- we need it
180     def relpath( self, path, start=os.curdir ):
181         """Return a relative version of a path"""
182
183         if not path:
184             raise ValueError("no path specified")
185
186         start_list = os.path.abspath(start).split(os.sep)
187         path_list = os.path.abspath(path).split(os.sep)
188
189         # Work out how much of the filepath is shared by start and path.
190         i = len(os.path.commonprefix([start_list, path_list]))
191
192         rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:]
193         if not rel_list:
194             return os.curdir
195         return os.path.join(*rel_list)
196
197     ## update with parsed cli options
198     def update_cli( self, options ):
199         self.src_dir    = os.path.normpath( options.src )
200         self.build_dir  = os.path.normpath( options.build )
201         self.prefix_dir = os.path.normpath( options.prefix )
202
203         ## special case if src == build: add build subdir
204         if os.path.abspath( self.src_dir ) == os.path.abspath( self.build_dir ):
205             self.build_dir = os.path.join( self.build_dir, 'build' )
206
207 ###############################################################################
208 ##
209 ## abstract action
210 ##
211 ## pretext = text which immediately follows 'probe:' output prefix
212 ## abort   = if true configure will exit on probe fail
213 ## head    = if true probe session is stripped of all but first line
214 ## session = output from command, including stderr
215 ## fail    = true if probe failed
216 ##
217 class Action( object ):
218     actions = []
219
220     def __init__( self, category, pretext='unknown', abort=False, head=False ):
221         if self not in Action.actions:
222             Action.actions.append( self )
223
224         self.category = category
225         self.pretext  = pretext
226         self.abort    = abort
227         self.head     = head
228         self.session  = None
229
230         self.run_done = False
231         self.fail     = True
232         self.msg_fail = 'fail'
233         self.msg_pass = 'pass'
234         self.msg_end  = 'end'
235
236     def _actionBegin( self ):
237         cfg.infof( '%s: %s...', self.category, self.pretext )
238
239     def _actionEnd( self ):
240         if self.fail:
241             cfg.infof( '(%s) %s\n', self.msg_fail, self.msg_end )
242             if self.abort:
243                 self._dumpSession( cfg.infof )
244                 cfg.errln( 'unable to continue' )
245             self._dumpSession( cfg.verbosef )
246         else:
247             cfg.infof( '(%s) %s\n', self.msg_pass, self.msg_end )
248             self._dumpSession( cfg.verbosef )
249
250     def _dumpSession( self, printf ):
251         if self.session and len(self.session):
252             for line in self.session:
253                 printf( '  : %s\n', line )
254         else:
255             printf( '  : <NO-OUTPUT>\n' )
256
257     def _parseSession( self ):
258         pass
259
260     def run( self ):
261         if self.run_done:
262             return
263         self.run_done = True
264         self._actionBegin()
265         self._action()
266         if not self.fail:
267             self._parseSession()
268         self._actionEnd()
269
270 ###############################################################################
271 ##
272 ## base probe: anything which runs in shell.
273 ##
274 ## pretext = text which immediately follows 'probe:' output prefix
275 ## command = full command and arguments to pipe
276 ## abort   = if true configure will exit on probe fail
277 ## head    = if true probe session is stripped of all but first line
278 ## session = output from command, including stderr
279 ## fail    = true if probe failed
280 ##
281 class ShellProbe( Action ):
282     def __init__( self, pretext, command, abort=False, head=False ):
283         super( ShellProbe, self ).__init__( 'probe', pretext, abort, head )
284         self.command = command
285
286     def _action( self ):
287         ## pipe and redirect stderr to stdout; effects communicate result
288         pipe = subprocess.Popen( self.command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
289
290         ## read data into memory buffers, only first element (stdout) data is used
291         data = pipe.communicate()
292         self.fail = pipe.returncode != 0
293
294         if data[0]:
295             self.session = data[0].splitlines()
296         else:
297             self.session = []
298
299         if pipe.returncode:
300             self.msg_end = 'code %d' % (pipe.returncode)
301
302     def _dumpSession( self, printf ):
303         printf( '  + %s\n', self.command )
304         super( ShellProbe, self )._dumpSession( printf )
305
306 ###############################################################################
307 ##
308 ## GNU host tuple probe: determine canonical platform type
309 ##
310 ## example results from various platforms:
311 ##
312 ##   i386-apple-darwin9.6.0     (Mac OS X 10.5.6 Intel)
313 ##   powerpc-apple-darwin9.6.0  (Mac OS X 10.5.6 PPC)
314 ##   i686-pc-cygwin             (Cygwin, Microsoft Vista)
315 ##   x86_64-unknown-linux-gnu   (Linux, Fedora 10 x86_64)
316 ##
317 class HostTupleProbe( ShellProbe, list ):
318     GNU_TUPLE_RE = '([^-]+)-?([^-]*)-([^0-9-]+)([^-]*)-?([^-]*)'
319
320     def __init__( self ):
321         super( HostTupleProbe, self ).__init__( 'host tuple', '%s/config.guess' % (cfg.dir), abort=True, head=True )
322
323     def _parseSession( self ):
324         if len(self.session):
325             self.spec = self.session[0]
326         else:
327             self.spec = ''
328
329         ## grok GNU host tuples
330         m = re.match( HostTupleProbe.GNU_TUPLE_RE, self.spec )
331         if not m:
332             self.fail = True
333             self.msg_end = 'invalid host tuple: %s' % (self.spec)
334             return
335
336         self.msg_end = self.spec
337
338         ## assign tuple from regex
339         self[:] = m.groups()
340
341         ## for clarity
342         self.machine = self[0]
343         self.vendor  = self[1]
344         self.system  = self[2]
345         self.release = self[3]
346         self.extra   = self[4]
347
348         ## nice formal name for 'system'
349         self.systemf = platform.system()
350
351         if self.match( '*-*-cygwin*' ):
352             self.systemf = self[2][0].upper() + self[2][1:]
353             
354     ## glob-match against spec
355     def match( self, *specs ):
356         for spec in specs:
357             if fnmatch.fnmatch( self.spec, spec ):
358                 return True
359         return False
360
361 ###############################################################################
362
363 class BuildAction( Action, list ):
364     def __init__( self ):
365         super( BuildAction, self ).__init__( 'compute', 'build tuple', abort=True )
366
367     def _action( self ):
368         ## check if --cross spec was used; must maintain 5-tuple compatibility with regex
369         if options.cross:
370             self.spec = os.path.basename( options.cross ).rstrip( '-' )
371         else:
372             self.spec = arch.mode[arch.mode.mode]
373
374         ## grok GNU host tuples
375         m = re.match( HostTupleProbe.GNU_TUPLE_RE, self.spec )
376         if not m:
377             self.msg_end = 'invalid host tuple: %s' % (self.spec)
378             return
379
380         self.msg_end = self.spec
381
382         ## assign tuple from regex
383         self[:] = m.groups()
384
385         ## for clarity
386         self.machine = self[0]
387         self.vendor  = self[1]
388         self.system  = self[2]
389         self.release = self[3]
390         self.extra   = self[4]
391         self.systemf = host.systemf
392
393         ## when cross we need switch for platforms
394         if options.cross:
395             if self.match( '*mingw*' ):
396                 self.systemf = 'MinGW'
397             elif self.systemf:
398                 self.systemf[0] = self.systemf[0].upper()
399             self.title = '%s %s' % (build.systemf,self.machine)
400         else:
401             self.title = '%s %s' % (build.systemf,arch.mode.mode)
402         self.fail = False
403
404     ## glob-match against spec
405     def match( self, *specs ):
406         for spec in specs:
407             if fnmatch.fnmatch( self.spec, spec ):
408                 return True
409         return False
410
411 ###############################################################################
412 ##
413 ## value wrapper; value is accepted only if one of host specs matcheds
414 ## otherwise it is None (or a keyword-supplied val)
415 ##
416 ## result is attribute 'value'
417 ##
418 class IfHost( object ):
419     def __init__( self, value, *specs, **kwargs ):
420         self.value = kwargs.get('none',None)
421         for spec in specs:
422             if host.match( spec ):
423                 self.value = value
424                 break
425
426     def __nonzero__( self ):
427         return self.value != None
428         
429     def __str__( self ):
430         return self.value
431
432
433 ###############################################################################
434 ##
435 ## platform conditional value; loops through list of tuples comparing
436 ## to first host match and sets value accordingly; the first value is
437 ## always default.
438 ##
439 class ForHost( object ):
440     def __init__( self, default, *tuples ):
441         self.value = default
442         for tuple in tuples:
443             if host.match( tuple[1] ):
444                 self.value = tuple[0]
445                 break
446
447     def __str__( self ):
448         return self.value
449
450 ###############################################################################
451
452 class ArchAction( Action ):
453     def __init__( self ):
454         super( ArchAction, self ).__init__( 'compute', 'available architectures', abort=True )
455         self.mode = SelectMode( 'architecture', (host.machine,host.spec) )
456
457     def _action( self ):
458         self.fail = False
459
460         ## some match on system should be made here; otherwise we signal a warning. 
461         if host.match( '*-*-cygwin*' ):
462             pass
463         elif host.match( '*-*-darwin*' ):
464             self.mode['i386']   = 'i386-apple-darwin%s'      % (host.release)
465             self.mode['x86_64'] = 'x86_64-apple-darwin%s'    % (host.release)
466             self.mode['ppc']    = 'powerpc-apple-darwin%s'   % (host.release)
467             self.mode['ppc64']  = 'powerpc64-apple-darwin%s' % (host.release)
468
469             ## special cases in that powerpc does not match gcc -arch value
470             ## which we like to use; so it has to be removed.
471             ## note: we don't know if apple will release Ssnow Leopad/ppc64 yet; just a guess.
472             if 'powerpc' in self.mode:
473                 del self.mode['powerpc']
474                 self.mode.mode = 'ppc'
475             elif 'powerpc64' in self.mode:
476                 del self.mode['powerpc64']
477                 self.mode.mode = 'ppc64'
478         elif host.match( '*-*-linux*' ):
479             pass
480         else:
481             self.msg_pass = 'WARNING'
482
483         self.msg_end = self.mode.toString()
484
485     ## glob-match against spec
486     def match( self, spec ):
487         return fnmatch.fnmatch( self.spec, spec )
488
489 ###############################################################################
490
491 class CoreProbe( Action ):
492     def __init__( self ):
493         super( CoreProbe, self ).__init__( 'probe', 'number of CPU cores' )
494         self.count = 1
495
496     def _action( self ):
497         if self.fail:
498             ## good for darwin9.6.0 and linux
499             try:
500                 self.count = os.sysconf( 'SC_NPROCESSORS_ONLN' )
501                 if self.count < 1:
502                     self.count = 1
503                 self.fail = False
504             except:
505                 pass
506
507         if self.fail:
508             ## windows
509             try:
510                 self.count = int( os.environ['NUMBER_OF_PROCESSORS'] )
511                 if self.count < 1:
512                     self.count = 1
513                 self.fail = False
514             except:
515                 pass
516
517         ## clamp
518         if self.count < 1:
519             self.count = 1
520         elif self.count > 32:
521             self.count = 32
522
523         if options.launch:
524             if options.launch_jobs == 0:
525                 self.jobs = core.count
526             else:
527                 self.jobs = options.launch_jobs
528         else:
529             self.jobs = core.count
530
531         self.msg_end = str(self.count)
532
533 ###############################################################################
534
535 class SelectMode( dict ):
536     def __init__( self, descr, *modes, **kwargs ):
537         super( SelectMode, self ).__init__( modes )
538         self.descr    = descr
539         self.modes    = modes
540         self.default  = kwargs.get('default',modes[0][0])
541         self.mode     = self.default
542
543     def cli_add_option( self, parser, option ):
544         parser.add_option( option, default=self.mode, metavar='MODE',
545             help='select %s mode: %s' % (self.descr,self.toString()),
546             action='callback', callback=self.cli_callback, type='str' )
547
548     def cli_callback( self, option, opt_str, value, parser, *args, **kwargs ):
549         if value not in self:
550             raise optparse.OptionValueError( 'invalid %s mode: %s (choose from %s)'
551                 % (self.descr,value,self.toString( True )) )
552         self.mode = value
553
554     def toString( self, nodefault=False ):
555         keys = self.keys()
556         keys.sort()
557         if len(self) == 1:
558             value = self.mode
559         elif nodefault:
560             value = ' '.join( keys )
561         else:
562             value = '%s [%s]' % (' '.join( keys ), self.mode )
563         return value
564
565 ###############################################################################
566 ##
567 ## Repository object.
568 ## Holds information gleaned from subversion working dir.
569 ##
570 ## Builds are classed into one of the following types:
571 ##
572 ##  release
573 ##      must be built from official svn with '/tags/' in the url
574 ##  developer
575 ##      must be built from official svn but is not a release
576 ##  unofficial
577 ##      all other builds
578 ##
579 class RepoProbe( ShellProbe ):
580     def __init__( self ):
581         super( RepoProbe, self ).__init__( 'svn info', 'svn info %s' % (cfg.src_dir) )
582
583         self.url       = 'svn://nowhere.com/project/unknown'
584         self.root      = 'svn://nowhere.com/project'
585         self.branch    = 'unknown'
586         self.uuid      = '00000000-0000-0000-0000-000000000000';
587         self.rev       = 0
588         self.date      = '0000-00-00 00:00:00 -0000'
589         self.official  = 0
590         self.type      = 'unofficial'
591
592     def _parseSession( self ):
593         for line in self.session:
594             ## grok fields
595             m = re.match( '([^:]+):\\s+(.+)', line )
596             if not m:
597                 continue
598
599             (name,value) = m.groups()
600             if name == 'URL':
601                 self.url = value
602             elif name == 'Repository Root':
603                 self.root = value
604             elif name == 'Repository UUID':
605                 self.uuid = value
606             elif name == 'Revision':
607                 self.rev = int( value )
608             elif name == 'Last Changed Date':
609                 # strip chars in parens
610                 if value.find( ' (' ):
611                     self.date = value[0:value.find(' (')]
612                 else:
613                     self.date = value
614
615         ## grok branch
616         i = self.url.rfind( '/' )
617         if i != -1 and i < len(self.url)-1:
618             self.branch = self.url[i+1:]
619
620         # type-classification via repository UUID
621         if self.uuid == 'b64f7644-9d1e-0410-96f1-a4d463321fa5':
622             self.official = 1
623             m = re.match( '([^:]+)://([^/]+)/(.+)', self.url )
624             if m and re.match( 'tags/', m.group( 3 )):
625                 self.type = 'release'
626             else:
627                 self.type = 'developer'
628
629         self.msg_end = self.url
630
631 ###############################################################################
632 ##
633 ## project object.
634 ##
635 ## Contains manually updated version numbers consistent with HB releases
636 ## and other project metadata.
637 ##
638 class Project( Action ):
639     def __init__( self ):
640         super( Project, self ).__init__( 'compute', 'project data' )
641
642         self.name          = 'HandBrake'
643         self.acro_lower    = 'hb'
644         self.acro_upper    = 'HB'
645         self.url_website   = 'http://code.google.com/p/hbfork'
646         self.url_community = 'http://forum.handbrake.fr'
647         self.url_irc       = 'irc://irc.freenode.net/handbrake'
648
649         self.name_lower = self.name.lower()
650         self.name_upper = self.name.upper()
651
652         self.vmajor = 0
653         self.vminor = 9
654         self.vpoint = 4
655
656     def _action( self ):
657         appcastfmt = 'http://handbrake.fr/appcast%s.xml'
658
659         if repo.type == 'release':
660             self.version = '%d.%d.%d' % (self.vmajor,self.vminor,self.vpoint)
661             self.url_appcast = appcastfmt % ('')
662             self.build = time.strftime('%Y%m%d') + '00'
663             self.title = '%s %s (%s)' % (self.name,self.version,self.build)
664         elif repo.type == 'developer':
665             self.version = 'svn%d' % (repo.rev)
666             self.url_appcast = appcastfmt % ('_unstable')
667             self.build = time.strftime('%Y%m%d') + '01'
668             self.title = '%s svn%d (%s)' % (self.name,repo.rev,self.build)
669         else:
670             self.version = 'svn%d' % (repo.rev)
671             self.url_appcast = appcastfmt % ('_unofficial')
672             self.build = time.strftime('%Y%m%d') + '99'
673             self.title = 'Unofficial svn%d (%s)' % (repo.rev,self.build)
674
675         self.msg_end = '%s (%s)' % (self.name,repo.type)
676         self.fail = False
677
678 ###############################################################################
679
680 class ToolProbe( Action ):
681     tools = []
682
683     def __init__( self, var, *names, **kwargs ):
684         super( ToolProbe, self ).__init__( 'find', abort=kwargs.get('abort',True) )
685         if not self in ToolProbe.tools:
686             ToolProbe.tools.append( self )
687         self.var    = var
688         self.names  = []
689         self.kwargs = kwargs
690         for name in names:
691             if name:
692                 self.names.append( str(name) )
693         self.name = self.names[0]
694         self.pretext = self.name
695         self.pathname = self.names[0]
696
697     def _action( self ):
698         self.session = []
699         for i,name in enumerate(self.names):
700             self.session.append( 'name[%d] = %s' % (i,name) )
701         for name in self.names:
702             f = cfg.findExecutable( name )
703             if f:
704                 self.pathname = f
705                 self.fail = False
706                 self.msg_end = f
707                 break
708         if self.fail:
709             self.msg_end = 'not found'
710
711     def cli_add_option( self, parser ):
712         parser.add_option( '--'+self.name, metavar='PROG',
713             help='[%s]' % (self.pathname),
714             action='callback', callback=self.cli_callback, type='str' )
715
716     def cli_callback( self, option, opt_str, value, parser, *args, **kwargs ):
717         self.__init__( self.var, value, **self.kwargs )
718         self.run()
719
720     def doc_add( self, doc ):
721         doc.add( self.var, self.pathname )
722
723 ###############################################################################
724
725 class SelectTool( Action ):
726     selects = []
727
728     def __init__( self, var, name, *pool, **kwargs ):
729         super( SelectTool, self ).__init__( 'select', abort=kwargs.get('abort',True) )
730         self.pretext = name
731         if not self in SelectTool.selects:
732             SelectTool.selects.append( self )
733         self.var      = var
734         self.name     = name
735         self.pool     = pool
736         self.kwargs   = kwargs
737
738     def _action( self ):
739         self.session = []
740         for i,(name,tool) in enumerate(self.pool):
741             self.session.append( 'tool[%d] = %s (%s)' % (i,name,tool.pathname) )
742         for (name,tool) in self.pool:
743             if not tool.fail:
744                 self.selected = name
745                 self.fail = False
746                 self.msg_end = '%s (%s)' % (name,tool.pathname)
747                 break
748         if self.fail:
749             self.msg_end = 'not found'
750
751     def cli_add_option( self, parser ):
752         parser.add_option( '--'+self.name, metavar='MODE',
753             help='select %s mode: %s' % (self.name,self.toString()),
754             action='callback', callback=self.cli_callback, type='str' )
755
756     def cli_callback( self, option, opt_str, value, parser, *args, **kwargs ):
757         found = False
758         for (name,tool) in self.pool:
759             if name == value:
760                 found = True
761                 self.__init__( self.var, self.name, [name,tool], **kwargs )
762                 self.run()
763                 break
764         if not found:
765             raise optparse.OptionValueError( 'invalid %s mode: %s (choose from %s)'
766                 % (self.name,value,self.toString( True )) )
767
768     def doc_add( self, doc ):
769         doc.add( self.var, self.selected )
770
771     def toString( self, nodefault=False ):
772         if len(self.pool) == 1:
773             value = self.pool[0][0]
774         else:
775             s = ''
776             for key,value in self.pool:
777                 s += ' ' + key
778             if nodefault:
779                 value = s[1:]
780             else:
781                 value = '%s [%s]' % (s[1:], self.selected )
782         return value
783
784 ###############################################################################
785 ##
786 ## config object used to output gnu-make or gnu-m4 output.
787 ##
788 ## - add() to add NAME/VALUE pairs suitable for both make/m4.
789 ## - addBlank() to add a linefeed for both make/m4.
790 ## - addMake() to add a make-specific line.
791 ## - addM4() to add a m4-specific line.
792 ##
793 class ConfigDocument:
794     def __init__( self ):
795         self._elements = []
796
797     def _outputMake( self, file, namelen, name, value, append ):
798         if append:
799             if value == None or len(str(value)) == 0:
800                 file.write( '%-*s +=\n' % (namelen, name) )
801             else:
802                 file.write( '%-*s += %s\n' % (namelen, name, value) )
803         else:
804             if value == None or len(str(value)) == 0:
805                 file.write( '%-*s  =\n' % (namelen, name) )
806             else:
807                 file.write( '%-*s  = %s\n' % (namelen, name, value) )
808
809     def _outputM4( self, file, namelen, name, value ):
810         namelen += 7
811         name = '<<__%s>>,' % name.replace( '.', '_' )
812         file.write( 'define(%-*s  <<%s>>)dnl\n' % (namelen, name, value ))
813
814     def add( self, name, value, append=False ):
815         self._elements.append( [name,value,append] )
816
817     def addBlank( self ):
818         self._elements.append( None )
819
820     def addComment( self, format, *args ):
821         self.addMake( '## ' + format % args )
822         self.addM4( 'dnl ' + format % args )
823
824     def addMake( self, line ):
825         self._elements.append( ('?make',line) )
826
827     def addM4( self, line ):
828         self._elements.append( ('?m4',line) )
829
830     def output( self, file, type ):
831         namelen = 0
832         for item in self._elements:
833             if item == None or item[0].find( '?' ) == 0:
834                 continue
835             if len(item[0]) > namelen:
836                 namelen = len(item[0])
837         for item in self._elements:
838             if item == None:
839                 if type == 'm4':
840                     file.write( 'dnl\n' )
841                 else:
842                     file.write( '\n' )
843                 continue
844             if item[0].find( '?' ) == 0:
845                 if item[0].find( type, 1 ) == 1:
846                     file.write( '%s\n' % (item[1]) )
847                 continue
848
849             if type == 'm4':
850                 self._outputM4( file, namelen, item[0], item[1] )
851             else:
852                 self._outputMake( file, namelen, item[0], item[1], item[2] )
853
854     def update( self, name, value ):
855         for item in self._elements:
856             if item == None:
857                 continue
858             if item[0] == name:
859                 item[1] = value
860                 return
861         raise ValueError( 'element not found: %s' % (name) )
862
863     def write( self, type ):
864         if type == 'make':
865             fname = 'GNUmakefile'
866         elif type == 'm4':
867             fname = os.path.join( 'project', project.name_lower + '.m4' )
868         else:
869             raise ValueError, 'unknown file type: ' + type
870
871         ftmp  = fname + '.tmp'
872         try:
873             try:
874                 file = cfg.open( ftmp, 'w' )
875                 self.output( file, type )
876             finally:
877                 try:
878                     file.close()
879                 except:
880                     pass
881         except Exception, x:
882             try:
883                 os.remove( ftmp )
884             except Exception, x:
885                 pass
886             cfg.errln( 'failed writing to %s\n%s', ftmp, x )
887
888         try:
889             os.rename( ftmp, fname )
890         except Exception, x:
891             cfg.errln( 'failed writing to %s\n%s', fname, x )
892
893 ###############################################################################
894 ##
895 ## create cli parser
896 ##
897
898 ## class to hook options and create CONF.args list
899 class Option( optparse.Option ):
900     conf_args = []
901
902     def _conf_record( self, opt, value ):
903         ## skip conf,force,launch
904         if re.match( '^--(conf|force|launch).*$', opt ):
905             return
906
907         ## remove duplicates (last duplicate wins)
908         for i,arg in enumerate( Option.conf_args ):
909             if opt == arg[0]:
910                 del Option.conf_args[i]
911                 break
912
913         if value:
914             Option.conf_args.append( [opt,'%s=%s' % (opt,value)] )
915         else:
916             Option.conf_args.append( [opt,'%s' % (opt)] )
917
918     def take_action( self, action, dest, opt, value, values, parser ):
919         self._conf_record( opt, value )
920         return optparse.Option.take_action( self, action, dest, opt, value, values, parser )
921
922 def createCLI():
923     cli = OptionParser( 'usage: %prog [OPTIONS...] [TARGETS...]' )
924     cli.option_class = Option
925
926     cli.description = ''
927     cli.description += 'Configure %s build system.' % (project.name)
928
929     ## add hidden options
930     cli.add_option( '--conf-method', default='terminal', action='store', help=optparse.SUPPRESS_HELP )
931     cli.add_option( '--force', default=False, action='store_true', help='overwrite existing build config' )
932     cli.add_option( '--verbose', default=False, action='store_true', help='increase verbosity' )
933
934     ## add install options
935     grp = OptionGroup( cli, 'Directory Locations' )
936     grp.add_option( '--src', default=cfg.src_dir, action='store', metavar='DIR',
937         help='specify top-level source dir [%s]' % (cfg.src_dir) )
938     grp.add_option( '--build', default=cfg.build_dir, action='store', metavar='DIR',
939         help='specify build scratch/output dir [%s]' % (cfg.build_dir) )
940     grp.add_option( '--prefix', default=cfg.prefix_dir, action='store', metavar='DIR',
941         help='specify install dir for products [%s]' % (cfg.prefix_dir) )
942     cli.add_option_group( grp )
943
944     ## add feature options
945     grp = OptionGroup( cli, 'Feature Options' )
946
947     h = IfHost( 'enable assembly code in non-contrib modules', 'NOMATCH*-*-darwin*', 'NOMATCH*-*-linux*', none=optparse.SUPPRESS_HELP ).value
948     grp.add_option( '--enable-asm', default=False, action='store_true', help=h )
949
950     h = IfHost( 'disable GTK GUI', '*-*-linux*', none=optparse.SUPPRESS_HELP ).value
951     grp.add_option( '--disable-gtk', default=False, action='store_true', help=h )
952     h = IfHost( 'enable GTK GUI (mingw)', '*-*-mingw*', none=optparse.SUPPRESS_HELP ).value
953     grp.add_option( '--enable-gtk-mingw', default=False, action='store_true', help=h )
954
955     h = IfHost( 'disable Xcode', '*-*-darwin*', none=optparse.SUPPRESS_HELP ).value
956     grp.add_option( '--disable-xcode', default=False, action='store_true', help=h )
957
958     cli.add_option_group( grp )
959
960     ## add launch options
961     grp = OptionGroup( cli, 'Launch Options' )
962     grp.add_option( '--launch', default=False, action='store_true',
963         help='launch build, capture log and wait for completion' )
964     grp.add_option( '--launch-jobs', default=1, action='store', metavar='N', type='int',
965         help='allow N jobs at once; 0 to match CPU count [1]' )
966     grp.add_option( '--launch-args', default=None, action='store', metavar='ARGS',
967         help='specify additional ARGS for launch command' )
968     grp.add_option( '--launch-quiet', default=False, action='store_true',
969         help='do not echo build output while waiting' )
970     cli.add_option_group( grp )
971
972     ## add compile options
973     grp = OptionGroup( cli, 'Compiler Options' )
974     debugMode.cli_add_option( grp, '--debug' )
975     optimizeMode.cli_add_option( grp, '--optimize' )
976     arch.mode.cli_add_option( grp, '--arch' )
977     grp.add_option( '--cross', default=None, action='store', metavar='SPEC',
978         help='specify GCC cross-compilation spec' )
979     cli.add_option_group( grp )
980
981     ## add tool locations
982     grp = OptionGroup( cli, 'Tool Basenames and Locations' )
983     for tool in ToolProbe.tools:
984         tool.cli_add_option( grp )
985     cli.add_option_group( grp )
986
987     ## add tool modes
988     grp = OptionGroup( cli, 'Tool Options' )
989     for select in SelectTool.selects:
990         select.cli_add_option( grp )
991     cli.add_option_group( grp )
992     return cli
993
994 ###############################################################################
995 ##
996 ## launcher - used for QuickStart method; launch; build and capture log.
997 ##
998 class Launcher:
999     def __init__( self, targets ):
1000         # open build logfile
1001         self._file = cfg.open( 'log/build.txt', 'w' )
1002
1003         cmd = '%s -j%d' % (Tools.gmake.pathname,core.jobs)
1004         if options.launch_args:
1005             cmd += ' ' + options.launch_args
1006         if len(targets):
1007             cmd += ' ' + ' '.join(targets)
1008
1009         ## record begin
1010         timeBegin = time.time()
1011         self.infof( 'time begin: %s\n', time.asctime() )
1012         self.infof( 'launch: %s\n', cmd )
1013         if options.launch_quiet:
1014             stdout.write( 'building to %s ...\n' % (os.path.abspath( cfg.build_final )))
1015         else:
1016             stdout.write( '%s\n' % ('-' * 79) )
1017
1018         ## launch/pipe
1019         try:
1020             pipe = subprocess.Popen( cmd, shell=True, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
1021         except Exception, x:
1022             cfg.errln( 'launch failure: %s', x )
1023         for line in pipe.stdout:
1024             self.echof( '%s', line )
1025         pipe.wait()
1026
1027         ## record end
1028         timeEnd = time.time()
1029         elapsed = timeEnd - timeBegin
1030
1031         if pipe.returncode:
1032             result = 'FAILURE (code %d)' % pipe.returncode
1033         else:
1034             result = 'SUCCESS'
1035
1036         ## present duration in decent format
1037         seconds = elapsed
1038         hours = int(seconds / 3600)
1039         seconds -= hours * 3600
1040         minutes = int(seconds / 60)
1041         seconds -= minutes * 60
1042
1043         segs = []
1044         duration = ''
1045
1046         if hours == 1:
1047             segs.append( '%d hour' % hours )
1048         elif hours > 1:
1049             segs.append( '%d hours' % hours )
1050
1051         if len(segs) or minutes == 1:
1052             segs.append( '%d minute' % minutes )
1053         elif len(segs) or  minutes > 1:
1054             segs.append( '%d minutes' % minutes )
1055
1056         if seconds == 1:
1057             segs.append( '%d second' % seconds )
1058         else:
1059             segs.append( '%d seconds' % seconds )
1060
1061         if not options.launch_quiet:
1062             stdout.write( '%s\n' % ('-' * 79) )
1063         self.infof( 'time end: %s\n', time.asctime() )
1064         self.infof( 'duration: %s (%.2fs)\n', ', '.join(segs), elapsed )
1065         self.infof( 'result: %s\n', result )
1066
1067         ## cleanup
1068         self._file.close()
1069
1070     def echof( self, format, *args ):
1071         line = format % args
1072         self._file.write( line )
1073         if not options.launch_quiet:
1074             stdout.write( '  : %s' % line )
1075             stdout.flush()
1076
1077     def infof( self, format, *args ):
1078         line = format % args
1079         self._file.write( line )
1080         cfg.infof( '%s', line )
1081
1082 ###############################################################################
1083 ##
1084 ## main program
1085 ##
1086 try:
1087     ## we need to pre-check argv for -h or --help or --verbose to deal with
1088     ## initializing Configure correctly.
1089     verbose = Configure.OUT_INFO
1090     for arg in sys.argv:
1091         if arg == '-h' or arg == '--help':
1092             verbose = Configure.OUT_QUIET
1093             break
1094         if arg == '--verbose':
1095             verbose = Configure.OUT_VERBOSE
1096
1097     ## create main objects; actions/probes run() is delayed.
1098     ## if any actions must be run earlier (eg: for configure --help purposes)
1099     ## then run() must be invoked earlier. subequent run() invocations
1100     ## are ignored.
1101     cfg   = Configure( verbose )
1102     host  = HostTupleProbe(); host.run()
1103
1104     cfg.prefix_dir = ForHost( '/usr/local', ['/Applications','*-*-darwin*'] ).value
1105
1106     build = BuildAction()
1107     arch  = ArchAction(); arch.run()
1108
1109     ## create remaining main objects
1110     core    = CoreProbe()
1111     repo    = RepoProbe()
1112     project = Project()
1113
1114     ## create tools in a scope
1115     class Tools:
1116         ar    = ToolProbe( 'AR.exe',    'ar' )
1117         cp    = ToolProbe( 'CP.exe',    'cp' )
1118         curl  = ToolProbe( 'CURL.exe',  'curl', abort=False )
1119         gcc   = ToolProbe( 'GCC.gcc',   'gcc', IfHost( 'gcc-4', '*-*-cygwin*' ))
1120
1121         if host.match( '*-*-darwin*' ):
1122             gmake = ToolProbe( 'GMAKE.exe', 'make', 'gmake' )
1123         else:
1124             gmake = ToolProbe( 'GMAKE.exe', 'gmake', 'make' )
1125
1126         m4     = ToolProbe( 'M4.exe',     'm4' )
1127         mkdir  = ToolProbe( 'MKDIR.exe',  'mkdir' )
1128         patch  = ToolProbe( 'PATCH.exe',  'gpatch', 'patch' )
1129         rm     = ToolProbe( 'RM.exe',     'rm' )
1130         ranlib = ToolProbe( 'RANLIB.exe', 'ranlib' )
1131         strip  = ToolProbe( 'STRIP.exe',  'strip' )
1132         tar    = ToolProbe( 'TAR.exe',    'gtar', 'tar' )
1133         wget   = ToolProbe( 'WGET.exe',   'wget', abort=False )
1134         yasm   = ToolProbe( 'YASM.exe',   'yasm', abort=False )
1135
1136         xcodebuild = ToolProbe( 'XCODEBUILD.exe', 'xcodebuild', abort=False )
1137         lipo       = ToolProbe( 'LIPO.exe',       'lipo', abort=False )
1138
1139         fetch = SelectTool( 'FETCH.select', 'fetch', ['wget',wget], ['curl',curl] )
1140
1141     ## run tool probes
1142     for tool in ToolProbe.tools:
1143         tool.run()
1144     for select in SelectTool.selects:
1145         select.run()
1146
1147     debugMode = SelectMode( 'debug', ('none','none'), ('min','min'), ('std','std'), ('max','max') )
1148     optimizeMode = SelectMode( 'optimize', ('none','none'), ('speed','speed'), ('size','size'), default='speed' )
1149
1150     ## create CLI and parse
1151     cli = createCLI()
1152     (options,args) = cli.parse_args()
1153
1154     ## update cfg with cli directory locations
1155     cfg.update_cli( options )
1156
1157     ## prepare list of targets and NAME=VALUE args to pass to make
1158     targets = []
1159     exports = []
1160     rx_exports = re.compile( '([^=]+)=(.*)' )
1161     for arg in args:
1162         m = rx_exports.match( arg )
1163         if m:
1164             exports.append( m.groups() )
1165         else:
1166             targets.append( arg )
1167
1168     ## re-run tools with cross-compilation needs
1169     if options.cross:
1170         for tool in ( Tools.ar, Tools.gcc, Tools.ranlib, Tools.strip ):
1171             tool.__init__( tool.var, '%s-%s' % (options.cross,tool.name), **tool.kwargs )
1172             tool.run()
1173
1174     ## run delayed actions
1175     for action in Action.actions:
1176         action.run()
1177
1178     ## cfg hook before doc prep
1179     cfg.doc_ready()
1180
1181     ## create document object
1182     doc = ConfigDocument()
1183     doc.addComment( 'generated by configure on %s', time.strftime( '%c' ))
1184
1185     ## add configure line for reconfigure purposes
1186     doc.addBlank()
1187     args = []
1188     for arg in Option.conf_args:
1189         args.append( arg[1] )
1190     doc.add( 'CONF.args', ' '.join( args ))
1191
1192     doc.addBlank()
1193     doc.add( 'HB.title',         project.title )
1194     doc.add( 'HB.name',          project.name )
1195     doc.add( 'HB.name.lower',    project.name_lower )
1196     doc.add( 'HB.name.upper',    project.name_upper )
1197     doc.add( 'HB.acro.lower',    project.acro_lower )
1198     doc.add( 'HB.acro.upper',    project.acro_upper )
1199
1200     doc.add( 'HB.url.website',   project.url_website )
1201     doc.add( 'HB.url.community', project.url_community )
1202     doc.add( 'HB.url.irc',       project.url_irc )
1203     doc.add( 'HB.url.appcast',   project.url_appcast )
1204
1205     doc.add( 'HB.version.major',  project.vmajor )
1206     doc.add( 'HB.version.minor',  project.vminor )
1207     doc.add( 'HB.version.point',  project.vpoint )
1208     doc.add( 'HB.version',        project.version )
1209     doc.add( 'HB.version.hex',    '%04x%02x%02x%08x' % (project.vmajor,project.vminor,project.vpoint,repo.rev) )
1210
1211     doc.add( 'HB.build', project.build )
1212
1213     doc.add( 'HB.repo.url',       repo.url )
1214     doc.add( 'HB.repo.root',      repo.root )
1215     doc.add( 'HB.repo.branch',    repo.branch )
1216     doc.add( 'HB.repo.uuid',      repo.uuid )
1217     doc.add( 'HB.repo.rev',       repo.rev )
1218     doc.add( 'HB.repo.date',      repo.date )
1219     doc.add( 'HB.repo.official',  repo.official )
1220     doc.add( 'HB.repo.type',      repo.type )
1221
1222     doc.addBlank()
1223     doc.add( 'HOST.spec',    host.spec )
1224     doc.add( 'HOST.machine', host.machine )
1225     doc.add( 'HOST.vendor',  host.vendor )
1226     doc.add( 'HOST.system',  host.system )
1227     doc.add( 'HOST.systemf', host.systemf )
1228     doc.add( 'HOST.release', host.release )
1229     doc.add( 'HOST.extra',   host.extra )
1230     doc.add( 'HOST.title',   '%s %s' % (host.systemf,arch.mode.default) )
1231     doc.add( 'HOST.ncpu',    core.count )
1232
1233     doc.addBlank()
1234     doc.add( 'BUILD.spec',    build.spec )
1235     doc.add( 'BUILD.machine', build.machine )
1236     doc.add( 'BUILD.vendor',  build.vendor )
1237     doc.add( 'BUILD.system',  build.system )
1238     doc.add( 'BUILD.systemf', build.systemf )
1239     doc.add( 'BUILD.release', build.release )
1240     doc.add( 'BUILD.extra',   build.extra )
1241     doc.add( 'BUILD.title',   build.title )
1242     doc.add( 'BUILD.ncpu',    core.count )
1243     doc.add( 'BUILD.jobs',    core.jobs )
1244
1245     doc.add( 'BUILD.cross',        int(options.cross != None or arch.mode.mode != arch.mode.default) )
1246     if options.cross:
1247         doc.add( 'BUILD.cross.prefix', '%s-' % (options.cross) )
1248     else:
1249         doc.add( 'BUILD.cross.prefix', '' )
1250
1251     doc.add( 'BUILD.method',       'terminal' )
1252     doc.add( 'BUILD.date',         time.strftime('%c') )
1253     doc.add( 'BUILD.arch',         arch.mode.mode )
1254
1255     doc.addBlank()
1256     doc.add( 'CONF.method', options.conf_method )
1257
1258     doc.addBlank()
1259     doc.add( 'SRC',     cfg.src_final )
1260     doc.add( 'SRC/',    cfg.src_final + os.sep )
1261     doc.add( 'BUILD',   cfg.build_final )
1262     doc.add( 'BUILD/',  cfg.build_final + os.sep )
1263     doc.add( 'PREFIX',  cfg.prefix_final )
1264     doc.add( 'PREFIX/', cfg.prefix_final + os.sep )
1265     
1266     doc.addBlank()
1267     doc.add( 'FEATURE.asm',   'disabled' )
1268     doc.add( 'FEATURE.gtk',   int( not options.disable_gtk ))
1269     doc.add( 'FEATURE.gtk.mingw',   int( options.enable_gtk_mingw ))
1270     doc.add( 'FEATURE.xcode', int( not (Tools.xcodebuild.fail or options.disable_xcode or options.cross) ))
1271
1272     if not Tools.xcodebuild.fail and not options.disable_xcode:
1273         doc.addBlank()
1274         doc.add( 'XCODE.external.src',    cfg.xcode_x_src )
1275         doc.add( 'XCODE.external.build',  cfg.xcode_x_build )
1276         doc.add( 'XCODE.external.prefix', cfg.xcode_x_prefix )
1277
1278     doc.addMake( '' )
1279     doc.addMake( '## include definitions' )
1280     doc.addMake( 'include $(SRC/)make/include/main.defs' )
1281
1282     doc.addBlank()
1283     for tool in ToolProbe.tools:
1284         tool.doc_add( doc )
1285
1286     doc.addBlank()
1287     for select in SelectTool.selects:
1288         select.doc_add( doc )
1289
1290     doc.addBlank()
1291     if arch.mode.mode != arch.mode.default:
1292         doc.add( 'GCC.archs', arch.mode.mode )
1293     else:
1294         doc.add( 'GCC.archs', '' )
1295     doc.add( 'GCC.g', debugMode.mode )
1296     doc.add( 'GCC.O', optimizeMode.mode )
1297
1298     if options.enable_asm and not Tools.yasm.fail:
1299         asm = ''
1300         if build.match( 'i?86-*' ):
1301             asm = 'x86'
1302             doc.add( 'LIBHB.GCC.D', 'HAVE_MMX', append=True )
1303             doc.add( 'LIBHB.YASM.D', 'ARCH_X86', append=True )
1304             if build.match( '*-*-darwin*' ):
1305                 doc.add( 'LIBHB.YASM.f', 'macho32' )
1306             else:
1307                 doc.add( 'LIBHB.YASM.f', 'elf32' )
1308             doc.add( 'LIBHB.YASM.m', 'x86' )
1309         elif build.match( 'x86_64-*' ):
1310             asm = 'x86'
1311             doc.add( 'LIBHB.GCC.D', 'HAVE_MMX ARCH_X86_64', append=True )
1312             if build.match( '*-*-darwin*' ):
1313                 doc.add( 'LIBHB.YASM.D', 'ARCH_X86_64 PIC', append=True )
1314                 doc.add( 'LIBHB.YASM.f', 'macho64' )
1315             else:
1316                 doc.add( 'LIBHB.YASM.D', 'ARCH_X86_64', append=True )
1317                 doc.add( 'LIBHB.YASM.f', 'elf64' )
1318             doc.add( 'LIBHB.YASM.m', 'amd64' )
1319         doc.update( 'FEATURE.asm', asm )
1320
1321     ## add exports to make
1322     if len(exports):
1323         doc.addBlank()
1324         doc.addComment( 'overrides via VARIABLE=VALUE on command-line' )
1325         for nv in exports:
1326             doc.add( nv[0], nv[1] )
1327
1328     doc.addMake( '' )
1329     doc.addMake( '## include custom definitions' )
1330     doc.addMake( '-include $(SRC/)custom.defs' )
1331     doc.addMake( '-include $(BUILD/)GNUmakefile.custom.defs' )
1332
1333     doc.addMake( '' )
1334     doc.addMake( '## include rules' )
1335     doc.addMake( 'include $(SRC/)make/include/main.rules' )
1336     doc.addMake( '-include $(SRC/)custom.rules' )
1337     doc.addMake( '-include $(BUILD/)GNUmakefile.custom.rules' )
1338
1339     ## chdir
1340     cfg.chdir()
1341
1342     ## perform
1343     doc.write( 'make' )
1344     doc.write( 'm4' )
1345     if options.launch:
1346         Launcher( targets )
1347
1348     cfg.record_log()
1349
1350     if os.path.normpath( cfg.build_dir ) == os.curdir:
1351         nocd = True
1352     else:
1353         nocd = False
1354
1355     stdout.write( '%s\n' % ('-' * 79) )
1356     if options.launch:
1357         stdout.write( 'Build is finished!\n' )
1358         if nocd:
1359             stdout.write( 'You may now examine the output.\n' )
1360         else:
1361             stdout.write( 'You may now cd into %s and examine the output.\n' % (cfg.build_dir) )
1362     else:
1363         stdout.write( 'Build is configured!\n' )
1364         if nocd:
1365             stdout.write( 'You may now run make (%s).\n' % (Tools.gmake.pathname) )
1366         else:
1367             stdout.write( 'You may now cd into %s and run make (%s).\n' % (cfg.build_dir,Tools.gmake.pathname) )
1368
1369 except AbortError, x:
1370     stderr.write( 'ERROR: %s\n' % (x) )
1371     try:
1372         cfg.record_log()
1373     except:
1374         pass        
1375     sys.exit( 1 )    
1376
1377 sys.exit( 0 )