OSDN Git Service

BuildSystem:
[handbrake-jp/handbrake-jp-git.git] / make / configure.py
1 import fnmatch
2 import optparse
3 import os
4 import platform
5 import re
6 import subprocess
7 import sys
8 import time
9
10 from optparse import OptionGroup
11 from optparse import OptionGroup
12 from optparse import OptionParser
13 from sys import stderr
14 from sys import stdout
15
16 ###############################################################################
17
18 def errf( format, *args ):
19     stderr.write( ('ERROR: ' + format + '\n') % args )
20     sys.exit( 1 )
21
22 def outf( format, *args ):
23     stdout.write( (format + '\n') % args )
24
25 ###############################################################################
26
27 ## Expand values of iterable object into a decent string representation.
28 ##
29 def expandValues( obj ):
30     buf = ''
31     for v in obj:
32         buf += ', ' + v
33     return '{ ' + buf[2:] + ' }'
34
35 ###############################################################################
36
37 ## Find executable by searching path.
38 ## On success, returns full pathname of executable.
39 ## On fail, returns None.
40 ##
41 def findExecutable( name ):
42     if len( os.path.split(name)[0] ):
43         return name if os.access( name, os.X_OK ) else None
44
45     if not os.environ.has_key( 'PATH' ) or os.environ[ 'PATH' ] == '':
46         path = os.defpath
47     else:
48         path = os.environ['PATH']
49
50     for dir in path.split( os.pathsep ):
51         f = os.path.join( dir, name )
52         if os.access( f, os.X_OK ):
53             return f
54     return None
55
56 ###############################################################################
57
58 def computeNumCPU():
59     ## good for darwin9.6.0 and linux
60     try:
61         n = os.sysconf( 'SC_NPROCESSORS_ONLN' )
62         if n < 1:
63             n = 1
64         return n
65     except:
66         pass
67     ## windows
68     try:
69         n = int( os.environ['NUMBER_OF_PROCESSORS'] )
70         if n < 1:
71             n = 1
72         return n
73     except:
74         pass
75     return 1
76
77 ###############################################################################
78
79 ## taken from python2.6 -- we need it
80 def relpath(path, start=os.path.curdir):
81     """Return a relative version of a path"""
82
83     if not path:
84         raise ValueError("no path specified")
85
86     start_list = os.path.abspath(start).split(os.sep)
87     path_list = os.path.abspath(path).split(os.sep)
88
89     # Work out how much of the filepath is shared by start and path.
90     i = len(os.path.commonprefix([start_list, path_list]))
91
92     rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:]
93     if not rel_list:
94         return os.path.curdir
95     return os.path.join(*rel_list)
96
97 ###############################################################################
98
99 # compute project dir which should be 2 dirs below this script
100 build_dir = os.curdir
101 project_dir = os.path.normpath( sys.argv[0] )
102 for i in range( 2 ):
103     project_dir = os.path.dirname( project_dir )
104 if len( project_dir ) == 0:
105     project_dir = os.curdir
106 initial_project_dir = project_dir
107
108 ###############################################################################
109
110 ## model gnu-autotools platform guess
111 ##
112 ## native format:
113 ##   (PROC)-(VENDOR)-(SYSTEM)(RELEASE)-(EXTRA)
114 ##
115 ## examples:
116 ##   i386-apple-darwin9.6.0    (Mac OS X 10.5.6 Intel)
117 ##   powerpc-apple-darwin9.6.0 (Mac OS X 10.5.6 PPC)
118 ##   i686-pc-cygwin            (Cygwin, Microsoft Vista)
119 ##   x86_64-unknown-linux-gnu  (Linux, Fedora 10 x86_64)
120 ##
121 class Guess:
122     def __init__( self ):
123         self.machine = 'unknown'
124         self.vendor  = 'unknown'
125         self.system  = 'unknown'
126         self.systemc = 'Unknown'
127         self.release = '0.0.0'
128         self.extra   = ''
129
130         p_system    = platform.system().lower()
131         p_systemc   = platform.system()
132         p_release   = platform.release().lower()
133         p_processor = platform.processor().lower()
134         p_machine   = platform.machine().lower()
135
136         if re.match( 'cygwin', p_system ):
137             self.machine = p_machine
138             self.vendor  = 'pc'
139             self.system  = 'cygwin'
140             self.systemc = 'Cygwin'
141             self.release = ''
142             self.extra   = ''
143         elif re.match( 'darwin', p_system ):
144             self.machine = p_processor
145             self.vendor  = 'apple'
146             self.system  = p_system
147             self.systemc = p_systemc
148             self.release = p_release
149             self.extra   = ''
150         elif re.match( 'linux', p_system ):
151             self.machine = p_machine
152             self.vendor  = 'unknown'
153             self.system  = p_system
154             self.systemc = p_systemc
155             self.release = ''
156             self.extra   = 'gnu'
157             self.title   = 'Linux %s' % (p_machine)
158         else:
159             errf( 'unrecognized host system: %s', p_system )
160
161     def __str__( self ):
162         if len(self.extra):
163             return '%s-%s-%s%s-%s' % (self.machine,self.vendor,self.system,self.release,self.extra)
164         else:
165             return '%s-%s-%s%s' % (self.machine,self.vendor,self.system,self.release)
166
167     def match( self, spec ):
168         return fnmatch.fnmatch( str(self), spec )
169
170 ###############################################################################
171
172 # a tool represents a command-line tool which may be searched for in PATH
173 class Tool:
174     def __init__( self, parent, optional, var, *pool ):
175         self.name     = pool[0]
176         self.optional = optional
177         self.var      = var
178         self.pool     = pool
179         self.found    = None
180         if parent:
181             parent.register( self )
182
183     def addToConfig( self, config ):
184         config.add( self.var, self.found )
185
186     def addToGroup( self, group ):
187         group.add_option( '', '--' + self.name, help='specify %s location' % (self.name), default=None, metavar='EXE' )
188
189     def locate( self, options ):
190         spec = options.__dict__[self.name]
191         pool = self.pool if not spec else [spec]
192         for p in pool:
193             self.found = findExecutable( p )
194             if self.found:
195                 outf( 'located %s: %s', self.name, self.found )
196                 return
197         if self.optional:
198             outf( 'missing: %s (optional)', self.name )
199         else:
200             errf( 'unable to locate tool: %s', self.name )
201
202 ## a select tool picks first found from a list of tools
203 class SelectTool( Tool ):
204     def __init__( self, parent, var, name, *pool ):
205         self.var     = var
206         self.name    = name
207         self.pool    = pool
208         self.found   = None
209
210         self.poolMap = {}
211         for p in self.pool:
212             self.poolMap[p.name] = p
213         if parent:
214             parent.register( self )
215
216     def addToConfig( self, config ):
217         config.add( self.var, self.found )
218
219     def addToGroup( self, group ):
220         group.add_option( '', '--' + self.name, help='select %s mode: %s' % (self.name,expandValues(self.poolMap)),
221             default=self.name, metavar='MODE' )
222
223     def locate( self, options ):
224         spec = options.__dict__[self.name]
225         if spec in self.poolMap:
226             self.found = spec
227             return
228         for p in self.pool:
229             if p.found:
230                 self.found = p.name
231                 outf( 'selected %s: %s', self.name, self.found )
232                 return
233         errf( 'require at least one location of: %s', expandValues( self.poolMap ))
234
235 ###############################################################################
236
237 class ToolSet:
238     def __init__( self ):
239         self.items = []
240         Tool( self, False, 'AR.exe',    'ar' )
241         Tool( self, False, 'CP.exe',    'cp' )
242         Tool( self, True,  'CURL.exe',  'curl' )
243         Tool( self, False, 'GCC.gcc',   'gcc', 'gcc-4' )
244         Tool( self, False, 'M4.exe',    'm4' )
245         Tool( self, False, 'MKDIR.exe', 'mkdir' )
246         Tool( self, False, 'PATCH.exe', 'patch' )
247         Tool( self, False, 'RM.exe',    'rm' )
248         Tool( self, False, 'TAR.exe',   'tar' )
249         Tool( self, True,  'WGET.exe',  'wget' )
250
251         SelectTool( self, 'FETCH.select', 'fetch', self.wget, self.curl )
252
253     def register( self, item ):
254         self.__dict__[item.name] = item
255         self.items.append( item )
256
257 ###############################################################################
258
259 class OptionMode( list ):
260     def __init__( self, default, *items ):
261         super( OptionMode, self ).__init__( items )
262         self.default = items[default]
263         self.mode = self.default
264
265     def __str__( self ):
266         s = ''
267         for a in self:
268             if a == self.mode:
269                 s += ' *' + a
270             else:
271                 s += ' ' + a
272         return s[1:]
273
274     def addToGroup( self, group, option, name ):
275         group.add_option( '', option, help='select %s mode: %s' % (name,self), default=self.mode, metavar='MODE' )
276
277     def setFromOption( self, name, mode ):
278         if mode not in self:
279             errf( 'invalid %s mode: %s', name, mode )
280         self.mode = mode
281
282 ###############################################################################
283
284 ## create singletons
285 guessHost  = Guess()
286 guessBuild = Guess()
287
288 makeTool = Tool( None, False, 'CONF.make', 'gmake', 'make' )
289 tools = ToolSet()
290
291 debugMode    = OptionMode( 0, 'none', 'min', 'std', 'max' )
292 optimizeMode = OptionMode( 1, 'none', 'speed', 'size' )
293
294 ## populate platform-specific architecture modes
295 if guessHost.match( 'i386-*-darwin8.*' ):
296     archMode = OptionMode( 0, 'i386', 'ppc' )
297 elif guessHost.match( 'powerpc-*-darwin8.*' ):
298     archMode = OptionMode( 1, 'i386', 'ppc' )
299 elif guessHost.match( 'i386-*-darwin9.*' ):
300     archMode = OptionMode( 0, 'i386', 'x86_64', 'ppc', 'ppc64' )
301 elif guessHost.match( 'powerpc-*-darwin9.*' ):
302     archMode = OptionMode( 2, 'i386', 'x86_64', 'ppc', 'ppc64' )
303 else:
304     archMode = OptionMode( 0, guessHost.machine )
305
306 if guessHost.match( '*-*-darwin*' ):
307     d_xcode = True
308     d_prefix = '/Applications'
309 else: 
310     d_xcode = False
311     d_prefix = '/usr/local'
312
313 ## create parser
314 parser = OptionParser( 'Usage: %prog' )
315
316 ## add hidden options
317 parser.add_option( '', '--conf-method', default='terminal', action='store', help=optparse.SUPPRESS_HELP )
318
319 ## add install options
320 group = OptionGroup( parser, 'Installation Options' )
321 group.add_option( '', '--prefix', default=d_prefix, action='store',
322     help='specify destination for final products (%s)' % (d_prefix) )
323 parser.add_option_group( group )
324
325 group = OptionGroup( parser, 'Feature Options' )
326 if d_xcode:
327     group.add_option( '', '--disable-xcode', default=False, action='store_true',
328         help='disable Xcode (Darwin only)' )
329 group.add_option( '', '--disable-gtk', default=False, action='store_true',
330     help='disable GTK GUI (Linux only)' )
331 parser.add_option_group( group )
332
333 ## add launch options
334 group = OptionGroup( parser, 'Launch Options' )
335 group.add_option( '', '--launch', default=False, action='store_true',
336     help='launch build, capture log and wait for completion' )
337 group.add_option( '', '--launch-jobs', default=1, action='store', metavar='N',
338     help='allow N jobs at once; 0 to match CPU count (1)' )
339 group.add_option( '', '--launch-args', default=None, action='store', metavar='ARGS',
340     help='specify additional ARGS for launch command' )
341 group.add_option( '', '--launch-dir', default='build', action='store', metavar='DIR',
342     help='specify scratch DIR to use for build (build)' )
343 group.add_option( '', '--launch-force', default=False, action='store_true',
344     help='force use of scratch directory even if exists' )
345 group.add_option( '', '--launch-log', default='log.txt', action='store', metavar='FILE',
346     help='specify log FILE (log.txt)' )
347 group.add_option( '', '--launch-quiet', default=False, action='store_true',
348     help='do not echo build output' )
349 parser.add_option_group( group )
350
351 ## add compile options
352 group = OptionGroup( parser, 'Compiler Options' )
353 debugMode.addToGroup( group, '--debug', 'debug' )
354 optimizeMode.addToGroup( group, '--optimize', 'optimize' )
355 archMode.addToGroup( group, '--arch', 'architecutre' )
356 parser.add_option_group( group )
357
358 ## add tool options
359 group = OptionGroup( parser, 'Tool Options' )
360 makeTool.addToGroup( group )
361 for tool in tools.items:
362     tool.addToGroup( group )
363 parser.add_option_group( group )
364
365 (options,args) = parser.parse_args()
366
367 exports = []
368 for arg in args:
369     m = re.match( '([^=]+)=(.*)', arg )
370     if m:
371         exports.append( m.groups() )
372
373 ## recompute values when launch mode
374 if options.launch:
375     options.launch_jobs = int(options.launch_jobs)
376     build_dir = options.launch_dir
377     if os.path.isabs( build_dir ):
378         project_dir = os.getcwd() 
379     else:
380         project_dir = os.path.normpath( relpath( project_dir, build_dir ))
381     if options.launch_jobs == 0:
382         options.launch_jobs = computeNumCPU()
383     if options.launch_jobs < 1:
384         options.launch_jobs = 1
385     elif options.launch_jobs > 8:
386         options.launch_jobs = 8
387
388 ## make sure configure does not run in source root
389 if os.path.abspath( project_dir ) == os.path.abspath( build_dir ):
390     errf( 'scratch (build) directory must not be the same as source root' )
391
392 ## validate modes
393 debugMode.setFromOption( 'debug', options.debug )
394 optimizeMode.setFromOption( 'optimize', options.optimize )
395 archMode.setFromOption( 'architecture', options.arch )
396
397 ## update guessBuild as per architecture mode
398 if guessHost.match( '*-*-darwin*' ):
399     if archMode.mode == 'i386':
400         guessBuild.machine = 'i386'
401     elif archMode.mode == 'x86_64':
402         guessBuild.machine = 'x86_64'
403     elif archMode.mode == 'ppc':
404         guessBuild.machine = 'powerpc'
405     elif archMode.mode == 'ppc64':
406         guessBuild.machine = 'powerpc64'
407 else:
408     guessBuild.machine = archMode.mode
409 guessBuild.cross = 0 if archMode.default == archMode.mode else 1
410
411 # locate tools
412 makeTool.locate( options )
413 for tool in tools.items:
414     tool.locate( options )
415
416 ###############################################################################
417
418 ## Repository object.
419 ## Holds information gleaned from subversion working dir.
420 ##
421 ## Builds are classed into one of the following types:
422 ##
423 ##  release
424 ##      must be built from official svn with '/tags/' in the url
425 ##  developer
426 ##      must be built from official svn but is not a release
427 ##  unofficial
428 ##      all other builds
429 ##
430 class Repository:
431     def __init__( self ):
432         self.url       = 'svn://nowhere.com/project/unknown'
433         self.root      = 'svn://nowhere.com/project'
434         self.branch    = 'unknown'
435         self.uuid      = '00000000-0000-0000-0000-000000000000';
436         self.rev       = 0
437         self.date      = '0000-00-00 00:00:00 -0000'
438         self.wcversion = 'exported'
439         self.official  = 0
440         self.type      = 'unofficial'
441
442         # parse output: svnversion PROJECT_DIR
443         cmd = 'svnversion ' + initial_project_dir
444         print 'attempting to probe subversion: %s' % (cmd)
445         try:
446             p = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE )
447             p.wait();
448             if p.returncode:
449                 sys.exit( 1 )
450             self.wcversion = p.stdout.readline().rstrip()
451         except:
452             pass
453
454         # parse output: svn info PROJECT_DIR
455         cmd = 'svn info ' + initial_project_dir
456         print 'attempting to probe subversion: %s' % (cmd)
457         try:
458             p = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE )
459             p.wait();
460             if p.returncode:
461                 sys.exit( 1 )
462             for line in p.stdout:
463                 (name,value) = re.match( '([^:]+):\\s+(.+)', line.rstrip() ).groups()
464                 if name == 'URL':
465                     self.url = value
466                 elif name == 'Repository Root':
467                     self.root = value
468                 elif name == 'Repository UUID':
469                     self.uuid = value
470                 elif name == 'Revision':
471                     self.rev = int( value )
472                 elif name == 'Last Changed Date':
473                     # strip chars in parens
474                     if value.find( ' (' ):
475                         self.date = value[0:value.find(' (')]
476                     else:
477                         self.date = value
478         except:
479             pass
480
481         i = self.url.rfind( '/' )
482         if i != -1 and i < len(self.url)-1:
483             self.branch = self.url[i+1:]
484
485         # official UUID behavior
486         if self.uuid == 'b64f7644-9d1e-0410-96f1-a4d463321fa5':
487             self.official = 1
488             m = re.match( '([^:]+)://([^/]+)/(.+)', self.url )
489             if m and re.match( 'tags/', m.group( 3 )):
490                 self.type = 'release'
491             else:
492                 self.type = 'developer'
493
494 ###############################################################################
495
496 ## Project object.
497 ## Contains manually updated version numbers consistent with HB releases
498 ## and other project metadata.
499 ##
500 class Project:
501     def __init__( self ):
502         self.name          = 'HandBrake'
503         self.acro_lower    = 'hb'
504         self.acro_upper    = 'HB'
505         self.url_website   = 'http://handbrake.fr'
506         self.url_community = 'http://forum.handbrake.fr'
507         self.url_irc       = 'irc://irc.freenode.net/handbrake'
508
509         self.name_lower = self.name.lower()
510         self.name_upper = self.name.upper()
511
512         self.vmajor = 0
513         self.vminor = 9
514         self.vpoint = 4
515
516         appcastfmt = 'http://handbrake.fr/appcast%s.xml'
517
518         if repo.type == 'release':
519             self.version = '%d.%d.%d' % (self.vmajor,self.vminor,self.vpoint)
520             self.url_appcast = appcastfmt % ('')
521             self.build = time.strftime('%Y%m%d') + '00'
522             self.title = '%s %s (%s)' % (self.name,self.version,self.build)
523         elif repo.type == 'developer':
524             self.version = 'svn%d' % (repo.rev)
525             self.url_appcast = appcastfmt % ('_unstable')
526             self.build = time.strftime('%Y%m%d') + '01'
527             self.title = '%s svn%d (%s)' % (self.name,repo.rev,self.build)
528         else:
529             self.version = 'svn%d' % (repo.rev)
530             self.version = '%d.%d.%d' % (self.vmajor,self.vminor,self.vpoint)
531             self.url_appcast = appcastfmt % ('_unofficial')
532             self.build = time.strftime('%Y%m%d') + '99'
533             self.title = 'Unofficial svn%d (%s)' % (repo.rev,self.build)
534
535 ###############################################################################
536
537 ## Config object used to output gnu-make or gnu-m4 output.
538 ##
539 ## Use add() to add NAME/VALUE pairs suitable for both make/m4.
540 ## Use addBlank() to add a linefeed for both make/m4.
541 ## Use addMake() to add a make-specific line.
542 ## Use addM4() to add a m4-specific line.
543 ##
544 class Config:
545     def __init__( self ):
546         self._items = []
547
548     def add( self, name, value ):
549         self._items.append( (name,value) )
550
551     def addBlank( self ):
552         self._items.append( None )
553
554     def addComment( self, format, *args ):
555         self.addMake( '## ' + format % args )
556         self.addM4( 'dnl ' + format % args )
557
558     def addMake( self, line ):
559         self._items.append( ('?make',line) )
560
561     def addM4( self, line ):
562         self._items.append( ('?m4',line) )
563
564     def output( self, file, type ):
565         namelen = 0
566         for item in self._items:
567             if item == None or item[0].find( '?' ) == 0:
568                 continue
569             if len(item[0]) > namelen:
570                 namelen = len(item[0])
571         for item in self._items:
572             if item == None:
573                 if type == 'm4':
574                     file.write( 'dnl\n' )
575                 else:
576                     file.write( '\n' )
577                 continue
578             if item[0].find( '?' ) == 0:
579                 if item[0].find( type, 1 ) == 1:
580                     file.write( '%s\n' % (item[1]) )
581                 continue
582
583             if type == 'm4':
584                 self._outputM4( file, namelen, item[0], item[1] )
585             else:
586                 self._outputMake( file, namelen, item[0], item[1] )
587
588     def _outputMake( self, file, namelen, name, value ):
589         file.write( '%-*s = %s\n' % (namelen, name, value ))
590
591     def _outputM4( self, file, namelen, name, value ):
592         namelen += 7
593         name = '<<__%s>>,' % name.replace( '.', '_' )
594         file.write( 'define(%-*s  <<%s>>)dnl\n' % (namelen, name, value ))
595
596 ###############################################################################
597
598 ## create configure line, stripping arg --launch, quoting others
599 configure = []
600 for arg in sys.argv[1:]:
601     if arg == '--launch':
602         continue
603     configure.append( "'%s'" % (arg.replace("'", '%c%c%c%c%c' % (0x27,0x22,0x27,0x22,0x27))) )
604
605 ## create singletones
606 repo = Repository()
607 project = Project()
608 config  = Config()
609
610 config.addComment( 'generated by configure on %s', time.strftime( '%c' ))
611
612 config.addBlank()
613 config.add( 'CONF.args', ' '.join( configure ))
614
615 config.addBlank()
616 config.add( 'HB.title',         project.title )
617 config.add( 'HB.name',          project.name )
618 config.add( 'HB.name.lower',    project.name_lower )
619 config.add( 'HB.name.upper',    project.name_upper )
620 config.add( 'HB.acro.lower',    project.acro_lower )
621 config.add( 'HB.acro.upper',    project.acro_upper )
622
623 config.add( 'HB.url.website',   project.url_website )
624 config.add( 'HB.url.community', project.url_community )
625 config.add( 'HB.url.irc',       project.url_irc )
626 config.add( 'HB.url.appcast',   project.url_appcast )
627
628 config.add( 'HB.version.major',  project.vmajor )
629 config.add( 'HB.version.minor',  project.vminor )
630 config.add( 'HB.version.point',  project.vpoint )
631 config.add( 'HB.version',        project.version )
632 config.add( 'HB.version.hex',    '%04x%02x%02x%08x' % (project.vmajor,project.vminor,project.vpoint,repo.rev) )
633
634 config.add( 'HB.build', project.build )
635
636 config.add( 'HB.repo.url',       repo.url )
637 config.add( 'HB.repo.root',      repo.root )
638 config.add( 'HB.repo.branch',    repo.branch )
639 config.add( 'HB.repo.uuid',      repo.uuid )
640 config.add( 'HB.repo.rev',       repo.rev )
641 config.add( 'HB.repo.date',      repo.date )
642 config.add( 'HB.repo.wcversion', repo.wcversion )
643 config.add( 'HB.repo.official',  repo.official )
644 config.add( 'HB.repo.type',      repo.type )
645
646 config.addBlank()
647 config.add( 'HOST.spec',    guessHost )
648 config.add( 'HOST.machine', guessHost.machine )
649 config.add( 'HOST.vendor',  guessHost.vendor )
650 config.add( 'HOST.system',  guessHost.system )
651 config.add( 'HOST.systemc', guessHost.systemc )
652 config.add( 'HOST.release', guessHost.release )
653 config.add( 'HOST.title',   '%s %s' % (guessHost.systemc,archMode.default) )
654 config.add( 'HOST.extra',   guessHost.extra )
655 config.add( 'HOST.ncpu',    computeNumCPU() )
656
657 config.addBlank()
658 config.add( 'BUILD.spec',    guessBuild )
659 config.add( 'BUILD.machine', guessBuild.machine )
660 config.add( 'BUILD.vendor',  guessBuild.vendor )
661 config.add( 'BUILD.system',  guessBuild.system )
662 config.add( 'BUILD.systemc', guessBuild.systemc )
663 config.add( 'BUILD.release', guessBuild.release )
664 config.add( 'BUILD.title',   '%s %s' % (guessBuild.systemc,archMode.mode) )
665 config.add( 'BUILD.extra',   guessBuild.extra )
666 config.add( 'BUILD.method',  'terminal' )
667 config.add( 'BUILD.cross',   guessBuild.cross )
668 config.add( 'BUILD.date',    time.strftime('%c') )
669 config.add( 'BUILD.arch',    archMode.mode )
670 config.add( 'BUILD.jobs',    computeNumCPU() )
671
672 config.addBlank()
673 config.add( 'CONF.method', options.conf_method )
674
675 config.addBlank()
676 config.add( 'BUILD/',   os.curdir + os.sep )
677 config.add( 'PROJECT/', project_dir + os.sep )
678
679 config.addBlank()
680 config.add( 'INSTALL.prefix', options.prefix )
681 config.add( 'INSTALL.prefix/', '$(INSTALL.prefix)/' )
682
683 config.addBlank()
684 config.add( 'FEATURE.xcode', 0 if not hasattr(options, 'disable_xcode') or options.disable_xcode else 1 )
685 config.add( 'FEATURE.gtk',   0 if options.disable_gtk   else 1 )
686
687 config.addMake( '' )
688 config.addMake( '## include main definitions' )
689 config.addMake( 'include $(PROJECT/)make/include/main.defs' )
690
691 config.addBlank()
692 for tool in tools.items:
693     tool.addToConfig( config )
694
695 config.addBlank()
696 config.add( 'GCC.archs', archMode.mode if guessBuild.cross else '' )
697 config.add( 'GCC.g', options.debug )
698 config.add( 'GCC.O', options.optimize )
699
700 if len(exports):
701     config.addBlank()
702     for nv in exports:
703         config.add( nv[0], nv[1] )
704
705 config.addMake( '' )
706 config.addMake( '## include (optional) customization file' )
707 config.addMake( '-include $(BUID/)GNUmakefile.custom' )
708
709 config.addMake( '' )
710 config.addMake( '## include main rules' )
711 config.addMake( 'include $(PROJECT/)make/include/main.rules' )
712
713 ###############################################################################
714
715 # generate make or m4 file
716 def generate( type ):
717     if type == 'make':
718         fname = 'GNUmakefile'
719     elif type == 'm4':
720         fname = os.path.join( 'project', project.name_lower + '.m4' )
721     else:
722         raise ValueError, 'unknown file type: ' + type
723
724     ftmp  = fname + '.tmp'
725
726     pdir = os.path.dirname( fname )
727     if pdir:
728         if not os.path.exists( pdir ):
729             os.makedirs( pdir )
730
731     try:
732         try:
733             outf( 'generating %s', fname )
734             file = open( ftmp, 'w' )
735             config.output( file, type )
736         finally:
737             try:
738                 file.close()
739             except:
740                 pass
741     except Exception, x:
742         try:
743             os.remove( ftmp )
744         except Exception, x:
745             pass
746         errf( 'failed writing to %s\n%s', ftmp, x )
747
748     try:
749         os.rename( ftmp, fname )
750     except Exception, x:
751         errf( 'failed writing to %s\n%s', fname, x )
752
753 ###############################################################################
754
755 if not options.launch:
756     generate( 'make' )
757     generate( 'm4' )
758     sys.exit( 0 )
759
760 ###############################################################################
761
762 if os.path.exists( options.launch_dir ):
763     if not options.launch_force:
764         errf( 'scratch directory already exists: %s', options.launch_dir )
765 else:
766     outf( 'creating %s', options.launch_dir )
767     os.makedirs( options.launch_dir )    
768
769 outf( 'chdir %s', options.launch_dir )
770 os.chdir( options.launch_dir )
771 generate( 'make' )
772 generate( 'm4' )
773
774 outf( 'opening %s', options.launch_log )
775 try:
776     log = open( options.launch_log, 'w' )
777 except Exception, x:
778     errf( 'open failure: %s', x )
779
780 cmd = '%s -j%d' % (makeTool.found,options.launch_jobs)
781 if options.launch_args:
782     cmd += ' ' + options.launch_args
783
784 ## record begin
785 timeBegin = time.time()
786 s = '###\n### TIME: %s\n### launch: %s\n###\n' % (time.asctime(),cmd)
787 stdout.write( s ); stdout.flush()
788 log.write( s ); log.flush()
789
790 ## launch/pipe
791 try:
792     pipe = subprocess.Popen( cmd, shell=True, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
793 except Exception, x:
794     errf( 'launch failure: %s', x )
795 for line in pipe.stdout:
796     if not options.launch_quiet:
797         stdout.write( line ); stdout.flush()
798     log.write( line ); log.flush()
799 pipe.wait()
800
801 ## record end
802 timeEnd = time.time()
803 elapsed = timeEnd - timeBegin
804 result = '%s (exit code %d)' % ('success' if pipe.returncode == 0 else 'failed',pipe.returncode)
805 s = '###\n### TIME: %s\n### finished: %.2f seconds\n### %s\n###\n' % (time.asctime(),elapsed,result)
806 stdout.write( s ); stdout.flush()
807 log.write( s ); log.flush()
808
809 log.close()
810 sys.exit( 0 )