|
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) |