OSDN Git Service

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