def isdirty(f, depth, max_changed, is_checked=state.File.is_checked, set_checked=state.File.set_checked_save): if vars.DEBUG >= 1: debug('%s?%s\n' % (depth, f.nicename())) if f.failed_runid: debug('%s-- DIRTY (failed last time)\n' % depth) return DIRTY if f.changed_runid == None: debug('%s-- DIRTY (never built)\n' % depth) return DIRTY if f.changed_runid > max_changed: debug('%s-- DIRTY (built)\n' % depth) return DIRTY # has been built more recently than parent if is_checked(f): if vars.DEBUG >= 1: debug('%s-- CLEAN (checked)\n' % depth) return CLEAN # has already been checked during this session if not f.stamp: debug('%s-- DIRTY (no stamp)\n' % depth) return DIRTY newstamp = f.read_stamp() if f.stamp != newstamp: if newstamp == state.STAMP_MISSING: debug('%s-- DIRTY (missing)\n' % depth) else: debug('%s-- DIRTY (mtime)\n' % depth) if f.csum: return [f] else: return DIRTY must_build = [] for mode, f2 in f.deps(): dirty = CLEAN if mode == 'c': if os.path.exists(os.path.join(vars.BASE, f2.name)): debug('%s-- DIRTY (created)\n' % depth) dirty = DIRTY elif mode == 'm': sub = isdirty(f2, depth=depth + ' ', max_changed=max(f.changed_runid, f.checked_runid), is_checked=is_checked, set_checked=set_checked) if sub: debug('%s-- DIRTY (sub)\n' % depth) dirty = sub else: assert (mode in ('c', 'm')) if not f.csum: # f is a "normal" target: dirty f2 means f is instantly dirty if dirty == DIRTY: # f2 is definitely dirty, so f definitely needs to # redo. return DIRTY elif isinstance(dirty, list): # our child f2 might be dirty, but it's not sure yet. It's # given us a list of targets we have to redo in order to # be sure. must_build += dirty else: # f is "checksummable": dirty f2 means f needs to redo, # but f might turn out to be clean after that (ie. our parent # might not be dirty). if dirty == DIRTY: # f2 is definitely dirty, so f definitely needs to # redo. However, after that, f might turn out to be # unchanged. return [f] elif isinstance(dirty, list): # our child f2 might be dirty, but it's not sure yet. It's # given us a list of targets we have to redo in order to # be sure. must_build += dirty if must_build: # f is *maybe* dirty because at least one of its children is maybe # dirty. must_build has accumulated a list of "topmost" uncertain # objects in the tree. If we build all those, we can then # redo-ifchange f and it won't have any uncertainty next time. return must_build debug('%s-- CLEAN\n' % (depth, )) # if we get here, it's because the target is clean if f.is_override: state.warn_override(f.name) set_checked(f) return CLEAN
def _start_do(self): assert(self.lock.owned) t = self.t sf = self.sf newstamp = sf.read_stamp() if (sf.is_generated and not sf.failed_runid and newstamp != state.STAMP_MISSING and (sf.stamp != newstamp or sf.is_override)): state.warn_override(_nice(t)) sf.set_override() sf.set_checked() sf.save() return self._after2(0) if (os.path.exists(t) and not os.path.exists(t + '/.') and not sf.is_generated): # an existing source file that was not generated by us. # This step is mentioned by djb in his notes. # For example, a rule called default.c.do could be used to try # to produce hello.c, but we don't want that to happen if # hello.c was created by the end user. # FIXME: always refuse to redo any file that was modified outside # of redo? That would make it easy for someone to override a # file temporarily, and could be undone by deleting the file. debug2("-- static (%r)\n" % t) sf.set_static() sf.save() return self._after2(0) sf.zap_deps1() (dodir, dofile, basedir, basename, ext) = _find_do_file(sf) if not dofile: if os.path.exists(t): sf.set_static() sf.save() return self._after2(0) else: err('no rule to make %r\n' % t) return self._after2(1) unlink(self.tmpname1) unlink(self.tmpname2) ffd = os.open(self.tmpname1, os.O_CREAT|os.O_RDWR|os.O_EXCL, 0666) close_on_exec(ffd, True) self.f = os.fdopen(ffd, 'w+') # this will run in the dofile's directory, so use only basenames here argv = ['sh', '-e', dofile, basename, # target name (no extension) ext, # extension (if any), including leading dot os.path.join(basedir, os.path.basename(self.tmpname2)) # temp output file name ] if vars.VERBOSE: argv[1] += 'v' if vars.XTRACE: argv[1] += 'x' if vars.VERBOSE or vars.XTRACE: log_('\n') firstline = open(os.path.join(dodir, dofile)).readline().strip() if firstline.startswith('#!/'): argv[0:2] = firstline[2:].split(' ') log('%s\n' % _nice(t)) self.dodir = dodir self.basename = basename self.ext = ext self.argv = argv sf.is_generated = True sf.save() dof = state.File(name=os.path.join(dodir, dofile)) dof.set_static() dof.save() state.commit() jwack.start_job(t, self._do_subproc, self._after)
def isdirty(f, depth, max_changed, already_checked, is_checked=state.File.is_checked, set_checked=state.File.set_checked_save): if f.id in already_checked: raise state.CyclicDependencyError() # make a copy of the list, so upon returning, our parent's copy # is unaffected already_checked = list(already_checked) + [f.id] if vars.DEBUG >= 1: debug('%s?%s\n' % (depth, f.nicename())) if f.failed_runid: debug('%s-- DIRTY (failed last time)\n' % depth) return DIRTY if f.changed_runid == None: debug('%s-- DIRTY (never built)\n' % depth) return DIRTY if f.changed_runid > max_changed: debug('%s-- DIRTY (built)\n' % depth) return DIRTY # has been built more recently than parent if is_checked(f): if vars.DEBUG >= 1: debug('%s-- CLEAN (checked)\n' % depth) return CLEAN # has already been checked during this session if not f.stamp: debug('%s-- DIRTY (no stamp)\n' % depth) return DIRTY newstamp = f.read_stamp() if f.stamp != newstamp: if newstamp == state.STAMP_MISSING: debug('%s-- DIRTY (missing)\n' % depth) if f.stamp and f.is_generated: # previously was stamped and generated, but suddenly missing. # We can safely forget that it is/was a target; if someone # does redo-ifchange on it and it doesn't exist, we'll mark # it a target again, but if someone creates it by hand, # it'll be a source. This should reduce false alarms when # files change from targets to sources as a project evolves. debug('%s converted target -> source\n' % depth) f.is_generated = False #f.update_stamp() f.save() else: debug('%s-- DIRTY (mtime)\n' % depth) if f.csum: return [f] else: return DIRTY must_build = [] for mode, f2 in f.deps(): dirty = CLEAN if mode == 'c': if os.path.exists(os.path.join(vars.BASE, f2.name)): debug('%s-- DIRTY (created)\n' % depth) dirty = DIRTY elif mode == 'm': sub = isdirty(f2, depth=depth + ' ', max_changed=max(f.changed_runid, f.checked_runid), already_checked=already_checked, is_checked=is_checked, set_checked=set_checked) if sub: debug('%s-- DIRTY (sub)\n' % depth) dirty = sub else: assert (mode in ('c', 'm')) if not f.csum: # f is a "normal" target: dirty f2 means f is instantly dirty if dirty == DIRTY: # f2 is definitely dirty, so f definitely needs to # redo. return DIRTY elif isinstance(dirty, list): # our child f2 might be dirty, but it's not sure yet. It's # given us a list of targets we have to redo in order to # be sure. must_build += dirty else: # f is "checksummable": dirty f2 means f needs to redo, # but f might turn out to be clean after that (ie. our parent # might not be dirty). if dirty == DIRTY: # f2 is definitely dirty, so f definitely needs to # redo. However, after that, f might turn out to be # unchanged. return [f] elif isinstance(dirty, list): # our child f2 might be dirty, but it's not sure yet. It's # given us a list of targets we have to redo in order to # be sure. must_build += dirty if must_build: # f is *maybe* dirty because at least one of its children is maybe # dirty. must_build has accumulated a list of "topmost" uncertain # objects in the tree. If we build all those, we can then # redo-ifchange f and it won't have any uncertainty next time. return must_build debug('%s-- CLEAN\n' % (depth, )) # if we get here, it's because the target is clean if f.is_override: state.warn_override(f.name) set_checked(f) return CLEAN
def _start_do(self): assert (self.lock.owned) t = self.t sf = self.sf newstamp = sf.read_stamp() if (sf.is_generated and newstamp != state.STAMP_MISSING and (sf.stamp != newstamp or sf.is_override)): state.warn_override(_nice(t)) if not sf.is_override: warn('%s - old: %r\n' % (_nice(t), sf.stamp)) warn('%s - new: %r\n' % (_nice(t), newstamp)) sf.set_override() sf.set_checked() sf.save() return self._after2(0) if (os.path.exists(t) and not os.path.isdir(t + '/.') and not sf.is_generated): # an existing source file that was not generated by us. # This step is mentioned by djb in his notes. # For example, a rule called default.c.do could be used to try # to produce hello.c, but we don't want that to happen if # hello.c was created by the end user. debug2("-- static (%r)\n" % t) sf.set_static() sf.save() return self._after2(0) sf.zap_deps1() (dodir, dofile, basedir, basename, ext) = paths.find_do_file(sf) if not dofile: if os.path.exists(t): sf.set_static() sf.save() return self._after2(0) else: err('no rule to make %r\n' % t) return self._after2(1) unlink(self.tmpname1) unlink(self.tmpname2) ffd = os.open(self.tmpname1, os.O_CREAT | os.O_RDWR | os.O_EXCL, 0666) close_on_exec(ffd, True) self.f = os.fdopen(ffd, 'w+') # this will run in the dofile's directory, so use only basenames here arg1 = basename + ext # target name (including extension) arg2 = basename # target name (without extension) argv = [ 'sh', '-e', dofile, arg1, arg2, # temp output file name state.relpath(os.path.abspath(self.tmpname2), dodir), ] if vars.VERBOSE: argv[1] += 'v' if vars.XTRACE: argv[1] += 'x' if vars.VERBOSE or vars.XTRACE: log_('\n') firstline = open(os.path.join(dodir, dofile)).readline().strip() if firstline.startswith('#!/'): argv[0:2] = firstline[2:].split(' ') log('%s\n' % _nice(t)) self.dodir = dodir self.basename = basename self.ext = ext self.argv = argv sf.is_generated = True sf.save() dof = state.File(name=os.path.join(dodir, dofile)) dof.set_static() dof.save() state.commit() jwack.start_job(t, self._do_subproc, self._after)
def isdirty(f, depth, max_changed, is_checked=state.File.is_checked, set_checked=state.File.set_checked_save): ''' Determine whether a file needs to be built @param f The file name @param depth The recursion depth @param max_changed The maximum changed run ID @param The function for determining whether f is checked @param The function for setting that the file is checked and saving state @return One of the following: CLEAN (the file is clean); or DIRTY (the file is dirty); or a list of targets to build ''' if vars.DEBUG >= 1: debug('%s?%s\n' % (depth, f.nicename())) if f.failed_runid: debug('%s-- DIRTY (failed last time)\n' % depth) return DIRTY if f.changed_runid == None: debug('%s-- DIRTY (never built)\n' % depth) return DIRTY if f.changed_runid > max_changed: debug('%s-- DIRTY (built)\n' % depth) return DIRTY # has been built more recently than parent if is_checked(f): if vars.DEBUG >= 1: debug('%s-- CLEAN (checked)\n' % depth) return CLEAN # has already been checked during this session if not f.stamp: debug('%s-- DIRTY (no stamp)\n' % depth) return DIRTY newstamp = f.read_stamp() if f.stamp != newstamp: if newstamp == state.STAMP_MISSING: debug('%s-- DIRTY (missing)\n' % depth) else: debug('%s-- DIRTY (mtime)\n' % depth) if f.csum: return [f] else: return DIRTY targets = [] for mode, f2 in f.deps(): status = CLEAN if mode == 'c' and os.path.exists(os.path.join(vars.BASE, f2.name)): debug('%s-- DIRTY (created)\n' % depth) status = DIRTY elif mode == 'm': sub = isdirty(f2, depth=depth + ' ', max_changed=max(f.changed_runid, f.checked_runid), is_checked=is_checked, set_checked=set_checked) if sub: debug('%s-- DIRTY (sub)\n' % depth) status = sub else: assert (mode in ('c', 'm')) if f.csum: # f is "checksummable": dirty f2 means f needs to redo, # but f might turn out to be clean after that (ie. our parent # might not be dirty). if status == DIRTY: # f2 is definitely dirty, so f definitely needs to # redo. However, after that, f might turn out to be # unchanged. return [f] elif isinstance(status, list): # our child f2 might be dirty, but it's not sure yet. It's # given us a list of targets we have to redo in order to # be sure. targets += status elif not status == CLEAN: # f is a "normal" target: dirty f2 means f is instantly dirty return status if targets: # f is *maybe* dirty because at least one of its children is maybe # dirty. targets has accumulated a list of "topmost" uncertain # objects in the tree. If we build all those, we can then # redo-ifchange f and it won't have any uncertainty next time. return targets # if we get here, it's because the target is clean if f.is_override: state.warn_override(f.name) set_checked(f) return CLEAN
def isdirty(f, depth, max_changed, is_checked=state.File.is_checked, set_checked=state.File.set_checked_save): if vars.DEBUG >= 1: debug('%s?%s\n' % (depth, f.nicename())) if f.failed_runid: debug('%s-- DIRTY (failed last time)\n' % depth) return DIRTY if f.changed_runid == None: debug('%s-- DIRTY (never built)\n' % depth) return DIRTY if f.changed_runid > max_changed: debug('%s-- DIRTY (built)\n' % depth) return DIRTY # has been built more recently than parent if is_checked(f): if vars.DEBUG >= 1: debug('%s-- CLEAN (checked)\n' % depth) return CLEAN # has already been checked during this session if not f.stamp: debug('%s-- DIRTY (no stamp)\n' % depth) return DIRTY newstamp = f.read_stamp() if f.stamp != newstamp: if newstamp == state.STAMP_MISSING: debug('%s-- DIRTY (missing)\n' % depth) else: debug('%s-- DIRTY (mtime)\n' % depth) if f.csum: return [f] else: return DIRTY must_build = [] for mode,f2 in f.deps(): dirty = CLEAN if mode == 'c': if os.path.exists(os.path.join(vars.BASE, f2.name)): debug('%s-- DIRTY (created)\n' % depth) dirty = DIRTY elif mode == 'm': sub = isdirty(f2, depth = depth + ' ', max_changed = max(f.changed_runid or 0, f.checked_runid or 0), is_checked=is_checked, set_checked=set_checked) if sub: debug('%s-- DIRTY (sub)\n' % depth) dirty = sub else: assert(mode in ('c','m')) if not f.csum: # f is a "normal" target: dirty f2 means f is instantly dirty if dirty: # if dirty==DIRTY, this means f is definitely dirty. # if dirty==[...], it's a list of the uncertain children. return dirty else: # f is "checksummable": dirty f2 means f needs to redo, # but f might turn out to be clean after that (ie. our parent # might not be dirty). if dirty == DIRTY: # f2 is definitely dirty, so f definitely needs to # redo. However, after that, f might turn out to be # unchanged. return [f] elif isinstance(dirty,list): # our child f2 might be dirty, but it's not sure yet. It's # given us a list of targets we have to redo in order to # be sure. must_build += dirty if must_build: # f is *maybe* dirty because at least one of its children is maybe # dirty. must_build has accumulated a list of "topmost" uncertain # objects in the tree. If we build all those, we can then # redo-ifchange f and it won't have any uncertainty next time. return must_build # if we get here, it's because the target is clean if f.is_override: state.warn_override(f.name) set_checked(f) return CLEAN