OSDN Git Service

WinGui:
[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( 'disable gstreamer (live preview)', '*-*-linux*', none=optparse.SUPPRESS_HELP ).value
1063     grp.add_option( '--disable-gst', default=False, action='store_true', help=h )
1064     h = IfHost( 'enable use of ffmpeg mpeg2 decoding', '*-*-*', none=optparse.SUPPRESS_HELP ).value
1065     grp.add_option( '--enable-ff-mpeg2', default=False, action='store_true', help=h )
1066
1067     h = IfHost( 'disable Xcode', '*-*-darwin*', none=optparse.SUPPRESS_HELP ).value
1068     grp.add_option( '--disable-xcode', default=False, action='store_true', help=h )
1069
1070     cli.add_option_group( grp )
1071
1072     ## add launch options
1073     grp = OptionGroup( cli, 'Launch Options' )
1074     grp.add_option( '--launch', default=False, action='store_true',
1075         help='launch build, capture log and wait for completion' )
1076     grp.add_option( '--launch-jobs', default=1, action='store', metavar='N', type='int',
1077         help='allow N jobs at once; 0 to match CPU count [1]' )
1078     grp.add_option( '--launch-args', default=None, action='store', metavar='ARGS',
1079         help='specify additional ARGS for launch command' )
1080     grp.add_option( '--launch-quiet', default=False, action='store_true',
1081         help='do not echo build output while waiting' )
1082     cli.add_option_group( grp )
1083
1084     ## add compile options
1085     grp = OptionGroup( cli, 'Compiler Options' )
1086     debugMode.cli_add_option( grp, '--debug' )
1087     optimizeMode.cli_add_option( grp, '--optimize' )
1088     arch.mode.cli_add_option( grp, '--arch' )
1089     grp.add_option( '--cross', default=None, action='store', metavar='SPEC',
1090         help='specify GCC cross-compilation spec' )
1091     h = IfHost( 'Min OS X Version', '*-*-darwin*', none=optparse.SUPPRESS_HELP ).value
1092     grp.add_option( '--minver', default=None, action='store', metavar='VER',
1093         help=h )
1094     cli.add_option_group( grp )
1095
1096     ## add tool locations
1097     grp = OptionGroup( cli, 'Tool Basenames and Locations' )
1098     for tool in ToolProbe.tools:
1099         tool.cli_add_option( grp )
1100     cli.add_option_group( grp )
1101
1102     ## add tool modes
1103     grp = OptionGroup( cli, 'Tool Options' )
1104     for select in SelectTool.selects:
1105         select.cli_add_option( grp )
1106     cli.add_option_group( grp )
1107     return cli
1108
1109 ###############################################################################
1110 ##
1111 ## launcher - used for QuickStart method; launch; build and capture log.
1112 ##
1113 class Launcher:
1114     def __init__( self, targets ):
1115         # open build logfile
1116         self._file = cfg.open( 'log/build.txt', 'w' )
1117
1118         cmd = '%s -j%d' % (Tools.gmake.pathname,core.jobs)
1119         if options.launch_args:
1120             cmd += ' ' + options.launch_args
1121         if len(targets):
1122             cmd += ' ' + ' '.join(targets)
1123
1124         ## record begin
1125         timeBegin = time.time()
1126         self.infof( 'time begin: %s\n', time.asctime() )
1127         self.infof( 'launch: %s\n', cmd )
1128         if options.launch_quiet:
1129             stdout.write( 'building to %s ...\n' % (os.path.abspath( cfg.build_final )))
1130         else:
1131             stdout.write( '%s\n' % ('-' * 79) )
1132
1133         ## launch/pipe
1134         try:
1135             pipe = subprocess.Popen( cmd, shell=True, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
1136         except Exception, x:
1137             cfg.errln( 'launch failure: %s', x )
1138         for line in pipe.stdout:
1139             self.echof( '%s', line )
1140         pipe.wait()
1141
1142         ## record end
1143         timeEnd = time.time()
1144         elapsed = timeEnd - timeBegin
1145
1146         if pipe.returncode:
1147             result = 'FAILURE (code %d)' % pipe.returncode
1148         else:
1149             result = 'SUCCESS'
1150
1151         ## present duration in decent format
1152         seconds = elapsed
1153         hours = int(seconds / 3600)
1154         seconds -= hours * 3600
1155         minutes = int(seconds / 60)
1156         seconds -= minutes * 60
1157
1158         segs = []
1159         duration = ''
1160
1161         if hours == 1:
1162             segs.append( '%d hour' % hours )
1163         elif hours > 1:
1164             segs.append( '%d hours' % hours )
1165
1166         if len(segs) or minutes == 1:
1167             segs.append( '%d minute' % minutes )
1168         elif len(segs) or  minutes > 1:
1169             segs.append( '%d minutes' % minutes )
1170
1171         if seconds == 1:
1172             segs.append( '%d second' % seconds )
1173         else:
1174             segs.append( '%d seconds' % seconds )
1175
1176         if not options.launch_quiet:
1177             stdout.write( '%s\n' % ('-' * 79) )
1178         self.infof( 'time end: %s\n', time.asctime() )
1179         self.infof( 'duration: %s (%.2fs)\n', ', '.join(segs), elapsed )
1180         self.infof( 'result: %s\n', result )
1181
1182         ## cleanup
1183         self._file.close()
1184
1185     def echof( self, format, *args ):
1186         line = format % args
1187         self._file.write( line )
1188         if not options.launch_quiet:
1189             stdout.write( '  : %s' % line )
1190             stdout.flush()
1191
1192     def infof( self, format, *args ):
1193         line = format % args
1194         self._file.write( line )
1195         cfg.infof( '%s', line )
1196
1197 ###############################################################################
1198 ##
1199 ## main program
1200 ##
1201 try:
1202     ## we need to pre-check argv for -h or --help or --verbose to deal with
1203     ## initializing Configure correctly.
1204     verbose = Configure.OUT_INFO
1205     for arg in sys.argv:
1206         if arg == '-h' or arg == '--help':
1207             verbose = Configure.OUT_QUIET
1208             break
1209         if arg == '--verbose':
1210             verbose = Configure.OUT_VERBOSE
1211
1212     ## create main objects; actions/probes run() is delayed.
1213     ## if any actions must be run earlier (eg: for configure --help purposes)
1214     ## then run() must be invoked earlier. subequent run() invocations
1215     ## are ignored.
1216     cfg   = Configure( verbose )
1217     host  = HostTupleProbe(); host.run()
1218
1219     cfg.prefix_dir = ForHost( '/usr/local', ['/Applications','*-*-darwin*'] ).value
1220
1221     build = BuildAction()
1222     arch  = ArchAction(); arch.run()
1223
1224     ## create remaining main objects
1225     core    = CoreProbe()
1226     repo    = RepoProbe()
1227     project = Project()
1228
1229     ## create tools in a scope
1230     class Tools:
1231         ar    = ToolProbe( 'AR.exe',    'ar' )
1232         cp    = ToolProbe( 'CP.exe',    'cp' )
1233         curl  = ToolProbe( 'CURL.exe',  'curl', abort=False )
1234         gcc   = ToolProbe( 'GCC.gcc',   'gcc', IfHost( 'gcc-4', '*-*-cygwin*' ))
1235
1236         if host.match( '*-*-darwin*' ):
1237             gmake = ToolProbe( 'GMAKE.exe', 'make', 'gmake' )
1238         else:
1239             gmake = ToolProbe( 'GMAKE.exe', 'gmake', 'make' )
1240
1241         m4     = ToolProbe( 'M4.exe',     'm4' )
1242         mkdir  = ToolProbe( 'MKDIR.exe',  'mkdir' )
1243         patch  = ToolProbe( 'PATCH.exe',  'gpatch', 'patch' )
1244         rm     = ToolProbe( 'RM.exe',     'rm' )
1245         ranlib = ToolProbe( 'RANLIB.exe', 'ranlib' )
1246         strip  = ToolProbe( 'STRIP.exe',  'strip' )
1247         tar    = ToolProbe( 'TAR.exe',    'gtar', 'tar' )
1248         wget   = ToolProbe( 'WGET.exe',   'wget', abort=False )
1249         yasm   = ToolProbe( 'YASM.exe',   'yasm', abort=False )
1250
1251         xcodebuild = ToolProbe( 'XCODEBUILD.exe', 'xcodebuild', abort=False )
1252         lipo       = ToolProbe( 'LIPO.exe',       'lipo', abort=False )
1253
1254         fetch = SelectTool( 'FETCH.select', 'fetch', ['wget',wget], ['curl',curl] )
1255
1256     ## run tool probes
1257     for tool in ToolProbe.tools:
1258         tool.run()
1259     for select in SelectTool.selects:
1260         select.run()
1261
1262     debugMode = SelectMode( 'debug', ('none','none'), ('min','min'), ('std','std'), ('max','max') )
1263     optimizeMode = SelectMode( 'optimize', ('none','none'), ('speed','speed'), ('size','size'), default='speed' )
1264
1265     ## create CLI and parse
1266     cli = createCLI()
1267     (options,args) = cli.parse_args()
1268
1269     ## update cfg with cli directory locations
1270     cfg.update_cli( options )
1271
1272     ## prepare list of targets and NAME=VALUE args to pass to make
1273     targets = []
1274     exports = []
1275     rx_exports = re.compile( '([^=]+)=(.*)' )
1276     for arg in args:
1277         m = rx_exports.match( arg )
1278         if m:
1279             exports.append( m.groups() )
1280         else:
1281             targets.append( arg )
1282
1283     ## re-run tools with cross-compilation needs
1284     if options.cross:
1285         for tool in ( Tools.ar, Tools.gcc, Tools.ranlib, Tools.strip ):
1286             tool.__init__( tool.var, '%s-%s' % (options.cross,tool.name), **tool.kwargs )
1287             tool.run()
1288
1289     ## run delayed actions
1290     for action in Action.actions:
1291         action.run()
1292
1293     if build.system == 'mingw':
1294         dlfcn_test = """
1295 #include <dlfcn.h>
1296 #include <stdio.h>
1297
1298 void fnord() { int i=42;}
1299 int main ()
1300 {
1301   void *self = dlopen (0, RTLD_GLOBAL|RTLD_NOW);
1302   int status = 1;
1303
1304   if (self)
1305     {
1306       if (dlsym (self,"fnord"))       status = 0;
1307       else if (dlsym( self,"_fnord")) status = 0;
1308       /* dlclose (self); */
1309     }
1310   else
1311     puts (dlerror ());
1312
1313   return status;
1314 }
1315 """
1316         dlfcn = LDProbe( 'static dlfcn', '%s -static' % Tools.gcc.pathname, '-ldl', dlfcn_test )
1317         dlfcn.run()
1318
1319         pthread_test = """
1320 #include <stdio.h>
1321 #include <pthread.h>
1322 int main ()
1323 {
1324   pthread_t thread;
1325   pthread_create (&thread, NULL, NULL, NULL);
1326   return 0;
1327 }
1328 """
1329         pthread = LDProbe( 'static pthread', '%s -static' % Tools.gcc.pathname, '-lpthreadGC2', pthread_test )
1330         pthread.run()
1331
1332         bz2_test = """
1333 #include <stdio.h>
1334 #include <bzlib.h>
1335 int main ()
1336 {
1337   BZ2_bzReadOpen(NULL, NULL, 0, 0, NULL, 0);
1338   return 0;
1339 }
1340 """
1341         bz2 = LDProbe( 'static bz2', '%s -static' % Tools.gcc.pathname, '-lbz2', bz2_test )
1342         bz2.run()
1343
1344         libz_test = """
1345 #include <stdio.h>
1346 #include <zlib.h>
1347 int main ()
1348 {
1349   compress(NULL, NULL, NULL, 0);
1350   return 0;
1351 }
1352 """
1353         libz = LDProbe( 'static zlib', '%s -static' % Tools.gcc.pathname, '-lz', libz_test )
1354         libz.run()
1355
1356         iconv_test = """
1357 #include <stdio.h>
1358 #include <iconv.h>
1359 int main ()
1360 {
1361   iconv_open(NULL, NULL);
1362   return 0;
1363 }
1364 """
1365         iconv = LDProbe( 'static iconv', '%s -static' % Tools.gcc.pathname, '-liconv', iconv_test )
1366         iconv.run()
1367
1368     ## cfg hook before doc prep
1369     cfg.doc_ready()
1370
1371     ## create document object
1372     doc = ConfigDocument()
1373     doc.addComment( 'generated by configure on %s', time.strftime( '%c' ))
1374
1375     ## add configure line for reconfigure purposes
1376     doc.addBlank()
1377     args = []
1378     for arg in Option.conf_args:
1379         args.append( arg[1] )
1380     doc.add( 'CONF.args', ' '.join( args ))
1381
1382     doc.addBlank()
1383     doc.add( 'HB.title',       project.title )
1384     doc.add( 'HB.name',        project.name )
1385     doc.add( 'HB.name.lower',  project.name_lower )
1386     doc.add( 'HB.name.upper',  project.name_upper )
1387     doc.add( 'HB.acro.lower',  project.acro_lower )
1388     doc.add( 'HB.acro.upper',  project.acro_upper )
1389
1390     doc.add( 'HB.url.website',    project.url_website )
1391     doc.add( 'HB.url.community',  project.url_community )
1392     doc.add( 'HB.url.irc',        project.url_irc )
1393     doc.add( 'HB.url.appcast',    project.url_appcast )
1394     doc.add( 'HB.url.appnote',    project.url_appnote )
1395
1396     doc.add( 'HB.version.major',  project.vmajor )
1397     doc.add( 'HB.version.minor',  project.vminor )
1398     doc.add( 'HB.version.point',  project.vpoint )
1399     doc.add( 'HB.version',        project.version )
1400     doc.add( 'HB.version.hex',    '%04x%02x%02x%08x' % (project.vmajor,project.vminor,project.vpoint,repo.rev) )
1401
1402     doc.add( 'HB.build', project.build )
1403
1404     doc.add( 'HB.repo.url',       repo.url )
1405     doc.add( 'HB.repo.root',      repo.root )
1406     doc.add( 'HB.repo.branch',    repo.branch )
1407     doc.add( 'HB.repo.uuid',      repo.uuid )
1408     doc.add( 'HB.repo.rev',       repo.rev )
1409     doc.add( 'HB.repo.date',      repo.date )
1410     doc.add( 'HB.repo.official',  repo.official )
1411     doc.add( 'HB.repo.type',      repo.type )
1412
1413     doc.addBlank()
1414     doc.add( 'HOST.spec',    host.spec )
1415     doc.add( 'HOST.machine', host.machine )
1416     doc.add( 'HOST.vendor',  host.vendor )
1417     doc.add( 'HOST.system',  host.system )
1418     doc.add( 'HOST.systemf', host.systemf )
1419     doc.add( 'HOST.release', host.release )
1420     doc.add( 'HOST.extra',   host.extra )
1421     doc.add( 'HOST.title',   '%s %s' % (host.systemf,arch.mode.default) )
1422     doc.add( 'HOST.ncpu',    core.count )
1423
1424     doc.addBlank()
1425     doc.add( 'BUILD.spec',    build.spec )
1426     doc.add( 'BUILD.machine', build.machine )
1427     doc.add( 'BUILD.vendor',  build.vendor )
1428     doc.add( 'BUILD.system',  build.system )
1429     doc.add( 'BUILD.systemf', build.systemf )
1430     doc.add( 'BUILD.release', build.release )
1431     doc.add( 'BUILD.extra',   build.extra )
1432     doc.add( 'BUILD.title',   build.title )
1433     doc.add( 'BUILD.ncpu',    core.count )
1434     doc.add( 'BUILD.jobs',    core.jobs )
1435
1436     doc.add( 'BUILD.cross',        int(options.cross != None or arch.mode.mode != arch.mode.default) )
1437     if options.cross:
1438         doc.add( 'BUILD.cross.prefix', '%s-' % (options.cross) )
1439     else:
1440         doc.add( 'BUILD.cross.prefix', '' )
1441
1442     doc.add( 'BUILD.method', 'terminal' )
1443     doc.add( 'BUILD.date',   time.strftime('%c') )
1444     doc.add( 'BUILD.arch',   arch.mode.mode )
1445
1446     doc.addBlank()
1447     doc.add( 'CONF.method', options.conf_method )
1448
1449     doc.addBlank()
1450     doc.add( 'SRC',     cfg.src_final )
1451     doc.add( 'SRC/',    cfg.src_final + os.sep )
1452     doc.add( 'BUILD',   cfg.build_final )
1453     doc.add( 'BUILD/',  cfg.build_final + os.sep )
1454     doc.add( 'PREFIX',  cfg.prefix_final )
1455     doc.add( 'PREFIX/', cfg.prefix_final + os.sep )
1456     
1457     doc.addBlank()
1458     doc.add( 'FEATURE.asm',   'disabled' )
1459     doc.add( 'FEATURE.gtk',   int( not options.disable_gtk ))
1460     doc.add( 'FEATURE.gtk.update.checks',   int( not options.disable_gtk_update_checks ))
1461     doc.add( 'FEATURE.gtk.mingw',   int( options.enable_gtk_mingw ))
1462     doc.add( 'FEATURE.gst',   int( not options.disable_gst ))
1463     doc.add( 'FEATURE.ff.mpeg2',   int( options.enable_ff_mpeg2 ))
1464     doc.add( 'FEATURE.xcode', int( not (Tools.xcodebuild.fail or options.disable_xcode or options.cross) ))
1465
1466     if not Tools.xcodebuild.fail and not options.disable_xcode:
1467         doc.addBlank()
1468         doc.add( 'XCODE.external.src',    cfg.xcode_x_src )
1469         doc.add( 'XCODE.external.build',  cfg.xcode_x_build )
1470         doc.add( 'XCODE.external.prefix', cfg.xcode_x_prefix )
1471
1472     doc.addBlank()
1473     if build.system == 'mingw':
1474         if not dlfcn.fail:
1475             doc.add( 'HAS.dlfcn', 1 )
1476         if not pthread.fail:
1477             doc.add( 'HAS.pthread', 1 )
1478         if not bz2.fail:
1479             doc.add( 'HAS.bz2', 1 )
1480         if not libz.fail:
1481             doc.add( 'HAS.libz', 1 )
1482         if not iconv.fail:
1483             doc.add( 'HAS.iconv', 1 )
1484
1485     doc.addMake( '' )
1486     doc.addMake( '## define debug mode before other includes' )
1487     doc.addMake( '## since it is tested in some module.defs' )
1488     doc.add( 'GCC.g', debugMode.mode )
1489     doc.addBlank()
1490     doc.addMake( '## include definitions' )
1491     doc.addMake( 'include $(SRC/)make/include/main.defs' )
1492
1493     doc.addBlank()
1494     for tool in ToolProbe.tools:
1495         tool.doc_add( doc )
1496
1497     doc.addBlank()
1498     for select in SelectTool.selects:
1499         select.doc_add( doc )
1500
1501     doc.addBlank()
1502     if build.match( '*-*-darwin*' ):
1503         doc.add( 'GCC.archs', arch.mode.mode )
1504         doc.add( 'GCC.sysroot', cfg.sysroot_dir )
1505         doc.add( 'GCC.minver', cfg.minver )
1506     else:
1507         doc.add( 'GCC.archs', '' )
1508         doc.add( 'GCC.sysroot', '' )
1509         doc.add( 'GCC.minver', '' )
1510     doc.add( 'GCC.ldsysroot', '$(GCC.sysroot)' )
1511     doc.add( 'GCC.ldminver', '$(GCC.minver)' )
1512     doc.add( 'GCC.O', optimizeMode.mode )
1513
1514     if options.enable_asm and not Tools.yasm.fail:
1515         asm = ''
1516         if build.match( 'i?86-*' ):
1517             asm = 'x86'
1518             doc.add( 'LIBHB.GCC.D', 'HAVE_MMX', append=True )
1519             doc.add( 'LIBHB.YASM.D', 'ARCH_X86', append=True )
1520             if build.match( '*-*-darwin*' ):
1521                 doc.add( 'LIBHB.YASM.f', 'macho32' )
1522             else:
1523                 doc.add( 'LIBHB.YASM.f', 'elf32' )
1524             doc.add( 'LIBHB.YASM.m', 'x86' )
1525         elif build.match( 'x86_64-*' ):
1526             asm = 'x86'
1527             doc.add( 'LIBHB.GCC.D', 'HAVE_MMX ARCH_X86_64', append=True )
1528             if build.match( '*-*-darwin*' ):
1529                 doc.add( 'LIBHB.YASM.D', 'ARCH_X86_64 PIC', append=True )
1530                 doc.add( 'LIBHB.YASM.f', 'macho64' )
1531             else:
1532                 doc.add( 'LIBHB.YASM.D', 'ARCH_X86_64', append=True )
1533                 doc.add( 'LIBHB.YASM.f', 'elf64' )
1534             doc.add( 'LIBHB.YASM.m', 'amd64' )
1535         doc.update( 'FEATURE.asm', asm )
1536
1537     ## add exports to make
1538     if len(exports):
1539         doc.addBlank()
1540         doc.addComment( 'overrides via VARIABLE=VALUE on command-line' )
1541         for nv in exports:
1542             doc.add( nv[0], nv[1] )
1543
1544     doc.addMake( '' )
1545     doc.addMake( '## include custom definitions' )
1546     doc.addMake( '-include $(SRC/)custom.defs' )
1547     doc.addMake( '-include $(BUILD/)GNUmakefile.custom.defs' )
1548
1549     doc.addMake( '' )
1550     doc.addMake( '## include rules' )
1551     doc.addMake( 'include $(SRC/)make/include/main.rules' )
1552     doc.addMake( '-include $(SRC/)custom.rules' )
1553     doc.addMake( '-include $(BUILD/)GNUmakefile.custom.rules' )
1554
1555     ## chdir
1556     cfg.chdir()
1557
1558     ## perform
1559     doc.write( 'make' )
1560     doc.write( 'm4' )
1561     if options.launch:
1562         Launcher( targets )
1563
1564     cfg.record_log()
1565
1566     if os.path.normpath( cfg.build_dir ) == os.curdir:
1567         nocd = True
1568     else:
1569         nocd = False
1570
1571     stdout.write( '%s\n' % ('-' * 79) )
1572     if options.launch:
1573         stdout.write( 'Build is finished!\n' )
1574         if nocd:
1575             stdout.write( 'You may now examine the output.\n' )
1576         else:
1577             stdout.write( 'You may now cd into %s and examine the output.\n' % (cfg.build_dir) )
1578     else:
1579         stdout.write( 'Build is configured!\n' )
1580         if nocd:
1581             stdout.write( 'You may now run make (%s).\n' % (Tools.gmake.pathname) )
1582         else:
1583             stdout.write( 'You may now cd into %s and run make (%s).\n' % (cfg.build_dir,Tools.gmake.pathname) )
1584
1585 except AbortError, x:
1586     stderr.write( 'ERROR: %s\n' % (x) )
1587     try:
1588         cfg.record_log()
1589     except:
1590         pass        
1591     sys.exit( 1 )    
1592
1593 sys.exit( 0 )