Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y python3 python3-pip python3-virtualenv python3-all
pip install wheel stdeb
pip install build wheel stdeb

- name: Install fpm dependencies
run: |
Expand Down
2 changes: 1 addition & 1 deletion debian/DEBIAN/control
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ Architecture: all
Maintainer: Ferenc Nandor Janky & Attila Gombos <info@effective-range.com>
Homepage: www.effective-range.com
Description: Tools for packaging libraries and applications.
Depends: cmake, python3, python3-pip, python3-wheel, ruby, ruby-dev, rubygems, build-essential, debhelper, devscripts, equivs, dh-virtualenv, dh-python, python3-virtualenv, python3-all, python3-stdeb
Depends: cmake, python3, python3-pip, python3-wheel, ruby, ruby-dev, rubygems, build-essential, debhelper, devscripts, equivs, dh-virtualenv, dh-python, python3-virtualenv, python3-all, python3-stdeb, python3-build
5 changes: 1 addition & 4 deletions python/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
apt-get update

# Install python3 and pip
apt-get install -y --no-install-recommends python3-all python3-pip python3-virtualenv

# Install wheel
pip3 install wheel
apt-get install -y --no-install-recommends python3-all python3-pip python3-virtualenv python3-wheel python3-build

# Install fpm
apt-get install -y --no-install-recommends ruby ruby-dev rubygems build-essential
Expand Down
21 changes: 18 additions & 3 deletions python/pack_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,36 @@
import sys
from collections import deque
from concurrent.futures import ThreadPoolExecutor
from enum import Enum
from fileinput import FileInput
from functools import partial
from os.path import exists
from subprocess import PIPE, Popen
from typing import Generator, Union, Optional


class ProjectType(Enum):
PYPROJECT_TOML = "pyproject.toml"
SETUP_PY = "setup.py"


def check_workspace(workspace_dir: str) -> None:
if not exists(workspace_dir):
print(f"Workspace directory {workspace_dir} does not exist", file=sys.stderr)
exit(1)

if not exists(f"{workspace_dir}/setup.py"):
print(f"There is no setup.py in the workspace directory {workspace_dir}", file=sys.stderr)
exit(2)

def get_project_type(workspace_dir: str) -> ProjectType:
if exists(f"{workspace_dir}/pyproject.toml"):
print(f"Found pyproject.toml in the workspace directory {workspace_dir}", file=sys.stderr)
return ProjectType.PYPROJECT_TOML

if exists(f"{workspace_dir}/setup.py"):
print(f"Found setup.py in the workspace directory {workspace_dir}", file=sys.stderr)
return ProjectType.SETUP_PY

print(f"There is no setup.py or pyproject.toml in the workspace directory {workspace_dir}", file=sys.stderr)
exit(2)


def get_build_architecture() -> str:
Expand Down
2 changes: 1 addition & 1 deletion python/pack_dh-virtualenv → python/pack_dh-virtualenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def _add_shlidbeps_parameters(arguments: Namespace, debian_dir: str) -> None:

