Run submakes within this process if we can.
authorBenjamin Smedberg <benjamin@smedbergs.us>
Wed, 18 Feb 2009 23:06:22 -0500
changeset 135 fcb8d4ddd21b
parent 134 ca40dbbeb55a
child 136 9c0fa7855fb0
push id76
push userbsmedberg@mozilla.com
push date2009-02-19 04:06 +0000
Run submakes within this process if we can.
pymake/command.py
pymake/data.py
pymake/process.py
--- a/pymake/command.py
+++ b/pymake/command.py
@@ -1,16 +1,21 @@
 """
 Logic to execute a command
 """
 
 import os, subprocess, sys, logging, time
 from optparse import OptionParser
 import pymake.data, pymake.parser
 
+# TODO: If this ever goes from relocatable package to system-installed, this may need to be
+# a configured-in path.
+
+makepypath = os.path.normpath(os.path.join(os.path.dirname(__file__), '../make.py'))
+
 def parsemakeflags(env):
     makeflags = env.get('MAKEFLAGS', '')
     makeflags = makeflags.strip()
 
     if makeflags == '':
         return []
 
     if makeflags[0] not in ('-', ' '):
@@ -53,41 +58,41 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO TH
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 DEALINGS IN THE SOFTWARE."""
 
 log = logging.getLogger('pymake.execution')
 
-op = OptionParser()
-op.add_option('-f', '--file', '--makefile',
-              action='append',
-              dest='makefiles',
-              default=[])
-op.add_option('-d',
-              action="store_true",
-              dest="verbose", default=False)
-op.add_option('--debug-log',
-              dest="debuglog", default=None)
-op.add_option('-C', '--directory',
-              dest="directory", default=None)
-op.add_option('-v', '--version', action="store_true",
-              dest="printversion", default=False)
-op.add_option('-j', '--jobs', type="int",
-              dest="jobcount", default=1)
-op.add_option('--parse-profile',
-              dest="parseprofile", default=None)
-op.add_option('--no-print-directory', action="store_false",
-              dest="printdir", default=True)
-
 def main(args, env, cwd):
     makelevel = int(env.get('MAKELEVEL', '0'))
     arglist = args + parsemakeflags(env)
 
+    op = OptionParser()
+    op.add_option('-f', '--file', '--makefile',
+                  action='append',
+                  dest='makefiles',
+                  default=[])
+    op.add_option('-d',
+                  action="store_true",
+                  dest="verbose", default=False)
+    op.add_option('--debug-log',
+                  dest="debuglog", default=None)
+    op.add_option('-C', '--directory',
+                  dest="directory", default=None)
+    op.add_option('-v', '--version', action="store_true",
+                  dest="printversion", default=False)
+    op.add_option('-j', '--jobs', type="int",
+                  dest="jobcount", default=1)
+    op.add_option('--parse-profile',
+                  dest="parseprofile", default=None)
+    op.add_option('--no-print-directory', action="store_false",
+                  dest="printdir", default=True)
+
     options, arguments = op.parse_args(arglist)
 
     if options.printversion:
         version()
         return 0
 
     shortflags = []
     longflags = []
@@ -126,18 +131,19 @@ def main(args, env, cwd):
             print "No makefile found"
             return 2
 
     try:
         def parse():
             i = 0
 
             while True:
-                m = pymake.data.Makefile(restarts=i, make='%s %s' % (sys.executable, sys.argv[0]),
-                                         makeflags=makeflags, makelevel=makelevel, workdir=workdir)
+                m = pymake.data.Makefile(restarts=i, make='%s %s' % (sys.executable, makepypath),
+                                         makeflags=makeflags, makelevel=makelevel, workdir=workdir,
+                                         env=env)
 
                 starttime = time.time()
                 targets = pymake.parser.parsecommandlineargs(m, arguments)
                 for f in options.makefiles:
                     m.include(f)
 
                 log.info("Parsing[%i] took %f seconds" % (i, time.time() - starttime,))
 
@@ -175,8 +181,9 @@ def main(args, env, cwd):
             print "make.py[%i]: Leaving directory '%s'" % (makelevel, workdir)
         sys.stdout.flush()
         return 2
 
     if options.printdir:
         print "make.py[%i]: Leaving directory '%s'" % (makelevel, workdir)
 
     sys.stdout.flush()
+    return 0
--- a/pymake/data.py
+++ b/pymake/data.py
@@ -173,18 +173,18 @@ class Variables(object):
     SOURCE_AUTOMATIC = 4
     # I have no intention of supporting builtin rules or variables that go with them
     # SOURCE_IMPLICIT = 5
 
     def __init__(self, parent=None):
         self._map = {}
         self.parent = parent
 
-    def readfromenvironment(self):
-        for k, v in os.environ.iteritems():
+    def readfromenvironment(self, env):
+        for k, v in env.iteritems():
             self.set(k, self.FLAVOR_SIMPLE, self.SOURCE_ENVIRONMENT, v)
 
     def get(self, name, expand=True):
         """
         Get the value of a named variable. Returns a tuple (flavor, source, value)
 
         If the variable is not present, returns (None, None, None)
 
