OSDN Git Service

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