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