1 ###############################################################################
3 ## This script is coded for minimum version of Python 2.4 .
4 ## Pyhthon3 is incompatible.
8 ###############################################################################
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
25 class AbortError( Exception ):
26 def __init__( self, format, *args ):
27 self.value = format % args
31 ###############################################################################
33 ## Main configure object.
35 ## dir = containing this configure script
36 ## cwd = current working dir at time of script launch
38 class Configure( object ):
43 def __init__( self, verbose ):
45 self._log_verbose = []
48 self.verbose = verbose
49 self.dir = os.path.dirname( sys.argv[0] )
50 self.cwd = os.getcwd()
54 ## compute src dir which is 2 dirs up from this script
55 self.src_dir = os.path.normpath( sys.argv[0] )
57 self.src_dir = os.path.dirname( self.src_dir )
58 if len( self.src_dir ) == 0:
59 self.src_dir = os.curdir
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 ))
67 dir = os.path.normpath( self.relpath( dir, chdir ))
71 def errln( self, format, *args ):
73 if re.match( '^.*[!?:;.]$', s ):
74 stderr.write( 'ERROR: %s configure stop.\n' % (s) )
76 stderr.write( 'ERROR: %s; configure stop.\n' % (s) )
79 def infof( self, format, *args ):
81 self._log_verbose.append( line )
82 if cfg.verbose >= Configure.OUT_INFO:
83 self._log_info.append( line )
85 def verbosef( self, format, *args ):
87 self._log_verbose.append( line )
88 if cfg.verbose >= Configure.OUT_VERBOSE:
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 )
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 )
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 )
108 ## perform chdir and enable log recording
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!' )
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 )
118 self.mkdirs( self.build_dir )
119 self.infof( 'chdir: %s\n', self.build_dir )
120 os.chdir( self.build_dir )
125 def mkdirs( self, dir ):
126 if len(dir) and not os.path.exists( dir ):
127 self.infof( 'mkdir: %s\n', dir )
130 def open( self, *args ):
131 dir = os.path.dirname( args[0] )
132 if len(args) > 1 and args[1].find('w') != -1:
134 m = re.match( '^(.*)\.tmp$', args[0] )
136 self.infof( 'write: %s\n', m.group(1) )
138 self.infof( 'write: %s\n', args[0] )
143 cfg.errln( 'open failure: %s', x )
145 def record_log( self ):
149 self.verbose = Configure.OUT_QUIET
150 file = cfg.open( 'log/config.info.txt', 'w' )
151 for line in self._log_info:
154 file = cfg.open( 'log/config.verbose.txt', 'w' )
155 for line in self._log_verbose:
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 ):
168 if not os.environ.has_key( 'PATH' ) or os.environ[ 'PATH' ] == '':
171 path = os.environ['PATH']
173 for dir in path.split( os.pathsep ):
174 f = os.path.join( dir, name )
175 if os.access( f, os.X_OK ):
179 ## taken from python2.6 -- we need it
180 def relpath( self, path, start=os.curdir ):
181 """Return a relative version of a path"""
184 raise ValueError("no path specified")
186 start_list = os.path.abspath(start).split(os.sep)
187 path_list = os.path.abspath(path).split(os.sep)
189 # Work out how much of the filepath is shared by start and path.
190 i = len(os.path.commonprefix([start_list, path_list]))
192 rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:]
195 return os.path.join(*rel_list)
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 )
205 self.sysroot_dir = ""
207 if options.minver != None:
208 self.minver = options.minver
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' )
216 ###############################################################################
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
226 class Action( object ):
229 def __init__( self, category, pretext='unknown', abort=False, head=False ):
230 if self not in Action.actions:
231 Action.actions.append( self )
233 self.category = category
234 self.pretext = pretext
239 self.run_done = False
241 self.msg_fail = 'fail'
242 self.msg_pass = 'pass'
245 def _actionBegin( self ):
246 cfg.infof( '%s: %s...', self.category, self.pretext )
248 def _actionEnd( self ):
250 cfg.infof( '(%s) %s\n', self.msg_fail, self.msg_end )
252 self._dumpSession( cfg.infof )
253 cfg.errln( 'unable to continue' )
254 self._dumpSession( cfg.verbosef )
256 cfg.infof( '(%s) %s\n', self.msg_pass, self.msg_end )
257 self._dumpSession( cfg.verbosef )
259 def _dumpSession( self, printf ):
260 if self.session and len(self.session):
261 for line in self.session:
262 printf( ' : %s\n', line )
264 printf( ' : <NO-OUTPUT>\n' )
266 def _parseSession( self ):
279 ###############################################################################
281 ## base probe: anything which runs in shell.
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
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
296 ## pipe and redirect stderr to stdout; effects communicate result
297 pipe = subprocess.Popen( self.command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
299 ## read data into memory buffers, only first element (stdout) data is used
300 data = pipe.communicate()
301 self.fail = pipe.returncode != 0
304 self.session = data[0].splitlines()
309 self.msg_end = 'code %d' % (pipe.returncode)
311 def _dumpSession( self, printf ):
312 printf( ' + %s\n', self.command )
313 super( ShellProbe, self )._dumpSession( printf )
315 ###############################################################################
317 ## Compile test probe: determine if compile time feature is supported
319 ## returns true if feature successfully compiles
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
329 ## write program file
330 file = open( 'conftest.c', 'w' )
331 file.write( self.test_file )
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 )
336 ## read data into memory buffers, only first element (stdout) data is used
337 data = pipe.communicate()
338 self.fail = pipe.returncode != 0
341 self.session = data[0].splitlines()
346 self.msg_end = 'code %d' % (pipe.returncode)
347 os.remove( 'conftest.c' )
349 os.remove( 'conftest.o' )
351 def _dumpSession( self, printf ):
352 printf( ' + %s\n', self.command )
353 super( CCProbe, self )._dumpSession( printf )
356 ###############################################################################
358 ## Compile test probe: determine if compile time feature is supported
360 ## returns true if feature successfully compiles
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
371 ## write program file
372 file = open( 'conftest.c', 'w' )
373 file.write( self.test_file )
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 )
378 ## read data into memory buffers, only first element (stdout) data is used
379 data = pipe.communicate()
380 self.fail = pipe.returncode != 0
383 self.session = data[0].splitlines()
388 self.msg_end = 'code %d' % (pipe.returncode)
390 os.remove( 'conftest.c' )
392 os.remove( 'conftest' )
394 def _dumpSession( self, printf ):
395 printf( ' + %s\n', self.command )
396 super( LDProbe, self )._dumpSession( printf )
399 ###############################################################################
401 ## GNU host tuple probe: determine canonical platform type
403 ## example results from various platforms:
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)
410 class HostTupleProbe( ShellProbe, list ):
411 GNU_TUPLE_RE = '([^-]+)-?([^-]*)-([^0-9-]+)([^-]*)-?([^-]*)'
413 def __init__( self ):
414 super( HostTupleProbe, self ).__init__( 'host tuple', '%s/config.guess' % (cfg.dir), abort=True, head=True )
416 def _parseSession( self ):
417 if len(self.session):
418 self.spec = self.session[0]
422 ## grok GNU host tuples
423 m = re.match( HostTupleProbe.GNU_TUPLE_RE, self.spec )
426 self.msg_end = 'invalid host tuple: %s' % (self.spec)
429 self.msg_end = self.spec
431 ## assign tuple from regex
435 self.machine = self[0]
436 self.vendor = self[1]
437 self.system = self[2]
438 self.release = self[3]
441 ## nice formal name for 'system'
442 self.systemf = platform.system()
444 if self.match( '*-*-cygwin*' ):
445 self.systemf = self[2][0].upper() + self[2][1:]
447 ## glob-match against spec
448 def match( self, *specs ):
450 if fnmatch.fnmatch( self.spec, spec ):
454 ###############################################################################
456 class BuildAction( Action, list ):
457 def __init__( self ):
458 super( BuildAction, self ).__init__( 'compute', 'build tuple', abort=True )
461 ## check if --cross spec was used; must maintain 5-tuple compatibility with regex
463 self.spec = os.path.basename( options.cross ).rstrip( '-' )
465 self.spec = arch.mode[arch.mode.mode]
467 ## grok GNU host tuples
468 m = re.match( HostTupleProbe.GNU_TUPLE_RE, self.spec )
470 self.msg_end = 'invalid host tuple: %s' % (self.spec)
473 self.msg_end = self.spec
475 ## assign tuple from regex
479 self.machine = self[0]
480 self.vendor = self[1]
481 self.system = self[2]
482 self.release = self[3]
484 self.systemf = host.systemf
486 ## when cross we need switch for platforms
488 if self.match( '*mingw*' ):
489 self.systemf = 'MinGW'
491 self.systemf[0] = self.systemf[0].upper()
492 self.title = '%s %s' % (build.systemf,self.machine)
494 self.title = '%s %s' % (build.systemf,arch.mode.mode)
497 ## glob-match against spec
498 def match( self, *specs ):
500 if fnmatch.fnmatch( self.spec, spec ):
504 ###############################################################################
506 ## value wrapper; value is accepted only if one of host specs matcheds
507 ## otherwise it is None (or a keyword-supplied val)
509 ## result is attribute 'value'
511 class IfHost( object ):
512 def __init__( self, value, *specs, **kwargs ):
513 self.value = kwargs.get('none',None)
515 if host.match( spec ):
519 def __nonzero__( self ):
520 return self.value != None
526 ###############################################################################
528 ## platform conditional value; loops through list of tuples comparing
529 ## to first host match and sets value accordingly; the first value is
532 class ForHost( object ):
533 def __init__( self, default, *tuples ):
536 if host.match( tuple[1] ):
537 self.value = tuple[0]
543 ###############################################################################
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) )
553 ## some match on system should be made here; otherwise we signal a warning.
554 if host.match( '*-*-cygwin*' ):
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)
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*' ):
574 self.msg_pass = 'WARNING'
576 self.msg_end = self.mode.toString()
578 ## glob-match against spec
579 def match( self, spec ):
580 return fnmatch.fnmatch( self.spec, spec )
582 ###############################################################################
584 class CoreProbe( Action ):
585 def __init__( self ):
586 super( CoreProbe, self ).__init__( 'probe', 'number of CPU cores' )
591 ## good for darwin9.6.0 and linux
593 self.count = os.sysconf( 'SC_NPROCESSORS_ONLN' )
603 self.count = int( os.environ['NUMBER_OF_PROCESSORS'] )
613 elif self.count > 32:
617 if options.launch_jobs == 0:
618 self.jobs = core.count
620 self.jobs = options.launch_jobs
622 self.jobs = core.count
624 self.msg_end = str(self.count)
626 ###############################################################################
628 class SelectMode( dict ):
629 def __init__( self, descr, *modes, **kwargs ):
630 super( SelectMode, self ).__init__( modes )
633 self.default = kwargs.get('default',modes[0][0])
634 self.mode = self.default
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' )
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 )) )
647 def toString( self, nodefault=False ):
653 value = ' '.join( keys )
655 value = '%s [%s]' % (' '.join( keys ), self.mode )
658 ###############################################################################
660 ## Repository object.
661 ## Holds information gleaned from subversion working dir.
663 ## Builds are classed into one of the following types:
666 ## must be built from official svn with '/tags/' in the url
668 ## must be built from official svn but is not a release
672 class RepoProbe( ShellProbe ):
673 def __init__( self ):
674 super( RepoProbe, self ).__init__( 'svn info', 'svn info %s' % (cfg.src_dir) )
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';
681 self.date = '0000-00-00 00:00:00 -0000'
683 self.type = 'unofficial'
685 def _parseSession( self ):
686 for line in self.session:
688 m = re.match( '([^:]+):\\s+(.+)', line )
692 (name,value) = m.groups()
695 elif name == 'Repository Root':
697 elif name == 'Repository UUID':
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(' (')]
709 i = self.url.rfind( '/' )
710 if i != -1 and i < len(self.url)-1:
711 self.branch = self.url[i+1:]
713 # type-classification via repository UUID
714 if self.uuid == 'b64f7644-9d1e-0410-96f1-a4d463321fa5':
716 m = re.match( '([^:]+)://([^/]+)/(.+)', self.url )
717 if m and re.match( '.*tags/.*', m.group( 3 )):
718 self.type = 'release'
720 self.type = 'developer'
722 self.msg_end = self.url
724 ###############################################################################
728 ## Contains manually updated version numbers consistent with HB releases
729 ## and other project metadata.
731 class Project( Action ):
732 def __init__( self ):
733 super( Project, self ).__init__( 'compute', 'project data' )
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'
742 self.name_lower = self.name.lower()
743 self.name_upper = self.name.upper()
750 ## add architecture to URL only for Mac
751 if fnmatch.fnmatch( build.spec, '*-*-darwin*' ):
752 url_arch = '.%s' % (arch.mode.mode)
756 if repo.type == 'release':
757 self.version = '%d.%d.%d' % (self.vmajor,self.vminor,self.vpoint)
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)
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)
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)
778 self.msg_end = '%s (%s)' % (self.name,repo.type)
781 ###############################################################################
783 class ToolProbe( Action ):
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 )
795 self.names.append( str(name) )
796 self.name = self.names[0]
797 self.pretext = self.name
798 self.pathname = self.names[0]
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 )
812 self.msg_end = 'not found'
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' )
819 def cli_callback( self, option, opt_str, value, parser, *args, **kwargs ):
820 self.__init__( self.var, value, **self.kwargs )
823 def doc_add( self, doc ):
824 doc.add( self.var, self.pathname )
826 ###############################################################################
828 class SelectTool( Action ):
831 def __init__( self, var, name, *pool, **kwargs ):
832 super( SelectTool, self ).__init__( 'select', abort=kwargs.get('abort',True) )
834 if not self in SelectTool.selects:
835 SelectTool.selects.append( self )
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:
849 self.msg_end = '%s (%s)' % (name,tool.pathname)
852 self.msg_end = 'not found'
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' )
859 def cli_callback( self, option, opt_str, value, parser, *args, **kwargs ):
861 for (name,tool) in self.pool:
864 self.__init__( self.var, self.name, [name,tool], **kwargs )
868 raise optparse.OptionValueError( 'invalid %s mode: %s (choose from %s)'
869 % (self.name,value,self.toString( True )) )
871 def doc_add( self, doc ):
872 doc.add( self.var, self.selected )
874 def toString( self, nodefault=False ):
875 if len(self.pool) == 1:
876 value = self.pool[0][0]
879 for key,value in self.pool:
884 value = '%s [%s]' % (s[1:], self.selected )
887 ###############################################################################
889 ## config object used to output gnu-make or gnu-m4 output.
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.
896 class ConfigDocument:
897 def __init__( self ):
900 def _outputMake( self, file, namelen, name, value, append ):
902 if value == None or len(str(value)) == 0:
903 file.write( '%-*s +=\n' % (namelen, name) )
905 file.write( '%-*s += %s\n' % (namelen, name, value) )
907 if value == None or len(str(value)) == 0:
908 file.write( '%-*s =\n' % (namelen, name) )
910 file.write( '%-*s = %s\n' % (namelen, name, value) )
912 def _outputM4( self, file, namelen, name, value ):
914 name = '<<__%s>>,' % name.replace( '.', '_' )
915 file.write( 'define(%-*s <<%s>>)dnl\n' % (namelen, name, value ))
917 def add( self, name, value, append=False ):
918 self._elements.append( [name,value,append] )
920 def addBlank( self ):
921 self._elements.append( None )
923 def addComment( self, format, *args ):
924 self.addMake( '## ' + format % args )
925 self.addM4( 'dnl ' + format % args )
927 def addMake( self, line ):
928 self._elements.append( ('?make',line) )
930 def addM4( self, line ):
931 self._elements.append( ('?m4',line) )
933 def output( self, file, type ):
935 for item in self._elements:
936 if item == None or item[0].find( '?' ) == 0:
938 if len(item[0]) > namelen:
939 namelen = len(item[0])
940 for item in self._elements:
943 file.write( 'dnl\n' )
947 if item[0].find( '?' ) == 0:
948 if item[0].find( type, 1 ) == 1:
949 file.write( '%s\n' % (item[1]) )
953 self._outputM4( file, namelen, item[0], item[1] )
955 self._outputMake( file, namelen, item[0], item[1], item[2] )
957 def update( self, name, value ):
958 for item in self._elements:
964 raise ValueError( 'element not found: %s' % (name) )
966 def write( self, type ):
968 fname = 'GNUmakefile'
970 fname = os.path.join( 'project', project.name_lower + '.m4' )
972 raise ValueError, 'unknown file type: ' + type
974 ftmp = fname + '.tmp'
977 file = cfg.open( ftmp, 'w' )
978 self.output( file, type )
989 cfg.errln( 'failed writing to %s\n%s', ftmp, x )
992 os.rename( ftmp, fname )
994 cfg.errln( 'failed writing to %s\n%s', fname, x )
996 ###############################################################################
1001 ## class to hook options and create CONF.args list
1002 class Option( optparse.Option ):
1005 def _conf_record( self, opt, value ):
1006 ## skip conf,force,launch
1007 if re.match( '^--(conf|force|launch).*$', opt ):
1010 ## remove duplicates (last duplicate wins)
1011 for i,arg in enumerate( Option.conf_args ):
1013 del Option.conf_args[i]
1017 Option.conf_args.append( [opt,'%s=%s' % (opt,value)] )
1019 Option.conf_args.append( [opt,'%s' % (opt)] )
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 )
1026 cli = OptionParser( 'usage: %prog [OPTIONS...] [TARGETS...]' )
1027 cli.option_class = Option
1029 cli.description = ''
1030 cli.description += 'Configure %s build system.' % (project.name)
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' )
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',
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 )
1050 ## add feature options
1051 grp = OptionGroup( cli, 'Feature Options' )
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 )
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 )
1067 h = IfHost( 'disable Xcode', '*-*-darwin*', none=optparse.SUPPRESS_HELP ).value
1068 grp.add_option( '--disable-xcode', default=False, action='store_true', help=h )
1070 cli.add_option_group( grp )
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 )
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',
1094 cli.add_option_group( grp )
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 )
1103 grp = OptionGroup( cli, 'Tool Options' )
1104 for select in SelectTool.selects:
1105 select.cli_add_option( grp )
1106 cli.add_option_group( grp )
1109 ###############################################################################
1111 ## launcher - used for QuickStart method; launch; build and capture log.
1114 def __init__( self, targets ):
1115 # open build logfile
1116 self._file = cfg.open( 'log/build.txt', 'w' )
1118 cmd = '%s -j%d' % (Tools.gmake.pathname,core.jobs)
1119 if options.launch_args:
1120 cmd += ' ' + options.launch_args
1122 cmd += ' ' + ' '.join(targets)
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 )))
1131 stdout.write( '%s\n' % ('-' * 79) )
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 )
1143 timeEnd = time.time()
1144 elapsed = timeEnd - timeBegin
1147 result = 'FAILURE (code %d)' % pipe.returncode
1151 ## present duration in decent format
1153 hours = int(seconds / 3600)
1154 seconds -= hours * 3600
1155 minutes = int(seconds / 60)
1156 seconds -= minutes * 60
1162 segs.append( '%d hour' % hours )
1164 segs.append( '%d hours' % hours )
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 )
1172 segs.append( '%d second' % seconds )
1174 segs.append( '%d seconds' % seconds )
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 )
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 )
1192 def infof( self, format, *args ):
1193 line = format % args
1194 self._file.write( line )
1195 cfg.infof( '%s', line )
1197 ###############################################################################
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
1209 if arg == '--verbose':
1210 verbose = Configure.OUT_VERBOSE
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
1216 cfg = Configure( verbose )
1217 host = HostTupleProbe(); host.run()
1219 cfg.prefix_dir = ForHost( '/usr/local', ['/Applications','*-*-darwin*'] ).value
1221 build = BuildAction()
1222 arch = ArchAction(); arch.run()
1224 ## create remaining main objects
1229 ## create tools in a scope
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*' ))
1236 if host.match( '*-*-darwin*' ):
1237 gmake = ToolProbe( 'GMAKE.exe', 'make', 'gmake' )
1239 gmake = ToolProbe( 'GMAKE.exe', 'gmake', 'make' )
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 )
1251 xcodebuild = ToolProbe( 'XCODEBUILD.exe', 'xcodebuild', abort=False )
1252 lipo = ToolProbe( 'LIPO.exe', 'lipo', abort=False )
1254 fetch = SelectTool( 'FETCH.select', 'fetch', ['wget',wget], ['curl',curl] )
1257 for tool in ToolProbe.tools:
1259 for select in SelectTool.selects:
1262 debugMode = SelectMode( 'debug', ('none','none'), ('min','min'), ('std','std'), ('max','max') )
1263 optimizeMode = SelectMode( 'optimize', ('none','none'), ('speed','speed'), ('size','size'), default='speed' )
1265 ## create CLI and parse
1267 (options,args) = cli.parse_args()
1269 ## update cfg with cli directory locations
1270 cfg.update_cli( options )
1272 ## prepare list of targets and NAME=VALUE args to pass to make
1275 rx_exports = re.compile( '([^=]+)=(.*)' )
1277 m = rx_exports.match( arg )
1279 exports.append( m.groups() )
1281 targets.append( arg )
1283 ## re-run tools with cross-compilation needs
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 )
1289 ## run delayed actions
1290 for action in Action.actions:
1293 if build.system == 'mingw':
1298 void fnord() { int i=42;}
1301 void *self = dlopen (0, RTLD_GLOBAL|RTLD_NOW);
1306 if (dlsym (self,"fnord")) status = 0;
1307 else if (dlsym( self,"_fnord")) status = 0;
1308 /* dlclose (self); */
1316 dlfcn = LDProbe( 'static dlfcn', '%s -static' % Tools.gcc.pathname, '-ldl', dlfcn_test )
1321 #include <pthread.h>
1325 pthread_create (&thread, NULL, NULL, NULL);
1329 pthread = LDProbe( 'static pthread', '%s -static' % Tools.gcc.pathname, '-lpthreadGC2', pthread_test )
1337 BZ2_bzReadOpen(NULL, NULL, 0, 0, NULL, 0);
1341 bz2 = LDProbe( 'static bz2', '%s -static' % Tools.gcc.pathname, '-lbz2', bz2_test )
1349 compress(NULL, NULL, NULL, 0);
1353 libz = LDProbe( 'static zlib', '%s -static' % Tools.gcc.pathname, '-lz', libz_test )
1361 iconv_open(NULL, NULL);
1365 iconv = LDProbe( 'static iconv', '%s -static' % Tools.gcc.pathname, '-liconv', iconv_test )
1368 ## cfg hook before doc prep
1371 ## create document object
1372 doc = ConfigDocument()
1373 doc.addComment( 'generated by configure on %s', time.strftime( '%c' ))
1375 ## add configure line for reconfigure purposes
1378 for arg in Option.conf_args:
1379 args.append( arg[1] )
1380 doc.add( 'CONF.args', ' '.join( args ))
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 )
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 )
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) )
1402 doc.add( 'HB.build', project.build )
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 )
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 )
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 )
1436 doc.add( 'BUILD.cross', int(options.cross != None or arch.mode.mode != arch.mode.default) )
1438 doc.add( 'BUILD.cross.prefix', '%s-' % (options.cross) )
1440 doc.add( 'BUILD.cross.prefix', '' )
1442 doc.add( 'BUILD.method', 'terminal' )
1443 doc.add( 'BUILD.date', time.strftime('%c') )
1444 doc.add( 'BUILD.arch', arch.mode.mode )
1447 doc.add( 'CONF.method', options.conf_method )
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 )
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) ))
1466 if not Tools.xcodebuild.fail and not options.disable_xcode:
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 )
1473 if build.system == 'mingw':
1475 doc.add( 'HAS.dlfcn', 1 )
1476 if not pthread.fail:
1477 doc.add( 'HAS.pthread', 1 )
1479 doc.add( 'HAS.bz2', 1 )
1481 doc.add( 'HAS.libz', 1 )
1483 doc.add( 'HAS.iconv', 1 )
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 )
1490 doc.addMake( '## include definitions' )
1491 doc.addMake( 'include $(SRC/)make/include/main.defs' )
1494 for tool in ToolProbe.tools:
1498 for select in SelectTool.selects:
1499 select.doc_add( doc )
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 )
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 )
1514 if options.enable_asm and not Tools.yasm.fail:
1516 if build.match( 'i?86-*' ):
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' )
1523 doc.add( 'LIBHB.YASM.f', 'elf32' )
1524 doc.add( 'LIBHB.YASM.m', 'x86' )
1525 elif build.match( 'x86_64-*' ):
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' )
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 )
1537 ## add exports to make
1540 doc.addComment( 'overrides via VARIABLE=VALUE on command-line' )
1542 doc.add( nv[0], nv[1] )
1545 doc.addMake( '## include custom definitions' )
1546 doc.addMake( '-include $(SRC/)custom.defs' )
1547 doc.addMake( '-include $(BUILD/)GNUmakefile.custom.defs' )
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' )
1566 if os.path.normpath( cfg.build_dir ) == os.curdir:
1571 stdout.write( '%s\n' % ('-' * 79) )
1573 stdout.write( 'Build is finished!\n' )
1575 stdout.write( 'You may now examine the output.\n' )
1577 stdout.write( 'You may now cd into %s and examine the output.\n' % (cfg.build_dir) )
1579 stdout.write( 'Build is configured!\n' )
1581 stdout.write( 'You may now run make (%s).\n' % (Tools.gmake.pathname) )
1583 stdout.write( 'You may now cd into %s and run make (%s).\n' % (cfg.build_dir,Tools.gmake.pathname) )
1585 except AbortError, x:
1586 stderr.write( 'ERROR: %s\n' % (x) )