Example #1
0
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
Example #2
0
 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)
Example #3
0
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
Example #4
0
 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)
Example #5
0
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
Example #6
0
File: deps.py Project: shamer/redo
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