From b75300bb02f4e7111167719c7cf7999bb9812abc Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Mon, 1 Jul 2019 07:20:16 -0300 Subject: [PATCH] Detect System package name by recipe settings (#5026) (#5215) * #5026 Detect package prefix name by settings - Each package manages has a specific prefix/suffix to deal with architectures. - Some package managers (e.g brew) do not provide multi-arch support, only an universal option. Signed-off-by: Uilian Ries * #5026 Validate SystemPackageTool with settings - Validate yum and apt when installing x86 Signed-off-by: Uilian Ries * #5026 Support custom platform - User is able to specify prefix/suffix according the arch Signed-off-by: Uilian Ries * #5026 Remove useless code Signed-off-by: Uilian Ries * #5026 Rename methods for package name Signed-off-by: Uilian Ries * #5026 Use conanfile for SystemPackageTool Signed-off-by: Uilian Ries * #5026 Validate output instance Co-Authored-By: Javier G. Sogo * #5026 Fix bad indented block Signed-off-by: Uilian Ries --- conans/client/tools/system_pm.py | 89 +++++++++++++++++-- .../unittests/client/tools/system_pm_test.py | 37 ++++++++ 2 files changed, 120 insertions(+), 6 deletions(-) diff --git a/conans/client/tools/system_pm.py b/conans/client/tools/system_pm.py index 1c9aa1beb07..7c09990844c 100644 --- a/conans/client/tools/system_pm.py +++ b/conans/client/tools/system_pm.py @@ -2,7 +2,7 @@ import sys from conans.client.runner import ConanRunner -from conans.client.tools.oss import OSInfo +from conans.client.tools.oss import OSInfo, cross_building, get_cross_building_settings from conans.client.tools.files import which from conans.errors import ConanException from conans.util.env_reader import get_env @@ -11,8 +11,8 @@ class SystemPackageTool(object): - def __init__(self, runner=None, os_info=None, tool=None, recommends=False, output=None): - + def __init__(self, runner=None, os_info=None, tool=None, recommends=False, output=None, conanfile=None): + output = output if output else conanfile.output if conanfile else None self._output = default_output(output, 'conans.client.tools.system_pm.SystemPackageTool') os_info = os_info or OSInfo() self._is_up_to_date = False @@ -20,6 +20,7 @@ def __init__(self, runner=None, os_info=None, tool=None, recommends=False, outpu self._tool._sudo_str = self._get_sudo_str() self._tool._runner = runner or ConanRunner(output=self._output) self._tool._recommends = recommends + self._conanfile = conanfile @staticmethod def _get_sudo_str(): @@ -87,11 +88,17 @@ def update(self): self._is_up_to_date = True self._tool.update() - def install(self, packages, update=True, force=False): + def install(self, packages, update=True, force=False, arch_names=None): + """ Get the system package tool install command. + + :param packages: String with all package to be installed e.g. "libusb-dev libfoobar-dev" + :param update: Run update command before to install + :param force: Force installing all packages + :param arch_names: Package suffix/prefix name used by installer tool e.g. {"x86_64": "amd64"} + :return: None """ - Get the system package tool install command. - '""" packages = [packages] if isinstance(packages, str) else list(packages) + packages = self._get_package_names(packages, arch_names) mode = self._get_sysrequire_mode() @@ -116,6 +123,24 @@ def install(self, packages, update=True, force=False): self.update() self._install_any(packages) + def _get_package_names(self, packages, arch_names): + """ Parse package names according it architecture + + :param packages: list with all package to be installed e.g. ["libusb-dev libfoobar-dev"] + :param arch_names: Package suffix/prefix name used by installer tool + :return: list with all parsed names e.g. ["libusb-dev:armhf libfoobar-dev:armhf"] + """ + if self._conanfile and self._conanfile.settings and cross_building(self._conanfile.settings): + _, build_arch, _, host_arch = get_cross_building_settings(self._conanfile.settings) + arch = host_arch or build_arch + parsed_packages = [] + for package in packages: + for package_name in package.split(" "): + parsed_packages.append(self._tool.get_package_name(package_name, arch, + arch_names)) + return parsed_packages + return packages + def _installed(self, packages): if not packages: return True @@ -141,6 +166,16 @@ class BaseTool(object): def __init__(self, output=None): self._output = default_output(output, 'conans.client.tools.system_pm.BaseTool') + def get_package_name(self, package, arch, arch_names): + """ Retrieve package name to installed according the target arch. + + :param package: Regular package name e.g libusb-dev + :param arch: Host arch from Conanfile.settings + :param arch_names: Dictionary with suffix/prefix names e.g {"x86_64": "amd64"} + :return: Package name for Tool e.g. libusb-dev:i386 + """ + return package + class NullTool(BaseTool): def add_repository(self, repository, repo_key=None): @@ -179,6 +214,20 @@ def installed(self, package_name): % package_name, None) return exit_code == 0 + def get_package_name(self, package, arch, arch_names): + if arch_names is None: + arch_names = {"x86_64": "amd64", + "x86": "i386", + "ppc32": "powerpc", + "ppc64le": "ppc64el", + "armv7": "arm", + "armv7hf": "armhf", + "armv8": "arm64", + "s390x": "s390x"} + if arch in arch_names: + return "%s:%s" % (package, arch_names[arch]) + return package + class YumTool(BaseTool): def add_repository(self, repository, repo_key=None): @@ -196,6 +245,20 @@ def installed(self, package_name): exit_code = self._runner("rpm -q %s" % package_name, None) return exit_code == 0 + def get_package_name(self, package, arch, arch_names): + if arch_names is None: + arch_names = {"x86_64": "x86_64", + "x86": "i?86", + "ppc32": "powerpc", + "ppc64le": "ppc64le", + "armv7": "armv7", + "armv7hf": "armv7hl", + "armv8": "aarch64", + "s390x": "s390x"} + if arch in arch_names: + return "%s.%s" % (package, arch_names[arch]) + return package + class BrewTool(BaseTool): def add_repository(self, repository, repo_key=None): @@ -275,6 +338,13 @@ def installed(self, package_name): exit_code = self._runner("pacman -Qi %s" % package_name, None) return exit_code == 0 + def get_package_name(self, package, arch, arch_names): + if arch_names is None: + arch_names = {"x86": "lib32"} + if arch in arch_names: + return "%s-%s" % (arch_names[arch], package) + return package + class ZypperTool(BaseTool): def add_repository(self, repository, repo_key=None): @@ -291,6 +361,13 @@ def installed(self, package_name): exit_code = self._runner("rpm -q %s" % package_name, None) return exit_code == 0 + def get_package_name(self, package, arch, arch_names): + if arch_names is None: + arch_names = {"x86": "i586"} + if arch in arch_names: + return "%s.%s" % (arch_names[arch], package) + return package + def _run(runner, command, output, accepted_returns=None): accepted_returns = accepted_returns or [0, ] diff --git a/conans/test/unittests/client/tools/system_pm_test.py b/conans/test/unittests/client/tools/system_pm_test.py index 3cd30265578..ec41e4ca10b 100644 --- a/conans/test/unittests/client/tools/system_pm_test.py +++ b/conans/test/unittests/client/tools/system_pm_test.py @@ -14,6 +14,7 @@ from conans.errors import ConanException from conans.test.unittests.util.tools_test import RunnerMock from conans.test.utils.tools import TestBufferConanOutput +from conans.test.utils.conanfile import MockSettings, MockConanfile class SystemPackageToolTest(unittest.TestCase): @@ -166,6 +167,16 @@ def system_package_tool_test(self): spt.install("a_package", force=True) self.assertEqual(runner.command_called, "sudo -A yum install -y a_package") + settings = MockSettings({"arch": "x86", "arch_build": "x86_64", "os": "Linux", + "os_build": "Linux"}) + conanfile = MockConanfile(settings) + spt = SystemPackageTool(runner=runner, os_info=os_info, output=self.out, + conanfile=conanfile) + spt.install("a_package", force=False) + self.assertEqual(runner.command_called, "rpm -q a_package.i?86") + spt.install("a_package", force=True) + self.assertEqual(runner.command_called, "sudo -A yum install -y a_package.i?86") + os_info.linux_distro = "debian" spt = SystemPackageTool(runner=runner, os_info=os_info, output=self.out) with self.assertRaises(ConanException): @@ -235,6 +246,32 @@ def system_package_tool_test(self): spt.update() self.assertEqual(runner.command_called, "apt-get update") + for arch, distro_arch in {"x86_64": "", "x86": ":i386", "ppc32": ":powerpc", + "ppc64le": ":ppc64el", "armv7": ":arm", "armv7hf": ":armhf", + "armv8": ":arm64", "s390x": ":s390x"}.items(): + settings = MockSettings({"arch": arch, + "arch_build": "x86_64", + "os": "Linux", + "os_build": "Linux"}) + conanfile = MockConanfile(settings) + spt = SystemPackageTool(runner=runner, os_info=os_info, output=self.out, + conanfile=conanfile) + spt.install("a_package", force=True) + self.assertEqual(runner.command_called, + "apt-get install -y --no-install-recommends a_package%s" % distro_arch) + + for arch, distro_arch in {"x86_64": "", "x86": ":all"}.items(): + settings = MockSettings({"arch": arch, + "arch_build": "x86_64", + "os": "Linux", + "os_build": "Linux"}) + conanfile = MockConanfile(settings) + spt = SystemPackageTool(runner=runner, os_info=os_info, output=self.out, + conanfile=conanfile) + spt.install("a_package", force=True, arch_names={"x86": "all"}) + self.assertEqual(runner.command_called, + "apt-get install -y --no-install-recommends a_package%s" % distro_arch) + os_info.is_macos = True os_info.is_linux = False os_info.is_windows = False