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