blob: dbe237cea9acbdf2502a088e1b892f1f938774aa [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2017 Dell, Inc.
# Copyright 2020 Intel, Inc.
# Copyright 2021 Mario Limonciello
#
# SPDX-License-Identifier: LGPL-2.1-or-later
#
import os
import sys
import argparse
WARNING = "\033[93m"
ENDC = "\033[0m"
# Minimum version of markdown required
MINIMUM_MARKDOWN = (3, 2, 0)
def get_possible_profiles():
return ["fedora", "centos", "debian", "ubuntu", "arch", "darwin", "freebsd"]
def detect_profile():
if os.path.exists("/Library/Apple"):
return "darwin"
try:
import distro
target = distro.id()
if target == "rhel":
return "centos"
if target not in get_possible_profiles():
target = distro.like()
return target
except ModuleNotFoundError:
pass
# fallback
try:
with open("/etc/os-release", "rb") as f:
for line in f.read().decode().split("\n"):
if line.startswith("ID="):
target = line[3:].replace('"', "")
if target == "rhel":
return "centos"
return target
except FileNotFoundError:
pass
# failed
return ""
def pip_install_package(debug, name):
import subprocess
cmd = ["python3", "-m", "pip", "install", "--upgrade", name]
if debug:
print(cmd)
subprocess.call(cmd)
def test_jinja2(debug):
try:
import jinja2
except ModuleNotFoundError:
print("python3-jinja2 must be installed/upgraded")
pip_install_package(debug, "jinja2")
def test_markdown(debug):
try:
import markdown
new_enough = markdown.__version_info__ >= MINIMUM_MARKDOWN
except ModuleNotFoundError:
new_enough = False
if not new_enough:
print("python3-markdown must be installed/upgraded")
pip_install_package(debug, "markdown")
def get_minimum_meson_version():
import re
directory = os.path.join(os.path.dirname(sys.argv[0]), "..", "..")
with open(os.path.join(directory, "meson.build")) as f:
for line in f:
if "meson_version" in line:
return re.search(r"(\d+\.\d+\.\d+)", line).group(1)
def test_meson(debug):
from importlib.metadata import version, PackageNotFoundError
minimum = get_minimum_meson_version()
try:
new_enough = version("meson") >= minimum
except PackageNotFoundError:
import subprocess
try:
ver = (
subprocess.check_output(["meson", "--version"]).strip().decode("utf-8")
)
new_enough = ver >= minimum
except FileNotFoundError:
new_enough = False
if not new_enough:
print("meson must be installed/upgraded")
pip_install_package(debug, "meson")
def parse_dependencies(OS, variant, add_control):
import xml.etree.ElementTree as etree
deps = []
dep = ""
directory = os.path.dirname(sys.argv[0])
tree = etree.parse(os.path.join(directory, "dependencies.xml"))
root = tree.getroot()
for child in root:
if "id" not in child.attrib:
continue
for distro in child:
if "id" not in distro.attrib:
continue
if distro.attrib["id"] != OS:
continue
control = ""
if add_control:
inclusive = []
exclusive = []
if not distro.findall("control"):
continue
for control_parent in distro.findall("control"):
for obj in control_parent.findall("inclusive"):
inclusive.append(obj.text)
for obj in control_parent.findall("exclusive"):
exclusive.append(obj.text)
if inclusive or exclusive:
inclusive = " ".join(inclusive).strip()
exclusive = " !".join(exclusive).strip()
if exclusive:
exclusive = f"!{exclusive}"
control = f" [{inclusive}{exclusive}]"
for package in distro.findall("package"):
if variant and "variant" in package.attrib:
if package.attrib["variant"] != variant:
continue
if package.text:
dep = package.text
else:
dep = child.attrib["id"]
dep += control
if dep:
deps.append(dep)
return deps
def _validate_deps(profile: str, deps):
validated = deps
if profile == "debian" or profile == "ubuntu":
try:
from apt import cache
cache = cache.Cache()
for pkg in deps:
if not cache.has_key(pkg) and not cache.is_virtual_package(pkg):
print(
f"{WARNING}WARNING:{ENDC} ignoring unavailable package %s" % pkg
)
validated.remove(pkg)
except ModuleNotFoundError:
print(
f"{WARNING}WARNING:{ENDC} Unable to validate package dependency list without python3-apt"
)
return validated
def get_build_dependencies(profile: str, variant: str):
parsed = parse_dependencies(profile, variant, False)
return _validate_deps(profile, parsed)
def _get_installer_cmd(profile: str, yes: bool):
if profile == "darwin":
return ["brew", "install"]
if profile in ["debian", "ubuntu"]:
installer = ["apt", "install"]
elif profile in ["fedora", "centos"]:
installer = ["dnf", "install"]
elif profile == "arch":
installer = ["pacman", "-Syu", "--noconfirm", "--needed"]
elif profile == "freebsd":
installer = ["pkg", "install"]
else:
print("unable to detect OS profile, use --os= to specify")
print(f"\tsupported profiles: {get_possible_profiles()}")
sys.exit(1)
if os.geteuid() != 0:
installer.insert(0, "sudo")
if yes:
installer += ["-y"]
return installer
def install_packages(profile: str, variant: str, yes: bool, debugging: bool, packages):
import subprocess
if packages == "build-dependencies":
packages = get_build_dependencies(profile, variant)
installer = _get_installer_cmd(profile, yes)
installer += packages
if debugging:
print(installer)
subprocess.check_call(installer)
if __name__ == "__main__":
command = None
# compat mode for old training documentation
if "generate_dependencies.py" in sys.argv[0]:
command = "get-dependencies"
parser = argparse.ArgumentParser()
if not command:
parser.add_argument(
"command",
choices=[
"get-dependencies",
"test-markdown",
"test-jinja2",
"test-meson",
"detect-profile",
"install-dependencies",
"install-pip",
],
help="command to run",
)
parser.add_argument(
"-o",
"--os",
default=detect_profile(),
choices=get_possible_profiles(),
help="calculate dependencies for OS profile",
)
parser.add_argument(
"-v", "--variant", help="optional machine variant for the OS profile"
)
parser.add_argument(
"-y", "--yes", action="store_true", help="Don't prompt to install"
)
parser.add_argument(
"-d", "--debug", action="store_true", help="Display all launched commands"
)
args = parser.parse_args()
# fall back in all cases
if not args.variant:
args.variant = os.uname().machine
if not command:
command = args.command
# command to run
if command == "test-markdown":
test_markdown(args.debug)
elif command == "test-jinja2":
test_jinja2(args.debug)
elif command == "test-meson":
test_meson(args.debug)
elif command == "detect-profile":
print(detect_profile())
elif command == "get-dependencies":
dependencies = get_build_dependencies(args.os, args.variant)
print(*dependencies, sep="\n")
elif command == "install-dependencies":
install_packages(
args.os, args.variant, args.yes, args.debug, "build-dependencies"
)
elif command == "install-pip":
if args.os == "darwin":
install_packages(args.os, args.variant, args.yes, args.debug, ["python"])
elif args.os == "freebsd":
install_packages(args.os, args.variant, args.yes, args.debug, ["py311-pip"])
else:
install_packages(
args.os, args.variant, args.yes, args.debug, ["python3-pip"]
)