Examples¶
The Makex Makexfile¶
The following is the Makex File used to build and deploy Makex.
You might get some ideas here:
#!makex
"""
Note: This file must be run with the MAKEX_DEVELOPER environment variable to something True.
Build/Deploy
- use the pypi task to build/deploy the latest version to pypi. you'll need twine set up properly.
- use the upload-documents task to upload the latest version of the documentation.
"""
# Python interpreter to use to create a virtual environment:
PYTHON = "python3"
VENV = task_path("venv")
VENV_BIN = VENV / "bin"
# NOTE: This must be updated on new release versions (YYYYMMSS[equence]).
VERSION = "20250102"
CHANGE = Environment.get("CHANGE", "")
PYPROJECT_TOML = source() / "python" / "pyproject.toml"
task(
name="hello-world",
steps=[
print("Hello World!"),
],
)
task(
name="documents-html",
requires=[
":venv",
find("documents/source"),
find("python"),
"README.md",
],
outputs=[
"html/index.html",
],
# Set the environment for future actions.
environment={
"BUILDDIR": task_path("documents-html"),
"LC_ALL": "C",
"RELEASE": VERSION,
},
steps=[
shell(
f"source {VENV}/bin/activate",
f"make -e -C documents clean html",
),
print(f"Documents available at file://{self.path}/html/index.html")
# all three of these are the same:
#shell(f"xdg-open $PWD/_output_/documents-html/html/index.html")
#shell(f"xdg-open {path('documents-html')}/html/index.html")
#execute("xdg-open", path('documents-html') / "html/index.html")
],
)
task(
name="upload-documents",
requires=[
":documents-html",
],
steps=[
execute(
"upload-documents", "makex", VERSION, "--latest", task_path("documents-html") / "html"
),
],
)
# stage a directory for a source package
task(
name="source-package",
requires=[
find("python", glob("**/*.py")),
":build-completions",
],
steps=[
copy("python", exclude=[glob("**requirements*.txt"), glob("**pyproject.toml")]),
copy(PYPROJECT_TOML),
#mirror(["documents/Makefile"]), # "documents/source"
copy("Makexfile"),
copy("README.md"), #archive(f"makex-{VERSION}-source.zip"),
execute("metacompany-generate-source-license", "--output", self.path / "LICENSE.md"),
copy(
["scripts/completions/makex.bash", "scripts/completions/makex.zsh"],
"python/makex/data/completions"
),
write("python/makex/version.py", f"""VERSION="{VERSION}"\nCHANGE="{CHANGE}"\n"""),
],
)
# Build and deploy the latest version
task(
name="pypi",
requires=[
":source-package",
":venv",
],
steps=[
#shell(
# build the wheel from the source distribution.
# python -m build --wheel --outdir {path('pypi')} .
# f"metacompany-twine {path('pypi')}/*",
#),
execute(
VENV_BIN / "pyproject-build",
"--sdist",
"--outdir",
task_path('source-package'),
task_path('source-package')
),
execute("metacompany-twine", task_path('source-package')),
],
)
PEX_ROOT = task_path("install-pex")
PEX_ARGS = []
PEX_ARGS += [f"--pex-root", f"{PEX_ROOT}"]
PEX_ARGS += ["-P", "makex@python"]
PEX_ARGS += ["-m", "makex"]
PEX_ARGS += ["--sh-boot"]
PEX_ARGS += ["--no-wheel", "--pip-version=latest"]
PEX_ARGS += ["--requirement", PEX_ROOT / "requirements.txt"]
# XXX: this doesn't work for some reason
#PEX_ARGS+= ["--interpreter-constraint", "CPython>=3.9"]
task(
name="install-pex",
requires=[
":venv", #Reference("venv"),
":source-package",
],
steps=[
execute(
"pip-requirements", "txt", "--required", PYPROJECT_TOML, PEX_ROOT / "requirements.txt"
),
execute(
VENV_BIN / "pex",
PEX_ARGS,
"--output-file",
self.outputs.executable,
),
],
outputs={
"executable": home() / ".local/bin/makex",
}
)
# create a venv
task(
name="venv",
requires=[
PYPROJECT_TOML,
],
outputs=[
VENV / "pyvenv.cfg",
],
steps=[
execute(PYTHON, "-m", "venv", "--clear", VENV),
execute(VENV_BIN / "pip", "install", "--upgrade", "pip"),
execute(
"pip-requirements",
"install",
"--pip",
VENV_BIN / "pip",
"--all",
"python/pyproject.toml"
),
],
)
task(
name="mypy",
steps=[
## || exit 0 because mypy returns a non-zero which causes makex to report errors
shell(
f"source {VENV}/bin/activate",
"pip install mypy",
# --cache-dir= because we don't want to pollute the source with .mypy_cache directories
# mypy returns a non-zero exit code so we have to use || exit 0
f"mypy --cache-dir={task_path('mypy')} --follow-imports=silent --config-file python/mypy.ini --explicit-package-bases --show-absolute-path -p makex || exit 0",
)
]
)
task(
name="build-completions",
requires=[
":venv",
# completions may change here:
"python/makex/__main__.py",
],
steps=[
shell(
f"source {VENV}/bin/activate",
# TODO: patch shtab to take preamble from file
#"preamble=\"$(cat scripts/bash-completion/makex-preamble.bash)\"",
#f"cd python && shtab --shell=bash -u makex.__main__.parser --preamble \"${{preamble}}\" | tee {source('scripts/bash-completion/makex.bash')}", #
"cd python",
f"{PYTHON} -m makex completions --shell bash {self.outputs.bash}", #
f"{PYTHON} -m makex completions --shell zsh {self.outputs.zsh}", #
)
],
outputs={
"bash": source('scripts/completions/makex.bash'),
"zsh": source('scripts/completions/makex.zsh'),
}, # makex completions --print bash > ~/.local/share/bash-completion/completions/makex
)
task(
name="test",
requires=[":venv"],
steps=[
shell(
f"source {VENV}/bin/activate",
f"pytest -o cache_dir={self.path}/pytest python",
)
]
)
# test creating a task using a macro
# todo: test creating action or list of actions with a macro
@macro
def nuitka_target_command_line(
name,
PYTHON_PACKAGE,
APPLICATION_NAME,
pythonpath,
main,
requires=None,
VENV_BIN=None,
extra=None,
):
""" Compile a command line application for nuitka. """
BUILD_NUITKA = task_path(name)
NUITKA_ARGS = []
NUITKA_ARGS += [f"--include-package={PYTHON_PACKAGE}"]
NUITKA_ARGS += [f"--report={BUILD_NUITKA}/nuitka-build-report.xml"]
NUITKA_ARGS += ["--follow-imports", "--disable-console"]
NUITKA_ARGS += [f"--output-dir={BUILD_NUITKA}"]
NUITKA_ARGS += [f"--output-filename={APPLICATION_NAME}"]
#NUITKA_ARGS += [f"--include-package-data={PYTHON_PACKAGE}.data.completions"]
NUITKA_ARGS += ["--python-flag=-O,nosite,no_docstrings"]
NUITKA_ARGS += ["--show-anti-bloat-changes"]
NUITKA_ARGS += ["--nofollow-import-to=*.tests"]
NUITKA_ARGS += ["--nofollow-import-to=*_test.py"]
#NUITKA_ARGS += ["--nofollow-import-to=shtab"]
NUITKA_ARGS += ["--nofollow-import-to=pytest"]
NUITKA_ARGS += ["--standalone", "--onefile"]
NUITKA_ARGS += ["--clang"]
NUITKA_ARGS += ["--static-libpython=yes"]
NUITKA_ARGS += ["--experimental=no-outside-dependencies"]
NUITKA_ARGS += ["--prefer-source-code"]
NUITKA_ARGS += ["--warn-unusual-code", "--warn-implicit-exceptions"]
NUITKA_ARGS += extra
task(
name=name,
requires=requires,
environment={
"PYTHONPATH": pythonpath,
"CCFLAGS": "-march=native -mtune=native -Os",
},
steps=[
execute(VENV_BIN / "nuitka3", NUITKA_ARGS, main),
],
)
nuitka_target_command_line(
name="nuitka2",
PYTHON_PACKAGE="makex",
APPLICATION_NAME="makex",
pythonpath=source("python"),
main=source("python/makex/__main__.py"),
requires=[":venv"],
VENV_BIN=VENV_BIN,
extra=[
f"--include-package-data=makex.data.completions",
"--nofollow-import-to=shtab",
]
)
# TODO: call(":nuitka_target_command_line", ...)
BUILD_NUITKA = task_path("nuitka")
PYTHON_PACKAGE = "makex"
APPLICATION_NAME = "makex"
NUITKA_ARGS = []
NUITKA_ARGS += [f"--include-package={PYTHON_PACKAGE}"]
NUITKA_ARGS += [f"--report={BUILD_NUITKA}/nuitka-build-report.xml"]
NUITKA_ARGS += ["--follow-imports", "--disable-console"]
NUITKA_ARGS += [f"--output-dir={BUILD_NUITKA}"]
NUITKA_ARGS += [f"--output-filename={APPLICATION_NAME}"]
NUITKA_ARGS += [f"--include-package-data={PYTHON_PACKAGE}.data.completions"]
NUITKA_ARGS += ["--python-flag=-O,nosite,no_docstrings"]
NUITKA_ARGS += ["--show-anti-bloat-changes"]
NUITKA_ARGS += ["--nofollow-import-to=*.tests"]
NUITKA_ARGS += ["--nofollow-import-to=*_test.py"]
NUITKA_ARGS += ["--nofollow-import-to=shtab"]
NUITKA_ARGS += ["--nofollow-import-to=pytest"]
NUITKA_ARGS += ["--standalone", "--onefile"]
NUITKA_ARGS += ["--clang"]
#NUITKA_ARGS += ["--static-libpython=yes"]
NUITKA_ARGS += ["--experimental=no-outside-dependencies"]
NUITKA_ARGS += ["--prefer-source-code"]
NUITKA_ARGS += ["--warn-unusual-code", "--warn-implicit-exceptions"]
task(
name="nuitka",
requires=[
":venv",
],
environment={
"PYTHONPATH": source("python"),
"CCFLAGS": "-march=native -mtune=native -Os",
},
steps=[
execute(VENV_BIN / "nuitka", NUITKA_ARGS, source("python/makex/__main__.py")),
]
)
task(
name="publish-documents",
requires=[":documents-html"],
steps=[
execute(
"metacompany-upload-documents",
"--latest",
"makex",
VERSION,
task_path("documents-html") / "html"
)
]
)
### Build RPM:
# RPM Parameters
# Release is typically unchanged
# Set MAKEX_RPM_DIST to override the dist
RPM_NAME = "makex"
RPM_VERSION = VERSION
BUILD_ROOT = task_path("rpm")
RPMBUILD_FLAGS = ["--define", f"_topdir {BUILD_ROOT}"]
RPMBUILD_FLAGS += ["--define", f"_rpmdir {BUILD_ROOT}"]
RPMBUILD_FLAGS += ["--define", "_srcrpmdir %{_rpmdir}"]
RPMBUILD_FLAGS += ["--define", f"version {RPM_VERSION}"]
RPM_SOURCES_FOLDER = task_path("rpm") / "SOURCES"
task(
name="rpm",
requires=[":source-package"],
steps=[
execute("mkdir", RPM_SOURCES_FOLDER),
copy("packaging/makex.sh", "SOURCES"),
shell(
f"cd {task_path('source-package')} && zip -r {RPM_SOURCES_FOLDER}/makex-source.zip *"
),
# TODO: use this instead
#archive(
# path=task_path("rpm") / "SOURCES/makex-source.zip",
# prefix=f"makex-{VERSION}",
# files=[
# "python"#
# ]
#),
execute("rpmbuild", RPMBUILD_FLAGS, "-v", "-ba", "packaging/makex.spec"),
]
)
# test out pyoxidizer
task(
name="pyoxidizer",
steps=[execute("pyoxidizer", "build", "--var", "build_path", task_path("pyoxidizer"))]
)