OSDN Git Service

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