ambuild2/nodetypes.py
author David Anderson <dvander@alliedmods.net>
Sat Feb 01 16:56:01 2014 -0800 (2014-02-01)
changeset 226 185e3cd6740b
parent 225 0b768cfdbace
permissions -rw-r--r--
Add "shared outputs" to account for aggregate output files (bug 6028, r=ds).
dvander@65
     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@117
    17
import os
dvander@65
    18
dvander@65
    19
# Source nodes are files that are leaf inputs to the build system, and are not
dvander@65
    20
# generated as part of the build process.
dvander@65
    21
Source = 'src'
dvander@65
    22
dvander@65
    23
# Command nodes have associated data in the command table, and produce some
dvander@65
    24
# kind of output.
dvander@65
    25
Command = 'cmd'
dvander@65
    26
dvander@65
    27
# Output nodes are files that have been generated as a result of a command.
dvander@67
    28
# All further node types described below are conceptually output nodes,
dvander@67
    29
# but they have special handling within AMBuild.
dvander@65
    30
Output = 'out'
dvander@65
    31
dvander@226
    32
# An output node that is modified or created by multiple commands. It cannot
dvander@226
    33
# be used as an input. Shared outputs are linked separately from normal
dvander@226
    34
# outputs.
dvander@226
    35
SharedOutput = 'sho'
dvander@226
    36
dvander@134
    37
# An aggregate node that reduces incoming edges to other nodes.
dvander@134
    38
Group = 'grp'
dvander@134
    39
dvander@66
    40
# Mkdir nodes represent a folder creation action, either explicit or
dvander@65
    41
# automatically generated.
dvander@66
    42
Mkdir = 'mkd'
dvander@65
    43
dvander@65
    44
# Copy nodes represent a file copy, from a source to a destination. They do
dvander@65
    45
# not have a command counterpart.
dvander@65
    46
Copy = 'cp'
dvander@65
    47
dvander@66
    48
# FolderCopy nodes snapshot the contents of a folder - non-recursively - and
dvander@66
    49
# become dirty when files are added or removed. They automatically maintain
dvander@66
    50
# individual copy links for each file in the folder.
dvander@66
    51
#
dvander@66
    52
# To ensure proper ordering, any files in the folder that are dependent on a
dvander@66
    53
# build action, will have their copy node properly depending on that action.
dvander@71
    54
CopyFolder = 'cpa'
dvander@66
    55
dvander@67
    56
# Link nodes are a special command node, representing a symlink, from a source
dvander@67
    57
# to a destination. On operating systems where symlinking is not available or
dvander@67
    58
# unreliable, copies may be performed instead.
dvander@65
    59
Symlink = 'ln'
dvander@65
    60
dvander@65
    61
# C++ nodes are a builtin type that are capable of performing post-processing
dvander@65
    62
# on the result of the command (for example, for dependency computation). They
dvander@65
    63
# are used when using AMBuild2's automated C++ builders.
dvander@65
    64
Cxx = 'cxx'
dvander@65
    65
dvander@190
    66
# RC nodes are similar to C++ nodes, but are specific for .rc files built with
dvander@190
    67
# rc.exe on Windows.
dvander@190
    68
Rc = 'rc'
dvander@190
    69
dvander@153
    70
NodeNames = {
dvander@153
    71
  Source: 'source',
dvander@153
    72
  Command: 'command',
dvander@153
    73
  Output: 'output',
dvander@226
    74
  SharedOutput: 'output',
dvander@153
    75
  Group: 'group',
dvander@153
    76
  Mkdir: 'mkdir',
dvander@153
    77
  Copy: 'copy',
dvander@153
    78
  CopyFolder: 'copy -R',
dvander@153
    79
  Symlink: 'symlink',
dvander@190
    80
  Cxx: 'c++',
dvander@190
    81
  Rc: 'rc'
dvander@153
    82
}
dvander@153
    83
dvander@136
    84
def IsFile(type):
dvander@136
    85
  return type == Output or type == Source
dvander@136
    86
dvander@134
    87
def IsCommand(type):
dvander@134
    88
  return type != Output and type != Source and type != Group
dvander@134
    89
dvander@136
    90
def HasAutoDependencies(type):
dvander@136
    91
  return type == CopyFolder or type == Cxx
dvander@136
    92
dvander@104
    93
NotDirty = 0
dvander@104
    94
KnownDirty = (1 << 0)
dvander@104
    95
NewDirty = (1 << 1)
dvander@104
    96
dvander@65
    97
# The basic properties of a node as it exists in the database.
dvander@153
    98
class Entry(object):
dvander@225
    99
  def __init__(self, id, type, path, blob, folder, stamp, dirty):
