ambuild2/context.py
author David Anderson <dvander@alliedmods.net>
Fri Feb 07 22:54:58 2014 -0800 (2014-02-07)
changeset 236 aa23c47cb040
parent 230 bdb80603bb85
permissions -rw-r--r--
Add an originalCwd property to builders (bug 6037, r=ds).
     1 # vim: set ts=8 sts=2 sw=2 tw=99 et:
     2 #
     3 # This file is part of AMBuild.
     4 # 
     5 # AMBuild is free software: you can redistribute it and/or modify
     6 # it under the terms of the GNU General Public License as published by
     7 # the Free Software Foundation, either version 3 of the License, or
     8 # (at your option) any later version.
     9 # 
    10 # AMBuild is distributed in the hope that it will be useful,
    11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
    12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13 # GNU General Public License for more details.
    14 # 
    15 # You should have received a copy of the GNU General Public License
    16 # along with AMBuild. If not, see <http://www.gnu.org/licenses/>.
    17 import time
    18 import traceback
    19 import os, sys, imp
    20 from ambuild2 import util, database, damage
    21 from ambuild2.builder import Builder
    22 from ambuild2.ipc import ProcessManager, MessagePump
    23 from optparse import OptionParser
    24 
    25 class Context(object):
    26   def __init__(self, buildPath, options, args):
    27     self.buildPath = buildPath
    28     self.options = options
    29     self.args = args
    30     self.cacheFolder = os.path.join(buildPath, '.ambuild2')
    31     self.dbpath = os.path.join(self.cacheFolder, 'graph')
    32 
    33     # This doesn't completely work yet because it's not communicated to child
    34     # processes. We'll have to send a message down or up to fix this.
    35     if self.options.no_color:
    36       util.DisableConsoleColors()
    37 
    38     with open(os.path.join(self.cacheFolder, 'vars'), 'rb') as fp:
    39       try:
    40         self.vars = util.pickle.load(fp)
    41       except ValueError as exn:
    42         sys.stderr.write('Build was configured with Python 3; use python3 instead.\n')
    43         sys.exit(1)
    44       except Exception as exn:
    45         if os.path.exists(os.path.join(self.cacheFolder, 'vars')):
    46           sys.stderr.write('There does not appear to be a build configured here.\n')
    47         else:
    48           sys.stderr.write('The build configured here looks corrupt; you will have to delete your objdir.\n')
    49         raise
    50         sys.exit(1)
    51     self.db = database.Database(self.dbpath)
    52     self.messagePump = MessagePump()
    53     self.procman = ProcessManager(self.messagePump)
    54     self.db.connect()
    55 
    56   def __enter__(self):
    57     return self
    58 
    59   def __exit__(self, type, value, traceback):
    60     self.procman.shutdown()
    61     self.db.close()
    62 
    63   def reconfigure(self):
    64     # See if we need to reconfigure.
    65     files = []
    66     reconfigure_needed = False
    67     self.db.query_scripts(lambda row,path,stamp: files.append((path, stamp)))
    68     for path, stamp in files:
    69       if not os.path.exists(path) or os.path.getmtime(path) > stamp:
    70         reconfigure_needed = True
    71         break
    72 
    73     if not reconfigure_needed:
    74       return True
    75 
    76     util.con_out(
    77       util.ConsoleHeader,
    78       'Reparsing build scripts.',
    79       util.ConsoleNormal
    80     )
    81 
    82     from ambuild2.frontend.amb2.gen import Generator
    83     gen = Generator.FromVars(self.vars, self.db, self.options.refactor)
    84     try:
    85       gen.generate()
    86     except:
    87       traceback.print_exc()
    88       util.con_err(
    89         util.ConsoleRed,
    90         'Failed to reparse build scripts.',
    91         util.ConsoleNormal
    92       )
    93       return False
    94 
    95     # We flush the node cache after this, since database.py expects to get
    96     # never-before-seen items at the start. We could change this and make
    97     # nodes individually import, which might be cleaner.
    98     self.db.flush_caches()
    99 
   100     return True
   101 
   102   def Build(self):
   103     if not self.reconfigure():
   104       return False
   105 
   106     return self.build_internal()
   107 
   108   def build_internal(self):
   109     if self.options.show_graph:
   110       self.db.printGraph()
   111       return True
   112 
   113     if self.options.show_changed:
   114       dmg_list = damage.ComputeDamageGraph(self.db, only_changed=True)
   115       for entry in dmg_list:
   116         if not entry.isFile():
   117           continue
   118         print(entry.format())
   119       return True
   120 
   121     dmg_graph = damage.ComputeDamageGraph(self.db)
   122     if not dmg_graph:
   123       return False
   124 
   125     # If we get here, we have to compute damage.
   126     if self.options.show_damage:
   127       dmg_graph.printGraph()
   128       return True
   129 
   130     dmg_graph.filter_commands()
   131 
   132     if self.options.show_commands:
   133       dmg_graph.printGraph()
   134       return True
   135     
   136     builder = Builder(self, dmg_graph)
   137     if self.options.show_steps:
   138       builder.printSteps()
   139       return True
   140 
   141     if not builder.update():
   142       util.con_err(
   143         util.ConsoleHeader,
   144         'Build failed.',
   145         util.ConsoleNormal
   146       )
   147       return False
   148 
   149     util.con_out(
   150       util.ConsoleHeader,
   151       'Build succeeded.',
   152       util.ConsoleNormal
   153     )
   154     return True