ambuild2/damage.py
author David Anderson <dvander@alliedmods.net>
Sat Feb 15 12:05:52 2014 -0800 (2014-02-15)
changeset 241 7d323620251f
parent 167 a559eb423181
permissions -rw-r--r--
Decode text properly on foreign language systems.
dvander@69
     1
# vim: set sts=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@75
    17
import os
dvander@167
    18
from ambuild2 import nodetypes
dvander@167
    19
from ambuild2.graph import Graph
dvander@69
    20
dvander@75
    21
def ComputeSourceDirty(node):
dvander@75
    22
  if not os.path.exists(node.path):
dvander@75
    23
    return True
dvander@75
    24
dvander@75
    25
  return os.path.getmtime(node.path) != node.stamp
dvander@75
    26
dvander@75
    27
def ComputeOutputDirty(node):
dvander@75
    28
  if not os.path.exists(node.path):
dvander@75
    29
    return True
dvander@75
    30
dvander@75
    31
  # If the timestamp on the object file has changed, then one of two things
dvander@75
    32
  # happened:
dvander@75
    33
  #  (1) The build command completed, but the build process crashed, and we
dvander@75
    34
  #      never got a chance to update the timestamp.
dvander@75
    35
  #  (2) The file was modified behind our back.
dvander@75
    36
  #
dvander@75
    37
  # In the first case, our preceding command node will not have been undirtied,
dvander@75
    38
  # so we should be able to find our incoming command in the graph. However,
dvander@75
    39
  # case #2 breaks that guarantee. To be safe, if the timestamp has changed,
dvander@104
    40
  # we mark the node as dirty.
dvander@75
    41
  stamp = os.path.getmtime(node.path)
dvander@104
    42
  return stamp != node.stamp
dvander@75
    43
dvander@75
    44
def ComputeDirty(node):
dvander@104
    45
  if node.type == nodetypes.Source:
dvander@104
    46
    dirty = ComputeSourceDirty(node)
dvander@104
    47
  elif node.type == nodetypes.Output:
dvander@104
    48
    dirty = ComputeOutputDirty(node)
dvander@104
    49
  elif node.type == nodetypes.CopyFolder:
dvander@104
    50
    dirty = ComputeCopyFolderDirty(node)
dvander@104
    51
  else:
dvander@104
    52
    raise Exception('cannot compute dirty bit for node type: ' + node.type)
dvander@104
    53
  if dirty:
dvander@104
    54
    node.dirty |= nodetypes.NewDirty
dvander@104
    55
  return dirty
dvander@75
    56
dvander@123
    57
def ComputeDamageGraph(database, only_changed = False):
dvander@71
    58
  graph = Graph(database)
dvander@69
    59
dvander@112
    60
  def maybe_mkdir(node):
dvander@112
    61
    if not os.path.exists(node.path):
dvander@112
    62
      graph.create.append(node)
dvander@112
    63
  database.query_mkdir(maybe_mkdir)
dvander@112
    64
dvander@71
    65
  dirty = []
dvander@71
    66
  def known_dirty(node):
dvander@71
    67
    dirty.append(node)
dvander@69
    68
dvander@71
    69
  def maybe_dirty(node):
dvander@71
    70
    if ComputeDirty(node):
dvander@71
    71
      dirty.append(node)
dvander@69
    72
dvander@71
    73
  database.query_known_dirty(known_dirty)
dvander@71
    74
  database.query_maybe_dirty(maybe_dirty)
dvander@69
    75
dvander@123
    76
  if only_changed:
dvander@123
    77
    return dirty
dvander@123
    78
dvander@106
    79
  def add_dirty(entry):
dvander@106
    80
    if entry.dirty == nodetypes.NewDirty:
dvander@106
    81
      # Mark this node as dirty in the DB so we don't have to check the
dvander@106
    82
      # filesystem next time.
dvander@106
    83
      database.mark_dirty(entry)
dvander@106
    84
dvander@106
    85
    graph.addEntry(entry)
dvander@106
    86
dvander@71
    87
  for entry in dirty:
dvander@104
    88
    if (entry.type == nodetypes.Output) and (entry.dirty == nodetypes.NewDirty):
dvander@104
    89
      # Ensure that our command has been marked as dirty.
dvander@136
    90
      incoming = database.query_strong_inputs(entry)
dvander@136
    91
      incoming |= database.query_dynamic_inputs(entry)
dvander@104
    92
dvander@104
    93
      # There should really only be one command to generate an output.
dvander@104
    94
      if len(incoming) != 1:
dvander@104
    95
        sys.stderr.write('Error in dependency graph: an output has multiple inputs.')
dvander@104
    96
        sys.stderr.write('Output: {0}'.format(entry.format()))
dvander@104
    97
        return None
dvander@104
    98
dvander@104
    99
      for cmd in incoming:
dvander@121
   100
        add_dirty(cmd)
dvander@121
   101
    else:
dvander@121
   102
      add_dirty(entry)
dvander@85
   103
dvander@136
   104
  graph.finish()
dvander@71
   105
  return graph