def _set_no_automatic_dbgsym(debian_dir: str) -> None:
with open(f"{debian_dir}/rules", "a") as file:
file.write(f"""override_dh_strip:
file.write("""override_dh_strip:
\tdh_strip --no-automatic-dbgsym
""")

Expand Down
6 changes: 4 additions & 2 deletions python/pack_fpm-deb → python/pack_fpm-deb.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

sys.path.insert(0, dirname(abspath(__file__)))

from pack_common import check_workspace, run_command, get_absolute_path
from pack_common import check_workspace, run_command, get_absolute_path, get_project_type


def main() -> None:
Expand All @@ -21,6 +21,8 @@ def main() -> None:

check_workspace(workspace_dir)

project_type = get_project_type(workspace_dir)

fpm_arguments = [
"-s",
"python",
Expand Down Expand Up @@ -60,7 +62,7 @@ def main() -> None:
if post_remove := arguments.postrm_file:
fpm_arguments.extend(["--after-remove", post_remove])

command = ["fpm", *fpm_arguments, "setup.py"]
command = ["fpm", *fpm_arguments, project_type.value]

results = run_command(workspace_dir, command, r'.*"(.+\.deb)"')

Expand Down
127 changes: 2 additions & 125 deletions python/pack_python
Original file line number Diff line number Diff line change
Expand Up @@ -4,135 +4,12 @@
# SPDX-FileCopyrightText: 2024 Attila Gombos <attila.gombos@effective-range.com>
# SPDX-License-Identifier: MIT

import re
import subprocess
import sys
from argparse import (
ArgumentParser,
ArgumentDefaultsHelpFormatter,
Namespace,
BooleanOptionalAction,
)
from configparser import ConfigParser
from os.path import exists, dirname, abspath
from os.path import dirname, abspath

sys.path.insert(0, dirname(abspath(__file__)))

from pack_common import check_workspace, get_absolute_path

DEFAULT_PACKAGING = "wheel"


def main() -> None:
arguments = _get_arguments()

workspace_dir = abspath(arguments.workspace_dir)

check_workspace(workspace_dir)

config_file = get_absolute_path(arguments.config_file, workspace_dir)

configuration, packaging = _parse_config(arguments, config_file)

print(f"Using configuration {configuration}", file=sys.stderr)

if arguments.scripts:
packaging = arguments.scripts.split()

scripts_dir = f"{abspath(dirname(__file__))}"

for script in packaging:
script_file = f"{scripts_dir}/pack_{script}"

if exists(script_file):
print(f"Running packaging script for {script}: {script_file}", file=sys.stderr)

command = [script_file, workspace_dir]

if services := configuration.get("service"):
for service in services.split(" "):
command.extend(["--service-file", service])
if pre_install := configuration.get("preinst"):
command.extend(["--preinst-file", pre_install])
if post_install := configuration.get("postinst"):
command.extend(["--postinst-file", post_install])
if pre_remove := configuration.get("prerm"):
command.extend(["--prerm-file", pre_remove])
if post_remove := configuration.get("postrm"):
command.extend(["--postrm-file", post_remove])

if arg_string := configuration.get(script):
print(f"Using arguments for {script}: {arg_string}", file=sys.stderr)
command.extend(_split_arguments(arg_string))

_run_script(arguments, command)
else:
print(f"Packaging script for {script} not found: {script_file}", file=sys.stderr)


def _run_script(arguments: Namespace, command: list[str]) -> None:
if arguments.python_bin:
command.extend(["-p", arguments.python_bin])

if arguments.output_dir:
command.extend(["-o", arguments.output_dir])

if arguments.target:
command.extend(["-t", arguments.target])

result = subprocess.run(command, text=True, stdout=subprocess.PIPE)

if result.returncode:
exit(result.returncode)

if result.stdout:
print(result.stdout.rstrip("\n"))


def _parse_config(
arguments: Namespace, config_file: str
) -> tuple[dict[str, str], list[str]]:
configuration = {}
packaging = [DEFAULT_PACKAGING]

if exists(config_file):
parser = ConfigParser()
parser.read(config_file)

if parser.has_section("pack-python"):
configuration = dict(parser["pack-python"])

packaging_options = configuration.get("packaging", "").split()

if arguments.all:
packaging = packaging_options
else:
default = configuration.get("default", DEFAULT_PACKAGING)
packaging = [default]

return configuration, packaging


def _get_arguments() -> Namespace:
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument("-s", "--scripts", help="space separated packaging scripts to run")
parser.add_argument("-a", "--all", help="run all configured packaging scripts",
action=BooleanOptionalAction, default=False)
parser.add_argument("-c", "--config-file", help="config file path relative to workspace directory",
default="setup.cfg")
parser.add_argument("-p", "--python-bin", help="python executable to use", default="python3")
parser.add_argument("-o", "--output-dir", help="package output directory")
parser.add_argument("-t", "--target", help="target architecture")
parser.add_argument("workspace_dir", help="workspace directory where setup.py is located")
return parser.parse_known_args()[0]


def _split_arguments(arg_string: str) -> list[str]:
# Split on spaces, but allow spaces inside double quotes
pattern = r"\"(.*?)\"|(\S+)"
matches = re.findall(pattern, arg_string)
return [match[0] if match[0] else match[1] for match in matches]

from pack_python import main

if __name__ == "__main__":
main()
142 changes: 142 additions & 0 deletions python/pack_python.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/usr/bin/env python3

# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky <ferenj@effective-range.com>
# SPDX-FileCopyrightText: 2024 Attila Gombos <attila.gombos@effective-range.com>
# SPDX-License-Identifier: MIT

import re
import subprocess
import sys
from argparse import (
ArgumentParser,
ArgumentDefaultsHelpFormatter,
Namespace,
BooleanOptionalAction,
)
from configparser import ConfigParser
from os.path import exists, dirname, abspath

sys.path.insert(0, dirname(abspath(__file__)))

from pack_common import check_workspace, get_absolute_path

DEFAULT_PACKAGING = "wheel"


def main() -> None:
arguments = _get_arguments()

workspace_dir = abspath(arguments.workspace_dir)

check_workspace(workspace_dir)

config_file = get_absolute_path(arguments.config_file, workspace_dir)

configuration, packaging = _parse_config(arguments, config_file)

print(f"Using configuration {configuration}", file=sys.stderr)

if arguments.scripts:
packaging = arguments.scripts.split()

scripts_dir = f"{abspath(dirname(__file__))}"

for script in packaging:
script_file = f"{scripts_dir}/pack_{script}.py"

if exists(script_file):
print(f"Running packaging script for {script}: {script_file}", file=sys.stderr)

command = [script_file, workspace_dir]

if services := configuration.get("service"):
for service in services.split(" "):
command.extend(["--service-file", service])
if pre_install := configuration.get("preinst"):
command.extend(["--preinst-file", pre_install])
if post_install := configuration.get("postinst"):
command.extend(["--postinst-file", post_install])
if pre_remove := configuration.get("prerm"):
command.extend(["--prerm-file", pre_remove])
if post_remove := configuration.get("postrm"):
command.extend(["--postrm-file", post_remove])

if arg_string := configuration.get(script):
print(f"Using arguments for {script}: {arg_string}", file=sys.stderr)
command.extend(_split_arguments(arg_string))

_run_script(arguments, command)
else:
print(f"Packaging script for {script} not found: {script_file}", file=sys.stderr)


def _run_script(arguments: Namespace, command: list[str]) -> None:
if arguments.python_bin:
command.extend(["-p", arguments.python_bin])

if arguments.output_dir:
command.extend(["-o", arguments.output_dir])

if arguments.target:
command.extend(["-t", arguments.target])

if not arguments.all and arguments.arguments:
command.extend(["-a", arguments.arguments])

result = subprocess.run(command, text=True, stdout=subprocess.PIPE)

if result.returncode:
exit(result.returncode)

if result.stdout:
print(result.stdout.rstrip("\n"))


def _parse_config(
arguments: Namespace, config_file: str
) -> tuple[dict[str, str], list[str]]:
configuration = {}
packaging = [DEFAULT_PACKAGING]

if exists(config_file):
parser = ConfigParser()
parser.read(config_file)

if parser.has_section("pack-python"):
configuration = dict(parser["pack-python"])

packaging_options = configuration.get("packaging", "").split()

if arguments.all:
packaging = packaging_options
else:
default = configuration.get("default", DEFAULT_PACKAGING)
packaging = [default]

return configuration, packaging


def _get_arguments() -> Namespace:
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument("-s", "--scripts", help="space separated packaging scripts to run")
parser.add_argument("-A", "--all", help="run all configured packaging scripts",
action=BooleanOptionalAction, default=False)
parser.add_argument("-a", "--arguments", help="arguments to pass to packaging scripts")
parser.add_argument("-c", "--config-file", help="config file path relative to workspace directory",
default="setup.cfg")
parser.add_argument("-p", "--python-bin", help="python executable to use", default="python3")
parser.add_argument("-o", "--output-dir", help="package output directory")
parser.add_argument("-t", "--target", help="target architecture")
parser.add_argument("workspace_dir", help="workspace directory where setup.py is located")
return parser.parse_known_args()[0]


def _split_arguments(arg_string: str) -> list[str]:
# Split on spaces, but allow spaces inside double quotes
pattern = r"\"(.*?)\"|(\S+)"
matches = re.findall(pattern, arg_string)
return [match[0] if match[0] else match[1] for match in matches]


if __name__ == "__main__":
main()
Loading