@@ -928,21 +928,25 @@ class PatternRule(object):
                     stem = p.match(file)
                     if stem is not None:
                         yield PatternRuleInstance(self, dir, stem, False)
 
     def prerequisitesforstem(self, dir, stem):
         return [p.resolve(dir, stem) for p in self.prerequisites]
 
 class Makefile(object):
-    def __init__(self, workdir=None, restarts=0, make=None, makeflags=None, makelevel=0):
+    def __init__(self, workdir=None, env=None, restarts=0, make=None, makeflags=None, makelevel=0):
         self.defaulttarget = None
 
+        if env is None:
+            env = os.environ
+        self.env = env
+
         self.variables = Variables()
-        self.variables.readfromenvironment()
+        self.variables.readfromenvironment(env)
 
         self.exportedvars = set()
         self.overrides = []
         self._targets = {}
         self._patternvariables = [] # of (pattern, variables)
         self.implicitrules = []
         self.parsingfinished = False
 
@@ -1102,17 +1106,17 @@ class Makefile(object):
                 log.info("included makefile '%s' was remade" % t.target)
                 reparse = True
 
         return reparse
 
     flagescape = re.compile(r'([\s\\])')
 
     def getsubenvironment(self, variables):
-        env = dict(os.environ)
+        env = dict(self.env)
         for vname in self.exportedvars:
             flavor, source, val = variables.get(vname)
             if val is None:
                 strval = ''
             else:
                 strval = val.resolve(self, variables, [vname])
             env[vname] = strval
 
--- a/pymake/process.py
+++ b/pymake/process.py
@@ -1,14 +1,15 @@
 """
 Skipping shell invocations is good, when possible. This wrapper around subprocess does dirty work of
 parsing command lines into argv and making sure that no shell magic is being used.
 """
 
-import subprocess, shlex, re, logging
+import subprocess, shlex, re, logging, sys
+import command
 
 _log = logging.getLogger('pymake.execution')
 
 blacklist = re.compile(r'[=\\$><;*?[{~`|&]')
 def clinetoargv(cline):
     """
     If this command line can safely skip the shell, return an argv array.
     """
@@ -30,10 +31,16 @@ def call(cline, env, cwd, loc):
     argv = clinetoargv(cline)
     if argv is None or (len(argv) and argv[0] in shellwords):
         _log.debug("%s: Running command through shell because of shell metacharacters" % (loc,))
         return subprocess.call(cline, shell=True, env=env, cwd=cwd)
 
     if not len(argv):
         return 0
 
+    if argv[0] == command.makepypath:
+        return command.main(argv[1:], env, cwd)
+
+    if argv[0:2] == [sys.executable, command.makepypath]:
+        return command.main(argv[2:], env, cwd)
+
     _log.debug("%s: skipping shell, no metacharacters found" % (loc,))
     return subprocess.call(argv, shell=False, env=env, cwd=cwd)