1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
import glob
import os
import re
import shutil
import subprocess
import debian.changelog
import setuptools
import setuptools.command.build_py
def fopen(path, mode="r", **kwargs):
return open(path, mode, encoding="UTF-8", **kwargs)
def deb2pep440(debian_version):
"""Get PEP 440 compliant version string from Debian version (``setuptools >=66`` mandates PEP 440)"""
pos = debian_version.find("~")
return debian_version if pos < 0 else debian_version[0:pos]
# Get version from debian/changelog
with open("./debian/changelog", "rb") as F:
MINI_BUILDD_VERSION = str(debian.changelog.Changelog(file=F).version)
PY_ARGCOMPLETE = shutil.which("register-python-argcomplete")
print(f"I: Found p-r-argcomplete executable: {PY_ARGCOMPLETE}")
class Scripts():
_INSTALL_PATHS = {1: "usr/bin/", 8: "usr/sbin/"}
def __init__(self):
# Auto-generate Debian install files.
self.debian_install = {
1: fopen("debian/mini-buildd-utils.install", "w"),
8: fopen("debian/mini-buildd.install", "w"),
}
# dh_bash-completion format is very limited. The only way to automate this is to write the resp. configs here.
self.debian_bash_completion = {
1: fopen("debian/mini-buildd-utils.bash-completion", "w"),
8: fopen("debian/mini-buildd.bash-completion", "w"),
}
for i in 1, 8:
self.debian_install[i].write("# Generated by setup.py\n")
# Static install contents for mini-buildd-util
self.debian_install[1].write(("share/emacs/site-lisp/*.el /usr/share/emacs/site-lisp/\n"))
# Static install contents for mini-buildd
self.debian_install[8].write(("share/package-templates/ /usr/share/mini-buildd/\n"
"etc/schroot/\n"
"etc/sudoers.d/\n"
"etc/profile.d/\n"))
def setup(self, prog_path, section):
prog = os.path.basename(prog_path)
man_page_file = f"{prog_path}.{section}"
# Hack to get the DESCRIPTION from script for '--name' argument. See: https://lintian.debian.org/tags/manpage-has-useless-whatis-entry.html
with fopen(f"{prog_path}") as prog_file:
name = re.search(r'DESCRIPTION.*=.*"(.+?)"', prog_file.read()).group(1)
print(f"I: Generating {man_page_file}...")
subprocess.run([prog_path, "--help"], check=True, stdout=subprocess.DEVNULL) # Pre-test to get 'error debug' from prog itself to stderr (help2man call below will not show them).
subprocess.check_call(["help2man", "--no-info", "--name", name, "--version-string", MINI_BUILDD_VERSION, "--section", str(section), "--output", man_page_file, prog_path])
# bash completion (python only)
with fopen(f"{prog_path}") as prog_file:
if re.match(r"#!/usr/bin/python", prog_file.readline()):
bash_completion_file = f"src/{prog}.bash-completion"
print(f"I: Generating {bash_completion_file}...")
os.makedirs(os.path.dirname(bash_completion_file), exist_ok=True)
with fopen(bash_completion_file, "w") as bash_completion:
bash_completion.write(f'eval "$({PY_ARGCOMPLETE} --shell=bash "{prog}")"\n')
self.debian_bash_completion[section].write(f"{bash_completion_file} {prog}\n")
self.debian_install[section].write(f"{prog_path} {self._INSTALL_PATHS[section]}\n")
class VersionPy(setuptools.command.build_py.build_py):
def run(self):
version_file = "src/mini_buildd/version.py"
print(f"I: Generating {version_file}...")
with fopen(version_file, "w") as init_py:
init_py.write(f"""\
__version__ = "{MINI_BUILDD_VERSION}"
""")
class BuildPy(setuptools.command.build_py.build_py):
def run(self):
VersionPy(self.distribution).run()
scripts = Scripts()
scripts.setup("src/mini-buildd", 8)
scripts.setup("src/mini-buildd-self-signed-certificate", 8)
scripts.setup("src/mini-buildd-backup", 8)
scripts.setup("src/mini-buildd-debootstrap-uname-2.6", 8)
scripts.setup("src/mini-buildd-ssh-setup", 8)
scripts.setup("src/mini-buildd-ssh-uploader-command", 8)
scripts.setup("src/mini-buildd-debug-build", 8)
scripts.setup("src/mini-buildd-cruft", 8)
scripts.setup("src/mini-buildd-import-08x", 8)
scripts.setup("src/mini-buildd-api", 1)
scripts.setup("src/mini-buildd-events", 1)
scripts.setup("src/mini-buildd-dput", 1)
scripts.setup("src/mini-buildd-internals", 1)
scripts.setup("src/mini-buildd-bootstrap-apt", 1)
scripts.setup("src/mini-buildd-super-portext", 1)
super().run()
# Auto-minifying some files. minify breaks "html" django templates, so skipped.
for extension in ["css", "svg", "js"]:
for f in glob.glob(f"{self.build_lib}/**/*.{extension}", recursive=True):
print(f"I: Minifying {f}...")
minified = f + ".minified"
subprocess.check_call(["minify", "--output", minified, f])
os.replace(minified, f)
def package_data_files(*patterns):
"""Little helper to collect file lists for package_data"""
package_path = "src/mini_buildd/"
for pattern in patterns:
for f in glob.glob(f"{package_path}{pattern}", recursive=True): # python 3.10's "root_dir" would help, but we need to support >=3.6.
yield f[len(package_path):]
setuptools.setup(
cmdclass={"version_py": VersionPy, "build_py": BuildPy},
name="mini-buildd",
version=deb2pep440(MINI_BUILDD_VERSION), # Taint Debian version to make setuptools not warn
description="Mini Debian build daemon",
author="Stephan Sürken",
author_email="absurd@debian.org",
package_dir={"": "src"},
packages=setuptools.find_packages("src"), # All traditional (dir has "__init__.py") packages: ['mini_buildd', 'mini_buildd.models']
package_data={
"mini_buildd": list(package_data_files("**/*.css", "**/*.js", "**/*.svg", "**/*.html", "templatetags/**/*.py")),
})
|