This weekend project is a small clone (most of the code is in a single file of just about 200LoC) of GNU Make with the twist that it rebuilds a target only if the hash of any of its sources changes. This is named Pyke as in Python + Make (I have no hope of being the first coming up with this name for a project).



Clone the project and then install it locally with pip.

$ git clone
$ cd pyke
$ pip install -e .


Create a in your project folder and add recipes to build your targets (for now just read below and look at the examples for the syntax).

The provided globals in this file are

  • pikefile is of type Pikefile and holds all the rules and handles the building of the project.
  • rule is a function and an alias for pikefile.rule and is used as a decorator to define new rules.
  • run is just an alias for os.system used to directly call shell commands.


This file stores the checksums of all built targets.


There are some examples in ./examples.


This example shows how the checksums approach is better in this case with respect to Make.

@rule('b.txt', ['a.txt'])
def _(target, source, sources):
    run(f'head -n 3 {source} > {target}')
    run(f'printf "\n" >> {target}')

@rule('c.txt', ['b.txt'])
def _(target, source, sources):
    run(f'cat {source} {source} {source} > {target}')

The file b.txt depends only on a part of a.txt so changes to its end don't trigger the recompilation of targets that depend only on b.txt. In this case, Make couldn't have figured that changes to the end of a.txt don't affect b.txt.


A simple example that shows a generic rule with "%" for creating object files from the sources main.c and util.c and then linking them.

@rule('%.o', ['%.c'])
def _(target, source, sources):
    run(f'gcc -c {source} -o {target}')

@rule('main', ['main.o', 'util.o'])
def _(target, source, sources):
    run(f'gcc -o {target} {" ".join(sources)}')


This example shows the cycle detection feature

@rule('a', ['c'])
def _(target, source, sources):

@rule('b', ['a'])
def _(target, source, sources):

@rule('c', ['b'])
def _(target, source, sources):

Trying to execute pyke for this project gives

$ pyke
[ERROR] Found dependency cycle caused by "c", aborting! Trace: ['c', 'b', 'a']


  • Move the code of pyke.build_with_args() directly inside bin/pike and decouple the logging code in its own module to let the logging level be changed from the start script.
GitHub - aziis98/pyke at
A small clone of GNU Make based on file checksums. Contribute to aziis98/pyke development by creating an account on GitHub.