Faster tests with dependency analysis
# path.root is the root of our repo
hashDir = os.path.join(path.root, '_hashes')


Python style guide recommends that global constants are ALL_CAPS

class BaseRule(object):
    '''base object, managing work like checking if results are up to date, and
    calling production rules if they are not'''

    outPath = None


Python style guide recommends that class level constants are ALL_CAPS

    def isUpToDate(self):
        if not os.path.exists(self.outPath):
            return False
        if not os.path.exists(self.hashPath):
            return False
        with open(self.outPath) as f:
            fileHash = self.getOutHas()


You don't use the file you open here, instead you open the file inside getOutHash

        with open(self.hashPath) as f:
            storedHash = f.read().strip()
        return storedHash == fileHash


    def get(self):
        inputPathes = dict([(key, inp.get()) for key, inp in self.inputs.items()])


No need for the square brackets. They make it so that it produces a list. But since you are creating a dictionary there is no need to make a list first.

        if not self.isUpToDate():
            self.func(outPath=self.outPath, **inputPathes)
            self.storeHash()
        return self.outPath

class StableSrc(BaseRule):
    'source file that never change'

    inputs = {}


I know you'll never modify this, but inputs is an object level attribute above, and its clearest if you keep it that way.

    def __init__(self, path):
        self.outPath = path

    def isUpToDate(self):
        return True



def copyTest():
    'test function'

    import shutil

    @rule({'inp' : StableSrc('test.txt')}, 'test2.txt')
    def newTest(inp, outPath):
        print 'copy'
        shutil.copy(inp, outPath)


Usually you've got a small number of possible actions with different inputs/outputs for each use. Your decorator method assumes that each action will be different. 


You aren't tracking dependencies amongst the rules. So you can't have some like .swig produces .cpp produces .o which is linked to produce .exe
Creating a class for every rule is odd. It would make more sense to create an object for each rule.
The only thing you've got over makefiles is your use of a hash rather then a timestamp