A Python-based build system with a Makefile-like experience
Project description
HXMK (Hexcell Make)
HXMK is a python-based build system for the Hexcell Projects. It brings a Makefile like experience to a familiar and simple environment.
Best of all: it has nice colors
Installation
pip install HXMK
Dependencies:
- colorama >= 0.4.1
Manual (latest version)
$ git clone https://github.com/Hexcell/HXMK.git
$ cd HXMK
$ pip install .
Usage
If the project directory contains a hxmk.py
file, you can do
hxmk [args|rules]
Arguments are specified like name=value
and rules like name
.
hxmk abc=123 somerule
In case no rule was given, HXMK will look for the rule @everything
and execute it (if it exists).
No arguments or rules have to be specified be default.
hxmk # <- this is completely valid
HXMK can also be used to clean directories.
hxmk clean [args|rules]
This will look for a .clean
file that contains a glob pattern on each line.
Example of a .clean
file:
bin
obj
*.o
__pycache__
clean
counts as a rule (although it's not an actual rule), so the rule @everything
will not be run if not explicitly stated like this:
hxmk clean everything # will clean and THEN run @everything
hxmk.py
Rules
The simplest rule would look something like this:
@rule()
def everything(c):
pass
c
is an instance of the class Commander
, it is used to execute commands. To do so, it overloads the lshift operator.
@rule()
def everything(c):
c << "echo Commands are executed like this."
Rules can have dependencies. They are given through the return annotation. A rules dependencies are executed before it.
@rule()
def everything(c) -> "other":
c << "echo What a lovely day it is today."
def other(c):
c << "echo @everything depends on me so I go first!"
Multiple dependencies are given in a list or tuple, whichever you prefer.
@rule()
def everything(c) -> ("other", "something")
Rules have triggers, which are state if and when a rule shall be executed. If no trigger is specified, the trigger always
will be set to True
.
For example, to execute a rule whenever at least one of the folders bin
and obj
are missing, you could do the following:
@rule(not_found=["bin", "obj"])
def dirs(c):
c << "mkdir -p bin"
c << "mkdir -p obj"
The following triggers are implemented so far:
always
, always execute the rule. It is a bool.dependencies
, execute it if one or more dependencies were executed. It is a bool.not_found
, execute when a specified path is not found (file or folder). It can be astr
,list
or atuple
.changed
, cache a file or list of files. Execute when any of the specified files are not found in the cache or have been changed. It can be astr
,list
, or atuple
.
If not_found
is given, the rule will assume that you are going to create the specified path. If that path is not found after the rule was executed, a warning will be shown.
An example of a caching rule:
@rule(changed=["a.cpp", "b.cpp"], not_found="program")
This above rule will be executed if a.cpp
or b.cpp
have been changed, or when program
was not found.
Though for this particular case, pattern rules would be recommended.
Pattern Rules
Pattern Rules are rules that are executed multiple times for multiple files. They look like this:
@pattern("src/*.c -> obj/*.o")
def somerule(c, src, dest):
pass
(The syntax for the patterns is src -> dest
.)
This basically means, every .c
file in src
will be turned into an .o
file in obj
.
The Pattern Rule will be executed for every .c
file.
This could be used to compile every .c
file in a directory.
@pattern("src/*.c -> obj/*.o")
def somerule(c, src, dest):
c << f"gcc -c {src} -o {dest}"
Pattern Rules can have multiple sources and destinations. In that case, the parameters src
and dest
are lists.
@pattern("src/*.c include/*.h -> obj/*.o")
def somerule(c, src, dest):
c << f"gcc -c {src[0]} -o {dest}"
Pattern rules are cached. Before executing, the Pattern Rule checks whether the source files have been modified since the last build and if the destination file exists already. If the destination file does not exist, the rule will be executed, else it will only be executed if the source file was modified or not found in the cache.
Builtins
All builtins are immediately available without having to import anything.
make
make(self, path, args=[], isolate=False)
make
will start HXMK in a different folder. Optionally args (in the sense of CLI args) can be passed in to args
.
By default all variables from the root module are readable in every module called by make
. This behavior can be stopped by setting isolate
to True
.
# hxmk.py
somevar = "hello"
@rule()
def everything(c):
make("other")
# other/hxmk.py
@rule()
def everything(c):
print(somevar)
>>> @all
>>> Entering other
>>> other/@all
>>> "hello"
default
default(arg, val)
default
is used to optain a value from the CLI or default to another value in case it's not found. The first argument is the name and the second one is the default value.
An example would be:
$ hxmk config=debug
config = default("config", "release")
assert config in ["debug", "release"]
# ...now config can be used
as_args
as_args(l, prefix="", suffix="")
as_args
is used to use a list as CLI arguments. The first argument is a list, tuple or dict, the keyword arguments are prefix
and suffix
.
An example use case would be this:
...
c << "ld ... %s" % as_args(["a.o", "b.o", "c.o"])
>>> ld ... "a.o" "b.o" "c.o"
Passing in a dict would return "key=value"
for each item.
...
c << "ld ... %s" % as_args({"a": "b", "c": "d"})
>>> ld ... "a=b" "c=d"
When passing in a dict, the prefix and suffix parameters can not be used.
glob
glob(pathname, *, recursive=False)
glob
is just the glob function from pythons standard library. For more information, look at Pythons documentation on it.
It could be used in combination with as_args
to link all object files.
...
c << "ld ... %s" % as_args(glob("obj/*.o"))
>>> ld ... "main.o" "utils.o"
Goals
The main goal is to create a simple build too with the same functionality as make (+ more) while maintaining readability and sanity.
Currently, HXMK runs just fine, but there have been and will most likely be many API changes. Bugs are to be expected, if you find one, do not hesitate to create an issue.
TODO
- rules
- pattern eules
- argument parsing
- build subdirectories
- cleaning
- add docstrings and comments for every function
- full api documentation
- better cross-platform support / more builtins (mkdir, ...)
- in depth compatibility testing with various python versions
Contributing
Any form of contribution is welcome ^^
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file HXMK-0.0.9.tar.gz
.
File metadata
- Download URL: HXMK-0.0.9.tar.gz
- Upload date:
- Size: 9.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.6.3 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.3
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 8169967caffe50bda0ecedcbc7ea665aac234e7d0afc637f4466fa3cb79cc8c9 |
|
MD5 | 176cff145b732ff7ef063a7a9df662a1 |
|
BLAKE2b-256 | 42b3492761d67751058cb9eb629d7eea7aa3acce51bfda19560d2d024490b8b1 |
File details
Details for the file HXMK-0.0.9-py3-none-any.whl
.
File metadata
- Download URL: HXMK-0.0.9-py3-none-any.whl
- Upload date:
- Size: 11.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.6.3 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.3
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 147e850a2e8d1b6157d590553a4ec1d8146fb486c77a0046a31209db6464e35b |
|
MD5 | de966369851ce5b4d30fd5eb22e4140e |
|
BLAKE2b-256 | 051f076fa6f63a042bb8f5c24d88c485d0a4dc030d7ba9b7029ea1b20c2de2aa |