OSDN Git Service

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