diff options
author | konablend <[email protected]> | 2009-03-08 22:50:57 +0000 |
---|---|---|
committer | konablend <[email protected]> | 2009-03-08 22:50:57 +0000 |
commit | 083ba4898a662cd50a86d2a65ca5ebe765fe882d (patch) | |
tree | c2460805275e7b23dd03965df15beecc7ac4589b /make | |
parent | 92d511d944b059caaf6c5e85fcb5202642aa4553 (diff) |
BuildSystem: general, configure and Xcode updates.
*** NOTICE:
This changeset requires a clean build directory. Please issue 'make xclean' inside
build dir(s) after 'svn up'. This will help ensure there are no unexpected results.
*** GENERAL:
- renamed makevar PROJECT/ -> SRC/ for clarity.
- renamed makevar PREFIX.install/ -> PREFIX/ for clarity.
- split custom GNUmakefile overrides into defs/rules files.
- inserted optional level of make customization at SRC/ level; see docs.
- dropped HB.repo.wcversion (svnversion is no longer used).
- corrected several 'rm' usages to use -f flag which avoids some build errors.
- refreshed generated 00-Building.*.txt docs; work still in progress on the wiki front.
*** CONFIGURE:
- made configure more robust; configure may now be run outside of build directory!
- adding log recording of configure activities.
- improved readability of default options for configure
- added:
--force overwrite existing build config
--src=DIR specify top-level source dir [.]
--build=DIR specify build scratch/output dir [.]
--prefix=DIR specify install dir for products [/Applications]
- dropped --launch-force (replaced by --force)
- dropped --launch-dir (replaced by --build)
- dropped --launch-log
*** XCODE:
- renamed pbxproj definition EXTERNAL_PROJECT -> EXTERNAL_SRC for clarity.
- fixed all configurations to default to EXTERNAL_JOBS=1 .
- enabled Xcode internal parallelization (libhb, HandBrakeCLI and HandBrake benefit).
- make now tickles Xcode build files to cause Info.plist regeneration and posting to HandBrake.app output;
ie: svn up; click build and HandBrake.app's about panel will reflect the new repository rev.
*** DARWIN:
- added support to build universal binaries; see docs.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@2242 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'make')
-rw-r--r-- | make/configure.py | 1670 | ||||
-rw-r--r-- | make/include/contrib.defs | 14 | ||||
-rw-r--r-- | make/include/main.defs | 20 | ||||
-rw-r--r-- | make/include/main.rules | 18 | ||||
-rw-r--r-- | make/variant/darwin.defs | 55 | ||||
-rw-r--r-- | make/variant/darwin.rules | 2 | ||||
-rw-r--r-- | make/variant/freebsd.defs | 5 | ||||
-rwxr-xr-x | make/xcodemake | 4 |
8 files changed, 1102 insertions, 686 deletions
diff --git a/make/configure.py b/make/configure.py index ce2884093..c984ccc3b 100644 --- a/make/configure.py +++ b/make/configure.py @@ -1,3 +1,12 @@ +############################################################################### +## +## This script is coded for minimum version of Python 2.4 . +## Pyhthon3 is incompatible. +## +## Authors: konablend +## +############################################################################### + import fnmatch import optparse import os @@ -13,408 +22,514 @@ from optparse import OptionParser from sys import stderr from sys import stdout +class AbortError( Exception ): + def __init__( self, format, *args ): + self.value = format % args + def __str__( self ): + return self.value + ############################################################################### +## +## Main configure object. +## +## dir = containing this configure script +## cwd = current working dir at time of script launch +## +class Configure( object ): + OUT_QUIET = 0 + OUT_INFO = 1 + OUT_VERBOSE = 2 + + def __init__( self, verbose ): + self._log_info = [] + self._log_verbose = [] + self._record = False + + self.verbose = verbose + self.dir = os.path.dirname( sys.argv[0] ) + self.cwd = os.getcwd() + + self.build_dir = '.' + + ## compute src dir which is 2 dirs up from this script + self.src_dir = os.path.normpath( sys.argv[0] ) + for i in range( 2 ): + self.src_dir = os.path.dirname( self.src_dir ) + if len( self.src_dir ) == 0: + self.src_dir = os.curdir + + def _final_dir( self, chdir, dir ): + dir = os.path.normpath( dir ) + if not os.path.isabs( dir ): + if os.path.isabs( chdir ): + dir = os.path.normpath( os.path.abspath(dir )) + else: + dir = os.path.normpath( self.relpath( dir, chdir )) + return dir + + ## output functions + def errln( self, format, *args ): + s = (format % args) + if re.match( '^.*[!?:;.]$', s ): + stderr.write( 'ERROR: %s configure stop.\n' % (s) ) + else: + stderr.write( 'ERROR: %s; configure stop.\n' % (s) ) + self.record_log() + sys.exit( 1 ) + def infof( self, format, *args ): + line = format % args + self._log_verbose.append( line ) + if cfg.verbose >= Configure.OUT_INFO: + self._log_info.append( line ) + stdout.write( line ) + def verbosef( self, format, *args ): + line = format % args + self._log_verbose.append( line ) + if cfg.verbose >= Configure.OUT_VERBOSE: + stdout.write( line ) + + ## doc is ready to be populated + def doc_ready( self ): + ## compute final paths as they are after chdir into build + self.build_final = os.curdir + self.src_final = self._final_dir( self.build_dir, self.src_dir ) + self.prefix_final = self._final_dir( self.build_dir, self.prefix_dir ) + + cfg.infof( 'compute: makevar SRC/ = %s\n', self.src_final ) + cfg.infof( 'compute: makevar BUILD/ = %s\n', self.build_final ) + cfg.infof( 'compute: makevar PREFIX/ = %s\n', self.prefix_final ) + + ## xcode does a chdir so we need appropriate values + macosx = os.path.join( self.src_dir, 'macosx' ) + self.xcode_x_src = self._final_dir( macosx, self.src_dir ) + self.xcode_x_build = self._final_dir( macosx, self.build_dir ) + self.xcode_x_prefix = self._final_dir( macosx, self.prefix_dir ) + + ## perform chdir and enable log recording + def chdir( self ): + if os.path.abspath( self.build_dir ) == os.path.abspath( self.src_dir ): + cfg.errln( 'build (scratch) directory must not be the same as top-level source root!' ) + + if self.build_dir != os.curdir: + if os.path.exists( self.build_dir ): + if not options.force: + self.errln( 'build directory already exists: %s (use --force to overwrite)', self.build_dir ) + else: + self.mkdirs( self.build_dir ) + self.infof( 'chdir: %s\n', self.build_dir ) + os.chdir( self.build_dir ) + + ## enable logging + self._record = True + + def mkdirs( self, dir ): + if len(dir) and not os.path.exists( dir ): + self.infof( 'mkdir: %s\n', dir ) + os.makedirs( dir ) + + def open( self, *args ): + dir = os.path.dirname( args[0] ) + if len(args) > 1 and args[1].find('w') != -1: + self.mkdirs( dir ) + m = re.match( '^(.*)\.tmp$', args[0] ) + if m: + self.infof( 'write: %s\n', m.group(1) ) + else: + self.infof( 'write: %s\n', args[0] ) -def errf( format, *args ): - stderr.write( ('ERROR: ' + format + '\n') % args ) - sys.exit( 1 ) + try: + return open( *args ) + except Exception, x: + cfg.errln( 'open failure: %s', x ) -def outf( format, *args ): - stdout.write( (format + '\n') % args ) + def record_log( self ): + if not self._record: + return + self._record = False + self.verbose = Configure.OUT_QUIET + file = cfg.open( 'log/config.info.txt', 'w' ) + for line in self._log_info: + file.write( line ) + file.close() + file = cfg.open( 'log/config.verbose.txt', 'w' ) + for line in self._log_verbose: + file.write( line ) + file.close() + + ## Find executable by searching path. + ## On success, returns full pathname of executable. + ## On fail, returns None. + def findExecutable( self, name ): + if len( os.path.split(name)[0] ): + if os.access( name, os.X_OK ): + return name + return None + + if not os.environ.has_key( 'PATH' ) or os.environ[ 'PATH' ] == '': + path = os.defpath + else: + path = os.environ['PATH'] + + for dir in path.split( os.pathsep ): + f = os.path.join( dir, name ) + if os.access( f, os.X_OK ): + return f + return None + + ## taken from python2.6 -- we need it + def relpath( self, path, start=os.curdir ): + """Return a relative version of a path""" + + if not path: + raise ValueError("no path specified") + + start_list = os.path.abspath(start).split(os.sep) + path_list = os.path.abspath(path).split(os.sep) + + # Work out how much of the filepath is shared by start and path. + i = len(os.path.commonprefix([start_list, path_list])) + + rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return os.curdir + return os.path.join(*rel_list) + + ## update with parsed cli options + def update_cli( self, options ): + self.src_dir = os.path.normpath( options.src ) + self.build_dir = os.path.normpath( options.build ) + self.prefix_dir = os.path.normpath( options.prefix ) + + ## special case if src == build: add build subdir + if os.path.abspath( self.src_dir ) == os.path.abspath( self.build_dir ): + self.build_dir = os.path.join( self.build_dir, 'build' ) ############################################################################### - -## Expand values of iterable object into a decent string representation. ## -def expandValues( obj ): - buf = '' - for v in obj: - buf += ', ' + v - return '{ ' + buf[2:] + ' }' +## abstract action +## +## pretext = text which immediately follows 'probe:' output prefix +## abort = if true configure will exit on probe fail +## head = if true probe session is stripped of all but first line +## session = output from command, including stderr +## fail = true if probe failed +## +class Action( object ): + actions = [] + + def __init__( self, category, pretext='unknown', abort=False, head=False ): + if self not in Action.actions: + Action.actions.append( self ) + + self.category = category + self.pretext = pretext + self.abort = abort + self.head = head + self.session = None + + self.run_done = False + self.fail = True + self.msg_fail = 'fail' + self.msg_pass = 'pass' + self.msg_end = 'end' + + def _actionBegin( self ): + cfg.infof( '%s: %s...', self.category, self.pretext ) + + def _actionEnd( self ): + if self.fail: + cfg.infof( '(%s) %s\n', self.msg_fail, self.msg_end ) + if self.abort: + self._dumpSession( cfg.infof ) + cfg.errln( 'unable to continue' ) + self._dumpSession( cfg.verbosef ) + else: + cfg.infof( '(%s) %s\n', self.msg_pass, self.msg_end ) + self._dumpSession( cfg.verbosef ) -############################################################################### + def _dumpSession( self, printf ): + if self.session and len(self.session): + for line in self.session: + printf( ' : %s\n', line ) + else: + printf( ' : <NO-OUTPUT>\n' ) + + def _parseSession( self ): + pass + + def run( self ): + if self.run_done: + return + self.run_done = True + self._actionBegin() + self._action() + if not self.fail: + self._parseSession() + self._actionEnd() -## Find executable by searching path. -## On success, returns full pathname of executable. -## On fail, returns None. +############################################################################### +## +## base probe: anything which runs in shell. +## +## pretext = text which immediately follows 'probe:' output prefix +## command = full command and arguments to pipe +## abort = if true configure will exit on probe fail +## head = if true probe session is stripped of all but first line +## session = output from command, including stderr +## fail = true if probe failed ## -def findExecutable( name ): - if len( os.path.split(name)[0] ): - return name if os.access( name, os.X_OK ) else None +class ShellProbe( Action ): + def __init__( self, pretext, command, abort=False, head=False ): + super( ShellProbe, self ).__init__( 'probe', pretext, abort, head ) + self.command = command - if not os.environ.has_key( 'PATH' ) or os.environ[ 'PATH' ] == '': - path = os.defpath - else: - path = os.environ['PATH'] + def _action( self ): + ## pipe and redirect stderr to stdout; effects communicate result + pipe = subprocess.Popen( self.command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) - for dir in path.split( os.pathsep ): - f = os.path.join( dir, name ) - if os.access( f, os.X_OK ): - return f - return None + ## read data into memory buffers, only first element (stdout) data is used + data = pipe.communicate() + self.fail = pipe.returncode != 0 -############################################################################### + if data[0]: + self.session = data[0].splitlines() + else: + self.session = [] -def computeNumCPU(): - ## good for darwin9.6.0 and linux - try: - n = os.sysconf( 'SC_NPROCESSORS_ONLN' ) - if n < 1: - n = 1 - return n - except: - pass - ## windows - try: - n = int( os.environ['NUMBER_OF_PROCESSORS'] ) - if n < 1: - n = 1 - return n - except: - pass - return 1 + if pipe.returncode: + self.msg_end = 'code %d' % (pipe.returncode) + + def _dumpSession( self, printf ): + printf( ' + %s\n', self.command ) + super( ShellProbe, self )._dumpSession( printf ) ############################################################################### +## +## GNU host tuple probe: determine canonical platform type +## +## example results from various platforms: +## +## i386-apple-darwin9.6.0 (Mac OS X 10.5.6 Intel) +## powerpc-apple-darwin9.6.0 (Mac OS X 10.5.6 PPC) +## i686-pc-cygwin (Cygwin, Microsoft Vista) +## x86_64-unknown-linux-gnu (Linux, Fedora 10 x86_64) +## +class HostTupleProbe( ShellProbe, list ): + GNU_TUPLE_RX = '([^-]+)-([^-]+)-([^0-9-]+)([^-]*)-?([^-]*)' + + def __init__( self ): + super( HostTupleProbe, self ).__init__( 'host tuple', '%s/config.guess' % (cfg.dir), abort=True, head=True ) -## taken from python2.6 -- we need it -def relpath(path, start=os.path.curdir): - """Return a relative version of a path""" + def _parseSession( self ): + if len(self.session): + self.spec = self.session[0] + else: + self.spec = '' - if not path: - raise ValueError("no path specified") + ## grok GNU host tuples + m = re.match( HostTupleProbe.GNU_TUPLE_RX, self.spec ) + if not m: + self.fail = True + self.msg_end = 'invalid host tuple: %s' % (self.spec) + return - start_list = os.path.abspath(start).split(os.sep) - path_list = os.path.abspath(path).split(os.sep) + self.msg_end = self.spec - # Work out how much of the filepath is shared by start and path. - i = len(os.path.commonprefix([start_list, path_list])) + ## assign tuple from regex + self[:] = m.groups() - rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:] - if not rel_list: - return os.path.curdir - return os.path.join(*rel_list) + ## for clarity + self.machine = self[0] + self.vendor = self[1] + self.system = self[2] + self.release = self[3] + self.extra = self[4] -############################################################################### + ## nice formal name for 'system' + self.systemf = platform.system() -# compute project dir which should be 2 dirs below this script -build_dir = os.curdir -project_dir = os.path.normpath( sys.argv[0] ) -for i in range( 2 ): - project_dir = os.path.dirname( project_dir ) -if len( project_dir ) == 0: - project_dir = os.curdir -initial_project_dir = project_dir + if self.match( '*-*-cygwin*' ): + self.systemf = self[2][0].upper() + self[2][1:] + + ## glob-match against spec + def match( self, spec ): + return fnmatch.fnmatch( self.spec, spec ) ############################################################################### -## model gnu-autotools platform guess +class BuildAction( Action, list ): + def __init__( self ): + super( BuildAction, self ).__init__( 'compute', 'build tuple', abort=True ) + + def _action( self ): + self.spec = arch.mode[arch.mode.mode] + + ## grok GNU host tuples + m = re.match( HostTupleProbe.GNU_TUPLE_RX, self.spec ) + if not m: + self.msg_end = 'invalid host tuple: %s' % (self.spec) + return + + self.msg_end = self.spec + + ## assign tuple from regex + self[:] = m.groups() + + ## for clarity + self.machine = self[0] + self.vendor = self[1] + self.system = self[2] + self.release = self[3] + self.extra = self[4] + self.systemf = host.systemf + + self.fail = False + +############################################################################### ## -## native format: -## (PROC)-(VENDOR)-(SYSTEM)(RELEASE)-(EXTRA) +## platform conditional string; if specs do not match host: ## -## examples: -## i386-apple-darwin9.6.0 (Mac OS X 10.5.6 Intel) -## powerpc-apple-darwin9.6.0 (Mac OS X 10.5.6 PPC) -## i686-pc-cygwin (Cygwin, Microsoft Vista) -## x86_64-unknown-linux-gnu (Linux, Fedora 10 x86_64) +## - str value is blank +## - evaluates to False ## -class Guess: - def __init__( self ): - self.machine = 'unknown' - self.vendor = 'unknown' - self.system = 'unknown' - self.systemc = 'Unknown' - self.release = '0.0.0' - self.extra = '' - - p_system = platform.system().lower() - p_systemc = platform.system() - p_release = platform.release().lower() - p_processor = platform.processor().lower() - p_machine = platform.machine().lower() - - if re.match( 'cygwin', p_system ): - self.machine = p_machine - self.vendor = 'pc' - self.system = 'cygwin' - self.systemc = 'Cygwin' - self.release = '' - self.extra = '' - elif re.match( 'darwin', p_system ): - self.machine = p_processor - self.vendor = 'apple' - self.system = p_system - self.systemc = p_systemc - self.release = p_release - self.extra = '' - elif re.match( 'linux', p_system ): - self.machine = p_machine - self.vendor = 'unknown' - self.system = p_system - self.systemc = p_systemc - self.release = '' - self.extra = 'gnu' - self.title = 'Linux %s' % (p_machine) - else: - errf( 'unrecognized host system: %s', p_system ) - +class IfHost( object ): + def __init__( self, value, *specs ): + self.value = '' + for spec in specs: + if host.match( spec ): + self.value = value + break + + def __nonzero__( self ): + return len(self.value) != 0 + def __str__( self ): - if len(self.extra): - return '%s-%s-%s%s-%s' % (self.machine,self.vendor,self.system,self.release,self.extra) - else: - return '%s-%s-%s%s' % (self.machine,self.vendor,self.system,self.release) + return self.value - def match( self, spec ): - return fnmatch.fnmatch( str(self), spec ) +############################################################################### +## +## platform conditional value; loops through list of tuples comparing +## to first host match and sets value accordingly; the first value is +## always default. +## +class ForHost( object ): + def __init__( self, default, *tuples ): + self.value = default + for tuple in tuples: + if host.match( tuple[1] ): + self.value = tuple[0] + break + + def __str__( self ): + return self.value ############################################################################### -# a tool represents a command-line tool which may be searched for in PATH -class Tool: - def __init__( self, parent, optional, var, *pool ): - self.name = pool[0] - self.optional = optional - self.var = var - self.pool = pool - self.found = None - if parent: - parent.register( self ) - - def addToConfig( self, config ): - config.add( self.var, self.found ) - - def addToGroup( self, group ): - group.add_option( '', '--' + self.name, help='specify %s location' % (self.name), default=None, metavar='EXE' ) - - def locate( self, options ): - spec = options.__dict__[self.name] - pool = self.pool if not spec else [spec] - for p in pool: - self.found = findExecutable( p ) - if self.found: - outf( 'located %s: %s', self.name, self.found ) - return - if self.optional: - outf( 'missing: %s (optional)', self.name ) +class ArchAction( Action ): + def __init__( self ): + super( ArchAction, self ).__init__( 'compute', 'available architectures', abort=True ) + self.mode = SelectMode( 'architecture', (host.machine,host.spec) ) + + def _action( self ): + self.fail = False + + ## some match on system should be made here; otherwise we signal a warning. + if host.match( '*-*-cygwin*' ): + pass + elif host.match( '*-*-darwin*' ): + self.mode['i386'] = 'i386-apple-darwin%s' % (host.release) + self.mode['x86_64'] = 'x86_64-apple-darwin%s' % (host.release) + self.mode['ppc'] = 'powerpc-apple-darwin%s' % (host.release) + self.mode['ppc64'] = 'powerpc64-apple-darwin%s' % (host.release) + elif host.match( '*-*-linux*' ): + pass else: - errf( 'unable to locate tool: %s', self.name ) - -## a select tool picks first found from a list of tools -class SelectTool( Tool ): - def __init__( self, parent, var, name, *pool ): - self.var = var - self.name = name - self.pool = pool - self.found = None - - self.poolMap = {} - for p in self.pool: - self.poolMap[p.name] = p - if parent: - parent.register( self ) - - def addToConfig( self, config ): - config.add( self.var, self.found ) - - def addToGroup( self, group ): - group.add_option( '', '--' + self.name, help='select %s mode: %s' % (self.name,expandValues(self.poolMap)), - default=self.name, metavar='MODE' ) - - def locate( self, options ): - spec = options.__dict__[self.name] - if spec in self.poolMap: - self.found = spec - return - for p in self.pool: - if p.found: - self.found = p.name - outf( 'selected %s: %s', self.name, self.found ) - return - errf( 'require at least one location of: %s', expandValues( self.poolMap )) + self.msg_pass = 'WARNING' + + self.msg_end = self.mode.toString() + + ## glob-match against spec + def match( self, spec ): + return fnmatch.fnmatch( self.spec, spec ) ############################################################################### -class ToolSet: +class CoreProbe( Action ): def __init__( self ): - self.items = [] - Tool( self, False, 'AR.exe', 'ar' ) - Tool( self, False, 'CP.exe', 'cp' ) - Tool( self, True, 'CURL.exe', 'curl' ) - Tool( self, False, 'GCC.gcc', 'gcc', 'gcc-4' ) - Tool( self, False, 'M4.exe', 'm4' ) - Tool( self, False, 'MKDIR.exe', 'mkdir' ) - Tool( self, False, 'PATCH.exe', 'patch' ) - Tool( self, False, 'RM.exe', 'rm' ) - Tool( self, False, 'TAR.exe', 'tar' ) - Tool( self, True, 'WGET.exe', 'wget' ) - - SelectTool( self, 'FETCH.select', 'fetch', self.wget, self.curl ) - - def register( self, item ): - self.__dict__[item.name] = item - self.items.append( item ) + super( CoreProbe, self ).__init__( 'probe', 'number of CPU cores' ) + self.count = 1 -############################################################################### + def _action( self ): + if self.fail: + ## good for darwin9.6.0 and linux + try: + self.count = os.sysconf( 'SC_NPROCESSORS_ONLN' ) + if self.count < 1: + self.count = 1 + self.fail = False + except: + pass + + if self.fail: + ## windows + try: + self.count = int( os.environ['NUMBER_OF_PROCESSORS'] ) + if self.count < 1: + self.count = 1 + self.fail = False + except: + pass -class OptionMode( list ): - def __init__( self, default, *items ): - super( OptionMode, self ).__init__( items ) - self.default = items[default] - self.mode = self.default + ## clamp + if self.count < 1: + self.count = 1 + elif self.count > 32: + self.count = 32 - def __str__( self ): - s = '' - for a in self: - if a == self.mode: - s += ' *' + a + if options.launch: + if options.launch_jobs == 0: + self.jobs = core.count else: - s += ' ' + a - return s[1:] - - def addToGroup( self, group, option, name ): - group.add_option( '', option, help='select %s mode: %s' % (name,self), default=self.mode, metavar='MODE' ) + self.jobs = options.launch_jobs + else: + self.jobs = core.count - def setFromOption( self, name, mode ): - if mode not in self: - errf( 'invalid %s mode: %s', name, mode ) - self.mode = mode + self.msg_end = str(self.count) ############################################################################### -## create singletons -guessHost = Guess() -guessBuild = Guess() - -makeTool = Tool( None, False, 'CONF.make', 'gmake', 'make' ) -tools = ToolSet() - -debugMode = OptionMode( 0, 'none', 'min', 'std', 'max' ) -optimizeMode = OptionMode( 1, 'none', 'speed', 'size' ) - -## populate platform-specific architecture modes -if guessHost.match( 'i386-*-darwin8.*' ): - archMode = OptionMode( 0, 'i386', 'ppc' ) -elif guessHost.match( 'powerpc-*-darwin8.*' ): - archMode = OptionMode( 1, 'i386', 'ppc' ) -elif guessHost.match( 'i386-*-darwin9.*' ): - archMode = OptionMode( 0, 'i386', 'x86_64', 'ppc', 'ppc64' ) -elif guessHost.match( 'powerpc-*-darwin9.*' ): - archMode = OptionMode( 2, 'i386', 'x86_64', 'ppc', 'ppc64' ) -else: - archMode = OptionMode( 0, guessHost.machine ) - -if guessHost.match( '*-*-darwin*' ): - d_xcode = True - d_prefix = '/Applications' -else: - d_xcode = False - d_prefix = '/usr/local' - -## create parser -parser = OptionParser( 'Usage: %prog' ) - -## add hidden options -parser.add_option( '', '--conf-method', default='terminal', action='store', help=optparse.SUPPRESS_HELP ) - -## add install options -group = OptionGroup( parser, 'Installation Options' ) -group.add_option( '', '--prefix', default=d_prefix, action='store', - help='specify destination for final products (%s)' % (d_prefix) ) -parser.add_option_group( group ) - -group = OptionGroup( parser, 'Feature Options' ) -if d_xcode: - group.add_option( '', '--disable-xcode', default=False, action='store_true', - help='disable Xcode (Darwin only)' ) -group.add_option( '', '--disable-gtk', default=False, action='store_true', - help='disable GTK GUI (Linux only)' ) -parser.add_option_group( group ) - -## add launch options -group = OptionGroup( parser, 'Launch Options' ) -group.add_option( '', '--launch', default=False, action='store_true', - help='launch build, capture log and wait for completion' ) -group.add_option( '', '--launch-jobs', default=1, action='store', metavar='N', - help='allow N jobs at once; 0 to match CPU count (1)' ) -group.add_option( '', '--launch-args', default=None, action='store', metavar='ARGS', - help='specify additional ARGS for launch command' ) -group.add_option( '', '--launch-dir', default='build', action='store', metavar='DIR', - help='specify scratch DIR to use for build (build)' ) -group.add_option( '', '--launch-force', default=False, action='store_true', - help='force use of scratch directory even if exists' ) -group.add_option( '', '--launch-log', default='log.txt', action='store', metavar='FILE', - help='specify log FILE (log.txt)' ) -group.add_option( '', '--launch-quiet', default=False, action='store_true', - help='do not echo build output' ) -parser.add_option_group( group ) - -## add compile options -group = OptionGroup( parser, 'Compiler Options' ) -debugMode.addToGroup( group, '--debug', 'debug' ) -optimizeMode.addToGroup( group, '--optimize', 'optimize' ) -archMode.addToGroup( group, '--arch', 'architecutre' ) -parser.add_option_group( group ) - -## add tool options -group = OptionGroup( parser, 'Tool Options' ) -makeTool.addToGroup( group ) -for tool in tools.items: - tool.addToGroup( group ) -parser.add_option_group( group ) - -(options,args) = parser.parse_args() - -exports = [] -for arg in args: - m = re.match( '([^=]+)=(.*)', arg ) - if m: - exports.append( m.groups() ) - -## recompute values when launch mode -if options.launch: - options.launch_jobs = int(options.launch_jobs) - build_dir = options.launch_dir - if os.path.isabs( build_dir ): - project_dir = os.getcwd() - else: - project_dir = os.path.normpath( relpath( project_dir, build_dir )) - if options.launch_jobs == 0: - options.launch_jobs = computeNumCPU() - if options.launch_jobs < 1: - options.launch_jobs = 1 - elif options.launch_jobs > 8: - options.launch_jobs = 8 - -## make sure configure does not run in source root -if os.path.abspath( project_dir ) == os.path.abspath( build_dir ): - errf( 'scratch (build) directory must not be the same as source root' ) - -## validate modes -debugMode.setFromOption( 'debug', options.debug ) -optimizeMode.setFromOption( 'optimize', options.optimize ) -archMode.setFromOption( 'architecture', options.arch ) - -## update guessBuild as per architecture mode -if guessHost.match( '*-*-darwin*' ): - if archMode.mode == 'i386': - guessBuild.machine = 'i386' - elif archMode.mode == 'x86_64': - guessBuild.machine = 'x86_64' - elif archMode.mode == 'ppc': - guessBuild.machine = 'powerpc' - elif archMode.mode == 'ppc64': - guessBuild.machine = 'powerpc64' -else: - guessBuild.machine = archMode.mode -guessBuild.cross = 0 if archMode.default == archMode.mode else 1 - -# locate tools -makeTool.locate( options ) -for tool in tools.items: - tool.locate( options ) +class SelectMode( dict ): + def __init__( self, descr, *modes, **kwargs ): + super( SelectMode, self ).__init__( modes ) + self.descr = descr + self.modes = modes + self.default = kwargs.get('default',modes[0][0]) + self.mode = self.default + + def cli_add_option( self, parser, option ): + parser.add_option( '', option, default=self.mode, metavar='MODE', + help='select %s mode: %s' % (self.descr,self.toString()), + action='callback', callback=self.cli_callback, type='str' ) + + def cli_callback( self, option, opt_str, value, parser, *args, **kwargs ): + if value not in self: + raise optparse.OptionValueError( 'invalid %s mode: %s (choose from %s)' + % (self.descr,value,self.toString( True )) ) + self.mode = value + + def toString( self, nodefault=False ): + keys = self.keys() + keys.sort() + if len(self) == 1: + value = self.mode + elif nodefault: + value = ' '.join( keys ) + else: + value = '%s [%s]' % (' '.join( keys ), self.mode ) + return value ############################################################################### - +## ## Repository object. ## Holds information gleaned from subversion working dir. ## @@ -427,62 +542,48 @@ for tool in tools.items: ## unofficial ## all other builds ## -class Repository: +class RepoProbe( ShellProbe ): def __init__( self ): + super( RepoProbe, self ).__init__( 'svn info', 'svn info %s' % (cfg.src_dir) ) + self.url = 'svn://nowhere.com/project/unknown' self.root = 'svn://nowhere.com/project' self.branch = 'unknown' self.uuid = '00000000-0000-0000-0000-000000000000'; self.rev = 0 self.date = '0000-00-00 00:00:00 -0000' - self.wcversion = 'exported' self.official = 0 self.type = 'unofficial' - # parse output: svnversion PROJECT_DIR - cmd = 'svnversion ' + initial_project_dir - print 'attempting to probe subversion: %s' % (cmd) - try: - p = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE ) - p.wait(); - if p.returncode: - sys.exit( 1 ) - self.wcversion = p.stdout.readline().rstrip() - except: - pass + def _parseSession( self ): + for line in self.session: + ## grok fields + m = re.match( '([^:]+):\\s+(.+)', line ) + if not m: + continue - # parse output: svn info PROJECT_DIR - cmd = 'svn info ' + initial_project_dir - print 'attempting to probe subversion: %s' % (cmd) - try: - p = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE ) - p.wait(); - if p.returncode: - sys.exit( 1 ) - for line in p.stdout: - (name,value) = re.match( '([^:]+):\\s+(.+)', line.rstrip() ).groups() - if name == 'URL': - self.url = value - elif name == 'Repository Root': - self.root = value - elif name == 'Repository UUID': - self.uuid = value - elif name == 'Revision': - self.rev = int( value ) - elif name == 'Last Changed Date': - # strip chars in parens - if value.find( ' (' ): - self.date = value[0:value.find(' (')] - else: - self.date = value - except: - pass + (name,value) = m.groups() + if name == 'URL': + self.url = value + elif name == 'Repository Root': + self.root = value + elif name == 'Repository UUID': + self.uuid = value + elif name == 'Revision': + self.rev = int( value ) + elif name == 'Last Changed Date': + # strip chars in parens + if value.find( ' (' ): + self.date = value[0:value.find(' (')] + else: + self.date = value + ## grok branch i = self.url.rfind( '/' ) if i != -1 and i < len(self.url)-1: self.branch = self.url[i+1:] - # official UUID behavior + # type-classification via repository UUID if self.uuid == 'b64f7644-9d1e-0410-96f1-a4d463321fa5': self.official = 1 m = re.match( '([^:]+)://([^/]+)/(.+)', self.url ) @@ -491,14 +592,19 @@ class Repository: else: self.type = 'developer' -############################################################################### + self.msg_end = self.url -## Project object. +############################################################################### +## +## project object. +## ## Contains manually updated version numbers consistent with HB releases ## and other project metadata. ## -class Project: +class Project( Action ): def __init__( self ): + super( Project, self ).__init__( 'compute', 'project data' ) + self.name = 'HandBrake' self.acro_lower = 'hb' self.acro_upper = 'HB' @@ -513,6 +619,7 @@ class Project: self.vminor = 9 self.vpoint = 4 + def _action( self ): appcastfmt = 'http://handbrake.fr/appcast%s.xml' if repo.type == 'release': @@ -527,48 +634,164 @@ class Project: self.title = '%s svn%d (%s)' % (self.name,repo.rev,self.build) else: self.version = 'svn%d' % (repo.rev) - self.version = '%d.%d.%d' % (self.vmajor,self.vminor,self.vpoint) self.url_appcast = appcastfmt % ('_unofficial') self.build = time.strftime('%Y%m%d') + '99' self.title = 'Unofficial svn%d (%s)' % (repo.rev,self.build) + self.msg_end = '%s (%s)' % (self.name,repo.type) + self.fail = False + ############################################################################### -## Config object used to output gnu-make or gnu-m4 output. +class ToolProbe( Action ): + tools = [] + + def __init__( self, var, *names, **kwargs ): + super( ToolProbe, self ).__init__( 'find', abort=kwargs.get('abort',True) ) + if not self in ToolProbe.tools: + ToolProbe.tools.append( self ) + self.var = var + self.names = [] + self.kwargs = kwargs + for name in names: + if name: + self.names.append( str(name) ) + self.name = self.names[0] + self.pretext = self.name + self.pathname = self.names[0] + + def _action( self ): + self.session = [] + for i,name in enumerate(self.names): + self.session.append( 'name[%d] = %s' % (i,name) ) + for name in self.names: + f = cfg.findExecutable( name ) + if f: + self.pathname = f + self.fail = False + self.msg_end = f + break + if self.fail: + self.msg_end = 'not found' + + def cli_add_option( self, parser ): + parser.add_option( '', '--'+self.name, metavar='PROG', + help='[%s]' % (self.pathname), + action='callback', callback=self.cli_callback, type='str' ) + + def cli_callback( self, option, opt_str, value, parser, *args, **kwargs ): + self.__init__( self.var, value, **self.kwargs ) + self.run() + + def doc_add( self, doc ): + doc.add( self.var, self.pathname ) + +############################################################################### + +class SelectTool( Action ): + selects = [] + + def __init__( self, var, name, *pool, **kwargs ): + super( SelectTool, self ).__init__( 'select', abort=kwargs.get('abort',True) ) + self.pretext = name + if not self in SelectTool.selects: + SelectTool.selects.append( self ) + self.var = var + self.name = name + self.pool = pool + self.kwargs = kwargs + + def _action( self ): + self.session = [] + for i,(name,tool) in enumerate(self.pool): + self.session.append( 'tool[%d] = %s (%s)' % (i,name,tool.pathname) ) + for (name,tool) in self.pool: + if not tool.fail: + self.selected = name + self.fail = False + self.msg_end = '%s (%s)' % (name,tool.pathname) + break + if self.fail: + self.msg_end = 'not found' + + def cli_add_option( self, parser ): + parser.add_option( '', '--'+self.name, metavar='MODE', + help='select %s mode: %s' % (self.name,self.toString()), + action='callback', callback=self.cli_callback, type='str' ) + + def cli_callback( self, option, opt_str, value, parser, *args, **kwargs ): + found = False + for (name,tool) in self.pool: + if name == value: + found = True + self.__init__( self.var, self.name, [name,tool], **kwargs ) + self.run() + break + if not found: + raise optparse.OptionValueError( 'invalid %s mode: %s (choose from %s)' + % (self.name,value,self.toString( True )) ) + + def doc_add( self, doc ): + doc.add( self.var, self.selected ) + + def toString( self, nodefault=False ): + if len(self.pool) == 1: + value = self.pool[0][0] + else: + s = '' + for key,value in self.pool: + s += ' ' + key + if nodefault: + value = s[1:] + else: + value = '%s [%s]' % (s[1:], self.selected ) + return value + +############################################################################### ## -## Use add() to add NAME/VALUE pairs suitable for both make/m4. -## Use addBlank() to add a linefeed for both make/m4. -## Use addMake() to add a make-specific line. -## Use addM4() to add a m4-specific line. +## config object used to output gnu-make or gnu-m4 output. ## -class Config: +## - add() to add NAME/VALUE pairs suitable for both make/m4. +## - addBlank() to add a linefeed for both make/m4. +## - addMake() to add a make-specific line. +## - addM4() to add a m4-specific line. +## +class ConfigDocument: def __init__( self ): - self._items = [] + self._elements = [] + + def _outputMake( self, file, namelen, name, value ): + file.write( '%-*s = %s\n' % (namelen, name, value )) + + def _outputM4( self, file, namelen, name, value ): + namelen += 7 + name = '<<__%s>>,' % name.replace( '.', '_' ) + file.write( 'define(%-*s <<%s>>)dnl\n' % (namelen, name, value )) def add( self, name, value ): - self._items.append( (name,value) ) + self._elements.append( (name,value) ) def addBlank( self ): - self._items.append( None ) + self._elements.append( None ) def addComment( self, format, *args ): self.addMake( '## ' + format % args ) self.addM4( 'dnl ' + format % args ) def addMake( self, line ): - self._items.append( ('?make',line) ) + self._elements.append( ('?make',line) ) def addM4( self, line ): - self._items.append( ('?m4',line) ) + self._elements.append( ('?m4',line) ) def output( self, file, type ): namelen = 0 - for item in self._items: + for item in self._elements: if item == None or item[0].find( '?' ) == 0: continue if len(item[0]) > namelen: namelen = len(item[0]) - for item in self._items: + for item in self._elements: if item == None: if type == 'm4': file.write( 'dnl\n' ) @@ -585,226 +808,445 @@ class Config: else: self._outputMake( file, namelen, item[0], item[1] ) - def _outputMake( self, file, namelen, name, value ): - file.write( '%-*s = %s\n' % (namelen, name, value )) - - def _outputM4( self, file, namelen, name, value ): - namelen += 7 - name = '<<__%s>>,' % name.replace( '.', '_' ) - file.write( 'define(%-*s <<%s>>)dnl\n' % (namelen, name, value )) + def write( self, type ): + if type == 'make': + fname = 'GNUmakefile' + elif type == 'm4': + fname = os.path.join( 'project', project.name_lower + '.m4' ) + else: + raise ValueError, 'unknown file type: ' + type -############################################################################### + ftmp = fname + '.tmp' + try: + try: + file = cfg.open( ftmp, 'w' ) + self.output( file, type ) + finally: + try: + file.close() + except: + pass + except Exception, x: + try: + os.remove( ftmp ) + except Exception, x: + pass + cfg.errln( 'failed writing to %s\n%s', ftmp, x ) -## create configure line, stripping arg --launch, quoting others -configure = [] -for arg in sys.argv[1:]: - if arg == '--launch': - continue - configure.append( "'%s'" % (arg.replace("'", '%c%c%c%c%c' % (0x27,0x22,0x27,0x22,0x27))) ) - -## create singletones -repo = Repository() -project = Project() -config = Config() - -config.addComment( 'generated by configure on %s', time.strftime( '%c' )) - -config.addBlank() -config.add( 'CONF.args', ' '.join( configure )) - -config.addBlank() -config.add( 'HB.title', project.title ) -config.add( 'HB.name', project.name ) -config.add( 'HB.name.lower', project.name_lower ) -config.add( 'HB.name.upper', project.name_upper ) -config.add( 'HB.acro.lower', project.acro_lower ) -config.add( 'HB.acro.upper', project.acro_upper ) - -config.add( 'HB.url.website', project.url_website ) -config.add( 'HB.url.community', project.url_community ) -config.add( 'HB.url.irc', project.url_irc ) -config.add( 'HB.url.appcast', project.url_appcast ) - -config.add( 'HB.version.major', project.vmajor ) -config.add( 'HB.version.minor', project.vminor ) -config.add( 'HB.version.point', project.vpoint ) -config.add( 'HB.version', project.version ) -config.add( 'HB.version.hex', '%04x%02x%02x%08x' % (project.vmajor,project.vminor,project.vpoint,repo.rev) ) - -config.add( 'HB.build', project.build ) - -config.add( 'HB.repo.url', repo.url ) -config.add( 'HB.repo.root', repo.root ) -config.add( 'HB.repo.branch', repo.branch ) -config.add( 'HB.repo.uuid', repo.uuid ) -config.add( 'HB.repo.rev', repo.rev ) -config.add( 'HB.repo.date', repo.date ) -config.add( 'HB.repo.wcversion', repo.wcversion ) -config.add( 'HB.repo.official', repo.official ) -config.add( 'HB.repo.type', repo.type ) - -config.addBlank() -config.add( 'HOST.spec', guessHost ) -config.add( 'HOST.machine', guessHost.machine ) -config.add( 'HOST.vendor', guessHost.vendor ) -config.add( 'HOST.system', guessHost.system ) -config.add( 'HOST.systemc', guessHost.systemc ) -config.add( 'HOST.release', guessHost.release ) -config.add( 'HOST.title', '%s %s' % (guessHost.systemc,archMode.default) ) -config.add( 'HOST.extra', guessHost.extra ) -config.add( 'HOST.ncpu', computeNumCPU() ) - -config.addBlank() -config.add( 'BUILD.spec', guessBuild ) -config.add( 'BUILD.machine', guessBuild.machine ) -config.add( 'BUILD.vendor', guessBuild.vendor ) -config.add( 'BUILD.system', guessBuild.system ) -config.add( 'BUILD.systemc', guessBuild.systemc ) -config.add( 'BUILD.release', guessBuild.release ) -config.add( 'BUILD.title', '%s %s' % (guessBuild.systemc,archMode.mode) ) -config.add( 'BUILD.extra', guessBuild.extra ) -config.add( 'BUILD.method', 'terminal' ) -config.add( 'BUILD.cross', guessBuild.cross ) -config.add( 'BUILD.date', time.strftime('%c') ) -config.add( 'BUILD.arch', archMode.mode ) -config.add( 'BUILD.jobs', computeNumCPU() ) - -config.addBlank() -config.add( 'CONF.method', options.conf_method ) - -config.addBlank() -config.add( 'BUILD/', os.curdir + os.sep ) -config.add( 'PROJECT/', project_dir + os.sep ) - -config.addBlank() -config.add( 'INSTALL.prefix', options.prefix ) -config.add( 'INSTALL.prefix/', '$(INSTALL.prefix)/' ) - -config.addBlank() -config.add( 'FEATURE.xcode', 0 if not hasattr(options, 'disable_xcode') or options.disable_xcode else 1 ) -config.add( 'FEATURE.gtk', 0 if options.disable_gtk else 1 ) - -config.addMake( '' ) -config.addMake( '## include main definitions' ) -config.addMake( 'include $(PROJECT/)make/include/main.defs' ) - -config.addBlank() -for tool in tools.items: - tool.addToConfig( config ) - -config.addBlank() -config.add( 'GCC.archs', archMode.mode if guessBuild.cross else '' ) -config.add( 'GCC.g', options.debug ) -config.add( 'GCC.O', options.optimize ) - -if len(exports): - config.addBlank() - for nv in exports: - config.add( nv[0], nv[1] ) - -config.addMake( '' ) -config.addMake( '## include (optional) customization file' ) -config.addMake( '-include $(BUID/)GNUmakefile.custom' ) - -config.addMake( '' ) -config.addMake( '## include main rules' ) -config.addMake( 'include $(PROJECT/)make/include/main.rules' ) + try: + os.rename( ftmp, fname ) + except Exception, x: + cfg.errln( 'failed writing to %s\n%s', fname, x ) ############################################################################### +## +## create cli parser +## +def createCLI(): + cli = OptionParser( 'usage: %prog [OPTIONS...] [TARGETS...]' ) + + cli.description = '' + cli.description += 'Configure %s build system.' % (project.name) + + ## add hidden options + cli.add_option( '', '--conf-method', default='terminal', action='store', help=optparse.SUPPRESS_HELP ) + cli.add_option( '', '--force', default=False, action='store_true', help='overwrite existing build config' ) + cli.add_option( '', '--verbose', default=False, action='store_true', help='increase verbosity' ) + + ## add install options + grp = OptionGroup( cli, 'Directory Locations' ) + grp.add_option( '', '--src', default=cfg.src_dir, action='store', metavar='DIR', + help='specify top-level source dir [%s]' % (cfg.src_dir) ) + grp.add_option( '', '--build', default=cfg.build_dir, action='store', metavar='DIR', + help='specify build scratch/output dir [%s]' % (cfg.build_dir) ) + grp.add_option( '', '--prefix', default=cfg.prefix_dir, action='store', metavar='DIR', + help='specify install dir for products [%s]' % (cfg.prefix_dir) ) + cli.add_option_group( grp ) + + ## add feature options + grp = OptionGroup( cli, 'Feature Options' ) + h = ForHost( optparse.SUPPRESS_HELP, ['disable Xcode (Darwin only)','*-*-darwin*'] ).value + grp.add_option( '', '--disable-xcode', default=False, action='store_true', help=h ) + h = ForHost( optparse.SUPPRESS_HELP, ['disable GTK GUI (Linux only)','*-*-linux*'] ).value + grp.add_option( '', '--disable-gtk', default=False, action='store_true', help=h ) + cli.add_option_group( grp ) + + ## add launch options + grp = OptionGroup( cli, 'Launch Options' ) + grp.add_option( '', '--launch', default=False, action='store_true', + help='launch build, capture log and wait for completion' ) + grp.add_option( '', '--launch-jobs', default=1, action='store', metavar='N', type='int', + help='allow N jobs at once; 0 to match CPU count [1]' ) + grp.add_option( '', '--launch-args', default=None, action='store', metavar='ARGS', + help='specify additional ARGS for launch command' ) + grp.add_option( '', '--launch-quiet', default=False, action='store_true', + help='do not echo build output' ) + cli.add_option_group( grp ) + + ## add compile options + grp = OptionGroup( cli, 'Compiler Options' ) + debugMode.cli_add_option( grp, '--debug' ) + optimizeMode.cli_add_option( grp, '--optimize' ) + arch.mode.cli_add_option( grp, '--arch' ) + cli.add_option_group( grp ) + + ## add tool locations + grp = OptionGroup( cli, 'Tool Basenames and Locations' ) + for tool in ToolProbe.tools: + tool.cli_add_option( grp ) + cli.add_option_group( grp ) + + ## add tool modes + grp = OptionGroup( cli, 'Tool Options' ) + for select in SelectTool.selects: + select.cli_add_option( grp ) + cli.add_option_group( grp ) + return cli -# generate make or m4 file -def generate( type ): - if type == 'make': - fname = 'GNUmakefile' - elif type == 'm4': - fname = os.path.join( 'project', project.name_lower + '.m4' ) - else: - raise ValueError, 'unknown file type: ' + type - - ftmp = fname + '.tmp' - - pdir = os.path.dirname( fname ) - if pdir: - if not os.path.exists( pdir ): - os.makedirs( pdir ) +############################################################################### +## +## launcher - used for QuickStart method; launch; build and capture log. +## +class Launcher: + def __init__( self, targets ): + # open build logfile + self._file = cfg.open( 'log/build.txt', 'w' ) + + cmd = '%s -j%d' % (Tools.gmake.pathname,core.jobs) + if options.launch_args: + cmd += ' ' + options.launch_args + if len(targets): + cmd += ' ' + ' '.join(targets) + + ## record begin + timeBegin = time.time() + self.infof( 'time begin: %s\n', time.asctime() ) + self.infof( 'launch: %s\n', cmd ) + if options.launch_quiet: + stdout.write( 'building to %s ...\n' % (os.path.abspath( cfg.build_final ))) + else: + stdout.write( '%s\n' % ('-' * 79) ) - try: + ## launch/pipe try: - outf( 'generating %s', fname ) - file = open( ftmp, 'w' ) - config.output( file, type ) - finally: - try: - file.close() - except: - pass - except Exception, x: - try: - os.remove( ftmp ) + pipe = subprocess.Popen( cmd, shell=True, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) except Exception, x: - pass - errf( 'failed writing to %s\n%s', ftmp, x ) + cfg.errln( 'launch failure: %s', x ) + for line in pipe.stdout: + self.echof( '%s', line ) + pipe.wait() - try: - os.rename( ftmp, fname ) - except Exception, x: - errf( 'failed writing to %s\n%s', fname, x ) + ## record end + timeEnd = time.time() + elapsed = timeEnd - timeBegin -############################################################################### + if pipe.returncode: + result = 'FAILURE (code %d)' % pipe.returncode + else: + result = 'SUCCESS' + + ## present duration in decent format + seconds = elapsed + hours = int(seconds / 3600) + seconds -= hours * 3600 + minutes = int(seconds / 60) + seconds -= minutes * 60 + + segs = [] + duration = '' + + if hours == 1: + segs.append( '%d hour' % hours ) + elif hours > 1: + segs.append( '%d hours' % hours ) + + if len(segs) or minutes == 1: + segs.append( '%d minute' % minutes ) + elif len(segs) or minutes > 1: + segs.append( '%d minutes' % minutes ) + + if seconds == 1: + segs.append( '%d second' % seconds ) + else: + segs.append( '%d seconds' % seconds ) -if not options.launch: - generate( 'make' ) - generate( 'm4' ) - sys.exit( 0 ) + if not options.launch_quiet: + stdout.write( '%s\n' % ('-' * 79) ) + self.infof( 'time end: %s\n', time.asctime() ) + self.infof( 'duration: %s (%.2fs)\n', ', '.join(segs), elapsed ) + self.infof( 'result: %s\n', result ) -############################################################################### + ## cleanup + self._file.close() -if os.path.exists( options.launch_dir ): - if not options.launch_force: - errf( 'scratch directory already exists: %s', options.launch_dir ) -else: - outf( 'creating %s', options.launch_dir ) - os.makedirs( options.launch_dir ) + def echof( self, format, *args ): + line = format % args + self._file.write( line ) + if not options.launch_quiet: + stdout.write( ' : %s' % line ) + stdout.flush() -outf( 'chdir %s', options.launch_dir ) -os.chdir( options.launch_dir ) -generate( 'make' ) -generate( 'm4' ) + def infof( self, format, *args ): + line = format % args + self._file.write( line ) + cfg.infof( '%s', line ) -outf( 'opening %s', options.launch_log ) +############################################################################### +## +## main program +## try: - log = open( options.launch_log, 'w' ) -except Exception, x: - errf( 'open failure: %s', x ) + ## we need to pre-check argv for -h or --help or --verbose to deal with + ## initializing Configure correctly. + verbose = Configure.OUT_INFO + for arg in sys.argv: + if arg == '-h' or arg == '--help': + verbose = Configure.OUT_QUIET + break + if arg == '--verbose': + verbose = Configure.OUT_VERBOSE + + ## create main objects; actions/probes run() is delayed. + ## if any actions must be run earlier (eg: for configure --help purposes) + ## then run() must be invoked earlier. subequent run() invocations + ## are ignored. + cfg = Configure( verbose ) + host = HostTupleProbe(); host.run() + + cfg.prefix_dir = ForHost( '/usr/local', ['/Applications','*-*-darwin*'] ).value + + build = BuildAction() + arch = ArchAction(); arch.run() + + ## create remaining main objects + core = CoreProbe() + repo = RepoProbe() + project = Project() + + ## create tools in a scope + class Tools: + ar = ToolProbe( 'AR.exe', 'ar' ) + cp = ToolProbe( 'CP.exe', 'cp' ) + curl = ToolProbe( 'CURL.exe', 'curl', abort=False ) + gcc = ToolProbe( 'GCC.gcc', 'gcc', IfHost('gcc-4','*-*-cygwin*') ) + + if host.match( '*-*-darwin*' ): + gmake = ToolProbe( 'GMAKE.exe', 'make', 'gmake' ) + else: + gmake = ToolProbe( 'GMAKE.exe', 'gmake', 'make' ) + + m4 = ToolProbe( 'M4.exe', 'm4' ) + mkdir = ToolProbe( 'MKDIR.exe', 'mkdir' ) + patch = ToolProbe( 'PATCH.exe', 'gpatch', 'patch' ) + rm = ToolProbe( 'RM.exe', 'rm' ) + tar = ToolProbe( 'TAR.exe', 'gtar', 'tar' ) + wget = ToolProbe( 'WGET.exe', 'wget', abort=False ) + + xcodebuild = ToolProbe( 'XCODEBUILD.exe', 'xcodebuild', abort=False ) + lipo = ToolProbe( 'LIPO.exe', 'lipo', abort=False ) + + fetch = SelectTool( 'FETCH.select', 'fetch', ['wget',wget], ['curl',curl] ) + + ## run tool probes + for tool in ToolProbe.tools: + tool.run() + for select in SelectTool.selects: + select.run() + + debugMode = SelectMode( 'debug', ('none','none'), ('min','min'), ('std','std'), ('max','max') ) + optimizeMode = SelectMode( 'optimize', ('none','none'), ('speed','speed'), ('size','size'), default='speed' ) + + ## create CLI and parse + cli = createCLI() + (options,args) = cli.parse_args() + + ## update cfg with cli directory locations + cfg.update_cli( options ) + + ## prepare list of targets and NAME=VALUE args to pass to make + targets = [] + exports = [] + rx_exports = re.compile( '([^=]+)=(.*)' ) + for arg in args: + m = rx_exports.match( arg ) + if m: + exports.append( m.groups() ) + else: + targets.append( arg ) + + ## run delayed actions + for action in Action.actions: + action.run() + + ## cfg hook before doc prep + cfg.doc_ready() + + ## create document object + doc = ConfigDocument() + doc.addComment( 'generated by configure on %s', time.strftime( '%c' )) + + ## add configure line for reconfigure purposes + doc.addBlank() + args = [] + for arg in sys.argv[1:]: + if arg == '--launch': + continue + args.append( "'%s'" % (arg.replace("'", '%c%c%c%c%c' % (0x27,0x22,0x27,0x22,0x27))) ) + doc.add( 'CONF.args', ' '.join( args )) + + doc.addBlank() + doc.add( 'HB.title', project.title ) + doc.add( 'HB.name', project.name ) + doc.add( 'HB.name.lower', project.name_lower ) + doc.add( 'HB.name.upper', project.name_upper ) + doc.add( 'HB.acro.lower', project.acro_lower ) + doc.add( 'HB.acro.upper', project.acro_upper ) + + doc.add( 'HB.url.website', project.url_website ) + doc.add( 'HB.url.community', project.url_community ) + doc.add( 'HB.url.irc', project.url_irc ) + doc.add( 'HB.url.appcast', project.url_appcast ) + + doc.add( 'HB.version.major', project.vmajor ) + doc.add( 'HB.version.minor', project.vminor ) + doc.add( 'HB.version.point', project.vpoint ) + doc.add( 'HB.version', project.version ) + doc.add( 'HB.version.hex', '%04x%02x%02x%08x' % (project.vmajor,project.vminor,project.vpoint,repo.rev) ) + + doc.add( 'HB.build', project.build ) + + doc.add( 'HB.repo.url', repo.url ) + doc.add( 'HB.repo.root', repo.root ) + doc.add( 'HB.repo.branch', repo.branch ) + doc.add( 'HB.repo.uuid', repo.uuid ) + doc.add( 'HB.repo.rev', repo.rev ) + doc.add( 'HB.repo.date', repo.date ) + doc.add( 'HB.repo.official', repo.official ) + doc.add( 'HB.repo.type', repo.type ) + + doc.addBlank() + doc.add( 'HOST.spec', host.spec ) + doc.add( 'HOST.machine', host.machine ) + doc.add( 'HOST.vendor', host.vendor ) + doc.add( 'HOST.system', host.system ) + doc.add( 'HOST.systemf', host.systemf ) + doc.add( 'HOST.release', host.release ) + doc.add( 'HOST.extra', host.extra ) + doc.add( 'HOST.title', '%s %s' % (host.systemf,arch.mode.default) ) + doc.add( 'HOST.ncpu', core.count ) + + doc.addBlank() + doc.add( 'BUILD.spec', build.spec ) + doc.add( 'BUILD.machine', build.machine ) + doc.add( 'BUILD.vendor', build.vendor ) + doc.add( 'BUILD.system', build.system ) + doc.add( 'BUILD.systemf', build.systemf ) + doc.add( 'BUILD.release', build.release ) + doc.add( 'BUILD.extra', build.extra ) + doc.add( 'BUILD.title', '%s %s' % (build.systemf,arch.mode.mode) ) + doc.add( 'BUILD.ncpu', core.count ) + doc.add( 'BUILD.jobs', core.jobs ) + + doc.add( 'BUILD.cross', int(arch.mode.mode != arch.mode.default) ) + doc.add( 'BUILD.method', 'terminal' ) + doc.add( 'BUILD.date', time.strftime('%c') ) + doc.add( 'BUILD.arch', arch.mode.mode ) + + doc.addBlank() + doc.add( 'CONF.method', options.conf_method ) + + doc.addBlank() + doc.add( 'SRC', cfg.src_final ) + doc.add( 'SRC/', cfg.src_final + os.sep ) + doc.add( 'BUILD', cfg.build_final ) + doc.add( 'BUILD/', cfg.build_final + os.sep ) + doc.add( 'PREFIX', cfg.prefix_final ) + doc.add( 'PREFIX/', cfg.prefix_final + os.sep ) + + doc.addBlank() + doc.add( 'FEATURE.xcode', int( not (Tools.xcodebuild.fail or options.disable_xcode) )) + doc.add( 'FEATURE.gtk', int( not options.disable_gtk )) + + if not Tools.xcodebuild.fail and not options.disable_xcode: + doc.addBlank() + doc.add( 'XCODE.external.src', cfg.xcode_x_src ) + doc.add( 'XCODE.external.build', cfg.xcode_x_build ) + doc.add( 'XCODE.external.prefix', cfg.xcode_x_prefix ) + + doc.addMake( '' ) + doc.addMake( '## include definitions' ) + doc.addMake( 'include $(SRC/)make/include/main.defs' ) + + doc.addBlank() + for tool in ToolProbe.tools: + tool.doc_add( doc ) + + doc.addBlank() + for select in SelectTool.selects: + select.doc_add( doc ) + + doc.addBlank() + if arch.mode.mode != arch.mode.default: + doc.add( 'GCC.archs', arch.mode.mode ) + else: + doc.add( 'GCC.archs', '' ) + doc.add( 'GCC.g', debugMode.mode ) + doc.add( 'GCC.O', optimizeMode.mode ) + + ## add exports to make + if len(exports): + doc.addBlank() + doc.addComment( 'overrides via VARIABLE=VALUE on command-line' ) + for nv in exports: + doc.add( nv[0], nv[1] ) + + doc.addMake( '' ) + doc.addMake( '## include custom definitions' ) + doc.addMake( '-include $(SRC/)custom.defs' ) + doc.addMake( '-include $(BUILD/)GNUmakefile.custom.defs' ) + + doc.addMake( '' ) + doc.addMake( '## include rules' ) + doc.addMake( 'include $(SRC/)make/include/main.rules' ) + doc.addMake( '-include $(SRC/)custom.rules' ) + doc.addMake( '-include $(BUILD/)GNUmakefile.custom.rules' ) + + ## chdir + cfg.chdir() + + ## perform + doc.write( 'make' ) + doc.write( 'm4' ) + if options.launch: + Launcher( targets ) + + cfg.record_log() + + if os.path.normpath( cfg.build_dir ) == os.curdir: + nocd = True + else: + nocd = False -cmd = '%s -j%d' % (makeTool.found,options.launch_jobs) -if options.launch_args: - cmd += ' ' + options.launch_args + stdout.write( '%s\n' % ('-' * 79) ) + if options.launch: + stdout.write( 'Build is finished!\n' ) + if nocd: + stdout.write( 'You may now examine the output.\n' ) + else: + stdout.write( 'You may now cd into %s and examine the output.\n' % (cfg.build_dir) ) + else: + stdout.write( 'Build is configured!\n' ) + if nocd: + stdout.write( 'You may now run make (%s).\n' % (Tools.gmake.pathname) ) + else: + stdout.write( 'You may now cd into %s and run make (%s).\n' % (cfg.build_dir,Tools.gmake.pathname) ) -## record begin -timeBegin = time.time() -s = '###\n### TIME: %s\n### launch: %s\n###\n' % (time.asctime(),cmd) -stdout.write( s ); stdout.flush() -log.write( s ); log.flush() +except AbortError, x: + stderr.write( 'ERROR: %s\n' % (x) ) + try: + cfg.record_log() + except: + pass + sys.exit( 1 ) -## launch/pipe -try: - pipe = subprocess.Popen( cmd, shell=True, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) -except Exception, x: - errf( 'launch failure: %s', x ) -for line in pipe.stdout: - if not options.launch_quiet: - stdout.write( line ); stdout.flush() - log.write( line ); log.flush() -pipe.wait() - -## record end -timeEnd = time.time() -elapsed = timeEnd - timeBegin -result = '%s (exit code %d)' % ('success' if pipe.returncode == 0 else 'failed',pipe.returncode) -s = '###\n### TIME: %s\n### finished: %.2f seconds\n### %s\n###\n' % (time.asctime(),elapsed,result) -stdout.write( s ); stdout.flush() -log.write( s ); log.flush() - -log.close() sys.exit( 0 ) diff --git a/make/include/contrib.defs b/make/include/contrib.defs index 3cd80303e..5f1f9e54b 100644 --- a/make/include/contrib.defs +++ b/make/include/contrib.defs @@ -1,5 +1,5 @@ CONTRIB.build/ = $(BUILD/)contrib/ -CONTRIB.download/ = $(PROJECT/)download/ +CONTRIB.download/ = $(SRC/)download/ CONTRIB.host = $(if $(filter 1,$(BUILD.cross)),$(BUILD.spec)) ############################################################################### @@ -17,7 +17,7 @@ define import.CONTRIB.defs ## ## common values useful across targets ## - $(1).src/ = $$(PROJECT/)contrib/$($(1).name)/ + $(1).src/ = $$(SRC/)contrib/$($(1).name)/ $(1).build/ = $$(CONTRIB.build/)$($(1).name)/ $(1).deps = $$(foreach n,$($(1).prerequisites),$$($$n.INSTALL.target)) @@ -209,7 +209,7 @@ $($(1).name).extract.touch: $$(TOUCH.exe) $$($(1).EXTRACT.target) $($(1).name).extract.untouch: - $$(RM.exe) $$($(1).EXTRACT.target) + $$(RM.exe) -f $$($(1).EXTRACT.target) ## ## target: patch @@ -224,7 +224,7 @@ $($(1).name).patch.touch: $$(TOUCH.exe) $$($(1).PATCH.target) $($(1).name).patch.untouch: - $$(RM.exe) $$($(1).PATCH.target) + $$(RM.exe) -f $$($(1).PATCH.target) ## ## target: configure @@ -240,7 +240,7 @@ $($(1).name).configure.touch: $$(TOUCH.exe) $$($(1).CONFIGURE.target) $($(1).name).configure.untouch: - $$(RM.exe) $$($(1).CONFIGURE.target) + $$(RM.exe) -f $$($(1).CONFIGURE.target) ## ## target: build @@ -256,7 +256,7 @@ $($(1).name).build.touch: $$(TOUCH.exe) $$($(1).BUILD.target) $($(1).name).build.untouch: - $$(RM.exe) $$($(1).BUILD.target) + $$(RM.exe) -f $$($(1).BUILD.target) ## ## target: install @@ -271,7 +271,7 @@ $($(1).name).install.touch: $$(TOUCH.exe) $$($(1).INSTALL.target) $($(1).name).install.untouch: - $$(RM.exe) $$($(1).INSTALL.target) + $$(RM.exe) -f $$($(1).INSTALL.target) ## ## target: uninstall diff --git a/make/include/main.defs b/make/include/main.defs index 93271a365..f07678b79 100644 --- a/make/include/main.defs +++ b/make/include/main.defs @@ -1,10 +1,10 @@ -include $(PROJECT/)make/include/base.defs -include $(PROJECT/)make/include/contrib.defs -include $(PROJECT/)make/include/function.defs -include $(PROJECT/)make/include/gcc.defs -include $(PROJECT/)make/include/select.defs -include $(PROJECT/)make/include/target.defs -include $(PROJECT/)make/include/tool.defs +include $(SRC/)make/include/base.defs +include $(SRC/)make/include/contrib.defs +include $(SRC/)make/include/function.defs +include $(SRC/)make/include/gcc.defs +include $(SRC/)make/include/select.defs +include $(SRC/)make/include/target.defs +include $(SRC/)make/include/tool.defs ############################################################################### @@ -62,6 +62,6 @@ MODULES += doc ############################################################################### -include $(MODULES:%=$(PROJECT/)%/module.defs) -include $(PROJECT/)make/variant/$(HOST.system).defs --include $(PROJECT/)make/variant/$(HOST.system).$(BUILD.machine).defs +include $(MODULES:%=$(SRC/)%/module.defs) +include $(SRC/)make/variant/$(HOST.system).defs +-include $(SRC/)make/variant/$(HOST.system).$(BUILD.machine).defs diff --git a/make/include/main.rules b/make/include/main.rules index 4625ae9db..51460a35d 100644 --- a/make/include/main.rules +++ b/make/include/main.rules @@ -5,12 +5,12 @@ ## file-wide conditional to use xcode rules if xcode=1 method=terminal ifeq ($(FEATURE.xcode):$(BUILD.method),1:terminal) - include $(PROJECT/)macosx/module.xcode + include $(SRC/)macosx/module.xcode else ## only included using special report targets ifneq (,$(REPORT)) - include $(PROJECT/)make/include/report.defs + include $(SRC/)make/include/report.defs endif ############################################################################### @@ -30,22 +30,22 @@ mrproper: xclean ############################################################################### -include $(PROJECT/)make/include/base.rules +include $(SRC/)make/include/base.rules -include $(MODULES:%=$(PROJECT/)%/module.rules) --include $(PROJECT/)make/variant/$(HOST.system).rules --include $(PROJECT/)make/variant/$(HOST.system).$(BUILD.machine).rules +include $(MODULES:%=$(SRC/)%/module.rules) +-include $(SRC/)make/variant/$(HOST.system).rules +-include $(SRC/)make/variant/$(HOST.system).$(BUILD.machine).rules ############################################################################### ## target which causes re-configure if project-root is svn update'd -$(BUILD/)GNUmakefile: $(wildcard $(PROJECT/).svn/entries) - $(PROJECT/)configure $(CONF.args) +$(BUILD/)GNUmakefile: $(wildcard $(SRC/).svn/entries) + $(SRC/)configure $(CONF.args) ## target useful to force reconfigure; only helpful for build-system development .PHONY: reconfigure reconfigure: - $(PROJECT/)configure $(CONF.args) + $(SRC/)configure $(CONF.args) ############################################################################### diff --git a/make/variant/darwin.defs b/make/variant/darwin.defs index 6f8f6729d..baffda595 100644 --- a/make/variant/darwin.defs +++ b/make/variant/darwin.defs @@ -1,40 +1,9 @@ -## UB build support -## -## PROCEDURE: -## -## 1. Perform a build (let us call it arch-X). -## 2. cd into arch-X's build/ driectory. -## 3. Launch builds of the remaining architectures. -## We compute $(UB.archs.other) to be the remaining archs to build. -## The entire list is defined $(UB.archs) . -## 4. Combine other architectures into arch-X's binaries. -## -## EXAMPLE: serial method, archs: i386,x86_64,ppc,ppc64 -## -## ./configure --launch -## cd build/ -## make ub.build.serial -## make ub.combine -## -## EXAMPLE: parallel method, archs: i386,x86_64,ppc,ppc64 -## -## ./configure --launch -## cd build/ -## make ub.build.parallel -## make ub.combine -## -## EXAMPLE: serial, archs: i386,x86_64 (assuming i386 is native) -## -## ./configure --launch -## cd build/ -## make ub.build.parallel UB.archs="i386 x86_64" -## make ub.combine UB.archs="i386 x86_64" -## -UB.archs = i386 x86_64 ppc ppc64 -UB.archs.other = $(filter-out $(BUILD.arch),$(UB.archs)) -UB.builds = $(wildcard $(foreach n,$(UB.archs.other),$(PROJECT/)build.$n)) +UB.archs = i386 x86_64 ppc ppc64 +UB.builds = $(wildcard $(foreach n,$(UB.archs),$(SRC/)build.$n)) +UB.first = $(word 1,$(UB.archs)) +UB.other = $(wordlist 2,999,$(UB.archs)) -UB.BUILD = $(PROJECT/)configure --launch --launch-dir=ub.$(1) --arch=$(1) +UB.BUILD = $(SRC/)configure --build=ub.$(1) --arch=$(1) --launch --launch-quiet ## linefeed is important define UB.BUILD.item @@ -43,19 +12,21 @@ define UB.BUILD.item endef define UB.BUILD.SERIAL - $(foreach n,$(UB.archs.other),$(call UB.BUILD.item,$n)) + $(foreach n,$(UB.archs),$(call UB.BUILD.item,$n)) endef define UB.BUILD.PARALLEL - $(call UB.BUILD,$(1)) 2>&1 > ub.$(1).log + $(call UB.BUILD,$(1)) >/dev/null 2>&1 endef define UB.COMBINE $(RM.exe) -fr ub.combine $(MKDIR.exe) -p ub.combine - $(CP.exe) -R HandBrake.app ub.combine/. - lipo $(1) $(foreach n,$(UB.archs.other),ub.$n/$(1)) -create -output ub.combine/$(1) + $(CP.exe) -R ub.$(UB.first)/HandBrake.app ub.combine/. + $(LIPO.exe) $(foreach n,$(UB.archs),ub.$n/$(1)) -create -output ub.combine/$(1) + @lipo -info ub.combine/$(1) + @sync @echo "" - @sync; lipo -info ub.combine/$(1) - @du -sh ub.combine/$(1) + @echo "$@: { $(UB.archs) } combined -> ub.combine/HandBrake.app" + @echo "$@: UB executable size: `du -sh ub.combine/$(1) | awk '{ print $$1 }'`" endef diff --git a/make/variant/darwin.rules b/make/variant/darwin.rules index c4f4f97a4..d59a222b4 100644 --- a/make/variant/darwin.rules +++ b/make/variant/darwin.rules @@ -5,7 +5,7 @@ ub.build.serial: ub.build.parallel: @set -e; \ - for arch in $(UB.archs.other); do \ + for arch in $(UB.archs); do \ $(call UB.BUILD.PARALLEL,$$arch) & \ children="$$children $$!"; \ echo "pid $$!: $(call UB.BUILD.PARALLEL,$$arch)"; \ diff --git a/make/variant/freebsd.defs b/make/variant/freebsd.defs index c58706ba1..9a807b8d4 100644 --- a/make/variant/freebsd.defs +++ b/make/variant/freebsd.defs @@ -2,3 +2,8 @@ TARGET.dylib.ext = .so GCC.args.dylib = -shared GCC.args.pic = 1 + +GCC.args.g.none = -g0 +GCC.args.g.min = -g1 +GCC.args.g.default = -g2 +GCC.args.g.max = -g3 diff --git a/make/xcodemake b/make/xcodemake index a75cd3ed6..fde684cdf 100755 --- a/make/xcodemake +++ b/make/xcodemake @@ -12,8 +12,6 @@ set -e MAKEFLAGS= MFLAGS= -cd $EXTERNAL_BUILD - ## re/configure if not configured by Xcode or if missing top-makefile if [ $EXTERNAL_METHOD != 'xcode' -o ! -f $EXTERNAL_BUILD/GNUmakefile ]; then ## compute --arch value based on Xcode configuration naming convention @@ -36,7 +34,7 @@ if [ $EXTERNAL_METHOD != 'xcode' -o ! -f $EXTERNAL_BUILD/GNUmakefile ]; then esac ## invoke configure with (hidden) option which indicates conf performed by xcode - $EXTERNAL_PROJECT/configure PATH=$PATH --conf-method=xcode $args + $EXTERNAL_SRC/configure --force --build=$EXTERNAL_BUILD --conf-method=xcode PATH=$PATH $args fi ## safeguard against passing blank value which would result in unlimited jobs |