From 9554839b12535deaa298514e1c1afbe5fc630dbd Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Sat, 21 May 2022 11:02:35 +0200 Subject: [PATCH 1/2] Introduces pkg_config_location in Prerequisite and use OpenSSLPrerequisite().pkg_config_location in hostpython3, so we can support ssl on hostpython3 just out of the box also on macOS --- pythonforandroid/prerequisites.py | 35 ++++++++++++-- .../recipes/hostpython3/__init__.py | 17 ++++++- tests/test_prerequisites.py | 47 +++++++++++++++++-- 3 files changed, 90 insertions(+), 9 deletions(-) diff --git a/pythonforandroid/prerequisites.py b/pythonforandroid/prerequisites.py index a5392eeafc..d85eb0b76d 100644 --- a/pythonforandroid/prerequisites.py +++ b/pythonforandroid/prerequisites.py @@ -10,6 +10,7 @@ class Prerequisite(object): name = "Default" + homebrew_formula_name = "" mandatory = dict(linux=False, darwin=False) installer_is_supported = dict(linux=False, darwin=False) @@ -108,6 +109,25 @@ def _darwin_get_brew_formula_location_prefix(self, formula, installed=False): else: return _stdout_res.decode("utf-8").strip() + def darwin_pkg_config_location(self): + warning( + f"pkg-config location is not supported on macOS for prerequisite: {self.name}" + ) + return "" + + def linux_pkg_config_location(self): + warning( + f"pkg-config location is not supported on linux for prerequisite: {self.name}" + ) + return "" + + @property + def pkg_config_location(self): + if sys.platform == "darwin": + return self.darwin_pkg_config_location() + elif sys.platform == "linux": + return self.linux_pkg_config_location() + class HomebrewPrerequisite(Prerequisite): name = "homebrew" @@ -245,19 +265,28 @@ def darwin_installer(self): class OpenSSLPrerequisite(Prerequisite): - name = "openssl@1.1" + name = "openssl" + homebrew_formula_name = "openssl@1.1" mandatory = dict(linux=False, darwin=True) installer_is_supported = dict(linux=False, darwin=True) def darwin_checker(self): return ( - self._darwin_get_brew_formula_location_prefix("openssl@1.1", installed=True) + self._darwin_get_brew_formula_location_prefix( + self.homebrew_formula_name, installed=True + ) is not None ) + def darwin_pkg_config_location(self): + return os.path.join( + self._darwin_get_brew_formula_location_prefix(self.homebrew_formula_name), + "lib/pkgconfig", + ) + def darwin_installer(self): info("Installing OpenSSL ...") - subprocess.check_output(["brew", "install", "openssl@1.1"]) + subprocess.check_output(["brew", "install", self.homebrew_formula_name]) class AutoconfPrerequisite(Prerequisite): diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index ff0f473f3f..ecdd98ae85 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -1,4 +1,5 @@ import sh +import os from multiprocessing import cpu_count from pathlib import Path @@ -11,6 +12,7 @@ current_directory, ensure_dir, ) +from pythonforandroid.prerequisites import OpenSSLPrerequisite HOSTPYTHON_VERSION_UNSET_MESSAGE = ( 'The hostpython recipe must have set version' @@ -60,6 +62,15 @@ def python_exe(self): '''Returns the full path of the hostpython executable.''' return join(self.get_path_to_python(), self._exe_name) + def get_recipe_env(self, arch=None): + env = os.environ.copy() + openssl_prereq = OpenSSLPrerequisite() + if env.get("PKG_CONFIG_PATH", ""): + os.pathsep.join(openssl_prereq.pkg_config_location, env["PKG_CONFIG_PATH"]) + else: + env["PKG_CONFIG_PATH"] = openssl_prereq.pkg_config_location + return env + def should_build(self, arch): if Path(self.python_exe).exists(): # no need to build, but we must set hostpython for our Context @@ -83,6 +94,8 @@ def get_path_to_python(self): return join(self.get_build_dir(), self.build_subdir) def build_arch(self, arch): + env = self.get_recipe_env(arch) + recipe_build_dir = self.get_build_dir(arch.arch) # Create a subdirectory to actually perform the build @@ -92,7 +105,7 @@ def build_arch(self, arch): # Configure the build with current_directory(build_dir): if not Path('config.status').exists(): - shprint(sh.Command(join(recipe_build_dir, 'configure'))) + shprint(sh.Command(join(recipe_build_dir, 'configure')), _env=env) with current_directory(recipe_build_dir): # Create the Setup file. This copying from Setup.dist is @@ -110,7 +123,7 @@ def build_arch(self, arch): SETUP_DIST_NOT_FIND_MESSAGE ) - shprint(sh.make, '-j', str(cpu_count()), '-C', build_dir) + shprint(sh.make, '-j', str(cpu_count()), '-C', build_dir, _env=env) # make a copy of the python executable giving it the name we want, # because we got different python's executable names depending on diff --git a/tests/test_prerequisites.py b/tests/test_prerequisites.py index 7c8ce9f5ff..70ffa0c0d1 100644 --- a/tests/test_prerequisites.py +++ b/tests/test_prerequisites.py @@ -1,5 +1,7 @@ import unittest -from unittest import mock +from unittest import mock, skipIf + +import sys from pythonforandroid.prerequisites import ( JDKPrerequisite, @@ -18,6 +20,7 @@ class PrerequisiteSetUpBaseClass: def setUp(self): self.mandatory = dict(linux=False, darwin=False) self.installer_is_supported = dict(linux=False, darwin=False) + self.expected_homebrew_formula_name = "" def test_is_mandatory_on_darwin(self): assert self.prerequisite.mandatory["darwin"] == self.mandatory["darwin"] @@ -37,6 +40,26 @@ def test_installer_is_supported_on_linux(self): == self.installer_is_supported["linux"] ) + def test_darwin_pkg_config_location(self): + self.assertEqual(self.prerequisite.darwin_pkg_config_location(), "") + + def test_linux_pkg_config_location(self): + self.assertEqual(self.prerequisite.linux_pkg_config_location(), "") + + @skipIf(sys.platform != "darwin", "Only run on macOS") + def test_pkg_config_location_property__darwin(self): + self.assertEqual( + self.prerequisite.pkg_config_location, + self.prerequisite.darwin_pkg_config_location(), + ) + + @skipIf(sys.platform != "linux", "Only run on Linux") + def test_pkg_config_location_property__linux(self): + self.assertEqual( + self.prerequisite.pkg_config_location, + self.prerequisite.linux_pkg_config_location(), + ) + class TestJDKPrerequisite(PrerequisiteSetUpBaseClass, unittest.TestCase): def setUp(self): @@ -76,6 +99,8 @@ def setUp(self): self.mandatory = dict(linux=False, darwin=True) self.installer_is_supported = dict(linux=False, darwin=True) self.prerequisite = OpenSSLPrerequisite() + self.expected_homebrew_formula_name = "openssl@1.1" + self.expected_homebrew_location_prefix = "/opt/homebrew/opt/openssl@1.1" @mock.patch( "pythonforandroid.prerequisites.Prerequisite._darwin_get_brew_formula_location_prefix" @@ -84,17 +109,31 @@ def test_darwin_checker(self, _darwin_get_brew_formula_location_prefix): _darwin_get_brew_formula_location_prefix.return_value = None self.assertFalse(self.prerequisite.darwin_checker()) _darwin_get_brew_formula_location_prefix.return_value = ( - "/opt/homebrew/opt/openssl@1.1" + self.expected_homebrew_location_prefix ) self.assertTrue(self.prerequisite.darwin_checker()) _darwin_get_brew_formula_location_prefix.assert_called_with( - "openssl@1.1", installed=True + self.expected_homebrew_formula_name, installed=True ) @mock.patch("pythonforandroid.prerequisites.subprocess.check_output") def test_darwin_installer(self, check_output): self.prerequisite.darwin_installer() - check_output.assert_called_once_with(["brew", "install", "openssl@1.1"]) + check_output.assert_called_once_with( + ["brew", "install", self.expected_homebrew_formula_name] + ) + + @mock.patch( + "pythonforandroid.prerequisites.Prerequisite._darwin_get_brew_formula_location_prefix" + ) + def test_darwin_pkg_config_location(self, _darwin_get_brew_formula_location_prefix): + _darwin_get_brew_formula_location_prefix.return_value = ( + self.expected_homebrew_location_prefix + ) + self.assertEqual( + self.prerequisite.darwin_pkg_config_location(), + f"{self.expected_homebrew_location_prefix}/lib/pkgconfig", + ) class TestAutoconfPrerequisite(PrerequisiteSetUpBaseClass, unittest.TestCase): From f379fc7d00c706b221607e783e0fa512da0808c6 Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Sat, 21 May 2022 18:03:43 +0200 Subject: [PATCH 2/2] Fixes a typo --- pythonforandroid/recipes/hostpython3/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index ecdd98ae85..5dc3c87200 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -66,7 +66,9 @@ def get_recipe_env(self, arch=None): env = os.environ.copy() openssl_prereq = OpenSSLPrerequisite() if env.get("PKG_CONFIG_PATH", ""): - os.pathsep.join(openssl_prereq.pkg_config_location, env["PKG_CONFIG_PATH"]) + env["PKG_CONFIG_PATH"] = os.pathsep.join( + openssl_prereq.pkg_config_location, env["PKG_CONFIG_PATH"] + ) else: env["PKG_CONFIG_PATH"] = openssl_prereq.pkg_config_location return env