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 https://github.com/aziis98/pyke
$ cd pyke
$ pip install -e .
Create a pykefile.py
in your project folder and add recipes to build your targets (for now just read below and look at the examples for the syntax).
-
pykefile.py
The provided globals in this file are
-
pikefile
is of typePikefile
and holds all the rules and handles the building of the project. -
rule
is a function and an alias forpikefile.rule
and is used as a decorator to define new rules. -
run
is just an alias foros.system
used to directly call shell commands.
-
-
.pykecache.json
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):
print("A")
@rule('b', ['a'])
def _(target, source, sources):
print("B")
@rule('c', ['b'])
def _(target, source, sources):
print("C")
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 insidebin/pike
and decouple the logging code in its own module to let the logging level be changed from the start script.