diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index a5b63407a..ed51e420c 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -1,4 +1,8 @@ on: [push, pull_request] + +env: + PYTHONFORANDROID_PREREQUISITES_INSTALL_INTERACTIVE: 0 + name: Android jobs: Integration: diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index e4a9853f7..d45f13c2e 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -3,9 +3,6 @@ name: iOS jobs: Integration: name: "Integration (${{ matrix.runs_on }}, ${{ matrix.python }})" - defaults: - run: - shell: ${{ matrix.run_wrapper || 'bash --noprofile --norc -eo pipefail {0}' }} runs-on: ${{ matrix.runs_on || 'macos-latest' }} strategy: matrix: @@ -13,7 +10,6 @@ jobs: - runs_on: macos-latest python: '3.9' - runs_on: apple-silicon-m1 - run_wrapper: arch -arm64 bash --noprofile --norc -eo pipefail {0} python: '3.9.7' steps: - name: Setup python diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f87e88f3..c14aaaf66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,58 @@ # Change Log +## [1.4.0](https://github.com/kivy/buildozer/tree/1.4.0) (2022-07-20) + +[Full Changelog](https://github.com/kivy/buildozer/compare/1.3.0...1.4.0) + +**Closed issues:** + +- error [\#1461](https://github.com/kivy/buildozer/issues/1461) +- error [\#1460](https://github.com/kivy/buildozer/issues/1460) +- ModuleNotFoundError: No module named '\_bz2' [\#1457](https://github.com/kivy/buildozer/issues/1457) +- `java.nio.file.NoSuchFileException` [\#1456](https://github.com/kivy/buildozer/issues/1456) +- kivy app doesnt start on android [\#1455](https://github.com/kivy/buildozer/issues/1455) +- \(WSL\) buildozer adb doesn't recognize usb [\#1453](https://github.com/kivy/buildozer/issues/1453) +- Error: Command \[...\] returned non-zero exit status 1 [\#1452](https://github.com/kivy/buildozer/issues/1452) +- Command failed: /home/username/.buildozer/android/platform/android-sdk/tools/bin/sdkmanager --sdk\_root=/home/username/.buildozer/android/platform/android-sdk platform-tools [\#1449](https://github.com/kivy/buildozer/issues/1449) +- gradlew assembly Debug execution error when building apk [\#1447](https://github.com/kivy/buildozer/issues/1447) +- How to run Python script in background in android? [\#1446](https://github.com/kivy/buildozer/issues/1446) +- buildozer appclean doesnt work. [\#1443](https://github.com/kivy/buildozer/issues/1443) +- Flags android.gradle\_dependencies && android.add\_jars don't work. [\#1442](https://github.com/kivy/buildozer/issues/1442) +- ModuleNotFoundError: No module named 'PIL' [\#1440](https://github.com/kivy/buildozer/issues/1440) +- Bulldozer error while running "buildozer -v android debug" [\#1439](https://github.com/kivy/buildozer/issues/1439) +- started app on three different andoid phones - there is no internet connection on 2 of them [\#1434](https://github.com/kivy/buildozer/issues/1434) +- Not Able to change the python version under buildozer 1.3.0 [\#1432](https://github.com/kivy/buildozer/issues/1432) +- ndk r22 invalid [\#1431](https://github.com/kivy/buildozer/issues/1431) +- error building numpy with bulldozer [\#1426](https://github.com/kivy/buildozer/issues/1426) +- sh.CommandNotFound: cmake [\#1424](https://github.com/kivy/buildozer/issues/1424) +- Compile-time name 'JNIUS\_PYTHON3' not defined [\#1422](https://github.com/kivy/buildozer/issues/1422) +- Error Building hostpython3 for arm64-v8a with buildozer [\#1421](https://github.com/kivy/buildozer/issues/1421) +- building for android failed [\#1420](https://github.com/kivy/buildozer/issues/1420) +- Error "Aidl not found, please install it." [\#1416](https://github.com/kivy/buildozer/issues/1416) +- Buildozer Release Signign Issue [\#1415](https://github.com/kivy/buildozer/issues/1415) +- Unable to run the buildozer command on initial application [\#1414](https://github.com/kivy/buildozer/issues/1414) +- buildozer -v android debug error,please help me,thank you! [\#1413](https://github.com/kivy/buildozer/issues/1413) +- Buildozer failed to create android app [\#1412](https://github.com/kivy/buildozer/issues/1412) +- module encodings found error or something else [\#1408](https://github.com/kivy/buildozer/issues/1408) +- \[ERROR\]: Build failed: Requested API target 27 is not available, install it with the SDK android tool. [\#1404](https://github.com/kivy/buildozer/issues/1404) +- buildozer requirements for firebase-admin Python? [\#1402](https://github.com/kivy/buildozer/issues/1402) +- I am given the following error, which I believe is due to recent changes for aab support in buildozer: " This buildozer version requires a python-for-android version with AAB \(Android App Bundle\) support. Please update your pinned version accordingly [\#1401](https://github.com/kivy/buildozer/issues/1401) +- How to mention Python modules used in Kivy buildozer.spec file? [\#1400](https://github.com/kivy/buildozer/issues/1400) +- Приложение вылетает при запуске [\#1399](https://github.com/kivy/buildozer/issues/1399) +- Buildozer deploy failed with python3 -m venv venv command [\#1389](https://github.com/kivy/buildozer/issues/1389) +- Gradlew Build error for Android on Linux [\#1371](https://github.com/kivy/buildozer/issues/1371) +- AAB [\#1353](https://github.com/kivy/buildozer/issues/1353) +- Error creating apk on Macos Big Sur [\#1345](https://github.com/kivy/buildozer/issues/1345) +- BUILD FAILED [\#1335](https://github.com/kivy/buildozer/issues/1335) + +**Merged pull requests:** + +- Updates default buildozer.spec NDK from 19b to 23b [\#1462](https://github.com/kivy/buildozer/pull/1462) ([misl6](https://github.com/misl6)) +- use p4a --add-source instead of manual copy of java files [\#1450](https://github.com/kivy/buildozer/pull/1450) ([tito](https://github.com/tito)) +- fix aar build [\#1444](https://github.com/kivy/buildozer/pull/1444) ([mzakharo](https://github.com/mzakharo)) +- Our self-hosted Apple Silicon runner now has been migrated to actions/runner v2.292.0 which now supports arm64 natively [\#1438](https://github.com/kivy/buildozer/pull/1438) ([misl6](https://github.com/misl6)) +- Changes for NDK23 [\#1427](https://github.com/kivy/buildozer/pull/1427) ([HyTurtle](https://github.com/HyTurtle)) +- Bump version to 1.4.0.dev0 [\#1411](https://github.com/kivy/buildozer/pull/1411) ([misl6](https://github.com/misl6)) ## [1.3.0](https://github.com/kivy/buildozer/tree/1.3.0) (2022-03-13) [Full Changelog](https://github.com/kivy/buildozer/compare/1.2.0...1.3.0) diff --git a/buildozer/__init__.py b/buildozer/__init__.py index b9814629c..7a7b15022 100644 --- a/buildozer/__init__.py +++ b/buildozer/__init__.py @@ -6,7 +6,7 @@ ''' -__version__ = '1.4.0.dev0' +__version__ = '1.4.1.dev0' import os import re @@ -26,6 +26,8 @@ from fnmatch import fnmatch from pprint import pformat +import shlex +import pexpect from urllib.request import FancyURLopener from configparser import ConfigParser @@ -157,7 +159,7 @@ def set_target(self, target): def prepare_for_build(self): '''Prepare the build. ''' - assert(self.target is not None) + assert self.target is not None if hasattr(self.target, '_build_prepared'): return @@ -188,8 +190,8 @@ def build(self): (:meth:`prepare_for_build` must have been call before.) ''' - assert(self.target is not None) - assert(hasattr(self.target, '_build_prepared')) + assert self.target is not None + assert hasattr(self.target, '_build_prepared') if hasattr(self.target, '_build_done'): return @@ -261,7 +263,6 @@ def cmd(self, command, **kwargs): kwargs.setdefault('stdout', PIPE) kwargs.setdefault('stderr', PIPE) kwargs.setdefault('close_fds', True) - kwargs.setdefault('shell', True) kwargs.setdefault('show_output', self.log_level > 1) show_output = kwargs.pop('show_output') @@ -357,7 +358,6 @@ def cmd(self, command, **kwargs): process.returncode) def cmd_expect(self, command, **kwargs): - from pexpect import spawnu # prepare the environ, based on the system + our own env env = environ.copy() @@ -378,7 +378,7 @@ def cmd_expect(self, command, **kwargs): self.debug('Run (expect) {0!r} ...'.format(command.split()[0])) self.debug('Cwd {}'.format(kwargs.get('cwd'))) - return spawnu(command, **kwargs) + return pexpect.spawnu(shlex.join(command), **kwargs) def check_configuration_tokens(self): '''Ensure the spec file is 'correct'. @@ -515,9 +515,11 @@ def check_application_requirements(self): def _install_application_requirement(self, module): self._ensure_virtualenv() self.debug('Install requirement {} in virtualenv'.format(module)) - self.cmd('pip install --target={} {}'.format(self.applibs_dir, module), - env=self.env_venv, - cwd=self.buildozer_dir) + self.cmd( + ["pip", "install", f"--target={self.applibs_dir}", module], + env=self.env_venv, + cwd=self.buildozer_dir, + ) def check_garden_requirements(self): garden_requirements = self.config.getlist('app', @@ -530,13 +532,15 @@ def _ensure_virtualenv(self): return self.venv = join(self.buildozer_dir, 'venv') if not self.file_exists(self.venv): - self.cmd('python3 -m venv ./venv', + self.cmd(["python3", "-m", "venv", "./venv"], cwd=self.buildozer_dir) # read virtualenv output and parse it - output = self.cmd('bash -c "source venv/bin/activate && env"', - get_stdout=True, - cwd=self.buildozer_dir) + output = self.cmd( + ["bash", "-c", "source venv/bin/activate && env"], + get_stdout=True, + cwd=self.buildozer_dir, + ) self.env_venv = copy(self.environ) for line in output[0].splitlines(): args = line.split('=', 1) @@ -594,22 +598,22 @@ def file_copy(self, source, target, cwd=None): def file_extract(self, archive, cwd=None): if archive.endswith('.tgz') or archive.endswith('.tar.gz'): - self.cmd('tar xzf {0}'.format(archive), cwd=cwd) + self.cmd(["tar", "xzf", archive], cwd=cwd) return if archive.endswith('.tbz2') or archive.endswith('.tar.bz2'): # XXX same as before - self.cmd('tar xjf {0}'.format(archive), cwd=cwd) + self.cmd(["tar", "xjf", archive], cwd=cwd) return if archive.endswith('.bin'): # To process the bin files for linux and darwin systems - self.cmd('chmod a+x {0}'.format(archive), cwd=cwd) - self.cmd('./{0}'.format(archive), cwd=cwd) + self.cmd(["chmod", "a+x", archive], cwd=cwd) + self.cmd([f"./{archive}"], cwd=cwd) return if archive.endswith('.zip'): - self.cmd('unzip -q {}'.format(join(cwd, archive)), cwd=cwd) + self.cmd(["unzip", "-q", join(cwd, archive)], cwd=cwd) return raise Exception('Unhandled extraction for type {0}'.format(archive)) diff --git a/buildozer/default.spec b/buildozer/default.spec index ce2eca232..98fda4903 100644 --- a/buildozer/default.spec +++ b/buildozer/default.spec @@ -108,7 +108,7 @@ fullscreen = 0 #android.sdk = 20 # (str) Android NDK version to use -#android.ndk = 19b +#android.ndk = 23b # (int) Android NDK API to use. This is the minimum API your app will support, it should usually match android.minapi. #android.ndk_api = 21 @@ -228,6 +228,9 @@ fullscreen = 0 # (str) XML file to include as an intent filters in tag #android.manifest.intent_filters = +# (list) Copy these files to src/main/res/xml/ (used for example with intent-filters) +#android.res_xml = PATH_TO_FILE, + # (str) launchMode to set for the main activity #android.manifest.launch_mode = standard @@ -287,9 +290,12 @@ android.allow_backup = True # (bool) disables the compilation of py to pyc/pyo files when packaging # android.no-compile-pyo = True -# (str) The format used to package the app for release mode (aab or apk). +# (str) The format used to package the app for release mode (aab or apk or aar). # android.release_artifact = aab +# (str) The format used to package the app for debug mode (apk or aar). +# android.debug_artifact = apk + # # Python for android (p4a) specific # @@ -332,6 +338,7 @@ android.allow_backup = True #p4a.extra_args = + # # iOS specific # diff --git a/buildozer/target.py b/buildozer/target.py index b9515f61f..df09485b3 100644 --- a/buildozer/target.py +++ b/buildozer/target.py @@ -102,7 +102,7 @@ def cmd_update(self, *args): def cmd_debug(self, *args): self.buildozer.prepare_for_build() self.build_mode = 'debug' - self.artifact_format = 'apk' + self.artifact_format = self.buildozer.config.getdefault('app', 'android.debug_artifact', 'apk') self.buildozer.build() def cmd_release(self, *args): @@ -228,8 +228,6 @@ def path_or_git_url(self, repo, owner='kivy', branch='master', branch = config.getdefault('app', '{}_branch'.format(key), branch) default_url = url_format.format(owner=owner, repo=repo, branch=branch) url = config.getdefault('app', '{}_url'.format(key), default_url) - if branch != 'master': - url = "--branch {} {}".format(branch, url) return path, url, branch def install_or_update_repo(self, repo, **kwargs): @@ -252,15 +250,14 @@ def install_or_update_repo(self, repo, **kwargs): custom_dir, clone_url, clone_branch = self.path_or_git_url(repo, **kwargs) if not self.buildozer.file_exists(install_dir): if custom_dir: - cmd('mkdir -p "{}"'.format(install_dir)) - cmd('cp -a "{}"/* "{}"/'.format(custom_dir, install_dir)) + cmd(["mkdir", "-p", install_dir]) + cmd(["cp", "-a", f"{custom_dir}/*", f"{install_dir}/"]) else: - cmd('git clone {}'.format(clone_url), - cwd=self.buildozer.platform_dir) + cmd(["git", "clone", "--branch", clone_branch, clone_url], cwd=self.buildozer.platform_dir) elif self.platform_update: if custom_dir: - cmd('cp -a "{}"/* "{}"/'.format(custom_dir, install_dir)) + cmd(["cp", "-a", f"{custom_dir}/*", f"{install_dir}/"]) else: - cmd('git clean -dxf', cwd=install_dir) - cmd('git pull origin {}'.format(clone_branch), cwd=install_dir) + cmd(["git", "clean", "-dxf"], cwd=install_dir) + cmd(["git", "pull", "origin", clone_branch], cwd=install_dir) return install_dir diff --git a/buildozer/targets/android.py b/buildozer/targets/android.py index fe6826126..d8e40c816 100644 --- a/buildozer/targets/android.py +++ b/buildozer/targets/android.py @@ -24,14 +24,15 @@ import io import re import ast -from pipes import quote from sys import platform, executable from buildozer import BuildozerException, USE_COLOR from buildozer.target import Target from os import environ from os.path import exists, join, realpath, expanduser, basename, relpath from platform import architecture -from shutil import copyfile, rmtree +from shutil import copyfile, rmtree, which +import shlex +import pexpect from glob import glob from time import sleep @@ -81,44 +82,41 @@ def __init__(self, *args, **kwargs): self._build_dir = join( self.buildozer.platform_dir, 'build-{}'.format(self.archs_snake)) executable = sys.executable or 'python' - self._p4a_cmd = '{} -m pythonforandroid.toolchain '.format(executable) + self._p4a_cmd = [executable, "-m", "pythonforandroid.toolchain"] self._p4a_bootstrap = self.buildozer.config.getdefault( 'app', 'p4a.bootstrap', 'sdl2') color = 'always' if USE_COLOR else 'never' - self.extra_p4a_args = ' --color={} --storage-dir="{}"'.format( - color, self._build_dir) + self.extra_p4a_args = [f"--color={color}", f"--storage-dir={self._build_dir}"] # minapi should match ndk-api, so can use the same default if # nothing is specified ndk_api = self.buildozer.config.getdefault( 'app', 'android.ndk_api', self.android_minapi) - self.extra_p4a_args += ' --ndk-api={}'.format(ndk_api) + self.extra_p4a_args.append(f"--ndk-api={ndk_api}") hook = self.buildozer.config.getdefault("app", "p4a.hook", None) if hook is not None: - self.extra_p4a_args += ' --hook={}'.format(realpath(expanduser(hook))) + self.extra_p4a_args.append(f"--hook={realpath(expanduser(hook))}") port = self.buildozer.config.getdefault('app', 'p4a.port', None) if port is not None: - self.extra_p4a_args += ' --port={}'.format(port) + self.extra_p4a_args.append(f"--port={port}") setup_py = self.buildozer.config.getdefault('app', 'p4a.setup_py', False) if setup_py: - self.extra_p4a_args += ' --use-setup-py' + self.extra_p4a_args.append("--use-setup-py") else: - self.extra_p4a_args += ' --ignore-setup-py' + self.extra_p4a_args.append("--ignore-setup-py") activity_class_name = self.buildozer.config.getdefault( 'app', 'android.activity_class_name', 'org.kivy.android.PythonActivity') if activity_class_name != 'org.kivy.android.PythonActivity': - self.extra_p4a_args += ' --activity-class-name={}'.format(activity_class_name) + self.extra_p4a_args.append(f"--activity-class-name={activity_class_name}") if self.buildozer.log_level >= 2: - self.extra_p4a_args += ' --debug' + self.extra_p4a_args.append("--debug") - user_extra_p4a_args = self.buildozer.config.getdefault('app', 'p4a.extra_args', - None) - if user_extra_p4a_args: - self.extra_p4a_args += ' ' + user_extra_p4a_args + user_extra_p4a_args = self.buildozer.config.getdefault('app', 'p4a.extra_args', "") + self.extra_p4a_args.extend(shlex.split(user_extra_p4a_args)) self.warn_on_deprecated_tokens() @@ -132,7 +130,7 @@ def warn_on_deprecated_tokens(self): def _p4a(self, cmd, **kwargs): kwargs.setdefault('cwd', self.p4a_dir) - return self.buildozer.cmd(self._p4a_cmd + cmd + self.extra_p4a_args, **kwargs) + return self.buildozer.cmd([*self._p4a_cmd, *cmd, *self.extra_p4a_args], **kwargs) @property def p4a_dir(self): @@ -189,11 +187,9 @@ def _sdkmanager(self, *args, **kwargs): # Use the android-sdk dir as cwd by default android_sdk_dir = self.android_sdk_dir kwargs['cwd'] = kwargs.get('cwd', android_sdk_dir) - sdkmanager_path = self.sdkmanager_path - sdk_root = f"--sdk_root={android_sdk_dir}" - command = f"{sdkmanager_path} {sdk_root} " + ' '.join(args) - return_child = kwargs.pop('return_child', False) - if return_child: + command = [self.sdkmanager_path, f"--sdk_root={android_sdk_dir}", *args] + + if kwargs.pop('return_child', False): return self.buildozer.cmd_expect(command, **kwargs) else: kwargs['get_stdout'] = kwargs.get('get_stdout', True) @@ -265,20 +261,18 @@ def check_requirements(self): self._set_win32_java_home() except: traceback.print_exc() - self.adb_cmd = join(self.android_sdk_dir, 'platform-tools', + self.adb_executable = join(self.android_sdk_dir, 'platform-tools', 'adb.exe') self.javac_cmd = self._locate_java('javac.exe') self.keytool_cmd = self._locate_java('keytool.exe') # darwin, linux else: - self.adb_cmd = join(self.android_sdk_dir, 'platform-tools', 'adb') + self.adb_executable = join(self.android_sdk_dir, 'platform-tools', 'adb') self.javac_cmd = self._locate_java('javac') self.keytool_cmd = self._locate_java('keytool') # Check for C header . - _, _, returncode_dpkg = self.buildozer.cmd('dpkg --version', - break_on_error=False) - is_debian_like = (returncode_dpkg == 0) + is_debian_like = which("dpkg") is not None if is_debian_like and \ not self.buildozer.file_exists('/usr/include/zlib.h'): raise BuildozerException( @@ -287,9 +281,8 @@ def check_requirements(self): # Adb arguments: adb_args = self.buildozer.config.getdefault( - "app", "android.adb_args", None) - if adb_args is not None: - self.adb_cmd += ' ' + adb_args + "app", "android.adb_args", "") + self.adb_args = shlex.split(adb_args) # Need to add internally installed ant to path for external tools # like adb to use @@ -327,7 +320,7 @@ def check_configuration_tokens(self): super().check_configuration_tokens(errors) def _p4a_have_aab_support(self): - returncode = self._p4a("aab -h", break_on_error=False, show_output=False)[2] + returncode = self._p4a(["aab", "-h"], break_on_error=False)[2] if returncode == 0: return True else: @@ -474,12 +467,13 @@ def _install_android_ndk(self): ext = 'tar.bz2' else: ext = 'zip' - archive = 'android-ndk-r{0}-' + _platform + '-{1}.' + ext + archive = 'android-ndk-r{0}-' + _platform + '{1}.' + ext is_64 = (os.uname()[4] == 'x86_64') else: raise SystemError('Unsupported platform: {}'.format(platform)) architecture = 'x86_64' if is_64 else 'x86' + architecture = '' if _version >= 23 else f'-{architecture}' unpacked = 'android-ndk-r{0}' archive = archive.format(self.android_ndk_version, architecture) unpacked = unpacked.format(self.android_ndk_version) @@ -560,19 +554,19 @@ def _android_update_sdk(self, *sdkmanager_commands): kwargs = {} if auto_accept_license: - # `SIGPIPE` is not being reported somehow, but `EPIPE` is. - # This leads to a stderr "Broken pipe" message which is harmless, - # but doesn't look good on terminal, hence redirecting to /dev/null - yes_command = 'yes 2>/dev/null' - android_sdk_dir = self.android_sdk_dir - sdkmanager_path = self.sdkmanager_path - sdk_root = f"--sdk_root={android_sdk_dir}" - command = f"{yes_command} | {sdkmanager_path} {sdk_root} --licenses" - self.buildozer.cmd(command, cwd=self.android_sdk_dir) + kwargs["return_child"] = True else: kwargs['show_output'] = True - self._sdkmanager(*sdkmanager_commands, **kwargs) + ret_child = self._sdkmanager(*sdkmanager_commands, **kwargs) + + if auto_accept_license: + while ret_child.isalive(): + pexp_match = ret_child.expect( + ["(y/N)", pexpect.EOF, pexpect.TIMEOUT], timeout=300 + ) + if pexp_match == 0: + ret_child.sendline("y") def _read_version_subdir(self, *args): versions = [] @@ -642,8 +636,7 @@ def _install_android_packages(self): latest_v_build_tools = sorted(available_v_build_tools)[-1] if latest_v_build_tools > installed_v_build_tools: if not skip_upd: - self._android_update_sdk( - '"build-tools;{}"'.format(latest_v_build_tools)) + self._android_update_sdk(f"build-tools;{latest_v_build_tools}") installed_v_build_tools = latest_v_build_tools else: self.buildozer.info( @@ -658,7 +651,7 @@ def _install_android_packages(self): android_platform = join(self.android_sdk_dir, 'platforms', 'android-{}'.format(self.android_api)) if not self.buildozer.file_exists(android_platform): if not skip_upd: - self._sdkmanager('"platforms;android-{}"'.format(self.android_api)) + self._sdkmanager(f"platforms;android-{self.android_api}") else: self.buildozer.info( 'Skipping install API {} platform tools due to spec setting'.format( @@ -749,12 +742,12 @@ def _install_p4a(self): # check that url/branch has not been changed if self.buildozer.file_exists(p4a_dir): cur_url = cmd( - 'git config --get remote.origin.url', + ["git", "config", "--get", "remote.origin.url"], get_stdout=True, cwd=p4a_dir, )[0].strip() cur_branch = cmd( - 'git branch -vv', get_stdout=True, cwd=p4a_dir + ["git", "branch", "-vv"], get_stdout=True, cwd=p4a_dir )[0].split()[1] if any([cur_url != p4a_url, cur_branch != p4a_branch]): self.buildozer.info( @@ -764,28 +757,29 @@ def _install_p4a(self): if not self.buildozer.file_exists(p4a_dir): cmd( - ( - 'git clone -b {p4a_branch} --single-branch ' - '{p4a_url} {p4a_dir}' - ).format( - p4a_branch=p4a_branch, - p4a_url=p4a_url, - p4a_dir=self.p4a_directory_name, - ), + [ + "git", + "clone", + "-b", + p4a_branch, + "--single-branch", + p4a_url, + self.p4a_directory_name, + ], cwd=self.buildozer.platform_dir, ) elif self.platform_update: - cmd('git clean -dxf', cwd=p4a_dir) - current_branch = cmd('git rev-parse --abbrev-ref HEAD', + cmd(["git", "clean", "-dxf"], cwd=p4a_dir) + current_branch = cmd(["git", "rev-parse", "--abbrev-ref", "HEAD"], get_stdout=True, cwd=p4a_dir)[0].strip() if current_branch == p4a_branch: - cmd('git pull', cwd=p4a_dir) + cmd(["git", "pull"], cwd=p4a_dir) else: - cmd('git fetch --tags origin {0}:{0}'.format(p4a_branch), + cmd(["git", "fetch", "--tags", "origin", "{0}:{0}".format(p4a_branch)], cwd=p4a_dir) - cmd('git checkout {}'.format(p4a_branch), cwd=p4a_dir) + cmd(["git", "checkout", p4a_branch], cwd=p4a_dir) if p4a_commit != 'HEAD': - cmd('git reset --hard {}'.format(p4a_commit), cwd=p4a_dir) + cmd(["git", "reset", "--hard", p4a_commit], cwd=p4a_dir) # also install dependencies (currently, only setup.py knows about it) # let's extract them. @@ -798,15 +792,12 @@ def _install_p4a(self): self.buildozer.error('Failed to read python-for-android setup.py at {}'.format( join(self.p4a_dir, 'setup.py'))) sys.exit(1) - pip_deps = [] - for dep in deps: - pip_deps.append("'{}'".format(dep)) # in virtualenv or conda env - options = "--user" + options = ["--user"] if "VIRTUAL_ENV" in os.environ or "CONDA_PREFIX" in os.environ: - options = "" - cmd('{} -m pip install -q {} {}'.format(executable, options, " ".join(pip_deps))) + options = [] + cmd([executable, "-m", "pip", "install", "-q", *options, *deps]) def compile_platform(self): app_requirements = self.buildozer.config.getlist( @@ -834,12 +825,12 @@ def compile_platform(self): options.append('--local-recipes') options.append(local_recipes) - p4a_create = "create --dist_name={} --bootstrap={} --requirements={} ".format(dist_name, self._p4a_bootstrap, requirements) + p4a_create = ["create", f"--dist_name={dist_name}", f"--bootstrap={self._p4a_bootstrap}", f"--requirements={requirements}"] for arch in self._archs: - p4a_create += "--arch {} ".format(arch) + p4a_create.append(f"--arch={arch}") - p4a_create += " ".join(options) + p4a_create.extend(options) self._p4a(p4a_create, get_stdout=True)[0] @@ -890,7 +881,7 @@ def execute_build_package(self, build_cmd): presplash_color = self.buildozer.config.getdefault('app', 'android.presplash_color', None) if presplash_color: cmd.append('--presplash-color') - cmd.append("'{}'".format(presplash_color)) + cmd.append("{}".format(presplash_color)) # support for services services = self.buildozer.config.getlist('app', 'services', []) @@ -917,6 +908,12 @@ def execute_build_package(self, build_cmd): cmd.append('--blacklist') cmd.append(realpath(expanduser(blacklist_src))) + # support for java directory + javadirs = self.buildozer.config.getlist('app', 'android.add_src', []) + for javadir in javadirs: + cmd.append('--add-source') + cmd.append(realpath(expanduser(javadir))) + # support for aars aars = self.buildozer.config.getlist('app', 'android.add_aars', []) for aar in aars: @@ -993,12 +990,11 @@ def execute_build_package(self, build_cmd): cmd.append('--arch') cmd.append(arch) - cmd = " ".join(cmd) self._p4a(cmd) def get_release_mode(self): # aab, also if unsigned is named as *-release - if self.check_p4a_sign_env() or self.artifact_format == "aab": + if self.check_p4a_sign_env() or self.artifact_format in ["aab", "aar"]: return "release" return "release-unsigned" @@ -1033,11 +1029,19 @@ def cmd_run(self, *args): self.buildozer.environ['ANDROID_SERIAL'] = serial self.buildozer.info('Run on {}'.format(serial)) self.buildozer.cmd( - '{adb} shell am start -n {package}/{entry} -a {entry}'.format( - adb=self.adb_cmd, - package=package, - entry=entrypoint), - cwd=self.buildozer.global_platform_dir) + [ + self.adb_executable, + *self.adb_args, + "shell", + "am", + "start", + "-n", + f"{package}/{entrypoint}", + "-a", + entrypoint, + ], + cwd=self.buildozer.global_platform_dir, + ) self.buildozer.environ.pop('ANDROID_SERIAL', None) while True: @@ -1062,14 +1066,14 @@ def cmd_p4a(self, *args): .format(self.targetname)) sys.stderr.write('PYTHONPATH={} {}\n'.format(self.p4a_dir, self._p4a_cmd)) else: - self._p4a(' '.join(args) if args else '') + self._p4a(args) def cmd_clean(self, *args): ''' Clean the build and distribution ''' - self._p4a("clean_builds") - self._p4a("clean_dists") + self._p4a(["clean_builds"]) + self._p4a(["clean_dists"]) def _get_package(self): config = self.buildozer.config @@ -1118,15 +1122,12 @@ def build_package(self): # update the project.properties libraries references self._update_libraries_references(dist_dir) - # add src files - self._add_java_src(dist_dir) - # generate the whitelist if needed self._generate_whitelist(dist_dir) # build the app build_cmd = [ - ("--name", quote(config.get('app', 'title'))), + ("--name", config.get('app', 'title')), ("--version", version), ("--package", package), ("--minsdk", config.getdefault('app', 'android.minapi', @@ -1155,6 +1156,12 @@ def build_package(self): for feature in features: build_cmd += [("--feature", feature)] + # add res_xml + xmlfiles = config.getlist('app', 'android.res_xml', []) + for xmlfile in xmlfiles: + build_cmd += [("--res_xml", join(self.buildozer.root_dir, + xmlfile))] + # android.entrypoint entrypoint = config.getdefault('app', 'android.entrypoint', 'org.kivy.android.PythonActivity') build_cmd += [('--android-entrypoint', entrypoint)] @@ -1328,6 +1335,9 @@ def build_package(self): artifact_dir = join(dist_dir, "build", "outputs", "apk", mode_sign) elif self.artifact_format == "aab": artifact_dir = join(dist_dir, "build", "outputs", "bundle", mode_sign) + elif self.artifact_format == "aar": + artifact_dir = join(dist_dir, "build", "outputs", "aar") + else: # on ant, the apk use the title, and have version bl = u'\'" ,' @@ -1405,26 +1415,6 @@ def _update_libraries_references(self, dist_dir): self.buildozer.debug('project.properties updated') - def _add_java_src(self, dist_dir): - java_src = self.buildozer.config.getlist('app', 'android.add_src', []) - - gradle_files = ["build.gradle", "gradle", "gradlew"] - is_gradle_build = any(( - exists(join(dist_dir, x)) for x in gradle_files)) - if is_gradle_build: - src_dir = join(dist_dir, "src", "main", "java") - self.buildozer.info( - "Gradle project detected, copy files {}".format(src_dir)) - else: - src_dir = join(dist_dir, 'src') - self.buildozer.info( - "Ant project detected, copy files in {}".format(src_dir)) - - for pattern in java_src: - for fn in glob(expanduser(pattern.strip())): - last_component = basename(fn) - self.buildozer.file_copytree(fn, join(src_dir, last_component)) - @property def serials(self): if hasattr(self, '_serials'): @@ -1432,8 +1422,9 @@ def serials(self): serial = environ.get('ANDROID_SERIAL') if serial: return serial.split(',') - lines = self.buildozer.cmd('{} devices'.format(self.adb_cmd), - get_stdout=True)[0].splitlines() + lines = self.buildozer.cmd( + [self.adb_executable, *self.adb_args, "devices"], get_stdout=True + )[0].splitlines() serials = [] for serial in lines: if not serial: @@ -1457,9 +1448,9 @@ def cmd_adb(self, *args): print('To set up ADB in this shell session, execute:') print(' alias adb=$(buildozer {} adb --alias 2>&1 >/dev/null)' .format(self.targetname)) - sys.stderr.write(self.adb_cmd + '\n') + sys.stderr.write(self.adb_executable + '\n') else: - self.buildozer.cmd(' '.join([self.adb_cmd] + args)) + self.buildozer.cmd([self.adb_executable, *self.adb_args, *args]) def cmd_deploy(self, *args): super().cmd_deploy(*args) @@ -1482,16 +1473,23 @@ def cmd_deploy(self, *args): for serial in self.serials: self.buildozer.environ['ANDROID_SERIAL'] = serial self.buildozer.info('Deploy on {}'.format(serial)) - self.buildozer.cmd('{0} install -r "{1}"'.format( - self.adb_cmd, full_apk), - cwd=self.buildozer.global_platform_dir) + self.buildozer.cmd( + [self.adb_executable, *self.adb_args, "install", "-r", full_apk], + cwd=self.buildozer.global_platform_dir, + ) self.buildozer.environ.pop('ANDROID_SERIAL', None) self.buildozer.info('Application pushed.') def _get_pid(self): pid, *_ = self.buildozer.cmd( - f'{self.adb_cmd} shell pidof {self._get_package()}', + [ + self.adb_executable, + *self.adb_args, + "shell", + "pidof", + self._get_package(), + ], get_stdout=True, show_output=False, break_on_error=False, @@ -1520,7 +1518,7 @@ def cmd_logcat(self, *args): extra_args.extend(('--pid', pid)) self.buildozer.cmd( - f"{self.adb_cmd} logcat {filters} {' '.join(extra_args)}", + [self.adb_executable, *self.adb_args, "logcat", filters, *extra_args], cwd=self.buildozer.global_platform_dir, show_output=True, run_condition=self._get_pid if pid else None, diff --git a/buildozer/targets/ios.py b/buildozer/targets/ios.py index e2d0b73b9..245f0ccc0 100644 --- a/buildozer/targets/ios.py +++ b/buildozer/targets/ios.py @@ -63,8 +63,8 @@ class TargetIos(Target): def __init__(self, buildozer): super().__init__(buildozer) executable = sys.executable or 'python' - self._toolchain_cmd = f"{executable} toolchain.py " - self._xcodebuild_cmd = "xcodebuild " + self._toolchain_cmd = [executable, "toolchain.py"] + self._xcodebuild_cmd = ["xcodebuild"] # set via install_platform() self.ios_dir = None self.ios_deploy_dir = None @@ -87,7 +87,7 @@ def check_requirements(self): self.buildozer.debug('Check availability of a iPhone SDK') sdk = cmd('xcodebuild -showsdks | fgrep "iphoneos" |' 'tail -n 1 | awk \'{print $2}\'', - get_stdout=True)[0] + get_stdout=True, shell=True)[0] if not sdk: raise Exception( 'No iPhone SDK found. Please install at least one iOS SDK.') @@ -95,7 +95,7 @@ def check_requirements(self): self.buildozer.debug(' -> found %r' % sdk) self.buildozer.debug('Check Xcode path') - xcode = cmd('xcode-select -print-path', get_stdout=True)[0] + xcode = cmd(["xcode-select", "-print-path"], get_stdout=True)[0] if not xcode: raise Exception('Unable to get xcode path') self.buildozer.debug(' -> found {0}'.format(xcode)) @@ -113,10 +113,11 @@ def install_platform(self): def toolchain(self, cmd, **kwargs): kwargs.setdefault('cwd', self.ios_dir) - return self.buildozer.cmd(self._toolchain_cmd + cmd, **kwargs) + return self.buildozer.cmd([*self._toolchain_cmd, *cmd], **kwargs) def xcodebuild(self, *args, **kwargs): - return self.buildozer.cmd(self._xcodebuild_cmd + ' '.join(arg for arg in args if arg is not None), **kwargs) + filtered_args = [arg for arg in args if arg is not None] + return self.buildozer.cmd([*self._xcodebuild_cmd, *filtered_args], **kwargs) @property def code_signing_allowed(self): @@ -130,7 +131,7 @@ def code_signing_development_team(self): return f"DEVELOPMENT_TEAM={team}" if team else None def get_available_packages(self): - available_modules = self.toolchain("recipes --compact", get_stdout=True)[0] + available_modules = self.toolchain(["recipes", "--compact"], get_stdout=True)[0] return available_modules.splitlines()[0].split() def load_plist_from_file(self, plist_rfn): @@ -174,8 +175,7 @@ def compile_platform(self): self.buildozer.info('Distribution already compiled, pass.') return - modules_str = ' '.join(ios_requirements) - self.toolchain(f"build {modules_str}") + self.toolchain(["build", *ios_requirements]) if not self.buildozer.file_exists(self.ios_deploy_dir, 'ios-deploy'): self.xcodebuild(cwd=self.ios_deploy_dir) @@ -199,15 +199,15 @@ def build_package(self): 'package.name')) ios_frameworks = self.buildozer.config.getlist('app', 'ios.frameworks', '') - frameworks_cmd = '' + frameworks_cmd = [] for framework in ios_frameworks: - frameworks_cmd += '--add-framework={} '.format(framework) + frameworks_cmd.append(f"--add-framework={framework}") self.app_project_dir = join(self.ios_dir, '{0}-ios'.format(app_name.lower())) if not self.buildozer.file_exists(self.app_project_dir): - cmd = f"create {frameworks_cmd}{app_name} {self.buildozer.app_dir}" + cmd = ["create", *frameworks_cmd, app_name, self.buildozer.app_dir] else: - cmd = f"update {frameworks_cmd}{app_name}-ios" + cmd = ["update", *frameworks_cmd, f"{app_name}-ios"] self.toolchain(cmd) # fix the plist @@ -247,12 +247,14 @@ def build_package(self): mode = self.build_mode.capitalize() self.xcodebuild( - f'-configuration {mode}', + "-configuration", + mode, '-allowProvisioningUpdates', 'ENABLE_BITCODE=NO', self.code_signing_allowed, self.code_signing_development_team, - 'clean build', + 'clean', + 'build', cwd=self.app_project_dir) ios_app_dir = '{app_lower}-ios/build/{mode}-iphoneos/{app_lower}.app'.format( app_lower=app_name.lower(), mode=mode) @@ -271,10 +273,14 @@ def build_package(self): self.buildozer.info('Creating archive...') self.xcodebuild( '-alltargets', - f'-configuration {mode}', - f'-scheme {app_name.lower()}', - f'-archivePath "{xcarchive}"', - '-destination \'generic/platform=iOS\'', + '-configuration', + mode, + '-scheme', + app_name.lower(), + '-archivePath', + xcarchive, + '-destination', + 'generic/platform=iOS', 'archive', 'ENABLE_BITCODE=NO', self.code_signing_allowed, @@ -325,8 +331,9 @@ def cmd_xcode(self, *args): app_name = app_name.lower() ios_dir = ios_dir = join(self.buildozer.platform_dir, 'kivy-ios') - self.buildozer.cmd('open {}.xcodeproj'.format( - app_name), cwd=join(ios_dir, '{}-ios'.format(app_name))) + self.buildozer.cmd( + ["open", f"{app_name}.xcodeproj"], cwd=join(ios_dir, f"{app_name}-ios") + ) def _run_ios_deploy(self, lldb=False): state = self.buildozer.state @@ -343,10 +350,11 @@ def _run_ios_deploy(self, lldb=False): debug_mode = '' self.buildozer.info('Deploy the application') - self.buildozer.cmd('{iosdeploy} {debug_mode} -b {app_dir}'.format( - iosdeploy=join(self.ios_deploy_dir, 'ios-deploy'), - debug_mode=debug_mode, app_dir=ios_app_dir), - cwd=self.ios_dir, show_output=True) + self.buildozer.cmd( + [join(self.ios_deploy_dir, "ios-deploy"), debug_mode, "-b", ios_app_dir], + cwd=self.ios_dir, + show_output=True, + ) def _create_icons(self): icon = self.buildozer.config.getdefault('app', 'icon.filename', '') @@ -357,7 +365,7 @@ def _create_icons(self): self.buildozer.error('Icon {} does not exists'.format(icon_fn)) return - self.toolchain(f"icon {self.app_project_dir} {icon_fn}") + self.toolchain(["icon", self.app_project_dir, icon_fn]) def check_configuration_tokens(self): errors = [] @@ -394,8 +402,9 @@ def cmd_list_identities(self, *args): print(' - {}'.format(x)) def _get_available_identities(self): - output = self.buildozer.cmd('security find-identity -v -p codesigning', - get_stdout=True)[0] + output = self.buildozer.cmd( + ["security", "find-identity", "-v", "-p", "codesigning"], get_stdout=True + )[0] lines = output.splitlines()[:-1] lines = [u'"{}"'.format(x.split('"')[1]) for x in lines] @@ -410,14 +419,17 @@ def _unlock_keychain(self): if not password: # no password available, try to unlock anyway... - error = self.buildozer.cmd('security unlock-keychain -u', + error = self.buildozer.cmd(["security", "unlock-keychain", "-u"], break_on_error=False)[2] if not error: return else: # password available, try to unlock - error = self.buildozer.cmd('security unlock-keychain -p {}'.format( - password), break_on_error=False, sensible=True)[2] + error = self.buildozer.cmd( + ["security", "unlock-keychain", "-p", password], + break_on_error=False, + sensible=True, + )[2] if not error: return @@ -427,8 +439,11 @@ def _unlock_keychain(self): while attempt: attempt -= 1 password = getpass('Password to unlock the default keychain:') - error = self.buildozer.cmd('security unlock-keychain -p "{}"'.format( - password), break_on_error=False, sensible=True)[2] + error = self.buildozer.cmd( + ["security", "unlock-keychain", "-p", password], + break_on_error=False, + sensible=True, + )[2] if not error: correct = True break diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 3f1b2af51..4fb667edd 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -14,49 +14,34 @@ First, install the buildozer project with:: Targeting Android ----------------- -Android on Ubuntu 20.04 (64bit) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Android on Ubuntu 20.04 and 22.04 (64bit) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (expected to work as well in later version, but only regularly tested in the latest LTS) :: sudo apt update - sudo apt install -y git zip unzip openjdk-13-jdk python3-pip autoconf libtool pkg-config zlib1g-dev libncurses5-dev libncursesw5-dev libtinfo5 cmake libffi-dev libssl-dev + sudo apt install -y git zip unzip openjdk-17-jdk python3-pip autoconf libtool pkg-config zlib1g-dev libncurses5-dev libncursesw5-dev libtinfo5 cmake libffi-dev libssl-dev pip3 install --user --upgrade Cython==0.29.19 virtualenv # the --user should be removed if you do this in a venv # add the following line at the end of your ~/.bashrc file export PATH=$PATH:~/.local/bin/ + +If openjdk-17 is not compatible with other installed programs, for Buildozer the minimum compatible openjdk version is 11. -Android on Windows 10 -~~~~~~~~~~~~~~~~~~~~~ +Android on Windows 10 or 11 +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To use buildozer in Windows 10 you need first to enable Windows Subsystem for Linux (WSL) and install a Linux distribution: https://docs.microsoft.com/en-us/windows/wsl/install-win10. +To use buildozer in Windows you need first to enable Windows Subsystem for Linux (WSL) and install a Linux distribution: https://docs.microsoft.com/en-us/windows/wsl/install. -These instructions were tested with WSL 1 and Ubuntu 18.04 LTS. +These instructions were tested with WSL 1 and Ubuntu 18.04 LTS, and WSL2 with Ubuntu 20.04 and 22.04. -After installing WSL and Ubuntu in your Windows 10 machine, open Ubuntu and do this: +After installing WSL and Ubuntu on your Windows machine, open Ubuntu, run the commands listed in the previous section, and restart your WSL terminal to enable the path change. -1) Run the commands listed on the previous section (Android in Ubuntu 18.04 (64-bit). -2) Run the following commands: +Copy your Kivy project directory from the Windows partition to the WSL partition, and follow the Quickstart Instructions. **Do not** change to the project directory on the Windows partition and build there, this may give unexpected and obscure fails. -:: - - # Use here the python version you need - sudo apt install -y python3.7-venv - # Create a folder for buildozer. For example: C:\buildozer - mkdir /mnt/c/buildozer - cd /mnt/c/buildozer - python3.7 -m venv venv-buildozer - source venv/bin/activate - python -m pip install --upgrade pip - python -m pip install --upgrade wheel - python -m pip install --upgrade cython - python -m pip install --upgrade virtualenv - python -m pip install --upgrade buildozer - # Restart your WSL terminal to enable the path change - -Windows Subsystem for Linux does not have direct access to USB. Due to this, you need to install the Windows version of ADB (Android Debug Bridge): +For debugging, WSL does not have direct access to USB. Copy the .apk file to the Windows partition and run ADB (Android Debug Bridge) from a Windows prompt. ADB is part of Android Studio, if you do not have this installed you can install just the platform tools which also contain ADB. - Go to https://developer.android.com/studio/releases/platform-tools and click on "Download SDK Platform-Tools for Windows". diff --git a/tests/targets/test_android.py b/tests/targets/test_android.py index 07a2c40a3..024e73057 100644 --- a/tests/targets/test_android.py +++ b/tests/targets/test_android.py @@ -1,7 +1,8 @@ import os import tempfile -from six import StringIO +from io import StringIO from unittest import mock +import sys import pytest @@ -109,26 +110,22 @@ def test_init(self): ".buildozer/android/platform/build-arm64-v8a_armeabi-v7a" ) assert target_android._p4a_bootstrap == "sdl2" - assert target_android._p4a_cmd.endswith( - "python -m pythonforandroid.toolchain " - ) + assert target_android._p4a_cmd == [sys.executable or 'python', "-m", "pythonforandroid.toolchain"] assert target_android.build_mode == "debug" - assert ( - target_android.extra_p4a_args == ( - ' --color=always' - ' --storage-dir="{buildozer_dir}/android/platform/build-arm64-v8a_armeabi-v7a" --ndk-api=21 --ignore-setup-py --debug'.format( - buildozer_dir=buildozer.buildozer_dir) - ) - ) + assert target_android.extra_p4a_args == [ + "--color=always", + f"--storage-dir={buildozer.buildozer_dir}/android/platform/build-arm64-v8a_armeabi-v7a", + "--ndk-api=21", + "--ignore-setup-py", + "--debug", + ] assert target_android.platform_update is False def test_init_positional_buildozer(self): """Positional `buildozer` argument is required.""" with pytest.raises(TypeError) as ex_info: TargetAndroid() - assert ex_info.value.args == ( - "__init__() missing 1 required positional argument: 'buildozer'", - ) + assert ex_info.value.args[-1].endswith("__init__() missing 1 required positional argument: 'buildozer'") def test_sdkmanager(self): """Tests the _sdkmanager() method.""" @@ -154,7 +151,8 @@ def test_check_requirements(self): """Basic tests for the check_requirements() method.""" target_android = init_target(self.temp_dir) buildozer = target_android.buildozer - assert not hasattr(target_android, "adb_cmd") + assert not hasattr(target_android, "adb_executable") + assert not hasattr(target_android, "adb_args") assert not hasattr(target_android, "javac_cmd") assert "PATH" not in buildozer.environ with patch_buildozer_checkbin() as m_checkbin: @@ -165,9 +163,8 @@ def test_check_requirements(self): mock.call("Java compiler (javac)", "javac"), mock.call("Java keytool (keytool)", "keytool"), ] - assert target_android.adb_cmd.endswith( - ".buildozer/android/platform/android-sdk/platform-tools/adb" - ) + assert target_android.adb_executable.endswith(".buildozer/android/platform/android-sdk/platform-tools/adb") + assert target_android.adb_args == [] assert target_android.javac_cmd == "javac" assert target_android.keytool_cmd == "keytool" assert "PATH" in buildozer.environ @@ -223,7 +220,7 @@ def test_build_package(self): assert m_execute_build_package.call_args_list == [ mock.call( [ - ("--name", "'My Application'"), + ("--name", "My Application"), ("--version", "0.1"), ("--package", "org.test.myapp"), ("--minsdk", "21"), @@ -246,14 +243,18 @@ def test_execute_build_package__debug__apk(self): target = TargetAndroid(buildozer) target.execute_build_package([("debug",)]) assert m__p4a.call_args_list == [ - mock.call( - "apk " - "--bootstrap sdl2 " - "--dist_name myapp " - "--copy-libs " - "--arch arm64-v8a " - "--arch armeabi-v7a" - ) + mock.call([ + "apk", + "--bootstrap", + "sdl2", + "--dist_name", + "myapp", + "--copy-libs", + "--arch", + "arm64-v8a", + "--arch", + "armeabi-v7a" + ]) ] def test_execute_build_package__release__apk(self): @@ -264,15 +265,19 @@ def test_execute_build_package__release__apk(self): target = TargetAndroid(buildozer) target.execute_build_package([("release",)]) assert m__p4a.call_args_list == [ - mock.call( - "apk " - "--bootstrap sdl2 " - "--dist_name myapp " - "--release " - "--copy-libs " - "--arch arm64-v8a " - "--arch armeabi-v7a" - ) + mock.call([ + "apk", + "--bootstrap", + "sdl2", + "--dist_name", + "myapp", + "--release", + "--copy-libs", + "--arch", + "arm64-v8a", + "--arch", + "armeabi-v7a" + ]) ] def test_execute_build_package__release__aab(self): @@ -284,15 +289,19 @@ def test_execute_build_package__release__aab(self): target.artifact_format = "aab" target.execute_build_package([("release",)]) assert m__p4a.call_args_list == [ - mock.call( - "aab " - "--bootstrap sdl2 " - "--dist_name myapp " - "--release " - "--copy-libs " - "--arch arm64-v8a " - "--arch armeabi-v7a" - ) + mock.call([ + "aab", + "--bootstrap", + "sdl2", + "--dist_name", + "myapp", + "--release", + "--copy-libs", + "--arch", + "arm64-v8a", + "--arch", + "armeabi-v7a", + ]) ] def test_numeric_version(self): @@ -305,7 +314,7 @@ def test_numeric_version(self): assert m_execute_build_package.call_args_list == [ mock.call( [ - ("--name", "'My Application'"), + ("--name", "My Application"), ("--version", "0.1"), ("--package", "org.test.myapp"), ("--minsdk", "21"), @@ -340,7 +349,7 @@ def test_build_package_intent_filters(self): assert m_execute_build_package.call_args_list == [ mock.call( [ - ('--name', "'My Application'"), + ('--name', "My Application"), ('--version', '0.1'), ('--package', 'org.test.myapp'), ('--minsdk', '21'), @@ -366,7 +375,7 @@ def test_allow_backup(self): assert m_execute_build_package.call_args_list == [ mock.call( [ - ("--name", "'My Application'"), + ("--name", "My Application"), ("--version", "0.1"), ("--package", "org.test.myapp"), ("--minsdk", "21"), @@ -392,7 +401,7 @@ def test_backup_rules(self): assert m_execute_build_package.call_args_list == [ mock.call( [ - ("--name", "'My Application'"), + ("--name", "My Application"), ("--version", "0.1"), ("--package", "org.test.myapp"), ("--minsdk", "21"), @@ -420,7 +429,7 @@ def test_install_platform_p4a_clone_url(self): target_android._install_p4a() assert mock.call( - 'git clone -b master --single-branch https://custom-p4a-url/p4a.git python-for-android', + ["git", "clone", "-b", "master", "--single-branch", "https://custom-p4a-url/p4a.git", "python-for-android"], cwd=mock.ANY) in m_cmd.call_args_list def test_install_platform_p4a_clone_fork(self): @@ -434,7 +443,7 @@ def test_install_platform_p4a_clone_fork(self): target_android._install_p4a() assert mock.call( - 'git clone -b master --single-branch https://github.com/fork/python-for-android.git python-for-android', + ["git", "clone", "-b", "master", "--single-branch", "https://github.com/fork/python-for-android.git", "python-for-android"], cwd=mock.ANY) in m_cmd.call_args_list def test_install_platform_p4a_clone_default(self): @@ -446,5 +455,5 @@ def test_install_platform_p4a_clone_default(self): target_android._install_p4a() assert mock.call( - 'git clone -b master --single-branch https://github.com/kivy/python-for-android.git python-for-android', + ["git", "clone", "-b", "master", "--single-branch", "https://github.com/kivy/python-for-android.git", "python-for-android"], cwd=mock.ANY) in m_cmd.call_args_list diff --git a/tests/targets/test_ios.py b/tests/targets/test_ios.py index 6c55014a4..5e13bec91 100644 --- a/tests/targets/test_ios.py +++ b/tests/targets/test_ios.py @@ -53,7 +53,6 @@ def test_check_requirements(self): """Basic tests for the check_requirements() method.""" target = init_target(self.temp_dir) buildozer = target.buildozer - assert not hasattr(target, "adb_cmd") assert not hasattr(target, "javac_cmd") assert "PATH" not in buildozer.environ with patch_buildozer_checkbin() as m_checkbin: @@ -68,8 +67,8 @@ def test_check_requirements(self): mock.call("automake", "automake"), mock.call("libtool", "libtool"), ] - assert target._toolchain_cmd.endswith("toolchain.py ") - assert target._xcodebuild_cmd == "xcodebuild " + assert target._toolchain_cmd[-1] == "toolchain.py" + assert target._xcodebuild_cmd == ["xcodebuild"] def test_check_configuration_tokens(self): """Basic tests for the check_configuration_tokens() method.""" @@ -97,7 +96,7 @@ def test_get_available_packages(self): m_toolchain.return_value = ("hostpython3 kivy pillow python3 sdl2", None, 0) available_packages = target.get_available_packages() assert m_toolchain.call_args_list == [ - mock.call("recipes --compact", get_stdout=True) + mock.call(["recipes", "--compact"], get_stdout=True) ] assert available_packages == [ "hostpython3", @@ -115,9 +114,24 @@ def test_install_platform(self): with patch_buildozer_cmd() as m_cmd: target.install_platform() assert m_cmd.call_args_list == [ - mock.call("git clone https://github.com/kivy/kivy-ios", cwd=mock.ANY), mock.call( - "git clone --branch 1.10.0 https://github.com/phonegap/ios-deploy", + [ + "git", + "clone", + "--branch", + "master", + "https://github.com/kivy/kivy-ios", + ], + cwd=mock.ANY, + ), + mock.call( + [ + "git", + "clone", + "--branch", + "1.10.0", + "https://github.com/phonegap/ios-deploy", + ], cwd=mock.ANY, ), ] @@ -137,7 +151,7 @@ def test_compile_platform(self): target.compile_platform() # fmt: on assert m_get_available_packages.call_args_list == [mock.call()] - assert m_toolchain.call_args_list == [mock.call("build python3")] + assert m_toolchain.call_args_list == [mock.call(["build", "python3"])] assert m_file_exists.call_args_list == [ mock.call(target.ios_deploy_dir, "ios-deploy") ] @@ -210,16 +224,31 @@ def test_build_package_no_signature(self): ] assert m_cmd.call_args_list == [ mock.call(mock.ANY, cwd=target.ios_dir), - mock.call( - "xcodebuild -configuration Debug -allowProvisioningUpdates ENABLE_BITCODE=NO " - "CODE_SIGNING_ALLOWED=NO clean build", + mock.call([ + "xcodebuild", + "-configuration", + "Debug", + "-allowProvisioningUpdates", + "ENABLE_BITCODE=NO", + "CODE_SIGNING_ALLOWED=NO", + "clean", + "build"], cwd="/ios/dir/myapp-ios", ), - mock.call( - "xcodebuild -alltargets -configuration Debug -scheme myapp " - "-archivePath \"/ios/dir/myapp-0.1.intermediates/myapp-0.1.xcarchive\" " - "-destination \'generic/platform=iOS\' " - "archive ENABLE_BITCODE=NO CODE_SIGNING_ALLOWED=NO", + mock.call([ + "xcodebuild", + "-alltargets", + "-configuration", + "Debug", + "-scheme", + "myapp", + "-archivePath", + "/ios/dir/myapp-0.1.intermediates/myapp-0.1.xcarchive", + "-destination", + "generic/platform=iOS", + "archive", + "ENABLE_BITCODE=NO", + "CODE_SIGNING_ALLOWED=NO"], cwd="/ios/dir/myapp-ios", ), ] diff --git a/tests/test_buildozer.py b/tests/test_buildozer.py index 6ef385da2..46f7552a0 100644 --- a/tests/test_buildozer.py +++ b/tests/test_buildozer.py @@ -4,7 +4,7 @@ import unittest import buildozer as buildozer_module from buildozer import Buildozer -from six import StringIO +from io import StringIO import tempfile from unittest import mock