diff options
Diffstat (limited to 'bitbake/lib/bb/cooker.py')
-rw-r--r-- | bitbake/lib/bb/cooker.py | 179 |
1 files changed, 40 insertions, 139 deletions
diff --git a/bitbake/lib/bb/cooker.py b/bitbake/lib/bb/cooker.py index 064e3cae6e..4089d003bb 100644 --- a/bitbake/lib/bb/cooker.py +++ b/bitbake/lib/bb/cooker.py | |||
@@ -22,7 +22,6 @@ from bb import utils, data, parse, event, cache, providers, taskdata, runqueue, | |||
22 | import queue | 22 | import queue |
23 | import signal | 23 | import signal |
24 | import prserv.serv | 24 | import prserv.serv |
25 | import pyinotify | ||
26 | import json | 25 | import json |
27 | import pickle | 26 | import pickle |
28 | import codecs | 27 | import codecs |
@@ -175,15 +174,8 @@ class BBCooker: | |||
175 | bb.debug(1, "BBCooker starting %s" % time.time()) | 174 | bb.debug(1, "BBCooker starting %s" % time.time()) |
176 | sys.stdout.flush() | 175 | sys.stdout.flush() |
177 | 176 | ||
178 | self.configwatcher = None | 177 | self.configwatched = {} |
179 | self.confignotifier = None | 178 | self.parsewatched = {} |
180 | |||
181 | self.watchmask = pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CREATE | pyinotify.IN_DELETE | \ | ||
182 | pyinotify.IN_DELETE_SELF | pyinotify.IN_MODIFY | pyinotify.IN_MOVE_SELF | \ | ||
183 | pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO | ||
184 | |||
185 | self.watcher = None | ||
186 | self.notifier = None | ||
187 | 179 | ||
188 | # If being called by something like tinfoil, we need to clean cached data | 180 | # If being called by something like tinfoil, we need to clean cached data |
189 | # which may now be invalid | 181 | # which may now be invalid |
@@ -194,8 +186,6 @@ class BBCooker: | |||
194 | self.hashserv = None | 186 | self.hashserv = None |
195 | self.hashservaddr = None | 187 | self.hashservaddr = None |
196 | 188 | ||
197 | self.inotify_modified_files = [] | ||
198 | |||
199 | # TOSTOP must not be set or our children will hang when they output | 189 | # TOSTOP must not be set or our children will hang when they output |
200 | try: | 190 | try: |
201 | fd = sys.stdout.fileno() | 191 | fd = sys.stdout.fileno() |
@@ -221,8 +211,6 @@ class BBCooker: | |||
221 | bb.debug(1, "BBCooker startup complete %s" % time.time()) | 211 | bb.debug(1, "BBCooker startup complete %s" % time.time()) |
222 | sys.stdout.flush() | 212 | sys.stdout.flush() |
223 | 213 | ||
224 | self.inotify_threadlock = threading.Lock() | ||
225 | |||
226 | def init_configdata(self): | 214 | def init_configdata(self): |
227 | if not hasattr(self, "data"): | 215 | if not hasattr(self, "data"): |
228 | self.initConfigurationData() | 216 | self.initConfigurationData() |
@@ -230,42 +218,6 @@ class BBCooker: | |||
230 | sys.stdout.flush() | 218 | sys.stdout.flush() |
231 | self.handlePRServ() | 219 | self.handlePRServ() |
232 | 220 | ||
233 | def setupConfigWatcher(self): | ||
234 | with bb.utils.lock_timeout(self.inotify_threadlock): | ||
235 | if self.configwatcher: | ||
236 | self.configwatcher.close() | ||
237 | self.confignotifier = None | ||
238 | self.configwatcher = None | ||
239 | self.configwatcher = pyinotify.WatchManager() | ||
240 | self.configwatcher.bbseen = set() | ||
241 | self.configwatcher.bbwatchedfiles = set() | ||
242 | self.confignotifier = pyinotify.Notifier(self.configwatcher, self.config_notifications) | ||
243 | |||
244 | def setupParserWatcher(self): | ||
245 | with bb.utils.lock_timeout(self.inotify_threadlock): | ||
246 | if self.watcher: | ||
247 | self.watcher.close() | ||
248 | self.notifier = None | ||
249 | self.watcher = None | ||
250 | self.watcher = pyinotify.WatchManager() | ||
251 | self.watcher.bbseen = set() | ||
252 | self.watcher.bbwatchedfiles = set() | ||
253 | self.notifier = pyinotify.Notifier(self.watcher, self.notifications) | ||
254 | |||
255 | def process_inotify_updates(self): | ||
256 | with bb.utils.lock_timeout(self.inotify_threadlock): | ||
257 | for n in [self.confignotifier, self.notifier]: | ||
258 | if n and n.check_events(timeout=0): | ||
259 | # read notified events and enqueue them | ||
260 | n.read_events() | ||
261 | |||
262 | def process_inotify_updates_apply(self): | ||
263 | with bb.utils.lock_timeout(self.inotify_threadlock): | ||
264 | for n in [self.confignotifier, self.notifier]: | ||
265 | if n and n.check_events(timeout=0): | ||
266 | n.read_events() | ||
267 | n.process_events() | ||
268 | |||
269 | def _baseconfig_set(self, value): | 221 | def _baseconfig_set(self, value): |
270 | if value and not self.baseconfig_valid: | 222 | if value and not self.baseconfig_valid: |
271 | bb.server.process.serverlog("Base config valid") | 223 | bb.server.process.serverlog("Base config valid") |
@@ -280,88 +232,16 @@ class BBCooker: | |||
280 | bb.server.process.serverlog("Parse cache invalidated") | 232 | bb.server.process.serverlog("Parse cache invalidated") |
281 | self.parsecache_valid = value | 233 | self.parsecache_valid = value |
282 | 234 | ||
283 | def config_notifications(self, event): | 235 | def add_filewatch(self, deps, configwatcher=False): |
284 | if event.maskname == "IN_Q_OVERFLOW": | 236 | if configwatcher: |
285 | bb.warn("inotify event queue overflowed, invalidating caches.") | 237 | watcher = self.configwatched |
286 | self._parsecache_set(False) | 238 | else: |
287 | self._baseconfig_set(False) | 239 | watcher = self.parsewatched |
288 | bb.parse.clear_cache() | ||
289 | return | ||
290 | if not event.pathname in self.configwatcher.bbwatchedfiles: | ||
291 | return | ||
292 | if "IN_ISDIR" in event.maskname: | ||
293 | if "IN_CREATE" in event.maskname or "IN_DELETE" in event.maskname: | ||
294 | if event.pathname in self.configwatcher.bbseen: | ||
295 | self.configwatcher.bbseen.remove(event.pathname) | ||
296 | # Could remove all entries starting with the directory but for now... | ||
297 | bb.parse.clear_cache() | ||
298 | if not event.pathname in self.inotify_modified_files: | ||
299 | self.inotify_modified_files.append(event.pathname) | ||
300 | self._baseconfig_set(False) | ||
301 | |||
302 | def notifications(self, event): | ||
303 | if event.maskname == "IN_Q_OVERFLOW": | ||
304 | bb.warn("inotify event queue overflowed, invalidating caches.") | ||
305 | self._parsecache_set(False) | ||
306 | bb.parse.clear_cache() | ||
307 | return | ||
308 | if event.pathname.endswith("bitbake-cookerdaemon.log") \ | ||
309 | or event.pathname.endswith("bitbake.lock"): | ||
310 | return | ||
311 | if "IN_ISDIR" in event.maskname: | ||
312 | if "IN_CREATE" in event.maskname or "IN_DELETE" in event.maskname: | ||
313 | if event.pathname in self.watcher.bbseen: | ||
314 | self.watcher.bbseen.remove(event.pathname) | ||
315 | # Could remove all entries starting with the directory but for now... | ||
316 | bb.parse.clear_cache() | ||
317 | if not event.pathname in self.inotify_modified_files: | ||
318 | self.inotify_modified_files.append(event.pathname) | ||
319 | self._parsecache_set(False) | ||
320 | 240 | ||
321 | def add_filewatch(self, deps, watcher=None, dirs=False): | ||
322 | if not watcher: | ||
323 | watcher = self.watcher | ||
324 | for i in deps: | 241 | for i in deps: |
325 | watcher.bbwatchedfiles.add(i[0]) | 242 | f = i[0] |
326 | if dirs: | 243 | mtime = i[1] |
327 | f = i[0] | 244 | watcher[f] = mtime |
328 | else: | ||
329 | f = os.path.dirname(i[0]) | ||
330 | if f in watcher.bbseen: | ||
331 | continue | ||
332 | watcher.bbseen.add(f) | ||
333 | watchtarget = None | ||
334 | while True: | ||
335 | # We try and add watches for files that don't exist but if they did, would influence | ||
336 | # the parser. The parent directory of these files may not exist, in which case we need | ||
337 | # to watch any parent that does exist for changes. | ||
338 | try: | ||
339 | watcher.add_watch(f, self.watchmask, quiet=False) | ||
340 | if watchtarget: | ||
341 | watcher.bbwatchedfiles.add(watchtarget) | ||
342 | break | ||
343 | except pyinotify.WatchManagerError as e: | ||
344 | if 'ENOENT' in str(e): | ||
345 | watchtarget = f | ||
346 | f = os.path.dirname(f) | ||
347 | if f in watcher.bbseen: | ||
348 | break | ||
349 | watcher.bbseen.add(f) | ||
350 | continue | ||
351 | if 'ENOSPC' in str(e): | ||
352 | providerlog.error("No space left on device or exceeds fs.inotify.max_user_watches?") | ||
353 | providerlog.error("To check max_user_watches: sysctl -n fs.inotify.max_user_watches.") | ||
354 | providerlog.error("To modify max_user_watches: sysctl -n -w fs.inotify.max_user_watches=<value>.") | ||
355 | providerlog.error("Root privilege is required to modify max_user_watches.") | ||
356 | raise | ||
357 | |||
358 | def handle_inotify_updates(self): | ||
359 | # reload files for which we got notifications | ||
360 | for p in self.inotify_modified_files: | ||
361 | bb.parse.update_cache(p) | ||
362 | if p in bb.parse.BBHandler.cached_statements: | ||
363 | del bb.parse.BBHandler.cached_statements[p] | ||
364 | self.inotify_modified_files = [] | ||
365 | 245 | ||
366 | def sigterm_exception(self, signum, stackframe): | 246 | def sigterm_exception(self, signum, stackframe): |
367 | if signum == signal.SIGTERM: | 247 | if signum == signal.SIGTERM: |
@@ -392,8 +272,7 @@ class BBCooker: | |||
392 | if mod not in self.orig_sysmodules: | 272 | if mod not in self.orig_sysmodules: |
393 | del sys.modules[mod] | 273 | del sys.modules[mod] |
394 | 274 | ||
395 | self.handle_inotify_updates() | 275 | self.configwatched = {} |
396 | self.setupConfigWatcher() | ||
397 | 276 | ||
398 | # Need to preserve BB_CONSOLELOG over resets | 277 | # Need to preserve BB_CONSOLELOG over resets |
399 | consolelog = None | 278 | consolelog = None |
@@ -436,7 +315,7 @@ class BBCooker: | |||
436 | self.disableDataTracking() | 315 | self.disableDataTracking() |
437 | 316 | ||
438 | for mc in self.databuilder.mcdata.values(): | 317 | for mc in self.databuilder.mcdata.values(): |
439 | self.add_filewatch(mc.getVar("__base_depends", False), self.configwatcher) | 318 | self.add_filewatch(mc.getVar("__base_depends", False), configwatcher=True) |
440 | 319 | ||
441 | self._baseconfig_set(True) | 320 | self._baseconfig_set(True) |
442 | self._parsecache_set(False) | 321 | self._parsecache_set(False) |
@@ -486,6 +365,29 @@ class BBCooker: | |||
486 | if hasattr(self, "data"): | 365 | if hasattr(self, "data"): |
487 | self.data.disableTracking() | 366 | self.data.disableTracking() |
488 | 367 | ||
368 | def revalidateCaches(self): | ||
369 | bb.parse.clear_cache() | ||
370 | |||
371 | clean = True | ||
372 | for f in self.configwatched: | ||
373 | if not bb.parse.check_mtime(f, self.configwatched[f]): | ||
374 | bb.server.process.serverlog("Found %s changed, invalid cache" % f) | ||
375 | self._baseconfig_set(False) | ||
376 | self._parsecache_set(False) | ||
377 | clean = False | ||
378 | break | ||
379 | |||
380 | if clean: | ||
381 | for f in self.parsewatched: | ||
382 | if not bb.parse.check_mtime(f, self.parsewatched[f]): | ||
383 | bb.server.process.serverlog("Found %s changed, invalid cache" % f) | ||
384 | self._parsecache_set(False) | ||
385 | clean = False | ||
386 | break | ||
387 | |||
388 | if not clean: | ||
389 | bb.parse.BBHandler.cached_statements = {} | ||
390 | |||
489 | def parseConfiguration(self): | 391 | def parseConfiguration(self): |
490 | self.updateCacheSync() | 392 | self.updateCacheSync() |
491 | 393 | ||
@@ -566,6 +468,7 @@ class BBCooker: | |||
566 | # Now update all the variables not in the datastore to match | 468 | # Now update all the variables not in the datastore to match |
567 | self.configuration.env = environment | 469 | self.configuration.env = environment |
568 | 470 | ||
471 | self.revalidateCaches() | ||
569 | if not clean: | 472 | if not clean: |
570 | logger.debug("Base environment change, triggering reparse") | 473 | logger.debug("Base environment change, triggering reparse") |
571 | self.reset() | 474 | self.reset() |
@@ -1644,8 +1547,6 @@ class BBCooker: | |||
1644 | if self.state == state.running: | 1547 | if self.state == state.running: |
1645 | return | 1548 | return |
1646 | 1549 | ||
1647 | self.handle_inotify_updates() | ||
1648 | |||
1649 | if not self.baseconfig_valid: | 1550 | if not self.baseconfig_valid: |
1650 | logger.debug("Reloading base configuration data") | 1551 | logger.debug("Reloading base configuration data") |
1651 | self.initConfigurationData() | 1552 | self.initConfigurationData() |
@@ -1667,7 +1568,7 @@ class BBCooker: | |||
1667 | 1568 | ||
1668 | if self.state != state.parsing and not self.parsecache_valid: | 1569 | if self.state != state.parsing and not self.parsecache_valid: |
1669 | bb.server.process.serverlog("Parsing started") | 1570 | bb.server.process.serverlog("Parsing started") |
1670 | self.setupParserWatcher() | 1571 | self.parsewatched = {} |
1671 | 1572 | ||
1672 | bb.parse.siggen.reset(self.data) | 1573 | bb.parse.siggen.reset(self.data) |
1673 | self.parseConfiguration () | 1574 | self.parseConfiguration () |
@@ -1692,9 +1593,9 @@ class BBCooker: | |||
1692 | total_masked += masked | 1593 | total_masked += masked |
1693 | searchdirs |= set(search) | 1594 | searchdirs |= set(search) |
1694 | 1595 | ||
1695 | # Add inotify watches for directories searched for bb/bbappend files | 1596 | # Add mtimes for directories searched for bb/bbappend files |
1696 | for dirent in searchdirs: | 1597 | for dirent in searchdirs: |
1697 | self.add_filewatch([[dirent]], dirs=True) | 1598 | self.add_filewatch([(dirent, bb.parse.cached_mtime_noerror(dirent))]) |
1698 | 1599 | ||
1699 | self.parser = CookerParser(self, mcfilelist, total_masked) | 1600 | self.parser = CookerParser(self, mcfilelist, total_masked) |
1700 | self._parsecache_set(True) | 1601 | self._parsecache_set(True) |
@@ -1881,7 +1782,7 @@ class CookerCollectFiles(object): | |||
1881 | collectlog.error("no recipe files to build, check your BBPATH and BBFILES?") | 1782 | collectlog.error("no recipe files to build, check your BBPATH and BBFILES?") |
1882 | bb.event.fire(CookerExit(), eventdata) | 1783 | bb.event.fire(CookerExit(), eventdata) |
1883 | 1784 | ||
1884 | # We need to track where we look so that we can add inotify watches. There | 1785 | # We need to track where we look so that we can know when the cache is invalid. There |
1885 | # is no nice way to do this, this is horrid. We intercept the os.listdir() | 1786 | # is no nice way to do this, this is horrid. We intercept the os.listdir() |
1886 | # (or os.scandir() for python 3.6+) calls while we run glob(). | 1787 | # (or os.scandir() for python 3.6+) calls while we run glob(). |
1887 | origlistdir = os.listdir | 1788 | origlistdir = os.listdir |