diff options
| author | Richard Purdie <rpurdie@linux.intel.com> | 2010-08-19 11:36:29 +0100 | 
|---|---|---|
| committer | Richard Purdie <rpurdie@linux.intel.com> | 2010-08-19 20:06:25 +0100 | 
| commit | 787c1cf81195e97dda5fdab3a0a5d8bda97f6770 (patch) | |
| tree | f6a3eb03758b2c9ad38f51ebe0f1f11115122c51 /bitbake/lib/bb/runqueue.py | |
| parent | 41a364f59f70f3c643924eff67ca63ab667b63ae (diff) | |
| download | poky-787c1cf81195e97dda5fdab3a0a5d8bda97f6770.tar.gz | |
bitbake: Initial scenequeue implementation (needs major fixes)
bitbake: scenequeue: Skip setscene if the underlying task already ran
bitbake/setscene: Make sure uneeded dependencies are removed recursively
Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
Diffstat (limited to 'bitbake/lib/bb/runqueue.py')
| -rw-r--r-- | bitbake/lib/bb/runqueue.py | 286 | 
1 files changed, 262 insertions, 24 deletions
diff --git a/bitbake/lib/bb/runqueue.py b/bitbake/lib/bb/runqueue.py index 488aa04d06..9127f248d5 100644 --- a/bitbake/lib/bb/runqueue.py +++ b/bitbake/lib/bb/runqueue.py  | |||
| @@ -27,6 +27,7 @@ from bb import msg, data, event | |||
| 27 | import signal | 27 | import signal | 
| 28 | import stat | 28 | import stat | 
| 29 | import fcntl | 29 | import fcntl | 
| 30 | import copy | ||
| 30 | 31 | ||
| 31 | class RunQueueStats: | 32 | class RunQueueStats: | 
| 32 | """ | 33 | """ | 
| @@ -57,12 +58,14 @@ class RunQueueStats: | |||
| 57 | # These values indicate the next step due to be run in the | 58 | # These values indicate the next step due to be run in the | 
| 58 | # runQueue state machine | 59 | # runQueue state machine | 
| 59 | runQueuePrepare = 2 | 60 | runQueuePrepare = 2 | 
| 60 | runQueueRunInit = 3 | 61 | runQueueSceneInit = 3 | 
| 61 | runQueueRunning = 4 | 62 | runQueueSceneRun = 4 | 
| 62 | runQueueFailed = 6 | 63 | runQueueRunInit = 5 | 
| 63 | runQueueCleanUp = 7 | 64 | runQueueRunning = 6 | 
| 64 | runQueueComplete = 8 | 65 | runQueueFailed = 7 | 
| 65 | runQueueChildProcess = 9 | 66 | runQueueCleanUp = 8 | 
| 67 | runQueueComplete = 9 | ||
| 68 | runQueueChildProcess = 10 | ||
| 66 | 69 | ||
| 67 | class RunQueueScheduler(object): | 70 | class RunQueueScheduler(object): | 
| 68 | """ | 71 | """ | 
| @@ -672,6 +675,16 @@ class RunQueueData: | |||
| 672 | 675 | ||
| 673 | #self.dump_data(taskData) | 676 | #self.dump_data(taskData) | 
| 674 | 677 | ||
| 678 | # Interate over the task list looking for tasks with a 'setscene' function | ||
| 679 | |||
| 680 | self.runq_setscene = [] | ||
| 681 | for task in range(len(self.runq_fnid)): | ||
| 682 | setscene = taskData.gettask_id(self.taskData.fn_index[self.runq_fnid[task]], self.runq_task[task] + "_setscene", False) | ||
| 683 | if not setscene: | ||
| 684 | continue | ||
| 685 | bb.note("Found setscene for %s %s" % (self.taskData.fn_index[self.runq_fnid[task]], self.runq_task[task])) | ||
| 686 | self.runq_setscene.append(task) | ||
| 687 | |||
| 675 | def dump_data(self, taskQueue): | 688 | def dump_data(self, taskQueue): | 
| 676 | """ | 689 | """ | 
| 677 | Dump some debug information on the internal data structures | 690 | Dump some debug information on the internal data structures | 
| @@ -802,6 +815,13 @@ class RunQueue: | |||
| 802 | return current | 815 | return current | 
| 803 | 816 | ||
| 804 | def check_stamp_task(self, task, taskname = None): | 817 | def check_stamp_task(self, task, taskname = None): | 
| 818 | def get_timestamp(f): | ||
| 819 | try: | ||
| 820 | if not os.access(f, os.F_OK): | ||
| 821 | return None | ||
| 822 | return os.stat(f)[stat.ST_MTIME] | ||
| 823 | except: | ||
| 824 | return None | ||
| 805 | 825 | ||
| 806 | if self.stamppolicy == "perfile": | 826 | if self.stamppolicy == "perfile": | 
| 807 | fulldeptree = False | 827 | fulldeptree = False | 
| @@ -825,23 +845,24 @@ class RunQueue: | |||
| 825 | bb.msg.debug(2, bb.msg.domain.RunQueue, "%s.%s is nostamp\n" % (fn, taskname)) | 845 | bb.msg.debug(2, bb.msg.domain.RunQueue, "%s.%s is nostamp\n" % (fn, taskname)) | 
| 826 | return False | 846 | return False | 
| 827 | 847 | ||
| 848 | if taskname.endswith("_setscene"): | ||
| 849 | return True | ||
| 850 | |||
| 828 | iscurrent = True | 851 | iscurrent = True | 
| 829 | t1 = os.stat(stampfile)[stat.ST_MTIME] | 852 | t1 = get_timestamp(stampfile) | 
| 830 | for dep in self.rqdata.runq_depends[task]: | 853 | for dep in self.rqdata.runq_depends[task]: | 
| 831 | if iscurrent: | 854 | if iscurrent: | 
| 832 | fn2 = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[dep]] | 855 | fn2 = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[dep]] | 
| 833 | taskname2 = self.rqdata.runq_task[dep] | 856 | taskname2 = self.rqdata.runq_task[dep] | 
| 834 | stampfile2 = "%s.%s" % (self.rqdata.dataCache.stamp[fn2], taskname2) | 857 | stampfile2 = "%s.%s" % (self.rqdata.dataCache.stamp[fn2], taskname2) | 
| 858 | t2 = get_timestamp(stampfile2) | ||
| 859 | t3 = get_timestamp(stampfile2 + "_setscene") | ||
| 860 | if t3 and t3 > t2: | ||
| 861 | continue | ||
| 835 | if fn == fn2 or (fulldeptree and fn2 not in stampwhitelist): | 862 | if fn == fn2 or (fulldeptree and fn2 not in stampwhitelist): | 
| 836 | try: | 863 | if not t2 or t1 < t2: | 
| 837 | t2 = os.stat(stampfile2)[stat.ST_MTIME] | 864 | bb.msg.debug(2, bb.msg.domain.RunQueue, "Stampfile %s < %s (or does not exist)" % (stampfile, stampfile2)) | 
| 838 | if t1 < t2: | ||
| 839 | bb.msg.debug(2, bb.msg.domain.RunQueue, "Stampfile %s < %s" % (stampfile, stampfile2)) | ||
| 840 | iscurrent = False | ||
| 841 | except: | ||
| 842 | bb.msg.debug(2, bb.msg.domain.RunQueue, "Exception reading %s for %s" % (stampfile2, stampfile)) | ||
| 843 | iscurrent = False | 865 | iscurrent = False | 
| 844 | |||
| 845 | return iscurrent | 866 | return iscurrent | 
| 846 | 867 | ||
| 847 | def execute_runqueue(self): | 868 | def execute_runqueue(self): | 
| @@ -855,7 +876,13 @@ class RunQueue: | |||
| 855 | 876 | ||
| 856 | if self.state is runQueuePrepare: | 877 | if self.state is runQueuePrepare: | 
| 857 | self.rqdata.prepare() | 878 | self.rqdata.prepare() | 
| 858 | self.state = runQueueRunInit | 879 | self.state = runQueueSceneInit | 
| 880 | |||
| 881 | if self.state is runQueueSceneInit: | ||
| 882 | self.rqexe = RunQueueExecuteScenequeue(self) | ||
| 883 | |||
| 884 | if self.state is runQueueSceneRun: | ||
| 885 | self.rqexe.execute() | ||
| 859 | 886 | ||
| 860 | if self.state is runQueueRunInit: | 887 | if self.state is runQueueRunInit: | 
| 861 | bb.msg.note(1, bb.msg.domain.RunQueue, "Executing runqueue") | 888 | bb.msg.note(1, bb.msg.domain.RunQueue, "Executing runqueue") | 
| @@ -948,14 +975,10 @@ class RunQueueExecute: | |||
| 948 | for pipe in self.build_pipes: | 975 | for pipe in self.build_pipes: | 
| 949 | self.build_pipes[pipe].read() | 976 | self.build_pipes[pipe].read() | 
| 950 | 977 | ||
| 951 | try: | 978 | if self.stats.active > 0: | 
| 952 | while self.stats.active > 0: | 979 | bb.event.fire(runQueueExitWait(self.stats.active), self.cfgData) | 
| 953 | bb.event.fire(runQueueExitWait(self.stats.active), self.cfgData) | 980 | self.runqueue_process_waitpid() | 
| 954 | if self.runqueue_process_waitpid() is None: | 981 | return | 
| 955 | return | ||
| 956 | except: | ||
| 957 | self.finish_now() | ||
| 958 | raise | ||
| 959 | 982 | ||
| 960 | if len(self.failed_fnids) != 0: | 983 | if len(self.failed_fnids) != 0: | 
| 961 | self.rq.state = runQueueFailed | 984 | self.rq.state = runQueueFailed | 
| @@ -1034,6 +1057,23 @@ class RunQueueExecuteTasks(RunQueueExecute): | |||
| 1034 | self.runq_buildable.append(1) | 1057 | self.runq_buildable.append(1) | 
| 1035 | else: | 1058 | else: | 
| 1036 | self.runq_buildable.append(0) | 1059 | self.runq_buildable.append(0) | 
| 1060 | if len(self.rqdata.runq_revdeps[task]) > 0 and self.rqdata.runq_revdeps[task].issubset(self.rq.scenequeue_covered): | ||
| 1061 | self.rq.scenequeue_covered.add(task) | ||
| 1062 | |||
| 1063 | found = True | ||
| 1064 | while found: | ||
| 1065 | found = False | ||
| 1066 | for task in range(self.stats.total): | ||
| 1067 | if task in self.rq.scenequeue_covered: | ||
| 1068 | continue | ||
| 1069 | if len(self.rqdata.runq_revdeps[task]) > 0 and self.rqdata.runq_revdeps[task].issubset(self.rq.scenequeue_covered): | ||
| 1070 | self.rq.scenequeue_covered.add(task) | ||
| 1071 | found = True | ||
| 1072 | |||
| 1073 | bb.note("Full skip list %s" % self.rq.scenequeue_covered) | ||
| 1074 | |||
| 1075 | for task in self.rq.scenequeue_covered: | ||
| 1076 | self.task_skip(task) | ||
| 1037 | 1077 | ||
| 1038 | event.fire(bb.event.StampUpdate(self.rqdata.target_pairs, self.rqdata.dataCache.stamp), self.cfgData) | 1078 | event.fire(bb.event.StampUpdate(self.rqdata.target_pairs, self.rqdata.dataCache.stamp), self.cfgData) | 
| 1039 | 1079 | ||
| @@ -1145,6 +1185,204 @@ class RunQueueExecuteTasks(RunQueueExecute): | |||
| 1145 | self.rq.state = runQueueComplete | 1185 | self.rq.state = runQueueComplete | 
| 1146 | return | 1186 | return | 
| 1147 | 1187 | ||
| 1188 | class RunQueueExecuteScenequeue(RunQueueExecute): | ||
| 1189 | def __init__(self, rq): | ||
| 1190 | RunQueueExecute.__init__(self, rq) | ||
| 1191 | |||
| 1192 | self.scenequeue_covered = set() | ||
| 1193 | self.scenequeue_notcovered = set() | ||
| 1194 | |||
| 1195 | # If we don't have any setscene functions, skip this step | ||
| 1196 | if len(self.rqdata.runq_setscene) == 0: | ||
| 1197 | rq.scenequeue_covered = set() | ||
| 1198 | rq.state = runQueueRunInit | ||
| 1199 | return | ||
| 1200 | |||
| 1201 | self.stats = RunQueueStats(len(self.rqdata.runq_setscene)) | ||
| 1202 | |||
| 1203 | endpoints = {} | ||
| 1204 | sq_revdeps = [] | ||
| 1205 | sq_revdeps_new = [] | ||
| 1206 | sq_revdeps_squash = [] | ||
| 1207 | |||
| 1208 | # We need to construct a dependency graph for the setscene functions. Intermediate | ||
| 1209 | # dependencies between the setscene tasks only complicate the code. This code | ||
| 1210 | # therefore aims to collapse the huge runqueue dependency tree into a smaller one | ||
| 1211 | # only containing the setscene functions. | ||
| 1212 | |||
| 1213 | for task in range(self.stats.total): | ||
| 1214 | self.runq_running.append(0) | ||
| 1215 | self.runq_complete.append(0) | ||
| 1216 | self.runq_buildable.append(0) | ||
| 1217 | |||
| 1218 | for task in range(len(self.rqdata.runq_fnid)): | ||
| 1219 | sq_revdeps.append(copy.copy(self.rqdata.runq_revdeps[task])) | ||
| 1220 | sq_revdeps_new.append(set()) | ||
| 1221 | if (len(self.rqdata.runq_revdeps[task]) == 0) and task not in self.rqdata.runq_setscene: | ||
| 1222 | endpoints[task] = None | ||
| 1223 | |||
| 1224 | for task in self.rqdata.runq_setscene: | ||
| 1225 | for dep in self.rqdata.runq_depends[task]: | ||
| 1226 | endpoints[dep] = task | ||
| 1227 | |||
| 1228 | def process_endpoints(endpoints): | ||
| 1229 | newendpoints = {} | ||
| 1230 | for point, task in endpoints.items(): | ||
| 1231 | tasks = set() | ||
| 1232 | if task: | ||
| 1233 | tasks.add(task) | ||
| 1234 | if sq_revdeps_new[point]: | ||
| 1235 | tasks |= sq_revdeps_new[point] | ||
| 1236 | sq_revdeps_new[point] = set() | ||
| 1237 | for dep in self.rqdata.runq_depends[point]: | ||
| 1238 | if point in sq_revdeps[dep]: | ||
| 1239 | sq_revdeps[dep].remove(point) | ||
| 1240 | if tasks: | ||
| 1241 | sq_revdeps_new[dep] |= tasks | ||
| 1242 | if (len(sq_revdeps[dep]) == 0 or len(sq_revdeps_new[dep]) != 0) and dep not in self.rqdata.runq_setscene: | ||
| 1243 | newendpoints[dep] = task | ||
| 1244 | if len(newendpoints) != 0: | ||
| 1245 | process_endpoints(newendpoints) | ||
| 1246 | |||
| 1247 | process_endpoints(endpoints) | ||
| 1248 | |||
| 1249 | for task in range(len(self.rqdata.runq_fnid)): | ||
| 1250 | if task in self.rqdata.runq_setscene: | ||
| 1251 | deps = set() | ||
| 1252 | for dep in sq_revdeps_new[task]: | ||
| 1253 | deps.add(self.rqdata.runq_setscene.index(dep)) | ||
| 1254 | sq_revdeps_squash.append(deps) | ||
| 1255 | elif len(sq_revdeps_new[task]) != 0: | ||
| 1256 | bb.msg.fatal(bb.msg.domain.RunQueue, "Something went badly wrong during scenequeue generation, aborting. Please report this problem.") | ||
| 1257 | |||
| 1258 | #for task in range(len(sq_revdeps_squash)): | ||
| 1259 | # print "Task %s: %s.%s is %s " % (task, self.taskData.fn_index[self.runq_fnid[self.runq_setscene[task]]], self.runq_task[self.runq_setscene[task]] + "_setscene", sq_revdeps_squash[task]) | ||
| 1260 | |||
| 1261 | self.sq_deps = [] | ||
| 1262 | self.sq_revdeps = sq_revdeps_squash | ||
| 1263 | self.sq_revdeps2 = copy.deepcopy(self.sq_revdeps) | ||
| 1264 | |||
| 1265 | for task in range(len(self.sq_revdeps)): | ||
| 1266 | self.sq_deps.append(set()) | ||
| 1267 | for task in range(len(self.sq_revdeps)): | ||
| 1268 | for dep in self.sq_revdeps[task]: | ||
| 1269 | self.sq_deps[dep].add(task) | ||
| 1270 | |||
| 1271 | for task in range(len(self.sq_revdeps)): | ||
| 1272 | if len(self.sq_revdeps[task]) == 0: | ||
| 1273 | self.runq_buildable[task] = 1 | ||
| 1274 | |||
| 1275 | bb.msg.note(1, bb.msg.domain.RunQueue, "Executing setscene Tasks") | ||
| 1276 | |||
| 1277 | self.rq.state = runQueueSceneRun | ||
| 1278 | |||
| 1279 | def scenequeue_updatecounters(self, task): | ||
| 1280 | for dep in self.sq_deps[task]: | ||
| 1281 | self.sq_revdeps2[dep].remove(task) | ||
| 1282 | if len(self.sq_revdeps2[dep]) == 0: | ||
| 1283 | self.runq_buildable[dep] = 1 | ||
| 1284 | |||
| 1285 | def task_complete(self, task): | ||
| 1286 | """ | ||
| 1287 | Mark a task as completed | ||
| 1288 | Look at the reverse dependencies and mark any task with | ||
| 1289 | completed dependencies as buildable | ||
| 1290 | """ | ||
| 1291 | |||
| 1292 | index = self.rqdata.runq_setscene[task] | ||
| 1293 | bb.msg.note(1, bb.msg.domain.RunQueue, "Found task %s could be accelerated" % self.rqdata.get_user_idstring(index)) | ||
| 1294 | |||
| 1295 | self.scenequeue_covered.add(task) | ||
| 1296 | self.scenequeue_updatecounters(task) | ||
| 1297 | |||
| 1298 | def task_fail(self, task, result): | ||
| 1299 | self.stats.taskFailed() | ||
| 1300 | index = self.rqdata.runq_setscene[task] | ||
| 1301 | bb.event.fire(runQueueTaskFailed(task, self.stats, self), self.cfgData) | ||
| 1302 | self.scenequeue_notcovered.add(task) | ||
| 1303 | self.scenequeue_updatecounters(task) | ||
| 1304 | |||
| 1305 | def task_failoutright(self, task): | ||
| 1306 | self.runq_running[task] = 1 | ||
| 1307 | self.runq_buildable[task] = 1 | ||
| 1308 | self.stats.taskCompleted() | ||
| 1309 | self.stats.taskSkipped() | ||
| 1310 | index = self.rqdata.runq_setscene[task] | ||
| 1311 | self.scenequeue_notcovered.add(task) | ||
| 1312 | self.scenequeue_updatecounters(task) | ||
| 1313 | |||
| 1314 | def task_skip(self, task): | ||
| 1315 | self.runq_running[task] = 1 | ||
| 1316 | self.runq_buildable[task] = 1 | ||
| 1317 | self.task_complete(task) | ||
| 1318 | self.stats.taskCompleted() | ||
| 1319 | self.stats.taskSkipped() | ||
| 1320 | |||
| 1321 | def execute(self): | ||
| 1322 | """ | ||
| 1323 | Run the tasks in a queue prepared by prepare_runqueue | ||
| 1324 | """ | ||
| 1325 | |||
| 1326 | task = None | ||
| 1327 | if self.stats.active < self.number_tasks: | ||
| 1328 | # Find the next setscene to run | ||
| 1329 | for nexttask in range(self.stats.total): | ||
| 1330 | if self.runq_buildable[nexttask] == 1 and self.runq_running[nexttask] != 1: | ||
| 1331 | #bb.note("Comparing %s to %s" % (self.sq_revdeps[nexttask], self.scenequeue_covered)) | ||
| 1332 | #if len(self.sq_revdeps[nexttask]) > 0 and self.sq_revdeps[nexttask].issubset(self.scenequeue_covered): | ||
| 1333 | # bb.note("Skipping task %s" % nexttask) | ||
| 1334 | # self.scenequeue_skip(nexttask) | ||
| 1335 | # return True | ||
| 1336 | task = nexttask | ||
| 1337 | break | ||
| 1338 | if task is not None: | ||
| 1339 | realtask = self.rqdata.runq_setscene[task] | ||
| 1340 | fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[realtask]] | ||
| 1341 | |||
| 1342 | taskname = self.rqdata.runq_task[realtask] + "_setscene" | ||
| 1343 | if self.rq.check_stamp_task(realtask, self.rqdata.runq_task[realtask]): | ||
| 1344 | bb.msg.debug(2, bb.msg.domain.RunQueue, "Stamp for underlying task %s (%s) is current so skipping setscene varient" % (task, self.rqdata.get_user_idstring(task))) | ||
| 1345 | self.task_failoutright(task) | ||
| 1346 | return True | ||
| 1347 | |||
| 1348 | if self.cooker.configuration.force: | ||
| 1349 | for target in self.target_pairs: | ||
| 1350 | if target[0] == fn and target[1] == self.rqdata.runq_task[realtask]: | ||
| 1351 | self.task_failoutright(task) | ||
| 1352 | return True | ||
| 1353 | |||
| 1354 | if self.rq.check_stamp_task(realtask, taskname): | ||
| 1355 | bb.msg.debug(2, bb.msg.domain.RunQueue, "Setscene stamp current task %s (%s) so skip it and its dependencies" % (task, self.rqdata.get_user_idstring(realtask))) | ||
| 1356 | self.task_skip(task) | ||
| 1357 | return True | ||
| 1358 | |||
| 1359 | pid, pipein, pipeout = self.fork_off_task(fn, realtask, taskname) | ||
| 1360 | |||
| 1361 | self.build_pids[pid] = task | ||
| 1362 | self.build_pipes[pid] = runQueuePipe(pipein, pipeout, self.cfgData) | ||
| 1363 | self.runq_running[task] = 1 | ||
| 1364 | self.stats.taskActive() | ||
| 1365 | if self.stats.active < self.number_tasks: | ||
| 1366 | return True | ||
| 1367 | |||
| 1368 | for pipe in self.build_pipes: | ||
| 1369 | self.build_pipes[pipe].read() | ||
| 1370 | |||
| 1371 | if self.stats.active > 0: | ||
| 1372 | if self.runqueue_process_waitpid() is None: | ||
| 1373 | return True | ||
| 1374 | return True | ||
| 1375 | |||
| 1376 | # Convert scenequeue_covered task numbers into full taskgraph ids | ||
| 1377 | oldcovered = self.scenequeue_covered | ||
| 1378 | self.rq.scenequeue_covered = set() | ||
| 1379 | for task in oldcovered: | ||
| 1380 | self.rq.scenequeue_covered.add(self.rqdata.runq_setscene[task]) | ||
| 1381 | |||
| 1382 | bb.note("We can skip tasks %s" % self.rq.scenequeue_covered) | ||
| 1383 | |||
| 1384 | self.rq.state = runQueueRunInit | ||
| 1385 | return True | ||
| 1148 | 1386 | ||
| 1149 | 1387 | ||
| 1150 | class TaskFailure(Exception): | 1388 | class TaskFailure(Exception): | 