dvander@66
   100
    # Unique node ID (integer)
dvander@65
   101
    self.id = id
dvander@66
   102
dvander@66
   103
    # Node type, from above.
dvander@65
   104
    self.type = type
dvander@66
   105
dvander@67
   106
    # For source nodes, this is an absolute path to the source file.
dvander@67
   107
    # For output nodes, this is a path relative to the build folder.
dvander@67
   108
    # For command nodes, it is available as an arbitrary string, which is
dvander@67
   109
    # included in testing node equivalency when merging graphs.
dvander@67
   110
    #
dvander@67
   111
    # It is expected that when paths are not NULL, they are unique.
dvander@65
   112
    self.path = path
dvander@66
   113
dvander@67
   114
    # Command nodes may have extra data associated with them; this is
dvander@67
   115
    # usually an argv serialized by Python.
dvander@67
   116
    self.blob = blob
dvander@67
   117
dvander@67
   118
    # For command nodes, this is a link to a 'Mkdir' node describing its
dvander@159
   119
    # working directory.
dvander@66
   120
    self.folder = folder
dvander@66
   121
dvander@66
   122
    # Last modification time.
dvander@65
   123
    self.stamp = stamp
dvander@66
   124
dvander@104
   125
    # 0 if the node was not dirty in the database.
dvander@104
   126
    # 1 if the node was dirty in the database.
dvander@104
   127
    # 2 if the node has become dirty in the meantime.
dvander@65
   128
    self.dirty = dirty
dvander@66
   129
dvander@136
   130
    #########################################
dvander@136
   131
    # Remaining fields are lazily computed. #
dvander@136
   132
    #########################################
dvander@136
   133
dvander@136
   134
    # Strong inputs are used to force updates. Dynamic inputs force updates,
dvander@136
   135
    # but are added and removed as dependencies change. Weak updates do not
dvander@136
   136
    # force updates, but only ordering.
dvander@136
   137
    self.strong_inputs = None
dvander@136
   138
    self.dynamic_inputs = None
dvander@136
   139
    self.weak_inputs = None
dvander@136
   140
dvander@68
   141
    self.outgoing = None
dvander@136
   142
    
dvander@71
   143
  def isCommand(self):
dvander@134
   144
    return IsCommand(self.type)
dvander@68
   145
dvander@136
   146
  def isFile(self):
dvander@136
   147
    return IsFile(self.type)
dvander@136
   148
dvander@117
   149
  @property
dvander@117
   150
  def folder_name(self):
dvander@117
   151
    if not self.folder:
dvander@117
   152
      return ''
dvander@117
   153
    return self.folder.path
dvander@117
   154
dvander@68
   155
  def format(self):
dvander@226
   156
    if self.type == Source or self.type == Output or self.type == SharedOutput:
dvander@68
   157
      return self.path
dvander@68
   158
    text = ''
dvander@112
   159
    if self.type == Mkdir:
dvander@112
   160
      return 'mkdir -p ' + self.path
dvander@115
   161
    if self.type == Symlink:
dvander@117
   162
      return 'ln -s "{0}" "{1}"'.format(self.blob[0], os.path.join(self.folder_name, self.blob[1]))
dvander@117
   163
    if self.type == Copy:
dvander@117
   164
      return 'cp "{0}" "{1}"'.format(self.blob[0], os.path.join(self.folder_name, self.blob[1]))
dvander@68
   165
    if self.type == Cxx:
dvander@68
   166
      return '[' + self.blob['type'] + ']' + ' -> ' + (' '.join([arg for arg in self.blob['argv']]))
dvander@190
   167
    if self.type == Rc:
dvander@190
   168
      return ' '.join([arg for arg in self.blob['cl_argv']]) + ' && ' + ' '.join([arg for arg in self.blob['rc_argv']])
dvander@136
   169
    if self.type == Group:
dvander@136
   170
      return 'group "{0}"'.format(self.path)
dvander@68
   171
    return (' '.join([arg for arg in self.blob]))
dvander@162
   172
dvander@162
   173
def combine(a, b):
dvander@176
   174
  if type(a) is Entry:
dvander@162
   175
    text_a = a.path
dvander@176
   176
  else:
dvander@176
   177
    text_a = a
dvander@176
   178
  if type(b) is Entry:
dvander@176
   179
    text_b = b.path
dvander@176
   180
  else:
dvander@162
   181
    if not len(b):
dvander@162
   182
      return text_a
dvander@162
   183
    text_b = b
dvander@176
   184
  if not text_a:
dvander@176
   185
    return text_b
dvander@162
   186
  return os.path.join(text_a, text_b)