diff --git a/pythonforandroid/bootstrap.py b/pythonforandroid/bootstrap.py index 5f4b3e7ab9..10830bf26c 100755 --- a/pythonforandroid/bootstrap.py +++ b/pythonforandroid/bootstrap.py @@ -8,7 +8,7 @@ import shlex import shutil -from pythonforandroid.logger import (shprint, info, logger, debug) +from pythonforandroid.logger import (shprint, info, info_main, logger, debug) from pythonforandroid.util import ( current_directory, ensure_dir, temp_directory, BuildInterruptingException, rmdir, move) @@ -184,11 +184,52 @@ def prepare_build_dir(self): def prepare_dist_dir(self): ensure_dir(self.dist_dir) + def _assemble_distribution_for_arch(self, arch): + """Per-architecture distribution assembly. + + Override this method to customize per-arch behavior. + Called once for each architecture in self.ctx.archs. + """ + self.distribute_libs(arch, [self.ctx.get_libs_dir(arch.arch)]) + self.distribute_aars(arch) + + python_bundle_dir = join(f'_python_bundle__{arch.arch}', '_python_bundle') + ensure_dir(python_bundle_dir) + site_packages_dir = self.ctx.python_recipe.create_python_bundle( + join(self.dist_dir, python_bundle_dir), arch) + if not self.ctx.with_debug_symbols: + self.strip_libraries(arch) + self.fry_eggs(site_packages_dir) + def assemble_distribution(self): - ''' Copies all the files into the distribution (this function is - overridden by the specific bootstrap classes to do this) - and add in the distribution info. - ''' + """Assemble the distribution by copying files and creating Python bundle. + + This default implementation works for most bootstraps. Override + _assemble_distribution_for_arch() for per-arch customization, or + override this entire method for fundamentally different behavior. + """ + info_main(f'# Creating Android project ({self.name})') + + rmdir(self.dist_dir) + shprint(sh.cp, '-r', self.build_dir, self.dist_dir) + + with current_directory(self.dist_dir): + with open('local.properties', 'w') as fileh: + fileh.write('sdk.dir={}'.format(self.ctx.sdk_dir)) + + with current_directory(self.dist_dir): + info('Copying Python distribution') + + self.distribute_javaclasses(self.ctx.javaclass_dir, + dest_dir=join("src", "main", "java")) + + for arch in self.ctx.archs: + self._assemble_distribution_for_arch(arch) + + if 'sqlite3' not in self.ctx.recipe_build_order: + with open('blacklist.txt', 'a') as fileh: + fileh.write('\nsqlite3/*\nlib-dynload/_sqlite3.so\n') + self._copy_in_final_files() self.distribution.save_info(self.dist_dir) diff --git a/pythonforandroid/bootstraps/_sdl_common/__init__.py b/pythonforandroid/bootstraps/_sdl_common/__init__.py index 034e52c7c1..3908909e2d 100644 --- a/pythonforandroid/bootstraps/_sdl_common/__init__.py +++ b/pythonforandroid/bootstraps/_sdl_common/__init__.py @@ -1,10 +1,7 @@ from os.path import join -import sh - -from pythonforandroid.toolchain import ( - Bootstrap, shprint, current_directory, info, info_main) -from pythonforandroid.util import ensure_dir, rmdir +from pythonforandroid.toolchain import Bootstrap +from pythonforandroid.util import ensure_dir class SDLGradleBootstrap(Bootstrap): @@ -12,38 +9,15 @@ class SDLGradleBootstrap(Bootstrap): recipe_depends = [] - def assemble_distribution(self): - info_main("# Creating Android project ({})".format(self.name)) - - rmdir(self.dist_dir) - info("Copying SDL/gradle build") - shprint(sh.cp, "-r", self.build_dir, self.dist_dir) - - # either the build use environment variable (ANDROID_HOME) - # or the local.properties if exists - with current_directory(self.dist_dir): - with open('local.properties', 'w') as fileh: - fileh.write('sdk.dir={}'.format(self.ctx.sdk_dir)) - - with current_directory(self.dist_dir): - info("Copying Python distribution") - - self.distribute_javaclasses(self.ctx.javaclass_dir, - dest_dir=join("src", "main", "java")) - - for arch in self.ctx.archs: - python_bundle_dir = join(f'_python_bundle__{arch.arch}', '_python_bundle') - ensure_dir(python_bundle_dir) - - self.distribute_libs(arch, [self.ctx.get_libs_dir(arch.arch)]) - site_packages_dir = self.ctx.python_recipe.create_python_bundle( - join(self.dist_dir, python_bundle_dir), arch) - if not self.ctx.with_debug_symbols: - self.strip_libraries(arch) - self.fry_eggs(site_packages_dir) - - if 'sqlite3' not in self.ctx.recipe_build_order: - with open('blacklist.txt', 'a') as fileh: - fileh.write('\nsqlite3/*\nlib-dynload/_sqlite3.so\n') - - super().assemble_distribution() + def _assemble_distribution_for_arch(self, arch): + """SDL bootstrap skips distribute_aars() - handled differently.""" + self.distribute_libs(arch, [self.ctx.get_libs_dir(arch.arch)]) + # Note: SDL bootstrap does not call distribute_aars() + + python_bundle_dir = join(f'_python_bundle__{arch.arch}', '_python_bundle') + ensure_dir(python_bundle_dir) + site_packages_dir = self.ctx.python_recipe.create_python_bundle( + join(self.dist_dir, python_bundle_dir), arch) + if not self.ctx.with_debug_symbols: + self.strip_libraries(arch) + self.fry_eggs(site_packages_dir) diff --git a/pythonforandroid/bootstraps/service_only/__init__.py b/pythonforandroid/bootstraps/service_only/__init__.py index 4f0d6cf20b..59712ea8c8 100644 --- a/pythonforandroid/bootstraps/service_only/__init__.py +++ b/pythonforandroid/bootstraps/service_only/__init__.py @@ -1,8 +1,4 @@ -import sh -from os.path import join -from pythonforandroid.toolchain import ( - Bootstrap, current_directory, info, info_main, shprint) -from pythonforandroid.util import ensure_dir, rmdir +from pythonforandroid.toolchain import Bootstrap class ServiceOnlyBootstrap(Bootstrap): @@ -13,40 +9,5 @@ class ServiceOnlyBootstrap(Bootstrap): set(Bootstrap.recipe_depends).union({'genericndkbuild'}) ) - def assemble_distribution(self): - info_main('# Creating Android project from build and {} bootstrap'.format( - self.name)) - - info('This currently just copies the build stuff straight from the build dir.') - rmdir(self.dist_dir) - shprint(sh.cp, '-r', self.build_dir, self.dist_dir) - with current_directory(self.dist_dir): - with open('local.properties', 'w') as fileh: - fileh.write('sdk.dir={}'.format(self.ctx.sdk_dir)) - - with current_directory(self.dist_dir): - info('Copying python distribution') - - self.distribute_javaclasses(self.ctx.javaclass_dir, - dest_dir=join("src", "main", "java")) - - for arch in self.ctx.archs: - self.distribute_libs(arch, [self.ctx.get_libs_dir(arch.arch)]) - self.distribute_aars(arch) - - python_bundle_dir = join(f'_python_bundle__{arch.arch}', '_python_bundle') - ensure_dir(python_bundle_dir) - site_packages_dir = self.ctx.python_recipe.create_python_bundle( - join(self.dist_dir, python_bundle_dir), arch) - if not self.ctx.with_debug_symbols: - self.strip_libraries(arch) - self.fry_eggs(site_packages_dir) - - if 'sqlite3' not in self.ctx.recipe_build_order: - with open('blacklist.txt', 'a') as fileh: - fileh.write('\nsqlite3/*\nlib-dynload/_sqlite3.so\n') - - super().assemble_distribution() - bootstrap = ServiceOnlyBootstrap() diff --git a/pythonforandroid/bootstraps/webview/__init__.py b/pythonforandroid/bootstraps/webview/__init__.py index 7604ed3b84..6795c17369 100644 --- a/pythonforandroid/bootstraps/webview/__init__.py +++ b/pythonforandroid/bootstraps/webview/__init__.py @@ -1,9 +1,4 @@ -from os.path import join - -import sh - -from pythonforandroid.toolchain import Bootstrap, current_directory, info, info_main, shprint -from pythonforandroid.util import ensure_dir, rmdir +from pythonforandroid.toolchain import Bootstrap class WebViewBootstrap(Bootstrap): @@ -13,39 +8,5 @@ class WebViewBootstrap(Bootstrap): set(Bootstrap.recipe_depends).union({'genericndkbuild'}) ) - def assemble_distribution(self): - info_main('# Creating Android project from build and {} bootstrap'.format( - self.name)) - - rmdir(self.dist_dir) - shprint(sh.cp, '-r', self.build_dir, self.dist_dir) - with current_directory(self.dist_dir): - with open('local.properties', 'w') as fileh: - fileh.write('sdk.dir={}'.format(self.ctx.sdk_dir)) - - with current_directory(self.dist_dir): - info('Copying python distribution') - - self.distribute_javaclasses(self.ctx.javaclass_dir, - dest_dir=join("src", "main", "java")) - - for arch in self.ctx.archs: - self.distribute_libs(arch, [self.ctx.get_libs_dir(arch.arch)]) - self.distribute_aars(arch) - - python_bundle_dir = join(f'_python_bundle__{arch.arch}', '_python_bundle') - ensure_dir(python_bundle_dir) - site_packages_dir = self.ctx.python_recipe.create_python_bundle( - join(self.dist_dir, python_bundle_dir), arch) - if not self.ctx.with_debug_symbols: - self.strip_libraries(arch) - self.fry_eggs(site_packages_dir) - - if 'sqlite3' not in self.ctx.recipe_build_order: - with open('blacklist.txt', 'a') as fileh: - fileh.write('\nsqlite3/*\nlib-dynload/_sqlite3.so\n') - - super().assemble_distribution() - bootstrap = WebViewBootstrap() diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index fc15bd45e6..ceb090de9e 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -353,34 +353,30 @@ def bootstrap_name(self): name of the bootstrap to test""" raise NotImplementedError("Not implemented in GenericBootstrapTest") + @mock.patch("pythonforandroid.bootstraps.qt.shprint") + @mock.patch("pythonforandroid.bootstraps.qt.rmdir") @mock.patch("pythonforandroid.bootstraps.qt.open", create=True) - @mock.patch("pythonforandroid.bootstraps.service_only.open", create=True) - @mock.patch("pythonforandroid.bootstraps.webview.open", create=True) - @mock.patch("pythonforandroid.bootstraps._sdl_common.open", create=True) + @mock.patch("pythonforandroid.bootstrap.open", create=True) @mock.patch("pythonforandroid.distribution.open", create=True) @mock.patch("pythonforandroid.bootstrap.Bootstrap.strip_libraries") @mock.patch("pythonforandroid.util.exists") @mock.patch("pythonforandroid.util.chdir") @mock.patch("pythonforandroid.bootstrap.listdir") - @mock.patch("pythonforandroid.bootstraps._sdl_common.rmdir") - @mock.patch("pythonforandroid.bootstraps.service_only.rmdir") - @mock.patch("pythonforandroid.bootstraps.webview.rmdir") - @mock.patch("pythonforandroid.bootstrap.sh.cp") + @mock.patch("pythonforandroid.bootstrap.rmdir") + @mock.patch("pythonforandroid.bootstrap.shprint") def test_assemble_distribution( self, - mock_sh_cp, - mock_rmdir1, - mock_rmdir2, - mock_rmdir3, + mock_shprint, + mock_rmdir, mock_listdir, mock_chdir, mock_ensure_dir, mock_strip_libraries, mock_open_dist_files, - mock_open_sdl_files, - mock_open_webview_files, - mock_open_service_only_files, - mock_open_qt_files + mock_open_bootstrap_files, + mock_open_qt_files, + mock_qt_rmdir, + mock_qt_shprint ): """ A test for any overwritten method of @@ -417,13 +413,11 @@ def test_assemble_distribution( bs.assemble_distribution() mock_open_dist_files.assert_called_once_with("dist_info.json", "w") - mock_open_bootstraps = { - "sdl2": mock_open_sdl_files, - "sdl3": mock_open_sdl_files, - "webview": mock_open_webview_files, - "service_only": mock_open_service_only_files, - "qt": mock_open_qt_files - } + # Qt bootstrap has its own assemble_distribution, others use base class + if self.bootstrap_name == "qt": + mock_open_bs = mock_open_qt_files + else: + mock_open_bs = mock_open_bootstrap_files expected_open_calls = { "sdl2": [ mock.call("local.properties", "w"), @@ -433,11 +427,16 @@ def test_assemble_distribution( mock.call("local.properties", "w"), mock.call("blacklist.txt", "a"), ], - "webview": [mock.call("local.properties", "w")], - "service_only": [mock.call("local.properties", "w")], + "webview": [ + mock.call("local.properties", "w"), + mock.call("blacklist.txt", "a"), + ], + "service_only": [ + mock.call("local.properties", "w"), + mock.call("blacklist.txt", "a"), + ], "qt": [mock.call("local.properties", "w")] } - mock_open_bs = mock_open_bootstraps[self.bootstrap_name] # test that the expected calls has been called for expected_call in expected_open_calls[self.bootstrap_name]: self.assertIn(expected_call, mock_open_bs.call_args_list) @@ -446,7 +445,7 @@ def test_assemble_distribution( mock.call().__enter__().write("sdk.dir=/opt/android/android-sdk"), mock_open_bs.mock_calls, ) - if self.bootstrap_name in ["sdl2", "sdl3"]: + if self.bootstrap_name in ["sdl2", "sdl3", "webview", "service_only"]: self.assertIn( mock.call() .__enter__() @@ -455,7 +454,7 @@ def test_assemble_distribution( ) # check that the other mocks we made are actually called - mock_sh_cp.assert_called() + mock_shprint.assert_called() mock_chdir.assert_called() mock_listdir.assert_called() mock_strip_libraries.assert_called()