From 05f9481b4566f84a26c32be6e0dfb10c64442d5e Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 8 Oct 2024 09:30:41 -0400 Subject: [PATCH] Modernize build system Remove `requirements.txt`; runtime dependencies are inlined in `install_requires` in `setup.py` and build dependencies are moved to `pyproject.toml`. This allows building with `uv` to succeed. --- .github/workflows/pylint.yml | 21 +++++--- MANIFEST.in | 1 - Makefile | 4 +- README.md | 11 ++-- pyproject.toml | 22 ++++++++ requirements.txt | 5 -- setup.py | 97 ++++++------------------------------ virtme_ng/run.py | 11 ++-- 8 files changed, 64 insertions(+), 108 deletions(-) delete mode 100644 MANIFEST.in create mode 100644 pyproject.toml delete mode 100644 requirements.txt diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index bb0adb1..869354a 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -11,17 +11,26 @@ jobs: matrix: python-version: ["3.8", "3.9", "3.10"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | + set -euxo pipefail + python -m pip install --upgrade pip - pip install -r requirements.txt - pip install pylint - pip install flake8 + + # Both build and runtime deps. This is the price of using pip. + pip install 'argparse-manpage[setuptools]' argcomplete requests + + pip install pylint flake8 - name: Analysing the code with pylint run: | - python setup.py lint + set -euxo pipefail + + pylint vng '**/*.py' + + flake8 vng + find . -name '*.py' | xargs flake8 diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index f9bd145..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -include requirements.txt diff --git a/Makefile b/Makefile index 9a17a89..52ccab7 100644 --- a/Makefile +++ b/Makefile @@ -11,8 +11,8 @@ init: install: install_from_source install_from_source: @echo "Version: $(GIT_DESCRIBE)" - BUILD_VIRTME_NG_INIT=1 pip3 install --verbose -r requirements.txt $(INSTALL_ARGS) . + BUILD_VIRTME_NG_INIT=1 pip3 install --verbose $(INSTALL_ARGS) . install_only_top: @echo "Version: $(GIT_DESCRIBE)" - BUILD_VIRTME_NG_INIT=0 pip3 install --verbose -r requirements.txt $(INSTALL_ARGS) . + BUILD_VIRTME_NG_INIT=0 pip3 install --verbose $(INSTALL_ARGS) . diff --git a/README.md b/README.md index 003499d..71e1006 100644 --- a/README.md +++ b/README.md @@ -79,18 +79,19 @@ To install virtme-ng from source you can clone this git repository and build a standalone virtme-ng running the following commands: ``` $ git clone --recurse-submodules https://github.com/arighi/virtme-ng.git - $ BUILD_VIRTME_NG_INIT=1 pip3 install --verbose -r requirements.txt . + $ BUILD_VIRTME_NG_INIT=1 pip3 install . ``` If you are in Debian/Ubuntu you may need to install the following packages to build virtme-ng from source properly: ``` - $ sudo apt install python3-pip python3-argcomplete flake8 pylint \ - cargo rustc qemu-system-x86 + $ sudo apt install python3-pip flake8 pylint cargo rustc qemu-system-x86 ``` -In recent versions of pip3 you may need to specify `--break-system-packages` to -properly install virtme-ng in your system from source. +If you'd prefer to use `uv`: +``` + $ BUILD_VIRTME_NG_INIT=1 uv tool install . +``` * Run from source diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a68c59c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[build-system] +requires = [ + "argparse-manpage[setuptools]", + "setuptools", + + # Runtime dependencies, needed to generate manpages. + "argcomplete", + "requests", +] + +[tool.build_manpages] +manpages = [ + """\ + man/vng.1\ + :pyfile=virtme_ng/run.py\ + :function=make_parser\ + :author=virtme-ng is written by Andrea Righi \ + :author=Based on virtme by Andy Lutomirski \ + :manual_title=virtme-ng\ + :description=Quickly run kernels inside a virtualized snapshot of your live system\ + """, +] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 40f89a7..0000000 --- a/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -argcomplete -argparse-manpage -requests -setuptools - diff --git a/setup.py b/setup.py index f9ed360..f8d9903 100755 --- a/setup.py +++ b/setup.py @@ -2,12 +2,10 @@ import os import sys -import platform import sysconfig -from glob import glob -from shutil import which -from subprocess import check_call, CalledProcessError -from setuptools import setup, Command +from subprocess import check_call +from build_manpages import build_manpages, get_build_py_cmd, get_install_cmd +from setuptools import setup from setuptools.command.build_py import build_py from setuptools.command.egg_info import egg_info from virtme_ng.version import get_version_string @@ -35,56 +33,6 @@ os.environ['PYTHONPATH'] = sysconfig.get_paths()['purelib'] -def is_arm_32bit(): - arch = platform.machine() - return arch.startswith("arm") and platform.architecture()[0] == "32bit" - - -def parse_requirements(filename): - with open(filename, 'r', encoding="utf-8") as file: - lines = file.readlines() - return [line.strip() for line in lines if line.strip() and not line.startswith('#')] - - -class LintCommand(Command): - description = "Run coding style checks" - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - try: - for cmd in ("flake8", "pylint"): - command = [cmd] - for pattern in ( - "vng", - "*.py", - "virtme/*.py", - "virtme/*/*.py", - "virtme_ng/*.py", - ): - command += glob(pattern) - check_call(command) - except CalledProcessError: - sys.exit(1) - - -man_command = f""" -argparse-manpage \ - --pyfile ./virtme_ng/run.py --function make_parser \ - --prog vng --version v{VERSION} \ - --author "virtme-ng is written by Andrea Righi " \ - --author "Based on virtme by Andy Lutomirski " \ - --project-name virtme-ng --manual-title virtme-ng \ - --description "Quickly run kernels inside a virtualized snapshot of your live system" \ - --url https://github.com/arighi/virtme-ng > vng.1 -""" - - class BuildPy(build_py): def run(self): print(f"BUILD_VIRTME_NG_INIT: {build_virtme_ng_init}") @@ -95,23 +43,6 @@ def run(self): ["strip", "-s", "../virtme/guest/bin/virtme-ng-init"], cwd="virtme_ng_init", ) - # Generate manpage - if which('argparse-manpage'): - env = os.environ.copy() - env["PYTHONPATH"] = os.path.dirname(os.path.abspath(__file__)) - check_call(man_command, shell=True, env=env) - - # Generate bash autocompletion scripts - completion_command = '' - if which("register-python-argcomplete"): - completion_command = "register-python-argcomplete" - elif which("register-python-argcomplete3"): - completion_command = "register-python-argcomplete3" - else: - print("ERROR: 'register-python-argcomplete' or 'register-python-argcomplete3' not found.") - sys.exit(1) - check_call(completion_command + ' virtme-ng > virtme-ng-prompt', shell=True) - check_call(completion_command + ' vng > vng-prompt', shell=True) # Run the rest of virtme-ng build build_py.run(self) @@ -154,26 +85,28 @@ def run(self): data_files = [ ("/etc", ["cfg/virtme-ng.conf"]), - ("/usr/share/bash-completion/completions", ["virtme-ng-prompt"]), - ("/usr/share/bash-completion/completions", ["vng-prompt"]), ] -if which('argparse-manpage'): - data_files.append(("/usr/share/man/man1", ["vng.1"])) - setup( name="virtme-ng", version=VERSION, author="Andrea Righi", author_email="andrea.righi@canonical.com", description="Build and run a kernel inside a virtualized snapshot of your live system", - url="https://git.launchpad.net/~arighi/+git/virtme-ng", + url="https://github.com/arighi/virtme-ng", license="GPLv2", long_description=open( os.path.join(os.path.dirname(__file__), "README.md"), "r", encoding="utf-8" ).read(), long_description_content_type="text/markdown", - install_requires=parse_requirements('requirements.txt'), + install_requires=[ + 'argcomplete', + 'requests', + # `pkg_resources` is removed in python 3.12, moved to setuptools. + # + # TODO: replace pkg_resources with importlib. # pylint: disable=fixme + 'setuptools', + ], entry_points={ "console_scripts": [ "vng = virtme_ng.run:main", @@ -184,13 +117,13 @@ def run(self): ] }, cmdclass={ - "build_py": BuildPy, + "build_manpages": build_manpages, + "build_py": get_build_py_cmd(BuildPy), + "install": get_install_cmd(), "egg_info": EggInfo, - "lint": LintCommand, }, packages=packages, package_data={"virtme.guest": package_files}, - data_files=data_files, scripts=[ "bin/virtme-prep-kdir-mods", ], diff --git a/virtme_ng/run.py b/virtme_ng/run.py index b776822..a577232 100644 --- a/virtme_ng/run.py +++ b/virtme_ng/run.py @@ -23,12 +23,8 @@ ) from select import select from pathlib import Path -try: - from argcomplete import autocomplete -except ModuleNotFoundError: - def autocomplete(*args, **kwargs): - # pylint: disable=unused-argument - pass + +import argcomplete from virtme.util import SilentError, uname, get_username from virtme_ng.utils import CONF_FILE, spinner_decorator @@ -79,6 +75,7 @@ def make_parser(): """Main virtme-ng command line parser.""" parser = argparse.ArgumentParser( + prog="vng", formatter_class=argparse.RawTextHelpFormatter, description="Build and run kernels inside a virtualized snapshot of your live system", epilog="""\ @@ -1245,7 +1242,7 @@ def dump(kern_source, args): def do_it() -> int: """Main body.""" - autocomplete(_ARGPARSER) + argcomplete.autocomplete(_ARGPARSER) args = _ARGPARSER.parse_args() kern_source = KernelSource()