Back to index

enigmail  1.4.3
command.py
Go to the documentation of this file.
00001 """
00002 Makefile execution.
00003 
00004 Multiple `makes` can be run within the same process. Each one has an entirely data.Makefile and .Target
00005 structure, environment, and working directory. Typically they will all share a parallel execution context,
00006 except when a submake specifies -j1 when the parent make is building in parallel.
00007 """
00008 
00009 import os, subprocess, sys, logging, time, traceback, re
00010 from optparse import OptionParser
00011 import data, parserdata, process, util
00012 
00013 # TODO: If this ever goes from relocatable package to system-installed, this may need to be
00014 # a configured-in path.
00015 
00016 makepypath = util.normaljoin(os.path.dirname(__file__), '../make.py')
00017 
00018 _simpleopts = re.compile(r'^[a-zA-Z]+(\s|$)')
00019 def parsemakeflags(env):
00020     """
00021     Parse MAKEFLAGS from the environment into a sequence of command-line arguments.
00022     """
00023 
00024     makeflags = env.get('MAKEFLAGS', '')
00025     makeflags = makeflags.strip()
00026 
00027     if makeflags == '':
00028         return []
00029 
00030     if _simpleopts.match(makeflags):
00031         makeflags = '-' + makeflags
00032 
00033     opts = []
00034     curopt = ''
00035 
00036     i = 0
00037     while i < len(makeflags):
00038         c = makeflags[i]
00039         if c.isspace():
00040             opts.append(curopt)
00041             curopt = ''
00042             i += 1
00043             while i < len(makeflags) and makeflags[i].isspace():
00044                 i += 1
00045             continue
00046 
00047         if c == '\\':
00048             i += 1
00049             if i == len(makeflags):
00050                 raise data.DataError("MAKEFLAGS has trailing backslash")
00051             c = makeflags[i]
00052             
00053         curopt += c
00054         i += 1
00055 
00056     if curopt != '':
00057         opts.append(curopt)
00058 
00059     return opts
00060 
00061 def _version(*args):
00062     print """pymake: GNU-compatible make program
00063 Copyright (C) 2009 The Mozilla Foundation <http://www.mozilla.org/>
00064 This is free software; see the source for copying conditions.
00065 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00066 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00067 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00068 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00069 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00070 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00071 DEALINGS IN THE SOFTWARE."""
00072 
00073 _log = logging.getLogger('pymake.execution')
00074 
00075 class _MakeContext(object):
00076     def __init__(self, makeflags, makelevel, workdir, context, env, targets, options, ostmts, overrides, cb):
00077         self.makeflags = makeflags
00078         self.makelevel = makelevel
00079 
00080         self.workdir = workdir
00081         self.context = context
00082         self.env = env
00083         self.targets = targets
00084         self.options = options
00085         self.ostmts = ostmts
00086         self.overrides = overrides
00087         self.cb = cb
00088 
00089         self.restarts = 0
00090 
00091         self.remakecb(True)
00092 
00093     def remakecb(self, remade, error=None):
00094         if error is not None:
00095             print error
00096             self.context.defer(self.cb, 2)
00097             return
00098 
00099         if remade:
00100             if self.restarts > 0:
00101                 _log.info("make.py[%i]: Restarting makefile parsing", self.makelevel)
00102 
00103             self.makefile = data.Makefile(restarts=self.restarts,
00104                                           make='%s %s' % (sys.executable.replace('\\', '/'), makepypath.replace('\\', '/')),
00105                                           makeflags=self.makeflags,
00106                                           makeoverrides=self.overrides,
00107                                           workdir=self.workdir,
00108                                           context=self.context,
00109                                           env=self.env,
00110                                           makelevel=self.makelevel,
00111                                           targets=self.targets,
00112                                           keepgoing=self.options.keepgoing,
00113                                           silent=self.options.silent,
00114                                           justprint=self.options.justprint)
00115 
00116             self.restarts += 1
00117 
00118             try:
00119                 self.ostmts.execute(self.makefile)
00120                 for f in self.options.makefiles:
00121                     self.makefile.include(f)
00122                 self.makefile.finishparsing()
00123                 self.makefile.remakemakefiles(self.remakecb)
00124             except util.MakeError, e:
00125                 print e
00126                 self.context.defer(self.cb, 2)
00127 
00128             return
00129 
00130         if len(self.targets) == 0:
00131             if self.makefile.defaulttarget is None:
00132                 print "No target specified and no default target found."
00133                 self.context.defer(self.cb, 2)
00134                 return
00135 
00136             _log.info("Making default target %s", self.makefile.defaulttarget)
00137             self.realtargets = [self.makefile.defaulttarget]
00138             self.tstack = ['<default-target>']
00139         else:
00140             self.realtargets = self.targets
00141             self.tstack = ['<command-line>']
00142 
00143         self.makefile.gettarget(self.realtargets.pop(0)).make(self.makefile, self.tstack, cb=self.makecb)
00144 
00145     def makecb(self, error, didanything):
00146         assert error in (True, False)
00147 
00148         if error:
00149             self.context.defer(self.cb, 2)
00150             return
00151 
00152         if not len(self.realtargets):
00153             if self.options.printdir:
00154                 print "make.py[%i]: Leaving directory '%s'" % (self.makelevel, self.workdir)
00155             sys.stdout.flush()
00156 
00157             self.context.defer(self.cb, 0)
00158         else:
00159             self.makefile.gettarget(self.realtargets.pop(0)).make(self.makefile, self.tstack, self.makecb)
00160 
00161 def main(args, env, cwd, cb):
00162     """
00163     Start a single makefile execution, given a command line, working directory, and environment.
00164 
00165     @param cb a callback to notify with an exit code when make execution is finished.
00166     """
00167 
00168     try:
00169         makelevel = int(env.get('MAKELEVEL', '0'))
00170 
00171         op = OptionParser()
00172         op.add_option('-f', '--file', '--makefile',
00173                       action='append',
00174                       dest='makefiles',
00175                       default=[])
00176         op.add_option('-d',
00177                       action="store_true",
00178                       dest="verbose", default=False)
00179         op.add_option('-k', '--keep-going',
00180                       action="store_true",
00181                       dest="keepgoing", default=False)
00182         op.add_option('--debug-log',
00183                       dest="debuglog", default=None)
00184         op.add_option('-C', '--directory',
00185                       dest="directory", default=None)
00186         op.add_option('-v', '--version', action="store_true",
00187                       dest="printversion", default=False)
00188         op.add_option('-j', '--jobs', type="int",
00189                       dest="jobcount", default=1)
00190         op.add_option('-w', '--print-directory', action="store_true",
00191                       dest="printdir")
00192         op.add_option('--no-print-directory', action="store_false",
00193                       dest="printdir", default=True)
00194         op.add_option('-s', '--silent', action="store_true",
00195                       dest="silent", default=False)
00196         op.add_option('-n', '--just-print', '--dry-run', '--recon',
00197                       action="store_true",
00198                       dest="justprint", default=False)
00199 
00200         options, arguments1 = op.parse_args(parsemakeflags(env))
00201         options, arguments2 = op.parse_args(args, values=options)
00202 
00203         op.destroy()
00204 
00205         arguments = arguments1 + arguments2
00206 
00207         if options.printversion:
00208             _version()
00209             cb(0)
00210             return
00211 
00212         shortflags = []
00213         longflags = []
00214 
00215         if options.keepgoing:
00216             shortflags.append('k')
00217 
00218         if options.printdir:
00219             shortflags.append('w')
00220 
00221         if options.silent:
00222             shortflags.append('s')
00223             options.printdir = False
00224 
00225         if options.justprint:
00226             shortflags.append('n')
00227 
00228         loglevel = logging.WARNING
00229         if options.verbose:
00230             loglevel = logging.DEBUG
00231             shortflags.append('d')
00232 
00233         logkwargs = {}
00234         if options.debuglog:
00235             logkwargs['filename'] = options.debuglog
00236             longflags.append('--debug-log=%s' % options.debuglog)
00237 
00238         if options.directory is None:
00239             workdir = cwd
00240         else:
00241             workdir = util.normaljoin(cwd, options.directory)
00242 
00243         if options.jobcount != 1:
00244             longflags.append('-j%i' % (options.jobcount,))
00245 
00246         makeflags = ''.join(shortflags)
00247         if len(longflags):
00248             makeflags += ' ' + ' '.join(longflags)
00249 
00250         logging.basicConfig(level=loglevel, **logkwargs)
00251 
00252         context = process.getcontext(options.jobcount)
00253 
00254         if options.printdir:
00255             print "make.py[%i]: Entering directory '%s'" % (makelevel, workdir)
00256             sys.stdout.flush()
00257 
00258         if len(options.makefiles) == 0:
00259             if os.path.exists(util.normaljoin(workdir, 'Makefile')):
00260                 options.makefiles.append('Makefile')
00261             else:
00262                 print "No makefile found"
00263                 cb(2)
00264                 return
00265 
00266         ostmts, targets, overrides = parserdata.parsecommandlineargs(arguments)
00267 
00268         _MakeContext(makeflags, makelevel, workdir, context, env, targets, options, ostmts, overrides, cb)
00269     except (util.MakeError), e:
00270         print e
00271         if options.printdir:
00272             print "make.py[%i]: Leaving directory '%s'" % (makelevel, workdir)
00273         sys.stdout.flush()
00274         cb(2)
00275         return