├── kivy_ios ├── __init__.py ├── recipes │ ├── __init__.py │ ├── hostpython3 │ │ ├── ModulesSetup │ │ ├── allow-cflags-override.patch │ │ ├── disable_sysconfig_cflags.patch │ │ └── __init__.py │ ├── python3 │ │ ├── ModulesSetup.mobile │ │ ├── posixmodule.patch │ │ ├── ctypes_duplicate.patch │ │ ├── dynload_shlib.patch │ │ ├── configure.patch │ │ ├── ModulesSetup │ │ └── __init__.py │ ├── zbarlight │ │ ├── zbarlight_1_2.patch │ │ └── __init__.py │ ├── plyer │ │ └── __init__.py │ ├── pykka │ │ └── __init__.py │ ├── pyobjus │ │ └── __init__.py │ ├── curly │ │ └── __init__.py │ ├── audiostream │ │ └── __init__.py │ ├── ios │ │ ├── src │ │ │ ├── setup.py │ │ │ ├── ios_wrapper.h │ │ │ ├── ios_browser.m │ │ │ ├── ios_filechooser.m │ │ │ ├── ios_mail.m │ │ │ ├── ios_utils.m │ │ │ └── ios.pyx │ │ └── __init__.py │ ├── photolibrary │ │ └── __init__.py │ ├── pycrypto │ │ ├── hash_SHA2_template.c.patch │ │ └── __init__.py │ ├── libzbar │ │ ├── werror.patch │ │ └── __init__.py │ ├── py3dns │ │ ├── __init__.py │ │ └── ios.patch │ ├── sdl2 │ │ ├── uikit-transparent.patch │ │ ├── disable-hidapi.patch │ │ └── __init__.py │ ├── cymunk │ │ └── __init__.py │ ├── python.py │ ├── pillow │ │ ├── bypass-find-library.patch │ │ └── __init__.py │ ├── click │ │ └── __init__.py │ ├── jinja2 │ │ └── __init__.py │ ├── werkzeug │ │ └── __init__.py │ ├── pyyaml │ │ └── __init__.py │ ├── flask │ │ └── __init__.py │ ├── itsdangerous │ │ └── __init__.py │ ├── libpng │ │ └── __init__.py │ ├── sdl2_ttf │ │ └── __init__.py │ ├── libcurl │ │ └── __init__.py │ ├── hostpython.py │ ├── libjpeg │ │ └── __init__.py │ ├── sdl2_image │ │ ├── fix-ios-xcodebuild.patch │ │ └── __init__.py │ ├── openssl │ │ └── __init__.py │ ├── numpy │ │ ├── duplicated_symbols.patch │ │ ├── __init__.py │ │ └── skip-math-test.patch │ ├── markupsafe │ │ └── __init__.py │ ├── sdl2_mixer │ │ └── __init__.py │ ├── netifaces │ │ └── __init__.py │ ├── ffpyplayer │ │ ├── __init__.py │ │ └── misc-visibility.patch │ ├── freetype │ │ └── __init__.py │ ├── hostopenssl │ │ └── __init__.py │ ├── kivy │ │ └── __init__.py │ ├── libffi │ │ ├── __init__.py │ │ └── enable-tramp-build.patch │ ├── ffmpeg │ │ └── __init__.py │ └── kivent_core │ │ └── __init__.py ├── tools │ ├── __init__.py │ ├── external │ │ └── __init__.py │ ├── templates │ │ ├── {{ cookiecutter.project_name }}-ios │ │ │ ├── YourApp │ │ │ │ ├── images │ │ │ │ │ ├── .empty │ │ │ │ │ ├── faust_github.jpg │ │ │ │ │ ├── 5509213687_ffd18df0b9_b.jpg │ │ │ │ │ └── 5552597274_de8b3fb5d2_b.jpg │ │ │ │ ├── android.txt │ │ │ │ ├── shadow32.png │ │ │ │ ├── README.txt │ │ │ │ └── pictures.kv │ │ │ ├── icon.png │ │ │ ├── {{ cookiecutter.project_name }}.xcodeproj │ │ │ │ └── project.xcworkspace │ │ │ │ │ └── contents.xcworkspacedata │ │ │ ├── {{ cookiecutter.project_name }} │ │ │ │ └── Images.xcassets │ │ │ │ │ └── AppIcon.appiconset │ │ │ │ │ └── Contents.json │ │ │ ├── bridge.h │ │ │ ├── {{ cookiecutter.project_name }}-Info.plist │ │ │ ├── Storyboards │ │ │ │ └── Launch Screen.storyboard │ │ │ ├── bridge.m │ │ │ └── main.m │ │ └── cookiecutter.json │ ├── biglink │ ├── cythonize.py │ └── liblink └── context_managers.py ├── Sources └── kivy-ios │ └── kivy_ios.swift ├── kivy_ios.egg-info ├── dependency_links.txt ├── top_level.txt ├── entry_points.txt ├── requires.txt ├── SOURCES.txt └── PKG-INFO ├── setup.py ├── toolchain.py ├── pyproject.toml ├── requirements.txt ├── .ci ├── constants.py ├── test_project.sh ├── utils.sh └── rebuild_updated_recipes.py ├── MANIFEST.in ├── .gitignore ├── Package.swift ├── tox.ini ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── pypi-release.yml │ ├── setup.yml │ ├── support.yml │ └── kivy_ios.yml ├── setup.cfg ├── LICENSE ├── tests ├── test_python3 │ ├── main.py │ └── main.m └── test_libs │ └── main.py └── README.md /kivy_ios/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /kivy_ios/recipes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /kivy_ios/tools/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Sources/kivy-ios/kivy_ios.swift: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /kivy_ios/tools/external/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /kivy_ios.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /kivy_ios.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | kivy_ios 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup() 4 | -------------------------------------------------------------------------------- /toolchain.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import main 2 | 3 | main() 4 | -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/images/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /kivy_ios/recipes/hostpython3/ModulesSetup: -------------------------------------------------------------------------------- 1 | zlib zlibmodule.c -I$(prefix)/include -lz 2 | -------------------------------------------------------------------------------- /kivy_ios.egg-info/entry_points.txt: -------------------------------------------------------------------------------- 1 | [console_scripts] 2 | toolchain = kivy_ios.toolchain:main 3 | 4 | -------------------------------------------------------------------------------- /kivy_ios.egg-info/requires.txt: -------------------------------------------------------------------------------- 1 | Cython 2 | cookiecutter 3 | pbxproj 4 | Pillow 5 | requests 6 | sh 7 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.0"] 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pbxproj==3.5.0 2 | Pillow>=6.1.0 3 | requests>=2.13 4 | cookiecutter==2.1.1 5 | sh==1.12.14 6 | Cython==0.29.17 -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/android.txt: -------------------------------------------------------------------------------- 1 | title=Pictures 2 | author=Kivy team 3 | orientation=landscape 4 | -------------------------------------------------------------------------------- /.ci/constants.py: -------------------------------------------------------------------------------- 1 | BROKEN_RECIPES = set(["netifaces"]) 2 | 3 | # recipes that were already built will be skipped 4 | CORE_RECIPES = set(["kivy", "hostpython3", "python3"]) 5 | -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kewlbear/kivy-ios/HEAD/kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/icon.png -------------------------------------------------------------------------------- /kivy_ios/recipes/python3/ModulesSetup.mobile: -------------------------------------------------------------------------------- 1 | # Ctypes 2 | _ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/stgdict.c _ctypes/cfield.c -I$(srcdir)/../../build/include/ffi 3 | -------------------------------------------------------------------------------- /kivy_ios/tools/templates/cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "", 3 | "project_name": "", 4 | "domain_name": "", 5 | "project_dir": "", 6 | "version": "1.0.0", 7 | "dist_dir": "" 8 | } 9 | -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/shadow32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kewlbear/kivy-ios/HEAD/kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/shadow32.png -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/images/faust_github.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kewlbear/kivy-ios/HEAD/kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/images/faust_github.jpg -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/images/5509213687_ffd18df0b9_b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kewlbear/kivy-ios/HEAD/kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/images/5509213687_ffd18df0b9_b.jpg -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/images/5552597274_de8b3fb5d2_b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kewlbear/kivy-ios/HEAD/kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/images/5552597274_de8b3fb5d2_b.jpg -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include kivy_ios *.py 2 | recursive-include kivy_ios/recipes *.py *.patch *.diff *.rst ModulesSetup ModulesSetup.mobile *.m *.h *.pyx *.so 3 | recursive-include kivy_ios/tools *.py biglink liblink 4 | recursive-include kivy_ios/tools/templates * 5 | 6 | prune .git -------------------------------------------------------------------------------- /kivy_ios/recipes/zbarlight/zbarlight_1_2.patch: -------------------------------------------------------------------------------- 1 | --- zbarlight-1.2/build/lib.macosx-10.13-x86_64-2.7/zbarlight/__init__.py 2 | +++ zbarlight-1.2/build/lib.macosx-10.13-x86_64-2.7/zbarlight/__init__.py 3 | @@ -10,1 +10,1 @@ 4 | -__version__ = pkg_resources.get_distribution('zbarlight').version 5 | +__version__ = '1.2' 6 | 7 | -------------------------------------------------------------------------------- /kivy_ios/recipes/plyer/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import PythonRecipe 2 | 3 | 4 | class PlyerRecipe(PythonRecipe): 5 | version = "master" 6 | url = "https://github.com/kivy/plyer/archive/{version}.zip" 7 | depends = ["python", "pyobjus"] 8 | archs = ["x86_64"] 9 | 10 | 11 | recipe = PlyerRecipe() 12 | -------------------------------------------------------------------------------- /kivy_ios/recipes/pykka/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import PythonRecipe 2 | 3 | 4 | class PykkaRecipe(PythonRecipe): 5 | version = '1.2.1' 6 | url = 'https://github.com/jodal/pykka/archive/v{version}.zip' 7 | 8 | depends = ['python3'] 9 | 10 | site_packages_name = 'pykka' 11 | 12 | 13 | recipe = PykkaRecipe() 14 | -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/{{ cookiecutter.project_name }}.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.ci/test_project.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/bin/bash 3 | 4 | set -eo pipefail 5 | 6 | toolchain create Touchtracer kivy-ci-clone/examples/demo/touchtracer 7 | 8 | xcodebuild -project touchtracer-ios/touchtracer.xcodeproj \ 9 | -scheme touchtracer \ 10 | -destination generic/platform=iOS\ 11 | clean build CODE_SIGNING_ALLOWED=NO | xcpretty -------------------------------------------------------------------------------- /kivy_ios/recipes/pyobjus/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import CythonRecipe 2 | 3 | 4 | class PyobjusRecipe(CythonRecipe): 5 | version = "master" 6 | url = "https://github.com/kivy/pyobjus/archive/{version}.zip" 7 | library = "libpyobjus.a" 8 | depends = ["python"] 9 | pre_build_ext = True 10 | 11 | 12 | recipe = PyobjusRecipe() 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swo 2 | *.pyc 3 | *.pyo 4 | *.swp 5 | freetype-* 6 | build/* 7 | dist/* 8 | src/SDL/Xcode-iPhoneOS/SDL/build/ 9 | src/SDL/Xcode-iOS/SDL/build/ 10 | tmp/* 11 | kivy/* 12 | libtremor/* 13 | libogg/* 14 | app-* 15 | .cache 16 | src/ios/build/ 17 | src/ios/iosbuild/ 18 | src/ios/ios.c 19 | *.DS_Store* 20 | #*-ios/ 21 | __pycache__ 22 | .tox 23 | venv 24 | *.egg-info -------------------------------------------------------------------------------- /kivy_ios/recipes/curly/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import CythonRecipe 2 | 3 | 4 | class CurlyRecipe(CythonRecipe): 5 | version = "master" 6 | url = "https://github.com/tito/curly/archive/{version}.zip" 7 | library = "libcurly.a" 8 | depends = ["python", "libcurl", "sdl2", "sdl2_image"] 9 | pre_build_ext = True 10 | 11 | 12 | recipe = CurlyRecipe() 13 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "kivy-ios", 7 | products: [ 8 | .library( 9 | name: "kivy-ios", 10 | targets: ["kivy-ios"]), 11 | ], 12 | targets: [ 13 | .target( 14 | name: "kivy-ios", 15 | dependencies: []), 16 | ] 17 | ) 18 | -------------------------------------------------------------------------------- /kivy_ios/recipes/audiostream/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import CythonRecipe 2 | 3 | 4 | class AudiostreamRecipe(CythonRecipe): 5 | version = "master" 6 | url = "https://github.com/kivy/audiostream/archive/{version}.zip" 7 | library = "libaudiostream.a" 8 | depends = ["python", "sdl2", "sdl2_mixer"] 9 | pre_build_ext = True 10 | 11 | 12 | recipe = AudiostreamRecipe() 13 | -------------------------------------------------------------------------------- /.ci/utils.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | arm64_set_path_and_python_version(){ 4 | python_version="$1" 5 | if [[ $(/usr/bin/arch) = arm64 ]]; then 6 | echo $PATH 7 | export PATH=/usr/local/bin:$PATH 8 | export PATH=/opt/homebrew/bin:$PATH 9 | eval "$(pyenv init --path)" 10 | pyenv install $python_version -s 11 | pyenv global $python_version 12 | export PATH=$(pyenv prefix)/bin:$PATH 13 | fi 14 | } 15 | -------------------------------------------------------------------------------- /kivy_ios/recipes/ios/src/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | 3 | 4 | setup(name='ios', 5 | version='1.1', 6 | ext_modules=[ 7 | Extension('ios', 8 | ['ios.c', 'ios_utils.m', 'ios_mail.m', 'ios_browser.m', 9 | 'ios_filechooser.m'], 10 | libraries=[], 11 | library_dirs=[], 12 | ) 13 | ] 14 | ) 15 | -------------------------------------------------------------------------------- /kivy_ios/recipes/ios/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import CythonRecipe 2 | 3 | 4 | class IosRecipe(CythonRecipe): 5 | version = "master" 6 | url = "src" 7 | library = "libios.a" 8 | depends = ["python"] 9 | pbx_frameworks = ["MessageUI", "CoreMotion", "UIKit", "WebKit", "Photos"] 10 | 11 | def install(self): 12 | self.install_python_package( 13 | name=self.so_filename("ios"), is_dir=False) 14 | 15 | 16 | recipe = IosRecipe() 17 | -------------------------------------------------------------------------------- /kivy_ios/recipes/photolibrary/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import CythonRecipe 2 | 3 | 4 | class PhotoRecipe(CythonRecipe): 5 | version = "master" 6 | url = "https://github.com/akshayaurora/photolibrary/archive/master.zip" 7 | library = "libphotolibrary.a" 8 | depends = ["python"] 9 | pbx_frameworks = ["UIKit", "Photos", "MobileCoreServices"] 10 | 11 | def install(self): 12 | self.install_python_package(name="photolibrary.so", is_dir=False) 13 | 14 | 15 | recipe = PhotoRecipe() 16 | -------------------------------------------------------------------------------- /kivy_ios/recipes/pycrypto/hash_SHA2_template.c.patch: -------------------------------------------------------------------------------- 1 | --- ../src/src/hash_SHA2_template.c 2015-07-15 15:25:59.000000000 +0530 2 | +++ src/hash_SHA2_template.c 2015-07-17 15:35:13.000000000 +0530 3 | @@ -87,7 +87,7 @@ 4 | * return 1 on success 5 | * return 0 if the length overflows 6 | */ 7 | -static int add_length(hash_state *hs, sha2_word_t inc) { 8 | +int add_length(hash_state *hs, sha2_word_t inc) { 9 | sha2_word_t overflow_detector; 10 | overflow_detector = hs->length_lower; 11 | hs->length_lower += inc; 12 | -------------------------------------------------------------------------------- /kivy_ios/recipes/python3/posixmodule.patch: -------------------------------------------------------------------------------- 1 | diff -Naur Python-3.9.2.orig/Modules/posixmodule.c Python-3.9.2/Modules/posixmodule.c 2 | --- Python-3.9.2.orig/Modules/posixmodule.c 2021-03-20 12:28:02.000000000 +0100 3 | +++ Python-3.9.2/Modules/posixmodule.c 2021-03-20 12:29:00.000000000 +0100 4 | @@ -326,7 +326,7 @@ 5 | # define HAVE_KILL 1 6 | # define HAVE_OPENDIR 1 7 | # define HAVE_PIPE 1 8 | -# define HAVE_SYSTEM 1 9 | +# undef HAVE_SYSTEM 10 | # define HAVE_WAIT 1 11 | # define HAVE_TTYNAME 1 12 | # endif /* _MSC_VER */ 13 | -------------------------------------------------------------------------------- /kivy_ios/recipes/libzbar/werror.patch: -------------------------------------------------------------------------------- 1 | diff --git a/configure.ac b/configure.ac 2 | index 256aedb..727caba 100644 3 | --- a/configure.ac 4 | +++ b/configure.ac 5 | @@ -3,7 +3,7 @@ AC_PREREQ([2.61]) 6 | AC_INIT([zbar], [0.10], [spadix@users.sourceforge.net]) 7 | AC_CONFIG_AUX_DIR(config) 8 | AC_CONFIG_MACRO_DIR(config) 9 | -AM_INIT_AUTOMAKE([1.10 -Wall -Werror foreign subdir-objects std-options dist-bzip2]) 10 | +AM_INIT_AUTOMAKE([1.10 -Wall foreign subdir-objects std-options dist-bzip2]) 11 | AC_CONFIG_HEADERS([include/config.h]) 12 | AC_CONFIG_SRCDIR(zbar/scanner.c) 13 | LT_PREREQ([2.2]) -------------------------------------------------------------------------------- /kivy_ios/recipes/py3dns/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import PythonRecipe 2 | 3 | 4 | class Py3DNSRecipe(PythonRecipe): 5 | site_packages_name = 'DNS' 6 | version = '3.2.1' 7 | url = 'https://launchpad.net/py3dns/trunk/{version}/' \ 8 | '+download/py3dns-{version}.tar.gz' 9 | depends = ['python3'] 10 | 11 | def prebuild_arch(self, arch): 12 | if self.has_marker("patched"): 13 | return 14 | 15 | self.apply_patch("ios.patch") 16 | self.set_marker("patched") 17 | 18 | 19 | recipe = Py3DNSRecipe() 20 | -------------------------------------------------------------------------------- /kivy_ios/recipes/ios/src/ios_wrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef __IOS_WRAPPER 2 | #define __IOS_WRAPPER 3 | 4 | typedef struct { 5 | float top; 6 | float bottom; 7 | float right; 8 | float left; 9 | } padding; 10 | 11 | float ios_uiscreen_get_scale(void); 12 | int ios_uiscreen_get_dpi(void); 13 | padding ios_get_safe_area(void); 14 | void ios_open_url(char *url); 15 | void load_url_webview(char *url, int x, int y, int width, int height); 16 | 17 | typedef void (*ios_send_email_cb)(char *, void *); 18 | 19 | int ios_send_email(char *subject, char *text, char *mimetype, char *filename, 20 | char *filename_alias, ios_send_email_cb callback, void *userdata); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /kivy_ios/recipes/sdl2/uikit-transparent.patch: -------------------------------------------------------------------------------- 1 | --- slime73-sdl-experiments-618662dc9e82/src/video/uikit/SDL_uikitopenglview.m.orig 2015-08-20 16:09:59.000000000 +0200 2 | +++ slime73-sdl-experiments-618662dc9e82/src/video/uikit/SDL_uikitopenglview.m 2015-08-20 16:11:25.000000000 +0200 3 | @@ -99,7 +99,7 @@ 4 | 5 | CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer; 6 | 7 | - eaglLayer.opaque = YES; 8 | + eaglLayer.opaque = SDL_getenv("UIKIT_TRANSPARENT") ? NO : YES; 9 | eaglLayer.drawableProperties = @{ 10 | kEAGLDrawablePropertyRetainedBacking:@(retained), 11 | kEAGLDrawablePropertyColorFormat:colorFormat 12 | -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/{{ cookiecutter.project_name }}/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "60x60", 21 | "scale" : "3x" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } -------------------------------------------------------------------------------- /kivy_ios/recipes/cymunk/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: Lawrence Du, Lukasz Mach 3 | E-mail: larrydu88@gmail.com, maho@pagema.net 4 | """ 5 | 6 | from kivy_ios.toolchain import CythonRecipe 7 | 8 | 9 | class CymunkRecipe(CythonRecipe): 10 | version = 'master' 11 | url = 'https://github.com/kivy/cymunk/archive/{version}.zip' 12 | name = 'cymunk' 13 | pre_build_ext = True 14 | library = 'libcymunk.a' 15 | 16 | depends = ['python'] 17 | 18 | def get_recipe_env(self, arch): 19 | ret = super(CymunkRecipe, self).get_recipe_env(arch) 20 | ret['CFLAGS'] += ' -Wno-implicit-function-declaration' 21 | return ret 22 | 23 | 24 | recipe = CymunkRecipe() 25 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | skipsdist = True 3 | envlist = pep8 4 | basepython = python3 5 | 6 | [testenv] 7 | deps = -r{toxinidir}/requirements.txt 8 | setenv = 9 | PYTHONPATH={toxinidir} 10 | 11 | [testenv:pep8] 12 | deps = flake8 13 | commands = flake8 kivy_ios/ tests/ .ci/ setup.py toolchain.py 14 | 15 | 16 | [flake8] 17 | ignore = 18 | # Closing bracket does not match indentation of opening bracket's line 19 | E123, 20 | # Continuation line over-indented for hanging indent 21 | E126, 22 | # Line too long (82 > 79 characters) 23 | E501, 24 | # Line break occurred before a binary operator 25 | W503, 26 | # Line break occurred after a binary operator 27 | W504 28 | -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/README.txt: -------------------------------------------------------------------------------- 1 | Pictures 2 | ======== 3 | 4 | A very simple image browser, supporting the reading only from images/ 5 | directory right now. 6 | So put any images files inside images/ directory, and start the app ! 7 | 8 | Android 9 | ------- 10 | 11 | You can copy/paste this directory into /sdcard/kivy/pictures in your 12 | android device. 13 | 14 | 15 | Licences 16 | -------- 17 | 18 | * faust_github.jpg: lucie's cat accepted to share his face 19 | * 5552597274_de8b3fb5d2_b.jpg: http://www.flickr.com/photos/chialin-gallery/5552597274/sizes/l/ 20 | * 5509213687_ffd18df0b9_b.jpg: http://www.flickr.com/photos/orsomk/5509213687/sizes/l/ 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'feature request' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /kivy_ios/recipes/python.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from kivy_ios.toolchain import Recipe 3 | import logging 4 | from os.path import join 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | class PythonAliasRecipe(Recipe): 10 | """ 11 | Note this recipe was created to handle both python2 and python3. 12 | As python2 support was dropped, this could probably be simplified. 13 | """ 14 | is_alias = True 15 | 16 | def init_after_import(self, ctx): 17 | python = ctx.state.get("python") 18 | if not python: 19 | python = "python3" 20 | self.depends = [python] 21 | self.recipe_dir = join(ctx.root_dir, "recipes", python) 22 | 23 | 24 | recipe = PythonAliasRecipe() 25 | -------------------------------------------------------------------------------- /kivy_ios/recipes/hostpython3/allow-cflags-override.patch: -------------------------------------------------------------------------------- 1 | --- setuptools.orig/_distutils/sysconfig.py 2023-02-18 18:21:53 2 | +++ setuptools/_distutils/sysconfig.py 2023-02-18 18:23:17 3 | @@ -329,7 +329,9 @@ 4 | if 'LDFLAGS' in os.environ: 5 | ldshared = ldshared + ' ' + os.environ['LDFLAGS'] 6 | if 'CFLAGS' in os.environ: 7 | - cflags = cflags + ' ' + os.environ['CFLAGS'] 8 | + # cflags = cflags + ' ' + os.environ['CFLAGS'] 9 | + # TODO: we may have found a bug in distutils? 10 | + cflags = os.environ['CFLAGS'] 11 | ldshared = ldshared + ' ' + os.environ['CFLAGS'] 12 | if 'CPPFLAGS' in os.environ: 13 | cpp = cpp + ' ' + os.environ['CPPFLAGS'] 14 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = kivy-ios 3 | version = 2023.01.29 4 | description = Kivy for iOS 5 | license = MIT License 6 | long_description = file: README.md 7 | long_description_content_type = text/markdown 8 | author = The Kivy team 9 | author_email = kivy-dev@googlegroups.com 10 | url = https://github.com/kivy/kivy-ios 11 | 12 | [options] 13 | python_requires >= "3.6.0" 14 | install_requires = 15 | Cython 16 | cookiecutter 17 | pbxproj 18 | Pillow 19 | requests 20 | sh 21 | packages = find: 22 | # note this is a bit excessive as it includes absolutely everything 23 | # make sure you run with from a clean directory 24 | include_package_data = True 25 | 26 | [options.entry_points] 27 | console_scripts = 28 | toolchain = kivy_ios.toolchain:main -------------------------------------------------------------------------------- /kivy_ios/recipes/python3/ctypes_duplicate.patch: -------------------------------------------------------------------------------- 1 | diff -Naur Python-3.8.2.orig/Modules/_ctypes/cfield.c Python-3.8.2/Modules/_ctypes/cfield.c 2 | --- Python-3.8.2.orig/Modules/_ctypes/cfield.c 2020-04-13 12:23:46.000000000 +0200 3 | +++ Python-3.8.2/Modules/_ctypes/cfield.c 2020-04-13 12:24:45.000000000 +0200 4 | @@ -1636,7 +1636,7 @@ 5 | struct _ffi_type **elements; 6 | } ffi_type; 7 | */ 8 | - 9 | +#if 0 10 | /* align and size are bogus for void, but they must not be zero */ 11 | ffi_type ffi_type_void = { 1, 1, FFI_TYPE_VOID }; 12 | 13 | @@ -1663,5 +1663,5 @@ 14 | FFI_TYPE_LONGDOUBLE }; 15 | 16 | ffi_type ffi_type_pointer = { sizeof(void *), VOID_P_ALIGN, FFI_TYPE_POINTER }; 17 | - 18 | +#endif 19 | /*---------------- EOF ----------------*/ 20 | -------------------------------------------------------------------------------- /.github/workflows/pypi-release.yml: -------------------------------------------------------------------------------- 1 | name: PyPI release 2 | on: [push] 3 | 4 | jobs: 5 | pypi_release: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v3 9 | - name: Set up Python 3.x 10 | uses: actions/setup-python@v4 11 | with: 12 | python-version: 3.x 13 | - name: Install dependencies 14 | run: | 15 | python -m pip install --upgrade setuptools wheel twine 16 | - name: Build 17 | run: | 18 | python setup.py sdist bdist_wheel 19 | twine check dist/* 20 | - name: Publish package 21 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') 22 | uses: pypa/gh-action-pypi-publish@v1.1.0 23 | with: 24 | user: __token__ 25 | password: ${{ secrets.pypi_password }} 26 | -------------------------------------------------------------------------------- /kivy_ios/tools/biglink: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | 7 | sofiles = [] 8 | for dir in sys.argv[2:]: 9 | for fn in os.listdir(dir): 10 | if fn.endswith(".so"): 11 | fn = os.path.join(dir, fn) 12 | if os.path.exists(fn + ".o") and os.path.exists(fn + ".libs"): 13 | sofiles.append(fn) 14 | 15 | args = [] # The raw argument list. 16 | for fn in sofiles: 17 | args.append(fn + ".o") 18 | args.extend(open(fn + ".libs").read().split(" ")) 19 | 20 | unique_args = ' '.join(x for x in sorted(set(args)) if x.endswith('.so.o')) 21 | print('Biglink create %s library' % sys.argv[1]) 22 | cmd = "ar -q %s %s" % (sys.argv[1], unique_args) 23 | print('Binlinking: {}'.format(cmd)) 24 | subprocess.Popen(cmd, shell=True).communicate() 25 | -------------------------------------------------------------------------------- /kivy_ios/recipes/pillow/bypass-find-library.patch: -------------------------------------------------------------------------------- 1 | diff -Naur Pillow-8.2.0.orig/setup.py Pillow-8.2.0/setup.py 2 | --- Pillow-8.2.0.orig/setup.py 2021-04-05 11:11:26.000000000 +0200 3 | +++ Pillow-8.2.0/setup.py 2021-04-05 11:16:12.000000000 +0200 4 | @@ -222,12 +222,9 @@ 5 | 6 | 7 | def _find_library_file(self, library): 8 | - ret = self.compiler.find_library_file(self.compiler.library_dirs, library) 9 | - if ret: 10 | - _dbg("Found library %s at %s", (library, ret)) 11 | - else: 12 | - _dbg("Couldn't find library %s in %s", (library, self.compiler.library_dirs)) 13 | - return ret 14 | + # The provided find method is broken on our implementation. 15 | + # We will select by --disable flags what is actually disabled. 16 | + return True 17 | 18 | 19 | def _find_include_dir(self, dirname, include): 20 | -------------------------------------------------------------------------------- /kivy_ios/recipes/hostpython3/disable_sysconfig_cflags.patch: -------------------------------------------------------------------------------- 1 | diff -Naur Python-3.9.2.orig/Lib/distutils/sysconfig.py Python-3.9.2/Lib/distutils/sysconfig.py 2 | --- Python-3.9.2.orig/Lib/distutils/sysconfig.py 2021-03-20 13:44:04.000000000 +0100 3 | +++ Python-3.9.2/Lib/distutils/sysconfig.py 2021-03-20 13:47:12.000000000 +0100 4 | @@ -196,6 +196,12 @@ 5 | (cc, cxx, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \ 6 | get_config_vars('CC', 'CXX', 'CFLAGS', 7 | 'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS') 8 | + 9 | + """ 10 | + sysconfig was overriding our cflags and extensions were failing to build. 11 | + This hack resets the cflags provided by sysconfig. 12 | + """ 13 | + cflags = "" 14 | 15 | if 'CC' in os.environ: 16 | newcc = os.environ['CC'] 17 | -------------------------------------------------------------------------------- /kivy_ios/recipes/click/__init__.py: -------------------------------------------------------------------------------- 1 | # pure-python package, this can be removed when we'll support any python package 2 | from kivy_ios.toolchain import PythonRecipe, shprint 3 | from os.path import join 4 | import sh 5 | import os 6 | 7 | 8 | class ClickRecipe(PythonRecipe): 9 | version = "7.1.2" 10 | url = "https://github.com/mitsuhiko/click/archive/{version}.zip" 11 | depends = ["python"] 12 | 13 | def install(self): 14 | arch = list(self.filtered_archs)[0] 15 | build_dir = self.get_build_dir(arch.arch) 16 | os.chdir(build_dir) 17 | hostpython = sh.Command(self.ctx.hostpython) 18 | build_env = arch.get_env() 19 | dest_dir = join(self.ctx.dist_dir, "root", "python3") 20 | build_env['PYTHONPATH'] = self.ctx.site_packages_dir 21 | shprint(hostpython, "setup.py", "install", "--prefix", dest_dir, _env=build_env) 22 | 23 | 24 | recipe = ClickRecipe() 25 | -------------------------------------------------------------------------------- /kivy_ios/recipes/jinja2/__init__.py: -------------------------------------------------------------------------------- 1 | # pure-python package, this can be removed when we'll support any python package 2 | from kivy_ios.toolchain import PythonRecipe, shprint 3 | from os.path import join 4 | import sh 5 | import os 6 | 7 | 8 | class Jinja2Recipe(PythonRecipe): 9 | version = "2.11.2" 10 | url = "https://github.com/mitsuhiko/jinja2/archive/{version}.zip" 11 | depends = ["python", "markupsafe"] 12 | 13 | def install(self): 14 | arch = list(self.filtered_archs)[0] 15 | build_dir = self.get_build_dir(arch.arch) 16 | os.chdir(build_dir) 17 | hostpython = sh.Command(self.ctx.hostpython) 18 | build_env = arch.get_env() 19 | dest_dir = join(self.ctx.dist_dir, "root", "python3") 20 | build_env['PYTHONPATH'] = self.ctx.site_packages_dir 21 | shprint(hostpython, "setup.py", "install", "--prefix", dest_dir, _env=build_env) 22 | 23 | 24 | recipe = Jinja2Recipe() 25 | -------------------------------------------------------------------------------- /kivy_ios/recipes/werkzeug/__init__.py: -------------------------------------------------------------------------------- 1 | # pure-python package, this can be removed when we'll support any python package 2 | from kivy_ios.toolchain import PythonRecipe, shprint 3 | from os.path import join 4 | import sh 5 | import os 6 | 7 | 8 | class WerkzeugRecipe(PythonRecipe): 9 | version = "1.0.1" 10 | url = "https://github.com/mitsuhiko/werkzeug/archive/{version}.zip" 11 | depends = ["python", "openssl"] 12 | 13 | def install(self): 14 | arch = list(self.filtered_archs)[0] 15 | build_dir = self.get_build_dir(arch.arch) 16 | os.chdir(build_dir) 17 | hostpython = sh.Command(self.ctx.hostpython) 18 | build_env = arch.get_env() 19 | dest_dir = join(self.ctx.dist_dir, "root", "python3") 20 | build_env['PYTHONPATH'] = self.ctx.site_packages_dir 21 | shprint(hostpython, "setup.py", "install", "--prefix", dest_dir, _env=build_env) 22 | 23 | 24 | recipe = WerkzeugRecipe() 25 | -------------------------------------------------------------------------------- /kivy_ios/recipes/pyyaml/__init__.py: -------------------------------------------------------------------------------- 1 | # pure-python package, this can be removed when we'll support any python package 2 | import os 3 | import sh 4 | from kivy_ios.toolchain import PythonRecipe, shprint 5 | 6 | 7 | class PyYamlRecipe(PythonRecipe): 8 | version = "3.11" 9 | url = "https://pypi.python.org/packages/source/P/PyYAML/PyYAML-{version}.tar.gz" 10 | depends = ["python"] 11 | 12 | def install(self): 13 | arch = list(self.filtered_archs)[0] 14 | build_dir = self.get_build_dir(arch.arch) 15 | os.chdir(build_dir) 16 | hostpython = sh.Command(self.ctx.hostpython) 17 | build_env = arch.get_env() 18 | dest_dir = os.path.join(self.ctx.dist_dir, "root", "python") 19 | build_env['PYTHONPATH'] = os.path.join(dest_dir, 'lib', 'python3.7', 'site-packages') 20 | shprint(hostpython, "setup.py", "install", "--prefix", dest_dir, _env=build_env) 21 | 22 | 23 | recipe = PyYamlRecipe() 24 | -------------------------------------------------------------------------------- /kivy_ios/recipes/flask/__init__.py: -------------------------------------------------------------------------------- 1 | # pure-python package, this can be removed when we'll support any python package 2 | from kivy_ios.toolchain import PythonRecipe, shprint 3 | from os.path import join 4 | import sh 5 | import os 6 | 7 | 8 | class FlaskRecipe(PythonRecipe): 9 | version = "1.1.2" 10 | url = "https://github.com/mitsuhiko/flask/archive/{version}.zip" 11 | depends = ["python", "jinja2", "werkzeug", "itsdangerous", "click"] 12 | 13 | def install(self): 14 | arch = list(self.filtered_archs)[0] 15 | build_dir = self.get_build_dir(arch.arch) 16 | os.chdir(build_dir) 17 | hostpython = sh.Command(self.ctx.hostpython) 18 | build_env = arch.get_env() 19 | dest_dir = join(self.ctx.dist_dir, "root", "python") 20 | build_env['PYTHONPATH'] = self.ctx.site_packages_dir 21 | shprint(hostpython, "setup.py", "install", "--prefix", dest_dir, _env=build_env) 22 | 23 | 24 | recipe = FlaskRecipe() 25 | -------------------------------------------------------------------------------- /kivy_ios/recipes/itsdangerous/__init__.py: -------------------------------------------------------------------------------- 1 | # pure-python package, this can be removed when we'll support any python package 2 | from kivy_ios.toolchain import PythonRecipe, shprint 3 | from kivy_ios.context_managers import cd 4 | from os.path import join 5 | import sh 6 | 7 | 8 | class ItsDangerousRecipe(PythonRecipe): 9 | version = "1.1.0" 10 | url = "https://github.com/mitsuhiko/itsdangerous/archive/{version}.zip" 11 | depends = ["python"] 12 | 13 | def install(self): 14 | arch = list(self.filtered_archs)[0] 15 | build_dir = self.get_build_dir(arch.arch) 16 | hostpython = sh.Command(self.ctx.hostpython) 17 | build_env = arch.get_env() 18 | dest_dir = join(self.ctx.dist_dir, "root", "python3") 19 | build_env['PYTHONPATH'] = self.ctx.site_packages_dir 20 | with cd(build_dir): 21 | shprint(hostpython, "setup.py", "install", "--prefix", dest_dir, _env=build_env) 22 | 23 | 24 | recipe = ItsDangerousRecipe() 25 | -------------------------------------------------------------------------------- /.github/workflows/setup.yml: -------------------------------------------------------------------------------- 1 | name: setup 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | checks: 7 | runs-on: macos-latest 8 | steps: 9 | - name: Checkout kivy-ios 10 | uses: actions/checkout@v3 11 | - name: Set up Python 3.x 12 | uses: actions/setup-python@v4 13 | with: 14 | python-version: 3.x 15 | - name: Install dependencies 16 | run: | 17 | pip install --upgrade setuptools wheel twine 18 | - name: sdist bdist_wheel 19 | run: | 20 | python setup.py sdist bdist_wheel 21 | - name: Twine check 22 | run: | 23 | twine check dist/* 24 | - name: Local install 25 | run: | 26 | python -m venv venv 27 | . venv/bin/activate 28 | pip install dist/kivy-ios-*.tar.gz 29 | pip install Cython==0.29.33 30 | brew install autoconf automake libtool pkg-config 31 | - name: Basic toolchain commands 32 | run: | 33 | . venv/bin/activate 34 | toolchain --help 35 | toolchain recipes 36 | -------------------------------------------------------------------------------- /kivy_ios/recipes/libpng/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from kivy_ios.toolchain import Recipe, shprint 3 | from os.path import join 4 | import sh 5 | 6 | 7 | class PngRecipe(Recipe): 8 | version = '1.6.26' 9 | url = 'http://downloads.sourceforge.net/sourceforge/libpng/libpng-{version}.tar.gz' 10 | depends = ["python"] 11 | library = '.libs/libpng16.a' 12 | 13 | def build_arch(self, arch): 14 | build_env = arch.get_env() 15 | configure = sh.Command(join(self.build_dir, "configure")) 16 | shprint(configure, 17 | "CC={}".format(build_env["CC"]), 18 | "LD={}".format(build_env["LD"]), 19 | "CFLAGS={}".format(build_env["CFLAGS"]), 20 | "LDFLAGS={}".format(build_env["LDFLAGS"]), 21 | "--prefix=/", 22 | "--host={}".format(arch.triple), 23 | "--disable-shared") 24 | shprint(sh.make, "clean") 25 | shprint(sh.make, self.ctx.concurrent_make, _env=build_env) 26 | 27 | 28 | recipe = PngRecipe() 29 | -------------------------------------------------------------------------------- /kivy_ios/recipes/sdl2_ttf/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import Recipe, shprint 2 | from os.path import join 3 | import sh 4 | 5 | 6 | class LibSDL2TTFRecipe(Recipe): 7 | version = "2.20.1" 8 | url = "https://github.com/libsdl-org/SDL_ttf/releases/download/release-{version}/SDL2_ttf-{version}.tar.gz" 9 | library = "Xcode/build/Release-{arch.sdk}/libSDL2_ttf.a" 10 | include_dir = "SDL_ttf.h" 11 | depends = ["sdl2"] 12 | 13 | def build_arch(self, arch): 14 | shprint(sh.xcodebuild, self.ctx.concurrent_xcodebuild, 15 | "ONLY_ACTIVE_ARCH=NO", 16 | "ARCHS={}".format(arch.arch), 17 | "BITCODE_GENERATION_MODE=bitcode", 18 | "HEADER_SEARCH_PATHS={}".format( 19 | join(self.ctx.include_dir, "common", "sdl2")), 20 | "-sdk", arch.sdk, 21 | "-project", "Xcode/SDL_ttf.xcodeproj", 22 | "-target", "Static Library", 23 | "-configuration", "Release") 24 | 25 | 26 | recipe = LibSDL2TTFRecipe() 27 | -------------------------------------------------------------------------------- /kivy_ios/recipes/libcurl/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import Recipe, shprint 2 | from os.path import join 3 | import sh 4 | 5 | 6 | class CurlRecipe(Recipe): 7 | version = "7.65.3" 8 | url = "https://curl.haxx.se/download/curl-{version}.tar.gz" 9 | library = "lib/.libs/libcurl.a" 10 | include_dir = "include" 11 | depends = ["openssl"] 12 | 13 | def build_arch(self, arch): 14 | build_env = arch.get_env() 15 | configure = sh.Command(join(self.build_dir, "configure")) 16 | shprint(configure, 17 | "CC={}".format(build_env["CC"]), 18 | "LD={}".format(build_env["LD"]), 19 | "CFLAGS={}".format(build_env["CFLAGS"]), 20 | "LDFLAGS={}".format(build_env["LDFLAGS"]), 21 | "--prefix=/", 22 | "--host={}".format(arch.triple), 23 | "--disable-shared", 24 | "--without-libidn2") 25 | shprint(sh.make, "clean") 26 | shprint(sh.make, self.ctx.concurrent_make) 27 | 28 | 29 | recipe = CurlRecipe() 30 | -------------------------------------------------------------------------------- /kivy_ios/recipes/python3/dynload_shlib.patch: -------------------------------------------------------------------------------- 1 | diff -Naur Python-3.8.2.orig/Python/dynload_shlib.c Python-3.8.2/Python/dynload_shlib.c 2 | --- Python-3.8.2.orig/Python/dynload_shlib.c 2020-04-12 00:17:24.000000000 +0200 3 | +++ Python-3.8.2/Python/dynload_shlib.c 2020-04-12 00:20:10.000000000 +0200 4 | @@ -74,6 +74,15 @@ 5 | 6 | PyOS_snprintf(funcname, sizeof(funcname), 7 | LEAD_UNDERSCORE "%.20s_%.200s", prefix, shortname); 8 | + /* On IOS, dlopen crash as soon as we try to open one of our library. 9 | + * Instead, we have done a redirection of linking to convert our .so into a 10 | + * .a. Then the main executable is linked with theses symbol. So, instead 11 | + * of trying to dlopen, directly do the dlsym. 12 | + * -- Mathieu 13 | + */ 14 | + return (dl_funcptr) dlsym(RTLD_SELF, funcname); 15 | + 16 | + #if 0 17 | 18 | if (fp != NULL) { 19 | int i; 20 | @@ -129,4 +138,5 @@ 21 | handles[nhandles++].handle = handle; 22 | p = (dl_funcptr) dlsym(handle, funcname); 23 | return p; 24 | + #endif 25 | } 26 | -------------------------------------------------------------------------------- /kivy_ios/recipes/hostpython.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | from kivy_ios.toolchain import Recipe 4 | import logging 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | class HostpythonAliasRecipe(Recipe): 10 | """ 11 | Note this recipe was created to handle both hostpython2 and hostpython3. 12 | As hostpython2 support was dropped, this could probably be simplified. 13 | """ 14 | is_alias = True 15 | 16 | def init_after_import(self, ctx): 17 | hostpython = ctx.state.get("hostpython") 18 | if not hostpython: 19 | # search in wanted_recipes if it's the first time 20 | if "hostpython3" in ctx.wanted_recipes: 21 | hostpython = "hostpython3" 22 | else: 23 | logger.error("No hostpython version set in the build.") 24 | logger.error("Add python3 in your recipes:") 25 | logger.error("./toolchain.py build python3 ...") 26 | sys.exit(1) 27 | if hostpython: 28 | self.depends = [hostpython] 29 | 30 | 31 | recipe = HostpythonAliasRecipe() 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2017 Kivy Team and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /kivy_ios/recipes/sdl2/disable-hidapi.patch: -------------------------------------------------------------------------------- 1 | diff -Naur SDL2-2.24.1.orig/Xcode/SDL/SDL.xcodeproj/project.pbxproj SDL2-2.24.1/Xcode/SDL/SDL.xcodeproj/project.pbxproj 2 | --- SDL2-2.24.1.orig/Xcode/SDL/SDL.xcodeproj/project.pbxproj 2023-03-10 12:58:05 3 | +++ SDL2-2.24.1/Xcode/SDL/SDL.xcodeproj/project.pbxproj 2023-03-10 12:58:49 4 | @@ -9674,7 +9674,7 @@ 5 | buildSettings = { 6 | CLANG_ENABLE_OBJC_ARC = YES; 7 | CLANG_LINK_OBJC_RUNTIME = NO; 8 | - GCC_PREPROCESSOR_DEFINITIONS = GLES_SILENCE_DEPRECATION; 9 | + GCC_PREPROCESSOR_DEFINITIONS = "GLES_SILENCE_DEPRECATION SDL_HIDAPI_DISABLED=1"; 10 | GCC_SYMBOLS_PRIVATE_EXTERN = YES; 11 | SKIP_INSTALL = YES; 12 | SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; 13 | @@ -9686,7 +9686,7 @@ 14 | buildSettings = { 15 | CLANG_ENABLE_OBJC_ARC = YES; 16 | CLANG_LINK_OBJC_RUNTIME = NO; 17 | - GCC_PREPROCESSOR_DEFINITIONS = GLES_SILENCE_DEPRECATION; 18 | + GCC_PREPROCESSOR_DEFINITIONS = "GLES_SILENCE_DEPRECATION SDL_HIDAPI_DISABLED=1"; 19 | GCC_SYMBOLS_PRIVATE_EXTERN = YES; 20 | SKIP_INSTALL = YES; 21 | SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; 22 | -------------------------------------------------------------------------------- /kivy_ios/recipes/libjpeg/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import Recipe, shprint 2 | from os.path import join 3 | import sh 4 | 5 | 6 | class JpegRecipe(Recipe): 7 | version = "v9d" 8 | url = "http://www.ijg.org/files/jpegsrc.{version}.tar.gz" 9 | library = ".libs/libjpeg.a" 10 | include_dir = [ 11 | ("jpeglib.h", ""), 12 | ("jconfig.h", ""), 13 | ("jerror.h", ""), 14 | ("jmorecfg.h", ""), 15 | ] 16 | include_per_arch = True 17 | 18 | def build_arch(self, arch): 19 | build_env = arch.get_env() 20 | configure = sh.Command(join(self.build_dir, "configure")) 21 | shprint(configure, 22 | "CC={}".format(build_env["CC"]), 23 | "LD={}".format(build_env["LD"]), 24 | "CFLAGS={}".format(build_env["CFLAGS"]), 25 | "LDFLAGS={}".format(build_env["LDFLAGS"]), 26 | "--prefix=/", 27 | "--host={}".format(arch.triple), 28 | "--disable-shared") 29 | shprint(sh.make, "clean") 30 | shprint(sh.make, self.ctx.concurrent_make) 31 | 32 | 33 | recipe = JpegRecipe() 34 | -------------------------------------------------------------------------------- /kivy_ios/recipes/sdl2_image/fix-ios-xcodebuild.patch: -------------------------------------------------------------------------------- 1 | diff --git a/Xcode/SDL_image.xcodeproj/project.pbxproj b/Xcode/SDL_image.xcodeproj/project.pbxproj 2 | index a64c8665..69565703 100644 3 | --- a/Xcode/SDL_image.xcodeproj/project.pbxproj 4 | +++ b/Xcode/SDL_image.xcodeproj/project.pbxproj 5 | @@ -7,7 +7,7 @@ 6 | objects = { 7 | 8 | /* Begin PBXBuildFile section */ 9 | - 007288A80F0DA79800C302A9 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 007288A60F0DA79800C302A9 /* ApplicationServices.framework */; }; 10 | + 007288A80F0DA79800C302A9 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 007288A60F0DA79800C302A9 /* ApplicationServices.framework */; platformFilters = (macos, ); }; 11 | 6313BF532785566D00F268AD /* IMG_qoi.c in Sources */ = {isa = PBXBuildFile; fileRef = 6313BF522785566D00F268AD /* IMG_qoi.c */; }; 12 | 6313BF542785566D00F268AD /* IMG_qoi.c in Sources */ = {isa = PBXBuildFile; fileRef = 6313BF522785566D00F268AD /* IMG_qoi.c */; }; 13 | AA23FC7D20A2A1B90017DFB9 /* IMG_svg.c in Sources */ = {isa = PBXBuildFile; fileRef = AA50AA461F9C7C50003B9C0C /* IMG_svg.c */; }; 14 | -------------------------------------------------------------------------------- /kivy_ios/recipes/py3dns/ios.patch: -------------------------------------------------------------------------------- 1 | diff --git a/DNS/Base.py b/DNS/Base.py 2 | index 34a6da7..b755e79 100644 3 | --- a/DNS/Base.py 4 | +++ b/DNS/Base.py 5 | @@ -15,6 +15,7 @@ import socket, string, types, time, select 6 | import errno 7 | from . import Type,Class,Opcode 8 | import asyncore 9 | +import os 10 | # 11 | # This random generator is used for transaction ids and port selection. This 12 | # is important to prevent spurious results from lost packets, and malicious 13 | @@ -50,8 +51,12 @@ defaults= { 'protocol':'udp', 'port':53, 'opcode':Opcode.QUERY, 14 | 15 | def ParseResolvConf(resolv_path="/etc/resolv.conf"): 16 | "parses the /etc/resolv.conf file and sets defaults for name servers" 17 | - with open(resolv_path, 'r') as stream: 18 | - return ParseResolvConfFromIterable(stream) 19 | + if os.path.exists(resolv_path): 20 | + with open(resolv_path, 'r') as stream: 21 | + return ParseResolvConfFromIterable(stream) 22 | + else: 23 | + defaults['server'].extend(['8.8.8.8', '8.8.4.4']) 24 | + return 25 | 26 | def ParseResolvConfFromIterable(lines): 27 | "parses a resolv.conf formatted stream and sets defaults for name servers" 28 | -------------------------------------------------------------------------------- /.github/workflows/support.yml: -------------------------------------------------------------------------------- 1 | name: 'Support Requests' 2 | 3 | on: 4 | issues: 5 | types: [labeled, unlabeled, reopened] 6 | 7 | permissions: 8 | issues: write 9 | 10 | jobs: 11 | action: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: dessant/support-requests@v2 15 | with: 16 | github-token: ${{ github.token }} 17 | support-label: 'support' 18 | issue-comment: > 19 | 👋 We use the issue tracker exclusively for bug reports and feature requests. 20 | However, this issue appears to be a support request. Please use our 21 | [support channels](https://github.com/kivy/kivy-ios/blob/master/README.md#support) 22 | to get help with the project. 23 | 24 | 25 | If you're having trouble installing or using kivy-ios, 26 | maybe you could be interested to [installation and requirements](https://github.com/kivy/kivy-ios/blob/master/README.md#installation--requirements). 27 | 28 | 29 | Let us know if this comment was made in error, and we'll be happy 30 | to reopen the issue. 31 | close-issue: true 32 | lock-issue: false -------------------------------------------------------------------------------- /kivy_ios/recipes/openssl/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import Recipe, shprint 2 | from os.path import join 3 | import sh 4 | 5 | 6 | arch_mapper = {'x86_64': 'darwin64-x86_64-cc', 7 | 'arm64': 'ios64-cross'} 8 | 9 | 10 | class OpensslRecipe(Recipe): 11 | version = "1.1.1l" 12 | url = "http://www.openssl.org/source/openssl-{version}.tar.gz" 13 | libraries = ["libssl.a", "libcrypto.a"] 14 | include_dir = "include" 15 | include_per_arch = True 16 | 17 | def build_arch(self, arch): 18 | build_env = arch.get_env() 19 | target = arch_mapper[arch.arch_arg()] 20 | shprint(sh.env, _env=build_env) 21 | sh.perl(join(self.build_dir, "Configure"), 22 | target, 23 | _env=build_env) 24 | if target.endswith('-cross'): 25 | with open('Makefile', 'r') as makefile: 26 | filedata = makefile.read() 27 | filedata = filedata.replace('$(CROSS_TOP)/SDKs/$(CROSS_SDK)', arch.sysroot) 28 | with open('Makefile', 'w') as makefile: 29 | makefile.write(filedata) 30 | shprint(sh.make, "clean") 31 | shprint(sh.make, self.ctx.concurrent_make, "build_libs") 32 | 33 | 34 | recipe = OpensslRecipe() 35 | -------------------------------------------------------------------------------- /kivy_ios/recipes/numpy/duplicated_symbols.patch: -------------------------------------------------------------------------------- 1 | diff -Naur numpy-1.24.2.orig/numpy/linalg/setup.py numpy-1.24.2/numpy/linalg/setup.py 2 | --- numpy-1.24.2.orig/numpy/linalg/setup.py 2023-03-11 19:31:34 3 | +++ numpy-1.24.2/numpy/linalg/setup.py 2023-03-11 19:32:12 4 | @@ -78,7 +78,7 @@ 5 | # umath_linalg module 6 | config.add_extension( 7 | '_umath_linalg', 8 | - sources=['umath_linalg.cpp', get_lapack_lite_sources], 9 | + sources=['umath_linalg.cpp'], 10 | depends=['lapack_lite/f2c.h'], 11 | extra_info=lapack_info, 12 | extra_cxx_compile_args=NPY_CXX_FLAGS, 13 | diff -Naur numpy-1.24.2.orig/numpy/random/setup.py numpy-1.24.2/numpy/random/setup.py 14 | --- numpy-1.24.2.orig/numpy/random/setup.py 2023-03-11 19:38:04 15 | +++ numpy-1.24.2/numpy/random/setup.py 2023-03-11 19:39:48 16 | @@ -139,7 +139,6 @@ 17 | config.add_extension('mtrand', 18 | sources=['mtrand.c', 19 | 'src/legacy/legacy-distributions.c', 20 | - 'src/distributions/distributions.c', 21 | ], 22 | include_dirs=['.', 'src', 'src/legacy'], 23 | libraries=mtrand_libs, -------------------------------------------------------------------------------- /kivy_ios/recipes/markupsafe/__init__.py: -------------------------------------------------------------------------------- 1 | # pure-python package, this can be removed when we'll support any python package 2 | from kivy_ios.toolchain import PythonRecipe, shprint 3 | from os.path import join 4 | import sh 5 | import os 6 | 7 | 8 | class MarkupSafeRecipe(PythonRecipe): 9 | version = "1.1.1" 10 | url = "https://github.com/mitsuhiko/markupsafe/archive/{version}.zip" 11 | depends = ["python"] 12 | 13 | def install(self): 14 | arch = list(self.filtered_archs)[0] 15 | build_dir = self.get_build_dir(arch.arch) 16 | os.chdir(build_dir) 17 | hostpython = sh.Command(self.ctx.hostpython) 18 | build_env = arch.get_env() 19 | dest_dir = join(self.ctx.dist_dir, "root", "python3") 20 | build_env['PYTHONPATH'] = self.ctx.site_packages_dir 21 | cmd = sh.Command("sed") 22 | shprint(cmd, "-i", "", "s/,.*Feature//g", "./setup.py", _env=build_env) 23 | shprint(cmd, "-i", "", "/^speedups = Feature/,/^)$/s/.*//g", "./setup.py", _env=build_env) 24 | shprint(cmd, "-i", "", "s/features\['speedups'\].*=.*speedups/pass/g", "./setup.py", _env=build_env) # noqa: W605 25 | shprint(hostpython, "setup.py", "install", "--prefix", dest_dir, _env=build_env) 26 | 27 | 28 | recipe = MarkupSafeRecipe() 29 | -------------------------------------------------------------------------------- /kivy_ios/recipes/sdl2_mixer/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import Recipe, shprint 2 | import sh 3 | 4 | 5 | class LibSDL2MixerRecipe(Recipe): 6 | version = "2.6.2" 7 | url = "https://github.com/libsdl-org/SDL_mixer/releases/download/release-{version}/SDL2_mixer-{version}.tar.gz" 8 | library = "Xcode/build/Release-{arch.sdk}/libSDL2_mixer.a" 9 | include_dir = "include/SDL_mixer.h" 10 | depends = ["sdl2"] 11 | pbx_frameworks = ["ImageIO"] 12 | pbx_libraries = ["libc++"] 13 | 14 | def build_arch(self, arch): 15 | # endian.h is in /usr/include/machine/ (Since MacOs Mojave?) 16 | # header is needed by libvorbis, so We're adding that folder 17 | # to HEADER_SEARCH_PATHS 18 | shprint(sh.xcodebuild, self.ctx.concurrent_xcodebuild, 19 | "ONLY_ACTIVE_ARCH=NO", 20 | "ARCHS={}".format(arch.arch), 21 | "BITCODE_GENERATION_MODE=bitcode", 22 | "HEADER_SEARCH_PATHS=$HEADER_SEARCH_PATHS /usr/include/machine {} ".format(" ".join(arch.include_dirs)), 23 | "-sdk", arch.sdk, 24 | "-project", "Xcode/SDL_mixer.xcodeproj", 25 | "-target", "Static Library", 26 | "-configuration", "Release") 27 | 28 | 29 | recipe = LibSDL2MixerRecipe() 30 | -------------------------------------------------------------------------------- /kivy_ios/recipes/netifaces/__init__.py: -------------------------------------------------------------------------------- 1 | import sh 2 | 3 | from os.path import join 4 | 5 | from kivy_ios.toolchain import CythonRecipe, shprint 6 | from kivy_ios.context_managers import cd 7 | 8 | 9 | class NetifacesRecipe(CythonRecipe): 10 | version = "0.10.9" 11 | url = "https://pypi.io/packages/source/n/netifaces/netifaces-{version}.tar.gz" 12 | depends = ["python3"] 13 | python_depends = ["setuptools"] 14 | library = "libnetifaces.a" 15 | cythonize = False 16 | 17 | def dest_dir(self): 18 | return join(self.ctx.dist_dir, "root", "python3") 19 | 20 | def get_netifaces_env(self, arch): 21 | build_env = arch.get_env() 22 | build_env["PYTHONPATH"] = self.ctx.site_packages_dir 23 | return build_env 24 | 25 | def install(self): 26 | arch = list(self.filtered_archs)[0] 27 | build_dir = self.get_build_dir(arch.arch) 28 | build_env = self.get_netifaces_env(arch) 29 | hostpython = sh.Command(self.ctx.hostpython) 30 | with cd(build_dir): 31 | shprint( 32 | hostpython, 33 | "setup.py", 34 | "install", 35 | "--prefix", 36 | self.dest_dir(), 37 | _env=build_env, 38 | ) 39 | 40 | 41 | recipe = NetifacesRecipe() 42 | -------------------------------------------------------------------------------- /kivy_ios/context_managers.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module houses context managers to assist in the managing of state during 3 | kivy-ios builds. 4 | """ 5 | from logging import getLogger 6 | from contextlib import contextmanager 7 | from os import getcwd, chdir, environ 8 | from os.path import expanduser 9 | 10 | 11 | logger = getLogger(__name__) 12 | 13 | 14 | @contextmanager 15 | def cd(newdir): 16 | """ 17 | Set the current working directory to `newdir` for the duration of the 18 | context. 19 | """ 20 | prevdir = getcwd() 21 | logger.info("cd {}".format(newdir)) 22 | chdir(expanduser(newdir)) 23 | try: 24 | yield 25 | finally: 26 | logger.info("cd {}".format(prevdir)) 27 | chdir(prevdir) 28 | 29 | 30 | @contextmanager 31 | def python_path(newdir): 32 | """ 33 | Set the PYTHONPATH environmnet variable to `newdir` for the duraiton of the 34 | context. 35 | """ 36 | prevdir = environ.get("PYTHONPATH") 37 | logger.debug("Setting PYTHONPATH to {}".format(newdir)) 38 | environ["PYTHONPATH"] = newdir 39 | try: 40 | yield 41 | finally: 42 | logger.debug("Setting PYTHONPATH to {}".format(prevdir)) 43 | if prevdir is None: 44 | environ.pop("PYTHONPATH") 45 | else: 46 | environ["PYTHONPATH"] = prevdir 47 | -------------------------------------------------------------------------------- /tests/test_python3/main.py: -------------------------------------------------------------------------------- 1 | print("Python 3 running!") 2 | import sys # noqa: E402 3 | print(f"sys.path: {sys.path}") 4 | import traceback # noqa: E402 5 | 6 | modules_to_tests = [ 7 | "math", "_sre", "array", 8 | "binascii", "multiprocessing", 9 | "subprocess" 10 | ] 11 | 12 | for name in modules_to_tests: 13 | print(f"- import {name}: ", end="") 14 | try: 15 | __import__(name) 16 | print("OK") 17 | except ImportError: 18 | print("FAIL") 19 | traceback.print_exc() 20 | 21 | # test pyobjus 22 | print("- import pyobjus start") 23 | import pyobjus # noqa: F401, E402 24 | print("- import done") 25 | from pyobjus import autoclass # noqa: E402 26 | NSNotificationCenter = autoclass("NSNotificationCenter") 27 | 28 | # test ios 29 | import ios # noqa: F401, E402 30 | 31 | from kivy.app import App # noqa: E402 32 | from kivy.lang import Builder # noqa: E402 33 | 34 | 35 | class TestApp(App): 36 | def build(self): 37 | return Builder.load_string(""" 38 | RelativeLayout: 39 | GridLayout: 40 | cols: 2 41 | 42 | Button: 43 | text: "Hello Python 3!" 44 | font_size: dp(48) 45 | 46 | TextInput: 47 | font_size: dp(24) 48 | 49 | Widget 50 | Widget 51 | 52 | """) 53 | 54 | 55 | TestApp().run() 56 | -------------------------------------------------------------------------------- /kivy_ios/recipes/ffpyplayer/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import CythonRecipe 2 | from os.path import join 3 | 4 | 5 | class FFPyplayerRecipe(CythonRecipe): 6 | version = "v4.3.5" 7 | url = "https://github.com/matham/ffpyplayer/archive/{version}.zip" 8 | library = "libffpyplayer.a" 9 | depends = ["python", "sdl2", "ffmpeg"] 10 | pbx_frameworks = [ 11 | "CoreVideo", "CoreMedia", "CoreImage", "AVFoundation", "UIKit", 12 | "CoreMotion"] 13 | pbx_libraries = ["libiconv"] 14 | pre_build_ext = True 15 | 16 | def get_recipe_env(self, arch): 17 | env = super(FFPyplayerRecipe, self).get_recipe_env(arch) 18 | env["CC"] += " -I{}".format( 19 | join(self.ctx.dist_dir, "include", arch.arch, "libffi")) 20 | env["SDL_INCLUDE_DIR"] = join(self.ctx.dist_dir, "include", 21 | "common", "sdl2") 22 | env["FFMPEG_INCLUDE_DIR"] = join(self.ctx.dist_dir, "include", 23 | arch.arch, "ffmpeg") 24 | env["CONFIG_POSTPROC"] = "0" 25 | return env 26 | 27 | def prebuild_arch(self, arch): 28 | # common to all archs 29 | if self.has_marker("patched"): 30 | return 31 | self.apply_patch("misc-visibility.patch") 32 | self.set_marker("patched") 33 | 34 | 35 | recipe = FFPyplayerRecipe() 36 | -------------------------------------------------------------------------------- /kivy_ios/recipes/freetype/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import Recipe, shprint 2 | from os.path import join 3 | import sh 4 | 5 | 6 | class FreetypeRecipe(Recipe): 7 | version = "2.5.5" 8 | url = "https://download.savannah.gnu.org/releases/freetype/freetype-old/freetype-{version}.tar.bz2" 9 | library = "objs/.libs/libfreetype.a" 10 | include_dir = ["include", ("builds/unix/ftconfig.h", "config/ftconfig.h")] 11 | include_per_arch = True 12 | 13 | def build_arch(self, arch): 14 | build_env = arch.get_env() 15 | configure = sh.Command(join(self.build_dir, "configure")) 16 | shprint(configure, 17 | "CC={}".format(build_env["CC"]), 18 | "LD={}".format(build_env["LD"]), 19 | "CFLAGS={}".format(build_env["CFLAGS"]), 20 | "LDFLAGS={}".format(build_env["LDFLAGS"]), 21 | "--prefix=/", 22 | "--host={}".format(arch.triple), 23 | "--without-png", 24 | "--without-bzip2", 25 | "--without-fsspec", 26 | "--without-harfbuzz", 27 | "--without-old-mac-fonts", 28 | "--enable-static=yes", 29 | "--enable-shared=no") 30 | shprint(sh.make, "clean") 31 | shprint(sh.make, self.ctx.concurrent_make) 32 | 33 | 34 | recipe = FreetypeRecipe() 35 | -------------------------------------------------------------------------------- /kivy_ios/recipes/sdl2/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import Recipe, shprint 2 | import sh 3 | 4 | 5 | class LibSDL2Recipe(Recipe): 6 | version = "2.24.1" 7 | url = "https://github.com/libsdl-org/SDL/releases/download/release-{version}/SDL2-{version}.tar.gz" 8 | library = "Xcode/SDL/build/Release-{arch.sdk}/libSDL2.a" 9 | include_dir = "include" 10 | pbx_frameworks = [ 11 | "OpenGLES", "AudioToolbox", "QuartzCore", "CoreGraphics", 12 | "CoreMotion", "GameController", "AVFoundation", "Metal", 13 | "UIKit", "CoreHaptics"] 14 | 15 | def prebuild_arch(self, arch): 16 | if self.has_marker("patched"): 17 | return 18 | self.apply_patch("uikit-transparent.patch") 19 | self.apply_patch("disable-hidapi.patch") 20 | self.set_marker("patched") 21 | 22 | def build_arch(self, arch): 23 | env = arch.get_env() 24 | shprint(sh.xcodebuild, self.ctx.concurrent_xcodebuild, 25 | "ONLY_ACTIVE_ARCH=NO", 26 | "ARCHS={}".format(arch.arch), 27 | "BITCODE_GENERATION_MODE=bitcode", 28 | "CC={}".format(env['CC']), 29 | "-sdk", arch.sdk, 30 | "-project", "Xcode/SDL/SDL.xcodeproj", 31 | "-target", "Static Library-iOS", 32 | "-configuration", "Release") 33 | 34 | 35 | recipe = LibSDL2Recipe() 36 | -------------------------------------------------------------------------------- /kivy_ios/recipes/sdl2_image/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import Recipe, shprint 2 | from os.path import join 3 | import sh 4 | 5 | 6 | class LibSDL2ImageRecipe(Recipe): 7 | version = "2.6.2" 8 | url = "https://github.com/libsdl-org/SDL_image/releases/download/release-{version}/SDL2_image-{version}.tar.gz" 9 | library = "Xcode/build/Release-{arch.sdk}/libSDL2_image.a" 10 | include_dir = "SDL_image.h" 11 | depends = ["sdl2"] 12 | pbx_frameworks = ["CoreGraphics", "MobileCoreServices"] 13 | 14 | def prebuild_arch(self, arch): 15 | if self.has_marker("patched"): 16 | return 17 | # fix-ios-xcodebuild is a patch taken from the SDL2_image repo 18 | # (See: https://github.com/libsdl-org/SDL_image/pull/292) 19 | # We will need to remove it once is included into a release 20 | self.apply_patch("fix-ios-xcodebuild.patch") 21 | self.set_marker("patched") 22 | 23 | def build_arch(self, arch): 24 | shprint(sh.xcodebuild, self.ctx.concurrent_xcodebuild, 25 | "ONLY_ACTIVE_ARCH=NO", 26 | "ARCHS={}".format(arch.arch), 27 | "BITCODE_GENERATION_MODE=bitcode", 28 | "HEADER_SEARCH_PATHS={}".format( 29 | join(self.ctx.include_dir, "common", "sdl2")), 30 | "-sdk", arch.sdk, 31 | "-project", "Xcode/SDL_image.xcodeproj", 32 | "-target", "Static Library", 33 | "-configuration", "Release") 34 | 35 | 36 | recipe = LibSDL2ImageRecipe() 37 | -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/bridge.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface bridge : NSObject { 5 | NSOperationQueue *queue; 6 | } 7 | 8 | @property (strong, nonatomic) CMMotionManager *motionManager; 9 | @property (nonatomic) double ac_x; 10 | @property (nonatomic) double ac_y; 11 | @property (nonatomic) double ac_z; 12 | 13 | @property (nonatomic) double gy_x; 14 | @property (nonatomic) double gy_y; 15 | @property (nonatomic) double gy_z; 16 | 17 | @property (nonatomic) double mg_x; 18 | @property (nonatomic) double mg_y; 19 | @property (nonatomic) double mg_z; 20 | 21 | @property (nonatomic) double sp_yaw; 22 | @property (nonatomic) double sp_pitch; 23 | @property (nonatomic) double sp_roll; 24 | 25 | @property (nonatomic) double g_x; 26 | @property (nonatomic) double g_y; 27 | @property (nonatomic) double g_z; 28 | 29 | @property (nonatomic) double q_x; 30 | @property (nonatomic) double q_y; 31 | @property (nonatomic) double q_z; 32 | @property (nonatomic) double q_w; 33 | 34 | @property (nonatomic) double rotation_rate_x; 35 | @property (nonatomic) double rotation_rate_y; 36 | @property (nonatomic) double rotation_rate_z; 37 | 38 | @property (nonatomic) double user_acc_x; 39 | @property (nonatomic) double user_acc_y; 40 | @property (nonatomic) double user_acc_z; 41 | 42 | @property (nonatomic) double mf_x; 43 | @property (nonatomic) double mf_y; 44 | @property (nonatomic) double mf_z; 45 | 46 | @property (nonatomic) double relative_altitude; 47 | @property (nonatomic) double pressure; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /.ci/rebuild_updated_recipes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Continuous Integration helper script. 4 | Automatically detects recipes modified in a changeset (compares with master) 5 | and recompiles them. 6 | 7 | Current limitations: 8 | - will fail on conflicting requirements 9 | """ 10 | import sh 11 | import subprocess 12 | from fnmatch import fnmatch 13 | from constants import CORE_RECIPES, BROKEN_RECIPES 14 | 15 | 16 | def modified_recipes(branch="origin/master"): 17 | """ 18 | Returns a set of modified recipes between the current branch and the one 19 | in param. 20 | """ 21 | # using the contrib version on purpose rather than sh.git, since it comes 22 | # with a bunch of fixes, e.g. disabled TTY, see: 23 | # https://stackoverflow.com/a/20128598/185510 24 | sh.contrib.git.fetch("origin", "master") 25 | git_diff = sh.contrib.git.diff("--name-only", "--diff-filter=d", branch) 26 | recipes = set() 27 | for file_path in git_diff: 28 | if fnmatch(file_path, "kivy_ios/recipes/*/__init__.py\n"): 29 | recipe = file_path.split("/")[2] 30 | recipes.add(recipe) 31 | return recipes 32 | 33 | 34 | def main(): 35 | recipes = modified_recipes() 36 | recipes -= CORE_RECIPES 37 | recipes -= BROKEN_RECIPES 38 | updated_recipes = " ".join(recipes) 39 | if updated_recipes != "": 40 | subprocess.run( 41 | f"toolchain build {updated_recipes}", shell=True, check=True 42 | ) 43 | else: 44 | print("Nothing to do. No updated recipes.") 45 | 46 | 47 | if __name__ == "__main__": 48 | main() 49 | -------------------------------------------------------------------------------- /kivy_ios/recipes/hostopenssl/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import HostRecipe, shprint 2 | from os.path import join 3 | import sh 4 | import logging 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | arch_mapper = {'x86_64': 'darwin64-x86_64-cc', 9 | 'arm64': 'darwin64-arm64-cc'} 10 | 11 | 12 | class HostOpensslRecipe(HostRecipe): 13 | version = "1.1.1l" 14 | url = "http://www.openssl.org/source/openssl-{version}.tar.gz" 15 | 16 | def get_build_env(self): 17 | build_env = self.ctx.env.copy() 18 | self.build_env = build_env 19 | return build_env 20 | 21 | def build_arch(self, arch): 22 | build_env = self.get_build_env() 23 | configure = sh.Command(join(self.build_dir, "Configure")) 24 | shprint(configure, 25 | arch_mapper[arch.arch], 26 | _env=build_env) 27 | shprint(sh.make, "clean") 28 | shprint(sh.make, self.ctx.concurrent_make, "build_libs") 29 | 30 | def install(self): 31 | arch = self.archs[0] 32 | sh.mkdir('-p', join(self.ctx.dist_dir, 'hostopenssl')) 33 | sh.cp('-r', join(self.get_build_dir(arch), 'include'), 34 | join(self.ctx.dist_dir, 'hostopenssl', 'include')) 35 | sh.mkdir('-p', join(self.ctx.dist_dir, 'hostopenssl', 'lib')) 36 | sh.cp(join(self.get_build_dir(arch), 'libssl.a'), 37 | join(self.ctx.dist_dir, 'hostopenssl', 'lib')) 38 | sh.cp(join(self.get_build_dir(arch), 'libcrypto.a'), 39 | join(self.ctx.dist_dir, 'hostopenssl', 'lib')) 40 | 41 | 42 | recipe = HostOpensslRecipe() 43 | -------------------------------------------------------------------------------- /kivy_ios/recipes/ios/src/ios_browser.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Browser support 3 | */ 4 | 5 | #import 6 | #import 7 | #include 8 | #include "ios_wrapper.h" 9 | 10 | void ios_open_url(char *url) 11 | { 12 | NSString *nsurl = [NSString stringWithCString:(char *)url encoding:NSUTF8StringEncoding]; 13 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString: nsurl]]; 14 | } 15 | 16 | /* 17 | * Webview support 18 | */ 19 | void load_url_webview(char *url, int x, int y, int width, int height) 20 | { 21 | NSString *nsurl = [NSString stringWithCString:(char *)url encoding:NSUTF8StringEncoding]; 22 | WKWebView *webView = [[WKWebView alloc] initWithFrame: CGRectMake(x, y, width, height)]; 23 | UIWindow *window = [[UIApplication sharedApplication] keyWindow]; 24 | UIView *view = [window.rootViewController view]; 25 | [view addSubview:webView]; 26 | NSURL *ur = [[NSURL alloc] initWithString: nsurl]; 27 | NSURLRequest *req = [[NSURLRequest alloc] initWithURL: ur]; 28 | [webView loadRequest: req]; 29 | [req release]; 30 | [ur release]; 31 | 32 | UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 33 | [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 34 | [button setTitle:@"X" forState:UIControlStateNormal]; 35 | button.frame = CGRectMake(0.0, 0.0, 40, 40); 36 | [button addTarget:webView 37 | action:@selector(removeFromSuperview) forControlEvents:UIControlEventTouchDown]; 38 | [webView addSubview:button]; 39 | [button release]; 40 | [webView release]; 41 | } 42 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 22 | 23 | **Versions** 24 | 25 | * Python : 26 | * MacOS version : 27 | * XCode Version : 28 | * Cython version : 29 | 30 | **Describe the bug** 31 | // REPLACE ME: A clear and concise description of what the bug is. 32 | 33 | **To Reproduce** 34 | // REPLACE ME: Add your `toolchain.py ....` command or a complete explanation of what you did so We can reproduce your error. 35 | 36 | **Expected behavior** 37 | // REPLACE ME: A clear and concise description of what you expected to happen. 38 | 39 | **Logs** 40 | ``` 41 | // REPLACE ME: Paste the build output containing the error 42 | ``` 43 | 44 | **Screenshots** 45 | 49 | 50 | **Additional context** 51 | Add any other context about the problem here. 52 | -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/pictures.kv: -------------------------------------------------------------------------------- 1 | #:kivy 1.0 2 | #:import kivy kivy 3 | #:import win kivy.core.window 4 | 5 | FloatLayout: 6 | canvas: 7 | Color: 8 | rgb: 1, 1, 1 9 | Rectangle: 10 | source: 'data/images/background.jpg' 11 | size: self.size 12 | 13 | BoxLayout: 14 | padding: 10 15 | spacing: 10 16 | size_hint: 1, None 17 | pos_hint: {'top': 1} 18 | height: 44 19 | Image: 20 | size_hint: None, None 21 | size: 24, 24 22 | source: 'data/logo/kivy-icon-24.png' 23 | Label: 24 | height: 24 25 | text_size: self.width, None 26 | color: (1, 1, 1, .8) 27 | text: 'Kivy %s - Pictures' % kivy.__version__ 28 | 29 | 30 | 31 | : 32 | # each time a picture is created, the image can delay the loading 33 | # as soon as the image is loaded, ensure that the center is changed 34 | # to the center of the screen. 35 | on_size: self.center = win.Window.center 36 | size: image.size 37 | size_hint: None, None 38 | 39 | Image: 40 | id: image 41 | source: root.source 42 | 43 | # create initial image to be 400 pixels width 44 | size: 400, 400 / self.image_ratio 45 | 46 | # add shadow background 47 | canvas.before: 48 | Color: 49 | rgba: 1,1,1,1 50 | BorderImage: 51 | source: 'shadow32.png' 52 | border: (36,36,36,36) 53 | size:(self.width+72, self.height+72) 54 | pos: (-36,-36) 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/{{ cookiecutter.project_name }}-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIcons 12 | 13 | CFBundleIdentifier 14 | {{ cookiecutter.domain_name }} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.1 23 | CFBundleVersion 24 | 1.1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | Launch Screen 29 | UIStatusBarHidden 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationLandscapeLeft 34 | UIInterfaceOrientationLandscapeRight 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationPortraitUpsideDown 37 | 38 | UISupportedInterfaceOrientations~ipad 39 | 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationPortraitUpsideDown 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /kivy_ios/recipes/ios/src/ios_filechooser.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #include "ios_wrapper.h" 4 | 5 | @interface NativeImagePicker : NSObject 6 | @end 7 | @implementation NativeImagePicker 8 | 9 | - (NSString*) getFileName:(NSURL *) ns_url { 10 | // Return the file name without the path or file extention 11 | NSString *image_name = ns_url.pathComponents.lastObject; 12 | NSArray *listItems = [image_name componentsSeparatedByString:@"."]; 13 | NSString *ret = listItems[0]; 14 | return ret; 15 | } 16 | 17 | - (NSString*) writeToPNG: (NSDictionary *) info { 18 | /* Given the info frozen dictionary returned by the file picker, convert 19 | the image selected to a PNG and return the full path. */ 20 | 21 | // Get the image name, stripped of path and extention 22 | NSString *image_name = [self getFileName: info[UIImagePickerControllerImageURL]]; 23 | 24 | // Get the png representation of the image 25 | UIImage *image = info[UIImagePickerControllerOriginalImage]; 26 | NSData *imageData = UIImagePNGRepresentation(image); 27 | 28 | // Generate the final image destination 29 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 30 | NSString *documentsDirectory = [paths objectAtIndex:0]; 31 | NSString *imagePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", image_name]]; 32 | 33 | // Write the image data to the file 34 | if (![imageData writeToFile:imagePath atomically:NO]) 35 | { 36 | NSLog(@"Failed to cache image data to disk"); 37 | return @""; 38 | } 39 | else 40 | { 41 | NSLog(@"the cachedImagedPath is %@",imagePath); 42 | return imagePath; 43 | } 44 | } 45 | @end -------------------------------------------------------------------------------- /kivy_ios/recipes/pycrypto/__init__.py: -------------------------------------------------------------------------------- 1 | '''Recipe for pycrypto on ios 2 | ''' 3 | from kivy_ios.toolchain import CythonRecipe, shprint 4 | from kivy_ios.context_managers import cd 5 | from os.path import join 6 | import sh 7 | 8 | 9 | class PycryptoRecipe(CythonRecipe): 10 | version = "2.6.1" 11 | url = "https://ftp.dlitz.net/pub/dlitz/crypto/pycrypto/pycrypto-{version}.tar.gz" 12 | depends = ["python", "openssl"] 13 | include_per_arch = True 14 | library = "libpycrypto.a" 15 | 16 | def build_arch(self, arch): 17 | build_env = arch.get_env() 18 | self.apply_patch('hash_SHA2_template.c.patch', target_dir=self.build_dir + '/src') 19 | configure = sh.Command(join(self.build_dir, "configure")) 20 | shprint(configure, 21 | "CC={}".format(build_env["CC"]), 22 | "LD={}".format(build_env["LD"]), 23 | "CFLAGS={}".format(build_env["CFLAGS"]), 24 | "LDFLAGS={} -Wno-error ".format(build_env["LDFLAGS"]), 25 | "--prefix=/", 26 | "--host={}".format(arch), 27 | "ac_cv_func_malloc_0_nonnull=yes", 28 | "ac_cv_func_realloc_0_nonnull=yes") 29 | super(PycryptoRecipe, self).build_arch(arch) 30 | 31 | def install(self): 32 | arch = list(self.filtered_archs)[0] 33 | build_dir = self.get_build_dir(arch.arch) 34 | hostpython = sh.Command(self.ctx.hostpython) 35 | build_env = arch.get_env() 36 | dest_dir = join(self.ctx.dist_dir, "root", "python") 37 | build_env['PYTHONPATH'] = join(dest_dir, 'lib', 'python3.7', 'site-packages') 38 | with cd(build_dir): 39 | shprint(hostpython, "setup.py", "install", "--prefix", dest_dir, _env=build_env) 40 | 41 | 42 | recipe = PycryptoRecipe() 43 | -------------------------------------------------------------------------------- /kivy_ios/tools/cythonize.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | 7 | # resolve cython executable 8 | cython = None 9 | 10 | 11 | def resolve_cython(): 12 | global cython 13 | for executable in ('cython', 'cython-2.7'): 14 | for path in os.environ['PATH'].split(':'): 15 | if not os.path.exists(path): 16 | continue 17 | if executable in os.listdir(path): 18 | cython = os.path.join(path, executable) 19 | return 20 | 21 | 22 | def do(fn): 23 | print('cythonize:', fn) 24 | assert fn.endswith('.pyx') 25 | parts = fn.split('/') 26 | if parts[0] == '.': 27 | parts.pop(0) 28 | modname = parts[-1][:-4] 29 | package = '_'.join(parts[:-1]) 30 | 31 | # cythonize 32 | subprocess.Popen([cython, fn], env=os.environ).communicate() 33 | 34 | if not package: 35 | print('no need to rewrite', fn) 36 | else: 37 | # get the .c, and change the initXXX 38 | fn_c = fn[:-3] + 'c' 39 | with open(fn_c) as fd: 40 | data = fd.read() 41 | modname = modname.split('.')[-1] 42 | pac_mod = '{}_{}'.format(package, modname) 43 | fmts = ('init{}(void)', 'PyInit_{}(void)', 'Pyx_NAMESTR("{}")', '"{}",') 44 | for i, fmt in enumerate(fmts): 45 | pat = fmt.format(modname) 46 | sub = fmt.format(pac_mod) 47 | print('{}: {} -> {}'.format(i + 1, pat, sub)) 48 | data = data.replace(pat, sub) 49 | print('rewrite', fn_c) 50 | with open(fn_c, 'w') as fd: 51 | fd.write(data) 52 | 53 | 54 | if __name__ == '__main__': 55 | print('-- cythonize', sys.argv) 56 | resolve_cython() 57 | for fn in sys.argv[1:]: 58 | do(fn) 59 | -------------------------------------------------------------------------------- /kivy_ios/recipes/ffpyplayer/misc-visibility.patch: -------------------------------------------------------------------------------- 1 | diff --git a/ffpyplayer/clib/misc.c b/ffpyplayer/clib/misc.c 2 | index 55181d1..6011ffa 100644 3 | --- a/ffpyplayer/clib/misc.c 4 | +++ b/ffpyplayer/clib/misc.c 5 | @@ -1,8 +1,7 @@ 6 | - 7 | #include "misc.h" 8 | 9 | #define FLAGS (o->type == AV_OPT_TYPE_FLAGS) ? AV_DICT_APPEND : 0 10 | -void print_all_libs_info(int flags, int level) 11 | +void __attribute__ ((visibility ("hidden"))) print_all_libs_info(int flags, int level) 12 | { 13 | #if CONFIG_AVUTIL 14 | PRINT_LIB_INFO(avutil, AVUTIL, flags, level); 15 | @@ -30,7 +29,7 @@ void print_all_libs_info(int flags, int level) 16 | #endif 17 | } 18 | 19 | -const AVOption *opt_find(void *obj, const char *name, const char *unit, 20 | +const AVOption __attribute__ ((visibility ("hidden"))) *opt_find(void *obj, const char *name, const char *unit, 21 | int opt_flags, int search_flags) 22 | { 23 | const AVOption *o = av_opt_find(obj, name, unit, opt_flags, search_flags); 24 | @@ -40,7 +39,7 @@ const AVOption *opt_find(void *obj, const char *name, const char *unit, 25 | } 26 | 27 | #define FLAGS (o->type == AV_OPT_TYPE_FLAGS) ? AV_DICT_APPEND : 0 28 | -int opt_default(const char *opt, const char *arg, 29 | +int __attribute__ ((visibility ("hidden"))) opt_default(const char *opt, const char *arg, 30 | struct SwsContext *sws_opts, AVDictionary **sws_dict, AVDictionary **swr_opts, 31 | AVDictionary **resample_opts, AVDictionary **format_opts, AVDictionary **codec_opts) 32 | { 33 | @@ -140,7 +139,7 @@ int opt_default(const char *opt, const char *arg, 34 | return AVERROR_OPTION_NOT_FOUND; 35 | } 36 | 37 | -int get_plane_sizes(int size[4], int required_plane[4], enum AVPixelFormat pix_fmt, 38 | +int __attribute__ ((visibility ("hidden"))) get_plane_sizes(int size[4], int required_plane[4], enum AVPixelFormat pix_fmt, 39 | int height, const int linesizes[4]) 40 | { 41 | int i, total_size; 42 | -------------------------------------------------------------------------------- /kivy_ios/recipes/libzbar/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import Recipe, shprint 2 | from os.path import join 3 | import sh 4 | 5 | 6 | class LibZBarRecipe(Recipe): 7 | 8 | version = '0.10' 9 | 10 | url = 'https://github.com/ZBar/ZBar/archive/{version}.zip' 11 | 12 | depends = ['hostpython3'] 13 | 14 | library = 'zbar/.libs/libzbar.a' 15 | 16 | include_per_arch = True 17 | include_dir = [ 18 | ("include", "") 19 | ] 20 | 21 | def prebuild_arch(self, arch): 22 | if self.has_marker("patched"): 23 | return 24 | self.apply_patch("werror.patch") 25 | self.set_marker("patched") 26 | 27 | def build_arch(self, arch): 28 | super(LibZBarRecipe, self).build_arch(arch) 29 | build_env = arch.get_env() 30 | build_env["CFLAGS"] = " ".join([ 31 | "-I{}".format(join(self.ctx.dist_dir, "build", "libiconv", arch.arch)) + 32 | " -arch {}".format(arch.arch), build_env['CFLAGS'] 33 | ]) 34 | shprint(sh.Command('autoreconf'), '-vif') 35 | shprint( 36 | sh.Command('./configure'), 37 | "CC={}".format(build_env["CC"]), 38 | "LD={}".format(build_env["LD"]), 39 | "CFLAGS={}".format(build_env["CFLAGS"]), 40 | "LDFLAGS={}".format(build_env["LDFLAGS"]), 41 | "--host={}".format(arch.triple), 42 | '--target={}'.format(arch.triple), 43 | # Python bindings are compiled in a separated recipe 44 | '--with-python=no', 45 | '--with-gtk=no', 46 | '--with-qt=no', 47 | '--with-x=no', 48 | '--with-jpeg=no', 49 | '--with-imagemagick=no', 50 | '--enable-pthread=no', 51 | '--enable-video=no', 52 | "--disable-shared", 53 | _env=build_env) 54 | shprint(sh.make, 'clean') 55 | shprint(sh.make, _env=build_env) 56 | 57 | 58 | recipe = LibZBarRecipe() 59 | -------------------------------------------------------------------------------- /kivy_ios/recipes/kivy/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import CythonRecipe 2 | from os.path import join 3 | import logging 4 | import shutil 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | class KivyRecipe(CythonRecipe): 10 | version = "0e77e2ab94e54ebaf1f232f5dd66d4d103e08fbb" # 2.1.0 + camera enhancements 11 | url = "https://github.com/kivy/kivy/archive/{version}.zip" 12 | library = "libkivy.a" 13 | depends = ["sdl2", "sdl2_image", "sdl2_mixer", "sdl2_ttf", "ios", 14 | "pyobjus", "python"] 15 | python_depends = ["certifi"] 16 | pbx_frameworks = ["OpenGLES", "Accelerate", "CoreMedia", "CoreVideo"] 17 | pre_build_ext = True 18 | 19 | def get_recipe_env(self, arch): 20 | env = super().get_recipe_env(arch) 21 | env["KIVY_SDL2_PATH"] = ":".join([ 22 | join(self.ctx.dist_dir, "include", "common", "sdl2"), 23 | join(self.ctx.dist_dir, "include", "common", "sdl2_image"), 24 | join(self.ctx.dist_dir, "include", "common", "sdl2_ttf"), 25 | join(self.ctx.dist_dir, "include", "common", "sdl2_mixer")]) 26 | return env 27 | 28 | def build_arch(self, arch): 29 | self._patch_setup() 30 | super().build_arch(arch) 31 | 32 | def _patch_setup(self): 33 | # patch setup to remove some functionnalities 34 | pyconfig = join(self.build_dir, "setup.py") 35 | 36 | def _remove_line(lines, pattern): 37 | for line in lines[:]: 38 | if pattern in line: 39 | lines.remove(line) 40 | with open(pyconfig) as fd: 41 | lines = fd.readlines() 42 | _remove_line(lines, "flags['libraries'] = ['GLESv2']") 43 | with open(pyconfig, "w") as fd: 44 | fd.writelines(lines) 45 | 46 | def reduce_python_package(self): 47 | dest_dir = join(self.ctx.site_packages_dir, "kivy") 48 | shutil.rmtree(join(dest_dir, "tools")) 49 | shutil.rmtree(join(dest_dir, "tests")) 50 | 51 | 52 | recipe = KivyRecipe() 53 | -------------------------------------------------------------------------------- /kivy_ios/tools/liblink: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import subprocess 5 | from os import environ 6 | 7 | libs = [ ] 8 | objects = [ ] 9 | output = None 10 | 11 | 12 | i = 1 13 | while i < len(sys.argv): 14 | opt = sys.argv[i] 15 | i += 1 16 | 17 | if opt == "-o": 18 | output = sys.argv[i] 19 | i += 1 20 | continue 21 | 22 | if opt.startswith("-l") or opt.startswith("-L"): 23 | libs.append(opt) 24 | continue 25 | 26 | if opt in ("-r", "-pipe", "-no-cpp-precomp"): 27 | continue 28 | 29 | if opt in ("--sysroot", "-isysroot", "-framework", "-undefined", 30 | "-macosx_version_min"): 31 | i += 1 32 | continue 33 | 34 | if opt.startswith("-I"): 35 | continue 36 | 37 | if opt.startswith("-m"): 38 | continue 39 | 40 | if opt.startswith("-f"): 41 | continue 42 | 43 | if opt.startswith("-O"): 44 | continue 45 | 46 | if opt.startswith("-g"): 47 | continue 48 | 49 | if opt.startswith("-D"): 50 | continue 51 | 52 | if opt.startswith('-arch'): 53 | continue 54 | 55 | if opt.startswith("-Wl,"): 56 | continue 57 | 58 | if opt.startswith("-W"): 59 | continue 60 | 61 | if opt.startswith("-"): 62 | print(sys.argv) 63 | print("Unknown option: ", opt) 64 | sys.exit(1) 65 | 66 | if not opt.endswith('.o'): 67 | continue 68 | 69 | objects.append(opt) 70 | 71 | 72 | f = open(output, "w") 73 | f.close() 74 | 75 | f = open(output + ".libs", "w") 76 | f.write(" ".join(libs)) 77 | f.close() 78 | 79 | print('Liblink redirect linking with', objects) 80 | ld = environ.get('ARM_LD') 81 | arch = environ.get('ARCH', 'arm64') 82 | min_version_flag = environ.get('KIVY_MIN_VERSION_FLAG', '-ios_version_min') 83 | call = [ld, '-r', '-o', output + '.o', min_version_flag, '9.0', '-arch', arch] 84 | if min_version_flag == "-ios_version_min": 85 | call += ["-bitcode_bundle"] 86 | call += objects 87 | print("Linking: {}".format(" ".join(call))) 88 | subprocess.call(call) 89 | -------------------------------------------------------------------------------- /tests/test_libs/main.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | from time import sleep 3 | 4 | FAILED = [] 5 | SUCCESS = [] 6 | 7 | 8 | def test_kivy(): 9 | import kivy 10 | import kivy.event 11 | import kivy.core.window 12 | import kivy.uix.widget # noqa: F401 13 | 14 | 15 | def test_audiostream(): 16 | from audiostream import get_output 17 | from audiostream.sources.wave import SineSource 18 | stream = get_output(channels=2, rate=22050, buffersize=128) 19 | source = SineSource(stream, 220) 20 | source.start() 21 | sleep(.5) 22 | source.stop() 23 | 24 | 25 | def test_numpy(): 26 | print("NPY: test import numpy") 27 | import numpy as np 28 | print("NPY: basic calculation") 29 | print(np.ones(10) * np.sin(2)) 30 | print("NPY: access to random module") 31 | print(np.random.mtrand.beta(1, 2)) 32 | print("NPY: access to fft") 33 | print(np.fft.fft(np.exp(2j * np.pi * np.arange(8) / 8))) 34 | print("NPY: access to linalg") 35 | from numpy import linalg as LA 36 | a = np.array([[1., 2.], [3., 4.]]) 37 | ainv = LA.inv(a) 38 | print(np.allclose(np.dot(a, ainv), np.eye(2))) 39 | 40 | 41 | def test_curly(): 42 | import curly # noqa: F401 43 | 44 | 45 | def run_test(f, name): 46 | # run a single test 47 | print("=> Run", name) 48 | try: 49 | f() 50 | SUCCESS.append(name) 51 | except Exception: 52 | print("!! Failed", name) 53 | traceback.print_exc() 54 | FAILED.append(name) 55 | 56 | 57 | def run(): 58 | # auto find test and run 59 | for key in globals(): 60 | if not key.startswith("test_"): 61 | continue 62 | print("FOUND", key) 63 | func = globals()[key] 64 | run_test(func, key) 65 | # print summary 66 | print("") 67 | print("=== [ LIBS SUMMARY ] ===") 68 | print("") 69 | print("{}/{} tests".format( 70 | len(SUCCESS), 71 | len(SUCCESS) + len(FAILED) 72 | )) 73 | print("Success: ", ", ".join(SUCCESS)) 74 | print("Failed: ", ", ".join(FAILED)) 75 | 76 | 77 | if __name__ == "__main__": 78 | run() 79 | -------------------------------------------------------------------------------- /kivy_ios/recipes/pillow/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import CythonRecipe, shprint 2 | from os.path import join 3 | import sh 4 | import os 5 | 6 | 7 | class PillowRecipe(CythonRecipe): 8 | version = "8.2.0" 9 | url = "https://pypi.python.org/packages/source/P/Pillow/Pillow-{version}.tar.gz" 10 | library = "libpillow.a" 11 | depends = [ 12 | "hostpython3", 13 | "freetype", 14 | "libjpeg", 15 | "python3", 16 | "ios", 17 | ] 18 | python_depends = ["setuptools"] 19 | pbx_libraries = ["libz", "libbz2"] 20 | include_per_arch = True 21 | cythonize = False 22 | 23 | def prebuild_arch(self, arch): 24 | if self.has_marker("patched"): 25 | return 26 | self.apply_patch("bypass-find-library.patch") 27 | self.set_marker("patched") 28 | 29 | def get_recipe_env(self, arch): 30 | env = super().get_recipe_env(arch) 31 | env["C_INCLUDE_PATH"] = join(arch.sysroot, "usr", "include") 32 | env["LIBRARY_PATH"] = join(arch.sysroot, "usr", "lib") 33 | env["CFLAGS"] += " ".join( 34 | [ 35 | " -I{}".format(join(self.ctx.dist_dir, "include", arch.arch, "freetype")) 36 | + " -I{}".format( 37 | join(self.ctx.dist_dir, "include", arch.arch, "libjpeg") 38 | ) 39 | + " -arch {}".format(arch.arch) 40 | ] 41 | ) 42 | env["PATH"] = os.environ["PATH"] 43 | env[ 44 | "PKG_CONFIG" 45 | ] = "ios-pkg-config" # ios-pkg-config does not exists, is needed to disable the pkg-config usage. 46 | return env 47 | 48 | def build_arch(self, arch): 49 | build_env = self.get_recipe_env(arch) 50 | hostpython3 = sh.Command(self.ctx.hostpython) 51 | shprint( 52 | hostpython3, 53 | "setup.py", 54 | "build_ext", 55 | "--disable-tiff", 56 | "--disable-webp", 57 | "--disable-jpeg2000", 58 | "--disable-lcms", 59 | "--disable-platform-guessing", 60 | "-g", 61 | _env=build_env, 62 | ) 63 | self.biglink() 64 | 65 | 66 | recipe = PillowRecipe() 67 | -------------------------------------------------------------------------------- /kivy_ios/recipes/libffi/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import Recipe, shprint 2 | import sh 3 | from os.path import exists, join 4 | 5 | 6 | class LibffiRecipe(Recipe): 7 | version = "3.4.2" 8 | url = "https://github.com/libffi/libffi/releases/download/v{version}/libffi-{version}.tar.gz" 9 | library = "build/Release-{arch.sdk}/libffi.a" 10 | include_per_arch = True 11 | include_dir = "build_{arch.sdk}-{arch_arg}/include" 12 | include_name = "ffi" 13 | archs = ["arm64", "x86_64"] 14 | 15 | def prebuild_arch(self, arch): 16 | if self.has_marker("patched"): 17 | return 18 | self.apply_patch("enable-tramp-build.patch") 19 | shprint(sh.sed, 20 | "-i.bak", 21 | "s/-miphoneos-version-min=7.0/-miphoneos-version-min=9.0/g", 22 | "generate-darwin-source-and-headers.py") 23 | shprint(sh.sed, 24 | "-i.bak", 25 | "s/build_target(ios_simulator_platform, platform_headers)/print('Skipping i386')/g", 26 | "generate-darwin-source-and-headers.py") 27 | self.set_marker("patched") 28 | 29 | def build_arch(self, arch): 30 | if exists("generate-darwin-source-and-headers.py"): 31 | shprint( 32 | sh.mv, 33 | "generate-darwin-source-and-headers.py", 34 | "_generate-darwin-source-and-headers.py") 35 | shprint(sh.touch, "generate-darwin-source-and-headers.py") 36 | python3 = sh.Command("python3") 37 | shprint(python3, "_generate-darwin-source-and-headers.py", "--only-ios") 38 | shprint(sh.xcodebuild, self.ctx.concurrent_xcodebuild, 39 | "ONLY_ACTIVE_ARCH=NO", 40 | "ARCHS={}".format("x86_64 arm64" if arch.arch == "x86_64" else arch.arch), 41 | "BITCODE_GENERATION_MODE=bitcode", 42 | "-sdk", arch.sdk, 43 | "-project", "libffi.xcodeproj", 44 | "-target", "libffi-iOS", 45 | "-configuration", "Release") 46 | 47 | def install_include(self): 48 | super().install_include() 49 | 50 | include_name = self.include_name or self.name 51 | src_dir = join(self.ctx.include_dir, "arm64", include_name) 52 | dest_dir = join(self.ctx.include_dir, "ios-arm64-simulator") 53 | shprint(sh.rm, "-rf", join(dest_dir, include_name)) 54 | shprint(sh.ln, "-s", src_dir, dest_dir) 55 | 56 | recipe = LibffiRecipe() 57 | -------------------------------------------------------------------------------- /kivy_ios/recipes/numpy/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import CythonRecipe 2 | from os.path import join 3 | import sh 4 | import shutil 5 | 6 | 7 | class NumpyRecipe(CythonRecipe): 8 | version = "1.24.2" 9 | url = "https://pypi.python.org/packages/source/n/numpy/numpy-{version}.tar.gz" 10 | library = "libnumpy.a" 11 | libraries = ["libnpymath.a", "libnpyrandom.a"] 12 | include_dir = "numpy/core/include" 13 | depends = ["python"] 14 | hostpython_prerequisites = ["Cython"] 15 | cythonize = False 16 | 17 | def prebuild_arch(self, arch): 18 | if self.has_marker("patched"): 19 | return 20 | self.apply_patch("skip-math-test.patch") 21 | self.apply_patch("duplicated_symbols.patch") 22 | self.set_marker("patched") 23 | 24 | def get_recipe_env(self, arch): 25 | env = super().get_recipe_env(arch) 26 | # CC must have the CFLAGS with arm arch, because numpy tries first to 27 | # compile and execute an empty C to see if the compiler works. This is 28 | # obviously not working when crosscompiling 29 | env["CC"] = "{} {}".format(env["CC"], env["CFLAGS"]) 30 | # Disable Accelerate.framework by disabling the optimized BLAS and LAPACK libraries cause it's now unsupported 31 | env["NPY_BLAS_ORDER"] = "" 32 | env["NPY_LAPACK_ORDER"] = "" 33 | return env 34 | 35 | def build_arch(self, arch): 36 | super().build_arch(arch) 37 | sh.cp(sh.glob(join(self.build_dir, "build", "temp.*", "libnpy*.a")), 38 | self.build_dir) 39 | 40 | def reduce_python_package(self): 41 | dest_dir = join(self.ctx.site_packages_dir, "numpy") 42 | shutil.rmtree(join(dest_dir, "core", "include")) 43 | shutil.rmtree(join(dest_dir, "core", "tests")) 44 | shutil.rmtree(join(dest_dir, "distutils")) 45 | shutil.rmtree(join(dest_dir, "doc")) 46 | shutil.rmtree(join(dest_dir, "f2py", "tests")) 47 | shutil.rmtree(join(dest_dir, "fft", "tests")) 48 | shutil.rmtree(join(dest_dir, "lib", "tests")) 49 | shutil.rmtree(join(dest_dir, "linalg", "tests")) 50 | shutil.rmtree(join(dest_dir, "ma", "tests")) 51 | shutil.rmtree(join(dest_dir, "matrixlib", "tests")) 52 | shutil.rmtree(join(dest_dir, "polynomial", "tests")) 53 | shutil.rmtree(join(dest_dir, "random", "tests")) 54 | shutil.rmtree(join(dest_dir, "tests")) 55 | sh.rm(join(dest_dir, "core", "lib", "libnpymath.a")) 56 | sh.rm(join(dest_dir, "random", "lib", "libnpyrandom.a")) 57 | 58 | 59 | recipe = NumpyRecipe() 60 | -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/Storyboards/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /kivy_ios/recipes/python3/configure.patch: -------------------------------------------------------------------------------- 1 | diff -Naur Python-3.10.10.orig/configure Python-3.10.10/configure 2 | --- Python-3.10.10.orig/configure 2023-02-12 15:08:28 3 | +++ Python-3.10.10/configure 2023-02-12 15:10:45 4 | @@ -3344,6 +3344,15 @@ 5 | *-*-cygwin*) 6 | ac_sys_system=Cygwin 7 | ;; 8 | + *-apple-ios) 9 | + ac_sys_system=iOS 10 | + ;; 11 | + *-apple-tvos) 12 | + ac_sys_system=tvOS 13 | + ;; 14 | + *-apple-watchos) 15 | + ac_sys_system=watchOS 16 | + ;; 17 | *-*-vxworks*) 18 | ac_sys_system=VxWorks 19 | ;; 20 | @@ -3391,6 +3400,15 @@ 21 | _host_cpu=$host_cpu 22 | esac 23 | ;; 24 | + *-apple-*) 25 | + case "$host_cpu" in 26 | + arm*) 27 | + _host_cpu=arm 28 | + ;; 29 | + *) 30 | + _host_cpu=$host_cpu 31 | + esac 32 | + ;; 33 | *-*-cygwin*) 34 | _host_cpu= 35 | ;; 36 | @@ -3469,6 +3487,13 @@ 37 | define_xopen_source=no;; 38 | Darwin/[12][0-9].*) 39 | define_xopen_source=no;; 40 | + # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. 41 | + iOS/*) 42 | + define_xopen_source=no;; 43 | + tvOS/*) 44 | + define_xopen_source=no;; 45 | + watchOS/*) 46 | + define_xopen_source=no;; 47 | # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from 48 | # defining NI_NUMERICHOST. 49 | QNX/6.3.2) 50 | @@ -5389,6 +5414,12 @@ 51 | case $ac_sys_system in #( 52 | Darwin*) : 53 | MULTIARCH="" ;; #( 54 | + iOS*) : 55 | + MULTIARCH="" ;; #( 56 | + tvOS*) : 57 | + MULTIARCH="" ;; #( 58 | + watchOS*) : 59 | + MULTIARCH="" ;; #( 60 | FreeBSD*) : 61 | MULTIARCH="" ;; #( 62 | *) : 63 | @@ -6249,11 +6280,17 @@ 64 | fi 65 | 66 | if test "$cross_compiling" = yes; then 67 | - case "$READELF" in 68 | - readelf|:) 69 | - as_fn_error $? "readelf for the host is required for cross builds" "$LINENO" 5 70 | - ;; 71 | - esac 72 | + case "$host" in 73 | + *-apple-*os) 74 | + # readelf not required for iOS cross builds. 75 | + ;; 76 | + *) 77 | + case "$READELF" in 78 | + readelf|:) 79 | + as_fn_error $? "readelf for the host is required for cross builds" "$LINENO" 5 80 | + ;; 81 | + esac 82 | + esac 83 | fi 84 | 85 | 86 | @@ -7051,7 +7088,6 @@ 87 | # tweak BASECFLAGS based on compiler and platform 88 | case $GCC in 89 | yes) 90 | - CFLAGS_NODIST="$CFLAGS_NODIST -std=c99" 91 | 92 | { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -Wextra" >&5 93 | $as_echo_n "checking for -Wextra... " >&6; } 94 | @@ -11912,6 +11948,10 @@ 95 | then 96 | case $ac_sys_system/$ac_sys_release in 97 | hp*|HP*) DYNLOADFILE="dynload_hpux.o";; 98 | + # Dynamic loading on iOS 99 | + iOS/*) DYNLOADFILE="dynload_shlib.o";; 100 | + tvOS/*) DYNLOADFILE="dynload_shlib.o";; 101 | + watchOS/*) DYNLOADFILE="dynload_shlib.o";; 102 | *) 103 | # use dynload_shlib.c and dlopen() if we have it; otherwise stub 104 | # out any dynamic loading 105 | -------------------------------------------------------------------------------- /kivy_ios/recipes/zbarlight/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from kivy_ios.toolchain import Recipe 3 | from os.path import join 4 | import sh 5 | import fnmatch 6 | from distutils.dir_util import copy_tree 7 | 8 | 9 | class ZbarLightRecipe(Recipe): 10 | version = '1.2' 11 | url = 'https://github.com/Polyconseil/zbarlight/archive/{version}.tar.gz' 12 | library = "zbarlight.a" 13 | depends = ['hostpython3', 'python3', 'libzbar'] 14 | pbx_libraries = ["libz", "libbz2", 'libc++', 'libsqlite3', 'CoreMotion'] 15 | include_per_arch = True 16 | 17 | def get_zbar_env(self, arch): 18 | build_env = arch.get_env() 19 | dest_dir = join(self.ctx.dist_dir, "root", "python") 20 | build_env["IOSROOT"] = self.ctx.root_dir 21 | build_env["IOSSDKROOT"] = arch.sysroot 22 | build_env["LDSHARED"] = join(self.ctx.root_dir, "tools", "liblink") 23 | build_env["ARM_LD"] = build_env["LD"] 24 | build_env["ARCH"] = arch.arch 25 | build_env["C_INCLUDE_PATH"] = join(arch.sysroot, "usr", "include") 26 | build_env["LIBRARY_PATH"] = join(arch.sysroot, "usr", "lib") 27 | build_env['PYTHONPATH'] = join(dest_dir, 'lib', 'python3.7', 'site-packages') 28 | build_env["CFLAGS"] = " ".join([ 29 | " -I{}".format(join(self.ctx.dist_dir, "include", arch.arch, "libzbar", 'zbar')) + 30 | " -arch {}".format(arch.arch) 31 | ]) 32 | build_env['LDFLAGS'] += " -lios -lpython -lzbar" 33 | return build_env 34 | 35 | def build_arch(self, arch): 36 | build_env = self.get_zbar_env(arch) 37 | hostpython = sh.Command(self.ctx.hostpython) 38 | shprint(hostpython, "setup.py", "build", # noqa: F821 39 | _env=build_env) 40 | self.apply_patch("zbarlight_1_2.patch") # Issue getting the version, hard coding for now 41 | self.biglink() 42 | 43 | def install(self): 44 | arch = list(self.filtered_archs)[0] 45 | build_dir = join(self.get_build_dir(arch.arch), 'build', 46 | 'lib.macosx-10.13-x86_64-2.7', 'zbarlight') 47 | dist_dir = join(self.ctx.dist_dir, 'root', 'python3', 'lib', 48 | 'python3.7', 'site-packages', 'zbarlight') 49 | # Patch before Copying 50 | # self.apply_patch("zbarlight_1_2.patch")#Issue getting the version, hard coding for now 51 | copy_tree(build_dir, dist_dir) 52 | os.remove(join(dist_dir, '_zbarlight.c')) 53 | 54 | def _patch__init__(self): 55 | init = join(self.ctx.dist_dir, 'root', 'python3', 'lib', 'python3.7', 56 | 'site-packages', 'zbarlight', "__init__.py") 57 | shprint( # noqa: F821 58 | sh.sed, "-i.bak", 59 | "s/__version__ = pkg_resources.get_distribution('zbarlight').version'" 60 | "/__version__ = '{version}'/g", 61 | init) 62 | 63 | def biglink(self): 64 | dirs = [] 65 | for root, dirnames, filenames in os.walk(self.build_dir): 66 | if fnmatch.filter(filenames, "*.so.libs"): 67 | dirs.append(root) 68 | cmd = sh.Command(join(self.ctx.root_dir, "tools", "biglink")) 69 | shprint(cmd, join(self.build_dir, "zbarlight.a"), *dirs) # noqa: F821 70 | 71 | 72 | recipe = ZbarLightRecipe() 73 | -------------------------------------------------------------------------------- /kivy_ios/recipes/python3/ModulesSetup: -------------------------------------------------------------------------------- 1 | ##################################################################### 2 | # Static compilation instructions for all binary modules. 3 | ##################################################################### 4 | 5 | *static* 6 | _asyncio _asynciomodule.c 7 | _bisect _bisectmodule.c 8 | _blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c 9 | _sqlite3 _sqlite/cache.c \ 10 | _sqlite/connection.c \ 11 | _sqlite/cursor.c \ 12 | _sqlite/microprotocols.c \ 13 | _sqlite/module.c \ 14 | _sqlite/prepare_protocol.c \ 15 | _sqlite/row.c \ 16 | _sqlite/statement.c \ 17 | _sqlite/util.c -DSQLITE_OMIT_LOAD_EXTENSION 18 | _bz2 _bz2module.c -I$(srcdir)/../bzip2/include -L$(srcdir)/../Support/BZip2 -lbz2 19 | _codecs_cn cjkcodecs/_codecs_cn.c 20 | _codecs_hk cjkcodecs/_codecs_hk.c 21 | _codecs_iso2022 cjkcodecs/_codecs_iso2022.c 22 | _codecs_jp cjkcodecs/_codecs_jp.c 23 | _codecs_kr cjkcodecs/_codecs_kr.c 24 | _codecs_tw cjkcodecs/_codecs_tw.c 25 | _contextvars _contextvarsmodule.c 26 | _crypt _cryptmodule.c 27 | _csv _csv.c 28 | _datetime _datetimemodule.c 29 | _elementtree _elementtree.c \ 30 | -I$(srcdir)/Modules/expat 31 | -DHAVE_EXPAT_CONFIG_H -DUSE_PYEXPAT_CAPI 32 | _hashlib _hashopenssl.c -lssl -DUSE_SSL 33 | _heapq _heapqmodule.c 34 | _json _json.c 35 | _lsprof _lsprof.o rotatingtree.c 36 | # _lzma _lzmamodule.c -I$(srcdir)/../xz/include -L$(srcdir)/../Support/XZ -llzma 37 | _md5 md5module.c 38 | _multibytecodec cjkcodecs/multibytecodec.c 39 | # _multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c 40 | _opcode _opcode.c 41 | _queue _queuemodule.c 42 | _pickle _pickle.c 43 | # _posixsubprocess _posixsubprocess.c 44 | _random _randommodule.c 45 | _sha1 sha1module.c 46 | _sha3 _sha3/sha3module.c 47 | _sha256 sha256module.c 48 | _sha512 sha512module.c 49 | _socket socketmodule.c 50 | _ssl _ssl.c -lssl -DUSE_SSL 51 | _struct _struct.c 52 | array arraymodule.c 53 | audioop audioop.c 54 | binascii binascii.c 55 | cmath cmathmodule.c _math.c 56 | fcntl fcntlmodule.c 57 | grp grpmodule.c 58 | math mathmodule.c 59 | mmap mmapmodule.c 60 | pyexpat expat/xmlparse.c \ 61 | expat/xmlrole.c \ 62 | expat/xmltok.c \ 63 | pyexpat.c \ 64 | -I$(srcdir)/Modules/expat \ 65 | -DHAVE_EXPAT_CONFIG_H -DUSE_PYEXPAT_CAPI -DXML_DEV_URANDOM 66 | resource resource.c 67 | select selectmodule.c 68 | syslog syslogmodule.c 69 | termios termios.c 70 | unicodedata unicodedata.c 71 | zlib zlibmodule.c -I$(prefix)/include -lz 72 | 73 | ##################################################################### 74 | # Testing modules 75 | ##################################################################### 76 | _ctypes_test _ctypes/_ctypes_test.c 77 | _testbuffer _testbuffer.c 78 | _testinternalcapi _testinternalcapi.c 79 | _testimportmultiple _testimportmultiple.c 80 | 81 | ##################################################################### 82 | # Modules that require additional frameworks 83 | ##################################################################### 84 | #_curses _cursesmodule.c -lcurses -ltermcap 85 | #_curses_panel _curses_panel.c -lpanel -lncurses 86 | #_dbm _dbmmodule.c 87 | #_gdbm _gdbmmodule.c -I/usr/local/include -L/usr/local/lib -lgdbm 88 | #_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -I... -L... 89 | #nis nismodule.c -lnsl 90 | #ossaudiodev 91 | #readline readline.c -lreadline -ltermcap 92 | #spwd spwdmodule.c 93 | -------------------------------------------------------------------------------- /kivy_ios/recipes/numpy/skip-math-test.patch: -------------------------------------------------------------------------------- 1 | diff -Naur numpy-1.24.2.orig/numpy/core/setup.py numpy-1.24.2/numpy/core/setup.py 2 | --- numpy-1.24.2.orig/numpy/core/setup.py 2023-03-11 19:49:17 3 | +++ numpy-1.24.2/numpy/core/setup.py 2023-03-11 19:51:42 4 | @@ -429,27 +429,7 @@ 5 | 6 | def check_mathlib(config_cmd): 7 | # Testing the C math library 8 | - mathlibs = [] 9 | - mathlibs_choices = [[], ["m"], ["cpml"]] 10 | - mathlib = os.environ.get("MATHLIB") 11 | - if mathlib: 12 | - mathlibs_choices.insert(0, mathlib.split(",")) 13 | - for libs in mathlibs_choices: 14 | - if config_cmd.check_func( 15 | - "log", 16 | - libraries=libs, 17 | - call_args="0", 18 | - decl="double log(double);", 19 | - call=True 20 | - ): 21 | - mathlibs = libs 22 | - break 23 | - else: 24 | - raise RuntimeError( 25 | - "math library missing; rerun setup.py after setting the " 26 | - "MATHLIB env variable" 27 | - ) 28 | - return mathlibs 29 | + return ["m"] 30 | 31 | 32 | def visibility_define(config): 33 | @@ -716,49 +696,7 @@ 34 | subst_dict = dict([("sep", os.path.sep), ("pkgname", "numpy.core")]) 35 | 36 | def get_mathlib_info(*args): 37 | - # Another ugly hack: the mathlib info is known once build_src is run, 38 | - # but we cannot use add_installed_pkg_config here either, so we only 39 | - # update the substitution dictionary during npymath build 40 | - config_cmd = config.get_config_cmd() 41 | - # Check that the toolchain works, to fail early if it doesn't 42 | - # (avoid late errors with MATHLIB which are confusing if the 43 | - # compiler does not work). 44 | - for lang, test_code, note in ( 45 | - ('c', 'int main(void) { return 0;}', ''), 46 | - ('c++', ( 47 | - 'int main(void)' 48 | - '{ auto x = 0.0; return static_cast(x); }' 49 | - ), ( 50 | - 'note: A compiler with support for C++11 language ' 51 | - 'features is required.' 52 | - ) 53 | - ), 54 | - ): 55 | - is_cpp = lang == 'c++' 56 | - if is_cpp: 57 | - # this a workaround to get rid of invalid c++ flags 58 | - # without doing big changes to config. 59 | - # c tested first, compiler should be here 60 | - bk_c = config_cmd.compiler 61 | - config_cmd.compiler = bk_c.cxx_compiler() 62 | - 63 | - # Check that Linux compiler actually support the default flags 64 | - if hasattr(config_cmd.compiler, 'compiler'): 65 | - config_cmd.compiler.compiler.extend(NPY_CXX_FLAGS) 66 | - config_cmd.compiler.compiler_so.extend(NPY_CXX_FLAGS) 67 | - 68 | - st = config_cmd.try_link(test_code, lang=lang) 69 | - if not st: 70 | - # rerun the failing command in verbose mode 71 | - config_cmd.compiler.verbose = True 72 | - config_cmd.try_link(test_code, lang=lang) 73 | - raise RuntimeError( 74 | - f"Broken toolchain: cannot link a simple {lang.upper()} " 75 | - f"program. {note}" 76 | - ) 77 | - if is_cpp: 78 | - config_cmd.compiler = bk_c 79 | - mlibs = check_mathlib(config_cmd) 80 | + mlibs = check_mathlib(None) 81 | 82 | posix_mlib = ' '.join(['-l%s' % l for l in mlibs]) 83 | msvc_mlib = ' '.join(['%s.lib' % l for l in mlibs]) -------------------------------------------------------------------------------- /kivy_ios/recipes/ffmpeg/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import Recipe, shprint 2 | from os.path import join 3 | import sh 4 | 5 | 6 | class FFMpegRecipe(Recipe): 7 | version = "n4.4.2" 8 | url = "https://github.com/FFmpeg/FFmpeg/archive/{version}.zip" 9 | include_per_arch = True 10 | include_dir = "dist/include" 11 | optional_depends = ["openssl"] 12 | libraries = [ 13 | "libavcodec/libavcodec.a", 14 | "libavdevice/libavdevice.a", 15 | "libavfilter/libavfilter.a", 16 | "libavformat/libavformat.a", 17 | "libavresample/libavresample.a", 18 | "libavutil/libavutil.a", 19 | "libswresample/libswresample.a", 20 | "libswscale/libswscale.a", 21 | ] 22 | pbx_frameworks = ["VideoToolbox"] 23 | 24 | def build_arch(self, arch): 25 | options = [ 26 | "--disable-everything", 27 | "--enable-parsers", 28 | "--enable-decoders", 29 | "--enable-demuxers", 30 | "--enable-filter=aresample,resample,crop,scale", 31 | "--enable-protocol=file,http,rtmp", 32 | "--enable-pic", 33 | "--enable-small", 34 | "--enable-hwaccels", 35 | "--enable-static", 36 | "--disable-shared", 37 | # libpostproc is GPL: https://ffmpeg.org/pipermail/ffmpeg-user/2012-February/005162.html 38 | "--enable-gpl", 39 | # disable some unused algo 40 | # note: "golomb" are the one used in our video test, so don't use --disable-golomb 41 | # note: and for aac decoding: "rdft", "mdct", and "fft" are needed 42 | "--disable-dxva2", 43 | "--disable-vdpau", 44 | "--disable-vaapi", 45 | "--disable-dct", 46 | # disable binaries / doc 47 | "--enable-cross-compile", 48 | "--disable-debug", 49 | "--disable-programs", 50 | "--disable-doc", 51 | "--enable-pic", 52 | "--enable-avresample"] 53 | 54 | if "openssl.build_all" in self.ctx.state: 55 | options += [ 56 | "--enable-openssl", 57 | "--enable-nonfree", 58 | "--enable-protocol=https,tls_openssl"] 59 | 60 | build_env = arch.get_env() 61 | build_env["VERBOSE"] = "1" 62 | configure = sh.Command(join(self.build_dir, "configure")) 63 | shprint(configure, 64 | "--target-os=darwin", 65 | "--arch={}".format(arch.arch), 66 | "--cc={}".format(build_env["CC"]), 67 | "--prefix={}/dist".format(self.build_dir), 68 | "--extra-cflags={}".format(build_env["CFLAGS"]), 69 | "--extra-cxxflags={}".format(build_env["CFLAGS"]), 70 | "--extra-ldflags={}".format(build_env["LDFLAGS"]), 71 | "--disable-x86asm", 72 | *options, 73 | _env=build_env) 74 | """ 75 | shprint(sh.sed, 76 | "-i.bak", 77 | "s/HAVE_CLOSESOCKET=yes//g", 78 | "config.mak") 79 | shprint(sh.sed, 80 | "-i.bak", 81 | "s/#define HAVE_CLOSESOCKET 1//g", 82 | "config.h") 83 | if exists("config.asm"): 84 | shprint(sh.sed, 85 | "-i.bak", 86 | "s/%define HAVE_CLOSESOCKET 1//g", 87 | "config.asm") 88 | """ 89 | shprint(sh.make, "clean", _env=build_env) 90 | shprint(sh.make, self.ctx.concurrent_make, _env=build_env) 91 | shprint(sh.make, "install") 92 | 93 | 94 | recipe = FFMpegRecipe() 95 | -------------------------------------------------------------------------------- /kivy_ios/recipes/ios/src/ios_mail.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Email support 3 | * 4 | * Very basic, could be upgraded to support HTML, and multiple attachment. No 5 | * need to let the user manipulate directly uikit API. 6 | */ 7 | 8 | #import 9 | #import 10 | #import 11 | #include "ios_wrapper.h" 12 | 13 | /* guess the view controller from our SDL window. 14 | */ 15 | UIViewController *get_viewcontroller(void) { 16 | UIWindow *window = [[UIApplication sharedApplication] keyWindow]; 17 | if ( window == NULL ) { 18 | printf("ios_wrapper: unable to get key window from shared application\n"); 19 | return NULL; 20 | } 21 | // return window.rootViewController; 22 | UIView* view = [window.subviews objectAtIndex:0]; 23 | id nextResponder = [view nextResponder]; 24 | if( [nextResponder isKindOfClass:[UIViewController class]] ) 25 | return (UIViewController *)nextResponder; 26 | return NULL; 27 | } 28 | 29 | @interface InAppEmailViewController : UIViewController { 30 | ios_send_email_cb callback; 31 | void * userdata; 32 | } 33 | 34 | @property(nonatomic, assign) ios_send_email_cb callback; 35 | @property(nonatomic, assign) void *userdata; 36 | 37 | @end 38 | 39 | @implementation InAppEmailViewController 40 | 41 | @synthesize userdata; 42 | @synthesize callback; 43 | 44 | - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error { 45 | static char *statuses[] = {"unknown", "cancelled", "saved", "sent", "failed"}; 46 | 47 | if ( callback != NULL ) { 48 | char *status = statuses[0]; 49 | switch (result) 50 | { 51 | case MFMailComposeResultCancelled: status = statuses[1]; break; 52 | case MFMailComposeResultSaved: status = statuses[2]; break; 53 | case MFMailComposeResultSent: status = statuses[3]; break; 54 | case MFMailComposeResultFailed: status = statuses[4]; break; 55 | default: break; 56 | } 57 | callback(status, userdata); 58 | } 59 | 60 | UIViewController* viewController = [controller presentingViewController]; 61 | [viewController dismissModalViewControllerAnimated:YES]; 62 | } 63 | 64 | @end 65 | 66 | int ios_send_email(char *subject, char *text, char *mimetype, char *filename, 67 | char *filename_alias, ios_send_email_cb callback, void *userdata) 68 | { 69 | UIViewController* viewController = get_viewcontroller(); 70 | if ( viewController == NULL ) { 71 | printf("ios_send_email: unable to get view controller.\n"); 72 | return 0; 73 | } 74 | 75 | if (! [MFMailComposeViewController canSendMail]) { 76 | printf("ios_send_email: no available mail provider configured.\n"); 77 | return -1; 78 | } 79 | 80 | MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init]; 81 | InAppEmailViewController *inAppVc = [[InAppEmailViewController alloc] init]; 82 | inAppVc.callback = callback; 83 | inAppVc.userdata = userdata; 84 | controller.mailComposeDelegate = inAppVc; 85 | 86 | if ( subject != NULL ) { 87 | NSString *nssubject = [NSString stringWithCString:(char *)subject encoding:NSUTF8StringEncoding]; 88 | [controller setSubject:nssubject]; 89 | } 90 | 91 | if ( text != NULL ) { 92 | NSString *nstext = [NSString stringWithCString:(char *)text encoding:NSUTF8StringEncoding]; 93 | [controller setMessageBody:nstext isHTML:NO]; 94 | } 95 | 96 | if ( mimetype != NULL && filename != NULL ) { 97 | NSString *nsmimetype = [NSString stringWithCString:(char *)mimetype encoding:NSUTF8StringEncoding]; 98 | NSString *nsfilename = [NSString stringWithCString:(char *)filename encoding:NSUTF8StringEncoding]; 99 | NSString *nsalias = [NSString stringWithCString:(char *)filename_alias encoding:NSUTF8StringEncoding]; 100 | NSData *myData = [NSData dataWithContentsOfFile:nsfilename]; 101 | [controller addAttachmentData:myData mimeType:nsmimetype fileName:nsalias]; 102 | } 103 | 104 | controller.modalPresentationStyle = UIModalPresentationPageSheet; 105 | [viewController presentModalViewController:controller animated:YES]; 106 | [controller release]; 107 | 108 | return 1; 109 | } -------------------------------------------------------------------------------- /kivy_ios/recipes/kivent_core/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: Lawrence Du 3 | E-mail: larrydu88@gmail.com 4 | """ 5 | 6 | from kivy_ios.toolchain import CythonRecipe, shprint 7 | import sh 8 | from os.path import join 9 | from os import chdir 10 | import logging 11 | 12 | logger = logging.getLogger(__name__) 13 | 14 | 15 | class KiventCoreRecipe(CythonRecipe): 16 | version = 'master' 17 | url = 'https://github.com/kivy/kivent/archive/{version}.zip' 18 | name = 'kivent_core' 19 | depends = ['libffi', 'kivy'] # note: unsure if libffi is necessary here 20 | pre_build_ext = False 21 | subbuilddir = False 22 | cythonize = True 23 | pbx_frameworks = ["OpenGLES"] # note: This line may be unnecessary 24 | 25 | def get_recipe_env(self, arch): 26 | env = super(KiventCoreRecipe, self).get_recipe_env(arch) 27 | env['CYTHONPATH'] = self.get_recipe( 28 | 'kivy', self.ctx).get_build_dir(arch.arch) 29 | return env 30 | 31 | def get_build_dir(self, arch, sub=False): 32 | """ 33 | Call this to get the correct build_dir, where setup.py is located which is 34 | actually under modules/core/setup.py 35 | """ 36 | builddir = super(KiventCoreRecipe, self).get_build_dir(str(arch)) 37 | if sub or self.subbuilddir: 38 | core_build_dir = join(builddir, 'modules', 'core') 39 | logger.info("Core build directory is located at {}".format(core_build_dir)) 40 | return core_build_dir 41 | else: 42 | logger.info("Building in {}".format(builddir)) 43 | return builddir 44 | 45 | def build_arch(self, arch): 46 | """ 47 | Override build.arch to avoid calling setup.py here (Call it in 48 | install() instead). 49 | """ 50 | self.subbuildir = True 51 | self.cythonize_build() 52 | self.biglink() 53 | self.subbuilddir = False 54 | 55 | def install(self): 56 | """ 57 | This method simply builds the command line call for calling 58 | kivent_core/modules/core/setup.py 59 | 60 | This constructs the equivalent of the command 61 | "$python setup.py build_ext install" 62 | only with the environment variables altered for each different architecture 63 | The appropriate version of kivy also needs to be added to the path, and this 64 | differs for each architecture (i386, x86_64, armv7, etc) 65 | 66 | Note: This method is called by build_all() in toolchain.py 67 | 68 | """ 69 | arch = list(self.filtered_archs)[0] 70 | 71 | build_dir = self.get_build_dir(arch.arch, sub=True) 72 | logger.info("Building kivent_core {} in {}".format(arch.arch, build_dir)) 73 | chdir(build_dir) 74 | hostpython = sh.Command(self.ctx.hostpython) 75 | 76 | # Get the appropriate environment for this recipe (including CYTHONPATH) 77 | # build_env = arch.get_env() 78 | build_env = self.get_recipe_env(arch) 79 | 80 | dest_dir = join(self.ctx.dist_dir, "root", "python") 81 | build_env['PYTHONPATH'] = join(dest_dir, 'lib', 'python3.7', 'site-packages') 82 | 83 | # Add Architecture specific kivy path for 'import kivy' to PYTHONPATH 84 | arch_kivy_path = self.get_recipe('kivy', self.ctx).get_build_dir(arch.arch) 85 | build_env['PYTHONPATH'] = join(build_env['PYTHONPATH'], ':', arch_kivy_path) 86 | 87 | # Make sure you call kivent_core/modules/core/setup.py 88 | subdir_path = self.get_build_dir(str(arch), sub=True) 89 | setup_path = join(subdir_path, "setup.py") 90 | 91 | # Print out directories for sanity check 92 | logger.info("ENVS", build_env) 93 | logger.info("ROOT", self.ctx.root_dir) 94 | logger.info("BUILD", self.ctx.build_dir) 95 | logger.info("INCLUDE", self.ctx.include_dir) 96 | logger.info("DISTDIR", self.ctx.dist_dir) 97 | logger.info("ARCH KIVY LOC", self.get_recipe('kivy', self.ctx).get_build_dir(arch.arch)) 98 | 99 | shprint(hostpython, setup_path, "build_ext", "install", _env=build_env) 100 | 101 | 102 | recipe = KiventCoreRecipe() 103 | -------------------------------------------------------------------------------- /kivy_ios/recipes/hostpython3/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import HostRecipe, shprint 2 | from os.path import join 3 | import os 4 | import sh 5 | import shutil 6 | import logging 7 | from kivy_ios.context_managers import cd 8 | 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | 13 | class Hostpython3Recipe(HostRecipe): 14 | version = "3.10.10" 15 | url = "https://www.python.org/ftp/python/{version}/Python-{version}.tgz" 16 | depends = ["hostopenssl"] 17 | optional_depends = [] 18 | build_subdir = 'native-build' 19 | 20 | def init_with_ctx(self, ctx): 21 | super().init_with_ctx(ctx) 22 | self.set_hostpython(self, "3.10") 23 | self.ctx.so_suffix = ".cpython-310m-darwin.so" 24 | self.ctx.hostpython = join(self.ctx.dist_dir, "hostpython3", "bin", "python") 25 | self.ctx.hostpgen = join(self.ctx.dist_dir, "hostpython3", "bin", "pgen") 26 | logger.info("Global: hostpython located at {}".format(self.ctx.hostpython)) 27 | logger.info("Global: hostpgen located at {}".format(self.ctx.hostpgen)) 28 | 29 | def get_build_subdir(self, arch): 30 | return join(self.get_build_dir(arch), self.build_subdir) 31 | 32 | def prebuild_arch(self, arch): 33 | if self.has_marker("patched"): 34 | return 35 | self.apply_patch("disable_sysconfig_cflags.patch") 36 | self.copy_file("ModulesSetup", "Modules/Setup.local") 37 | self.set_marker("patched") 38 | 39 | def postbuild_arch(self, arch): 40 | return 41 | 42 | def get_build_env(self): 43 | sdk_path = sh.xcrun("--sdk", "macosx", "--show-sdk-path").strip() 44 | 45 | build_env = self.ctx.env.copy() 46 | ccache = (build_env["CCACHE"] + ' ') if 'CCACHE' in build_env else '' 47 | build_env["CC"] = ccache + "clang -Qunused-arguments -fcolor-diagnostics" 48 | build_env["LDFLAGS"] = " ".join([ 49 | "-lsqlite3", 50 | "-lffi", 51 | ]) 52 | build_env["CFLAGS"] = " ".join([ 53 | "--sysroot={}".format(sdk_path), 54 | "-mmacosx-version-min=10.12", 55 | ]) 56 | return build_env 57 | 58 | def build_arch(self, arch): 59 | build_env = self.get_build_env() 60 | 61 | configure = sh.Command(join(self.build_dir, "configure")) 62 | 63 | build_subdir = self.get_build_subdir(arch.arch) 64 | os.makedirs(build_subdir, exist_ok=True) 65 | 66 | with cd(build_subdir): 67 | shprint(configure, 68 | "ac_cv_func_preadv=no", 69 | "ac_cv_func_pwritev=no", 70 | "ac_cv_func_sendfile=no", 71 | "--prefix={}".format(join(self.ctx.dist_dir, "hostpython3")), 72 | "--with-openssl={}".format(join(self.ctx.dist_dir, 'hostopenssl')), 73 | _env=build_env) 74 | shprint(sh.make, "-C", build_subdir, self.ctx.concurrent_make, 75 | _env=build_env) 76 | 77 | def install(self): 78 | arch = list(self.filtered_archs)[0] 79 | build_env = self.get_build_env() 80 | build_subdir = self.get_build_subdir(arch.arch) 81 | build_env["PATH"] = os.environ["PATH"] 82 | shprint(sh.make, self.ctx.concurrent_make, 83 | "-C", build_subdir, 84 | "install", 85 | _env=build_env) 86 | shutil.copy( 87 | join(self.ctx.dist_dir, "hostpython3", "bin", "python3"), 88 | join(self.ctx.dist_dir, "hostpython3", "bin", "python")) 89 | 90 | # hostpython3 installs bundled versions of `pip` 91 | # and `setuptools` in `lib/python3.10/site-packages`. 92 | # This is fine, but `setuptools` have a bug that prevents 93 | # it from working properly when cross-compiling, so we 94 | # patch it here. 95 | # We can't do that before cause the packaged setuptools 96 | # is installed from a wheel. 97 | self.apply_patch( 98 | "allow-cflags-override.patch", 99 | join( 100 | self.ctx.dist_dir, 101 | "hostpython3", 102 | "lib", 103 | "python3.10", 104 | "site-packages", 105 | "setuptools", 106 | ), 107 | ) 108 | 109 | 110 | recipe = Hostpython3Recipe() 111 | -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/bridge.m: -------------------------------------------------------------------------------- 1 | #import "bridge.h" 2 | 3 | @implementation bridge 4 | 5 | CMAltimeter *altimeterManager; 6 | 7 | - (id) init { 8 | if(self = [super init]) { 9 | self.motionManager = [[CMMotionManager alloc] init]; 10 | queue = [[NSOperationQueue alloc] init]; 11 | } 12 | return self; 13 | } 14 | 15 | - (void)startAccelerometer { 16 | 17 | if ([self.motionManager isAccelerometerAvailable] == YES) { 18 | [self.motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) { 19 | self.ac_x = accelerometerData.acceleration.x; 20 | self.ac_y = accelerometerData.acceleration.y; 21 | self.ac_z = accelerometerData.acceleration.z; 22 | }]; 23 | } 24 | } 25 | 26 | - (void)startGyroscope { 27 | 28 | if ([self.motionManager isGyroAvailable] == YES) { 29 | [self.motionManager startGyroUpdatesToQueue:queue withHandler:^(CMGyroData *gyroData, NSError *error) { 30 | self.gy_x = gyroData.rotationRate.x; 31 | self.gy_y = gyroData.rotationRate.y; 32 | self.gy_z = gyroData.rotationRate.z; 33 | }]; 34 | } 35 | } 36 | 37 | - (void)startMagnetometer { 38 | 39 | if (self.motionManager.magnetometerAvailable) { 40 | [self.motionManager startMagnetometerUpdatesToQueue:queue withHandler:^(CMMagnetometerData *magnetometerData, NSError *error) { 41 | self.mg_x = magnetometerData.magneticField.x; 42 | self.mg_y = magnetometerData.magneticField.y; 43 | self.mg_z = magnetometerData.magneticField.z; 44 | }]; 45 | } 46 | } 47 | 48 | - (void)startDeviceMotion { 49 | 50 | if (self.motionManager.deviceMotionAvailable) { 51 | [self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXTrueNorthZVertical toQueue:queue withHandler:^(CMDeviceMotion *deviceMotion, NSError *error) { 52 | self.sp_roll = deviceMotion.attitude.roll; 53 | self.sp_pitch = deviceMotion.attitude.pitch; 54 | self.sp_yaw = deviceMotion.attitude.yaw; 55 | 56 | self.g_x = deviceMotion.gravity.x; 57 | self.g_y = deviceMotion.gravity.y; 58 | self.g_z = deviceMotion.gravity.z; 59 | 60 | self.rotation_rate_x = deviceMotion.rotationRate.x; 61 | self.rotation_rate_y = deviceMotion.rotationRate.y; 62 | self.rotation_rate_z = deviceMotion.rotationRate.z; 63 | 64 | self.user_acc_x = deviceMotion.userAcceleration.x; 65 | self.user_acc_y = deviceMotion.userAcceleration.y; 66 | self.user_acc_z = deviceMotion.userAcceleration.z; 67 | 68 | self.q_x = deviceMotion.attitude.quaternion.x; 69 | self.q_y = deviceMotion.attitude.quaternion.y; 70 | self.q_z = deviceMotion.attitude.quaternion.z; 71 | self.q_w = deviceMotion.attitude.quaternion.w; 72 | }]; 73 | } 74 | } 75 | 76 | - (void)startDeviceMotionWithReferenceFrame { 77 | 78 | if (self.motionManager.deviceMotionAvailable) { 79 | [self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryCorrectedZVertical toQueue:queue withHandler:^(CMDeviceMotion *deviceMotion, NSError *error) { 80 | self.mf_x = deviceMotion.magneticField.field.x; 81 | self.mf_y = deviceMotion.magneticField.field.y; 82 | self.mf_z = deviceMotion.magneticField.field.z; 83 | }]; 84 | } 85 | } 86 | 87 | - (void)startRelativeAltitude { 88 | 89 | if ([CMAltimeter isRelativeAltitudeAvailable]) { 90 | altimeterManager = [[CMAltimeter alloc] init]; 91 | [altimeterManager startRelativeAltitudeUpdatesToQueue:queue withHandler:^(CMAltitudeData *altitudeData, NSError *error) { 92 | self.relative_altitude = altitudeData.relativeAltitude.floatValue; 93 | self.pressure = altitudeData.pressure.floatValue; 94 | }]; 95 | } 96 | } 97 | 98 | - (void) stopAccelerometer { 99 | [self.motionManager stopAccelerometerUpdates]; 100 | } 101 | 102 | - (void) stopGyroscope { 103 | [self.motionManager stopGyroUpdates]; 104 | } 105 | 106 | - (void) stopMagnetometer { 107 | [self.motionManager stopMagnetometerUpdates]; 108 | } 109 | 110 | - (void) stopDeviceMotion { 111 | [self.motionManager stopDeviceMotionUpdates]; 112 | } 113 | 114 | - (void) stopRelativeAltitude { 115 | [altimeterManager stopRelativeAltitudeUpdates]; 116 | } 117 | 118 | - (void) dealloc { 119 | [self.motionManager release]; 120 | [queue release]; 121 | [super dealloc]; 122 | } 123 | 124 | @end 125 | -------------------------------------------------------------------------------- /kivy_ios/recipes/libffi/enable-tramp-build.patch: -------------------------------------------------------------------------------- 1 | diff -Naur libffi-3.4.2.orig/libffi.xcodeproj/project.pbxproj libffi-3.4.2/libffi.xcodeproj/project.pbxproj 2 | --- libffi-3.4.2.orig/libffi.xcodeproj/project.pbxproj 2022-04-03 22:11:12.000000000 +0200 3 | +++ libffi-3.4.2/libffi.xcodeproj/project.pbxproj 2022-04-03 22:17:06.000000000 +0200 4 | @@ -10,6 +10,10 @@ 5 | 43B5D3F81D35473200D1E1FD /* ffiw64_x86_64.c in Sources */ = {isa = PBXBuildFile; fileRef = 43B5D3F71D35473200D1E1FD /* ffiw64_x86_64.c */; }; 6 | 43B5D3FA1D3547CE00D1E1FD /* win64_x86_64.S in Sources */ = {isa = PBXBuildFile; fileRef = 43B5D3F91D3547CE00D1E1FD /* win64_x86_64.S */; }; 7 | 43E9A5C81D352C1500926A8F /* unix64_x86_64.S in Sources */ = {isa = PBXBuildFile; fileRef = 43E9A5C61D352C1500926A8F /* unix64_x86_64.S */; }; 8 | + AC0D110927FA355D001BCB3D /* tramp.c in Sources */ = {isa = PBXBuildFile; fileRef = AC0D110827FA355D001BCB3D /* tramp.c */; }; 9 | + AC0D110A27FA355D001BCB3D /* tramp.c in Sources */ = {isa = PBXBuildFile; fileRef = AC0D110827FA355D001BCB3D /* tramp.c */; }; 10 | + AC0D110B27FA355D001BCB3D /* tramp.c in Sources */ = {isa = PBXBuildFile; fileRef = AC0D110827FA355D001BCB3D /* tramp.c */; }; 11 | + AC0D110C27FA355D001BCB3D /* tramp.c in Sources */ = {isa = PBXBuildFile; fileRef = AC0D110827FA355D001BCB3D /* tramp.c */; }; 12 | DBFA714A187F1D8600A76262 /* ffi.h in Headers */ = {isa = PBXBuildFile; fileRef = DBFA713E187F1D8600A76262 /* ffi.h */; }; 13 | DBFA714B187F1D8600A76262 /* ffi_common.h in Headers */ = {isa = PBXBuildFile; fileRef = DBFA713F187F1D8600A76262 /* ffi_common.h */; }; 14 | DBFA714C187F1D8600A76262 /* fficonfig.h in Headers */ = {isa = PBXBuildFile; fileRef = DBFA7140187F1D8600A76262 /* fficonfig.h */; }; 15 | @@ -130,6 +134,7 @@ 16 | 43E9A5DB1D35374400926A8F /* internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = internal.h; sourceTree = ""; }; 17 | 43E9A5DC1D35375400926A8F /* internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = internal.h; sourceTree = ""; }; 18 | 43E9A5DD1D35375400926A8F /* internal64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = internal64.h; sourceTree = ""; }; 19 | + AC0D110827FA355D001BCB3D /* tramp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = tramp.c; path = src/tramp.c; sourceTree = SOURCE_ROOT; }; 20 | DB13B1661849DF1E0010F42D /* libffi.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libffi.a; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | DB13B1911849DF510010F42D /* ffi.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = ffi.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | DBFA713E187F1D8600A76262 /* ffi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ffi.h; sourceTree = ""; }; 23 | @@ -214,6 +219,7 @@ 24 | DBFA7142187F1D8600A76262 /* src */ = { 25 | isa = PBXGroup; 26 | children = ( 27 | + AC0D110827FA355D001BCB3D /* tramp.c */, 28 | DBFA7143187F1D8600A76262 /* closures.c */, 29 | DBFA7145187F1D8600A76262 /* dlmalloc.c */, 30 | DBFA7147187F1D8600A76262 /* prep_cif.c */, 31 | @@ -513,6 +519,7 @@ 32 | 43E9A5C81D352C1500926A8F /* unix64_x86_64.S in Sources */, 33 | DBFA717E187F1D9B00A76262 /* ffi64_x86_64.c in Sources */, 34 | DBFA7179187F1D9B00A76262 /* ffi_armv7.c in Sources */, 35 | + AC0D110927FA355D001BCB3D /* tramp.c in Sources */, 36 | DBFA714E187F1D8600A76262 /* closures.c in Sources */, 37 | DBFA717A187F1D9B00A76262 /* sysv_armv7.S in Sources */, 38 | 43B5D3F81D35473200D1E1FD /* ffiw64_x86_64.c in Sources */, 39 | @@ -535,6 +542,7 @@ 40 | DBFA715B187F1D8600A76262 /* types.c in Sources */, 41 | DBFA7159187F1D8600A76262 /* raw_api.c in Sources */, 42 | DBFA714F187F1D8600A76262 /* closures.c in Sources */, 43 | + AC0D110B27FA355D001BCB3D /* tramp.c in Sources */, 44 | DBFA7194187F1DA100A76262 /* unix64_x86_64.S in Sources */, 45 | FDDB2F461F5D691E00EF414E /* win64_x86_64.S in Sources */, 46 | ); 47 | @@ -547,6 +555,7 @@ 48 | FDB52FB31F6144FA00AA92E6 /* unix64_x86_64.S in Sources */, 49 | FDB52FB51F6144FA00AA92E6 /* ffi64_x86_64.c in Sources */, 50 | FDB52FB61F6144FA00AA92E6 /* ffi_armv7.c in Sources */, 51 | + AC0D110A27FA355D001BCB3D /* tramp.c in Sources */, 52 | FDB52FB71F6144FA00AA92E6 /* closures.c in Sources */, 53 | FDB52FB81F6144FA00AA92E6 /* sysv_armv7.S in Sources */, 54 | FDB52FB91F6144FA00AA92E6 /* ffiw64_x86_64.c in Sources */, 55 | @@ -569,6 +578,7 @@ 56 | FDDB2F4F1F5D846400EF414E /* types.c in Sources */, 57 | FDDB2F501F5D846400EF414E /* raw_api.c in Sources */, 58 | FDDB2F511F5D846400EF414E /* closures.c in Sources */, 59 | + AC0D110C27FA355D001BCB3D /* tramp.c in Sources */, 60 | FDDB2F521F5D846400EF414E /* unix64_x86_64.S in Sources */, 61 | FDDB2F531F5D846400EF414E /* win64_x86_64.S in Sources */, 62 | ); 63 | -------------------------------------------------------------------------------- /kivy_ios/recipes/ios/src/ios_utils.m: -------------------------------------------------------------------------------- 1 | /* 2 | * iOS utils 3 | * 4 | */ 5 | 6 | #import 7 | #import 8 | #import 9 | #include "ios_wrapper.h" 10 | 11 | 12 | float ios_uiscreen_get_scale() { 13 | float scale = 1.0; 14 | if ([[UIScreen mainScreen] respondsToSelector:@selector(nativeScale)]) { 15 | scale = [[UIScreen mainScreen] nativeScale]; 16 | }; 17 | return scale; 18 | } 19 | 20 | int ios_uiscreen_get_dpi() { 21 | /* 22 | * dpi function from src/video/uikit/SDL_uikitmodes.m (SDL2) 23 | */ 24 | 25 | /* 26 | * A well up to date list of device info can be found here: 27 | * https://github.com/lmirosevic/GBDeviceInfo/blob/master/GBDeviceInfo/GBDeviceInfo_iOS.m 28 | */ 29 | NSDictionary* devices = @{ 30 | @"iPhone1,1": @163, 31 | @"iPhone1,2": @163, 32 | @"iPhone2,1": @163, 33 | @"iPhone3,1": @326, 34 | @"iPhone3,2": @326, 35 | @"iPhone3,3": @326, 36 | @"iPhone4,1": @326, 37 | @"iPhone5,1": @326, 38 | @"iPhone5,2": @326, 39 | @"iPhone5,3": @326, 40 | @"iPhone5,4": @326, 41 | @"iPhone6,1": @326, 42 | @"iPhone6,2": @326, 43 | @"iPhone7,1": @401, 44 | @"iPhone7,2": @326, 45 | @"iPhone8,1": @326, 46 | @"iPhone8,2": @401, 47 | @"iPhone8,4": @326, 48 | @"iPhone9,1": @326, 49 | @"iPhone9,2": @401, 50 | @"iPhone9,3": @326, 51 | @"iPhone9,4": @401, 52 | @"iPhone10,1": @326, 53 | @"iPhone10,2": @401, 54 | @"iPhone10,3": @458, 55 | @"iPhone10,4": @326, 56 | @"iPhone10,5": @401, 57 | @"iPhone10,6": @458, 58 | @"iPhone11,2": @458, 59 | @"iPhone11,4": @458, 60 | @"iPhone11,6": @458, 61 | @"iPhone11,8": @326, 62 | @"iPhone12,1": @326, 63 | @"iPhone12,3": @458, 64 | @"iPhone12,5": @458, 65 | @"iPhone12,8": @326, 66 | @"iPhone13,1": @476, 67 | @"iPhone13,2": @460, 68 | @"iPhone13,3": @460, 69 | @"iPhone13,4": @458, 70 | @"iPhone14,2": @460, 71 | @"iPhone14,3": @458, 72 | @"iPhone14,4": @476, 73 | @"iPhone14,5": @460, 74 | @"iPhone14,6": @326, 75 | @"iPad1,1": @132, 76 | @"iPad2,1": @132, 77 | @"iPad2,2": @132, 78 | @"iPad2,3": @132, 79 | @"iPad2,4": @132, 80 | @"iPad2,5": @163, 81 | @"iPad2,6": @163, 82 | @"iPad2,7": @163, 83 | @"iPad3,1": @264, 84 | @"iPad3,2": @264, 85 | @"iPad3,3": @264, 86 | @"iPad3,4": @264, 87 | @"iPad3,5": @264, 88 | @"iPad3,6": @264, 89 | @"iPad4,1": @264, 90 | @"iPad4,2": @264, 91 | @"iPad4,3": @264, 92 | @"iPad4,4": @326, 93 | @"iPad4,5": @326, 94 | @"iPad4,6": @326, 95 | @"iPad4,7": @326, 96 | @"iPad4,8": @326, 97 | @"iPad4,9": @326, 98 | @"iPad5,1": @326, 99 | @"iPad5,2": @326, 100 | @"iPad5,3": @264, 101 | @"iPad5,4": @264, 102 | @"iPad6,3": @264, 103 | @"iPad6,4": @264, 104 | @"iPad6,7": @264, 105 | @"iPad6,8": @264, 106 | @"iPad6,11": @264, 107 | @"iPad6,12": @264, 108 | @"iPad7,1": @264, 109 | @"iPad7,2": @264, 110 | @"iPad7,3": @264, 111 | @"iPad7,4": @264, 112 | @"iPad7,5": @264, 113 | @"iPad7,6": @264, 114 | @"iPad7,11": @264, 115 | @"iPad7,12": @264, 116 | @"iPad8,1": @264, 117 | @"iPad8,2": @264, 118 | @"iPad8,3": @264, 119 | @"iPad8,4": @264, 120 | @"iPad8,5": @264, 121 | @"iPad8,6": @264, 122 | @"iPad8,7": @264, 123 | @"iPad8,8": @264, 124 | @"iPad11,1": @326, 125 | @"iPad11,2": @326, 126 | @"iPad11,3": @326, 127 | @"iPad11,4": @326, 128 | @"iPod1,1": @163, 129 | @"iPod2,1": @163, 130 | @"iPod3,1": @163, 131 | @"iPod4,1": @326, 132 | @"iPod5,1": @326, 133 | @"iPod7,1": @326, 134 | @"iPod9,1": @326, 135 | }; 136 | struct utsname systemInfo; 137 | uname(&systemInfo); 138 | NSString* deviceName = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding]; 139 | id foundDPI = devices[deviceName]; 140 | if (foundDPI) { 141 | return (float)[foundDPI integerValue]; 142 | } else { 143 | /* 144 | * Estimate the DPI based on the screen scale multiplied by the base DPI for the device 145 | * type (e.g. based on iPhone 1 and iPad 1) 146 | */ 147 | float scale = ios_uiscreen_get_scale(); 148 | float dpi = 160 * scale; 149 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { 150 | dpi = 132 * scale; 151 | } else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { 152 | dpi = 163 * scale; 153 | } 154 | return dpi; 155 | } 156 | } 157 | 158 | padding ios_get_safe_area() { 159 | padding safearea; 160 | if (@available(iOS 11.0, *)){ 161 | UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; 162 | safearea.top = window.safeAreaInsets.top; 163 | safearea.bottom = window.safeAreaInsets.bottom; 164 | safearea.left = window.safeAreaInsets.left; 165 | safearea.right = window.safeAreaInsets.right; 166 | } else { 167 | safearea.top = 0; 168 | safearea.bottom = 0; 169 | safearea.left = 0; 170 | safearea.right = 0; 171 | } 172 | return safearea; 173 | } 174 | -------------------------------------------------------------------------------- /.github/workflows/kivy_ios.yml: -------------------------------------------------------------------------------- 1 | name: kivy-ios 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | flake8: 7 | name: Flake8 tests 8 | runs-on: macos-latest 9 | steps: 10 | - name: Checkout kivy-ios 11 | uses: actions/checkout@v3 12 | - name: Set up Python 13 | uses: actions/setup-python@v4 14 | with: 15 | python-version: 3.x 16 | - name: Run flake8 17 | run: | 18 | python -m pip install --upgrade pip 19 | pip install tox>=2.0 20 | tox -e pep8 21 | 22 | build_python3_kivy: 23 | runs-on: ${{ matrix.runs_on }} 24 | strategy: 25 | matrix: 26 | include: 27 | - runs_on: macos-latest 28 | python: 3.x 29 | - runs_on: apple-silicon-m1 30 | python: '3.11' 31 | steps: 32 | - name: Checkout kivy-ios 33 | uses: actions/checkout@v3 34 | - name: Set up Python 3.x 35 | # Needs to be skipped on our self-hosted runners tagged as 'apple-silicon-m1' 36 | if: ${{ matrix.runs_on != 'apple-silicon-m1' }} 37 | uses: actions/setup-python@v4 38 | with: 39 | python-version: ${{ matrix.python }} 40 | - name: Install requirements 41 | run: | 42 | source .ci/utils.sh 43 | arm64_set_path_and_python_version ${{ matrix.python }} 44 | brew install libjpeg 45 | pip3 install wheel 46 | pip3 install -r requirements.txt 47 | brew install autoconf automake libtool pkg-config 48 | brew link libtool 49 | pip3 install Cython==0.29.33 50 | sudo gem install xcpretty 51 | - name: Install kivy-ios 52 | run: | 53 | source .ci/utils.sh 54 | arm64_set_path_and_python_version ${{ matrix.python }} 55 | python setup.py install 56 | - name: Build Python & Kivy 57 | run: | 58 | source .ci/utils.sh 59 | arm64_set_path_and_python_version ${{ matrix.python }} 60 | toolchain build python3 kivy 61 | - name: Checkout kivy for tests apps 62 | uses: actions/checkout@v3 63 | with: 64 | repository: kivy/kivy 65 | path: kivy-ci-clone 66 | - name: Create & Build test project 67 | run: | 68 | source .ci/utils.sh 69 | arm64_set_path_and_python_version ${{ matrix.python }} 70 | .ci/test_project.sh 71 | 72 | build_python3_kivy_venv: 73 | runs-on: ${{ matrix.runs_on }} 74 | strategy: 75 | matrix: 76 | include: 77 | - runs_on: macos-latest 78 | python: 3.x 79 | - runs_on: apple-silicon-m1 80 | python: '3.11' 81 | steps: 82 | - name: Checkout kivy-ios 83 | uses: actions/checkout@v3 84 | - name: Set up Python 3.x 85 | # Needs to be skipped on our self-hosted runners tagged as 'apple-silicon-m1' 86 | if: ${{ matrix.runs_on != 'apple-silicon-m1' }} 87 | uses: actions/setup-python@v4 88 | with: 89 | python-version: ${{ matrix.python }} 90 | - name: Install requirements 91 | run: | 92 | source .ci/utils.sh 93 | arm64_set_path_and_python_version ${{ matrix.python }} 94 | python -m venv venv 95 | . venv/bin/activate 96 | brew install libjpeg 97 | pip install wheel 98 | pip install -r requirements.txt 99 | pip install sh 100 | brew install autoconf automake libtool pkg-config 101 | brew link libtool 102 | pip install Cython==0.29.33 103 | sudo gem install xcpretty 104 | - name: Install kivy-ios 105 | run: | 106 | source .ci/utils.sh 107 | arm64_set_path_and_python_version ${{ matrix.python }} 108 | python setup.py install 109 | - name: Build Python & Kivy 110 | run: | 111 | source .ci/utils.sh 112 | arm64_set_path_and_python_version ${{ matrix.python }} 113 | . venv/bin/activate 114 | toolchain build python3 kivy 115 | - name: Checkout kivy for tests apps 116 | uses: actions/checkout@v3 117 | with: 118 | repository: kivy/kivy 119 | path: kivy-ci-clone 120 | - name: Create & Build test project 121 | run: | 122 | source .ci/utils.sh 123 | arm64_set_path_and_python_version ${{ matrix.python }} 124 | . venv/bin/activate 125 | .ci/test_project.sh 126 | 127 | build_updated_recipes: 128 | needs: flake8 129 | runs-on: ${{ matrix.runs_on }} 130 | strategy: 131 | matrix: 132 | include: 133 | - runs_on: macos-latest 134 | python: 3.x 135 | - runs_on: apple-silicon-m1 136 | python: '3.11' 137 | steps: 138 | - name: Checkout kivy-ios 139 | uses: actions/checkout@v3 140 | - name: Set up Python 3.x 141 | # Needs to be skipped on our self-hosted runners tagged as 'apple-silicon-m1' 142 | if: ${{ matrix.runs_on != 'apple-silicon-m1' }} 143 | uses: actions/setup-python@v4 144 | with: 145 | python-version: ${{ matrix.python }} 146 | - name: Install requirements 147 | run: | 148 | source .ci/utils.sh 149 | arm64_set_path_and_python_version ${{ matrix.python }} 150 | brew install libjpeg 151 | pip3 install wheel 152 | pip3 install -r requirements.txt 153 | brew install autoconf automake libtool pkg-config 154 | brew link libtool 155 | pip3 install Cython==0.29.33 156 | - name: Install kivy-ios 157 | run: | 158 | source .ci/utils.sh 159 | arm64_set_path_and_python_version ${{ matrix.python }} 160 | python setup.py install 161 | - name: Build updated recipes 162 | run: | 163 | source .ci/utils.sh 164 | arm64_set_path_and_python_version ${{ matrix.python }} 165 | python3 .ci/rebuild_updated_recipes.py 166 | -------------------------------------------------------------------------------- /kivy_ios.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | .gitignore 2 | CHANGELOG.md 3 | LICENSE 4 | MANIFEST.in 5 | README.md 6 | pyproject.toml 7 | requirements.txt 8 | setup.cfg 9 | setup.py 10 | toolchain.py 11 | tox.ini 12 | .ci/constants.py 13 | .ci/rebuild_updated_recipes.py 14 | .ci/test_project.sh 15 | .github/ISSUE_TEMPLATE/bug_report.md 16 | .github/ISSUE_TEMPLATE/feature_request.md 17 | .github/workflows/kivy_ios.yml 18 | .github/workflows/pypi-release.yml 19 | .github/workflows/setup.yml 20 | kivy_ios/__init__.py 21 | kivy_ios/context_managers.py 22 | kivy_ios/toolchain.py 23 | kivy_ios.egg-info/PKG-INFO 24 | kivy_ios.egg-info/SOURCES.txt 25 | kivy_ios.egg-info/dependency_links.txt 26 | kivy_ios.egg-info/entry_points.txt 27 | kivy_ios.egg-info/requires.txt 28 | kivy_ios.egg-info/top_level.txt 29 | kivy_ios/recipes/__init__.py 30 | kivy_ios/recipes/hostpython.py 31 | kivy_ios/recipes/python.py 32 | kivy_ios/recipes/audiostream/__init__.py 33 | kivy_ios/recipes/click/__init__.py 34 | kivy_ios/recipes/curly/__init__.py 35 | kivy_ios/recipes/cymunk/__init__.py 36 | kivy_ios/recipes/ffmpeg/__init__.py 37 | kivy_ios/recipes/ffpyplayer/__init__.py 38 | kivy_ios/recipes/ffpyplayer/misc-visibility.patch 39 | kivy_ios/recipes/flask/__init__.py 40 | kivy_ios/recipes/freetype/__init__.py 41 | kivy_ios/recipes/host_setuptools/__init__.py 42 | kivy_ios/recipes/host_setuptools/setuptools/README.rst 43 | kivy_ios/recipes/host_setuptools3/__init__.py 44 | kivy_ios/recipes/host_setuptools3/setuptools/README.rst 45 | kivy_ios/recipes/hostlibffi/__init__.py 46 | kivy_ios/recipes/hostlibffi/ffi-3.0.13-sysv.S.patch 47 | kivy_ios/recipes/hostlibffi/fix-win32-unreferenced-symbol.patch 48 | kivy_ios/recipes/hostlibffi/generate-darwin-source-and-headers-python3-items.patch 49 | kivy_ios/recipes/hostlibffi/libffi-xcode10.patch 50 | kivy_ios/recipes/hostlibffi/public_include.patch 51 | kivy_ios/recipes/hostlibffi/staticlib.patch 52 | kivy_ios/recipes/hostlibffi/staticlib2.patch 53 | kivy_ios/recipes/hostopenssl/__init__.py 54 | kivy_ios/recipes/hostpython3/ModulesSetup 55 | kivy_ios/recipes/hostpython3/__init__.py 56 | kivy_ios/recipes/hostpython3/disable_sysconfig_cflags.patch 57 | kivy_ios/recipes/ios/__init__.py 58 | kivy_ios/recipes/ios/src/ios.pyx 59 | kivy_ios/recipes/ios/src/ios_browser.m 60 | kivy_ios/recipes/ios/src/ios_filechooser.m 61 | kivy_ios/recipes/ios/src/ios_mail.m 62 | kivy_ios/recipes/ios/src/ios_utils.m 63 | kivy_ios/recipes/ios/src/ios_wrapper.h 64 | kivy_ios/recipes/ios/src/setup.py 65 | kivy_ios/recipes/itsdangerous/__init__.py 66 | kivy_ios/recipes/jinja2/__init__.py 67 | kivy_ios/recipes/kivent_core/__init__.py 68 | kivy_ios/recipes/kivy/__init__.py 69 | kivy_ios/recipes/libcurl/__init__.py 70 | kivy_ios/recipes/libffi/__init__.py 71 | kivy_ios/recipes/libffi/ffi-3.0.13-sysv.S.patch 72 | kivy_ios/recipes/libffi/fix-win32-unreferenced-symbol.patch 73 | kivy_ios/recipes/libffi/generate-darwin-source-and-headers-python3-items.patch 74 | kivy_ios/recipes/libjpeg/__init__.py 75 | kivy_ios/recipes/libpng/__init__.py 76 | kivy_ios/recipes/libzbar/__init__.py 77 | kivy_ios/recipes/libzbar/werror.patch 78 | kivy_ios/recipes/markupsafe/__init__.py 79 | kivy_ios/recipes/netifaces/__init__.py 80 | kivy_ios/recipes/numpy/__init__.py 81 | kivy_ios/recipes/numpy/duplicated_symbols.patch 82 | kivy_ios/recipes/openssl/__init__.py 83 | kivy_ios/recipes/photolibrary/__init__.py 84 | kivy_ios/recipes/pillow/__init__.py 85 | kivy_ios/recipes/pillow/bypass-find-library.patch 86 | kivy_ios/recipes/plyer/__init__.py 87 | kivy_ios/recipes/pycrypto/__init__.py 88 | kivy_ios/recipes/pycrypto/hash_SHA2_template.c.patch 89 | kivy_ios/recipes/pykka/__init__.py 90 | kivy_ios/recipes/pyobjus/__init__.py 91 | kivy_ios/recipes/python3/ModulesSetup 92 | kivy_ios/recipes/python3/ModulesSetup.mobile 93 | kivy_ios/recipes/python3/__init__.py 94 | kivy_ios/recipes/python3/config.sub.patch 95 | kivy_ios/recipes/python3/configure.patch 96 | kivy_ios/recipes/python3/ctypes_duplicate.patch 97 | kivy_ios/recipes/python3/dynload_shlib.patch 98 | kivy_ios/recipes/python3/posixmodule.patch 99 | kivy_ios/recipes/python3/mock_modules/_sqlite3/__init__.py 100 | kivy_ios/recipes/python3/mock_modules/_sqlite3/_sqlite3.cpython-39-darwin.so 101 | kivy_ios/recipes/pyyaml/__init__.py 102 | kivy_ios/recipes/sdl2/__init__.py 103 | kivy_ios/recipes/sdl2/uikit-transparent.patch 104 | kivy_ios/recipes/sdl2_image/__init__.py 105 | kivy_ios/recipes/sdl2_mixer/__init__.py 106 | kivy_ios/recipes/sdl2_ttf/__init__.py 107 | kivy_ios/recipes/werkzeug/__init__.py 108 | kivy_ios/recipes/zbarlight/__init__.py 109 | kivy_ios/recipes/zbarlight/zbarlight_1_2.patch 110 | kivy_ios/tools/__init__.py 111 | kivy_ios/tools/biglink 112 | kivy_ios/tools/cythonize.py 113 | kivy_ios/tools/liblink 114 | kivy_ios/tools/external/__init__.py 115 | kivy_ios/tools/external/xcassets.py 116 | kivy_ios/tools/templates/cookiecutter.json 117 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/bridge.h 118 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/bridge.m 119 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/icon.png 120 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/main.m 121 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/{{ cookiecutter.project_name }}-Info.plist 122 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/LaunchImages/Default-568h@2x.png 123 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/LaunchImages/Default-667h@2x.png 124 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/LaunchImages/Default-763h@3x.png 125 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/LaunchImages/Default-Landscape.png 126 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/LaunchImages/Default-Landscape@2x.png 127 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/LaunchImages/Default-Portrait.png 128 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/LaunchImages/Default-Portrait@2x~ipad.png 129 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/LaunchImages/Default-Portrait@3x~ipad.png 130 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/LaunchImages/Default.png 131 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/LaunchImages/Default@2x.png 132 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/LaunchImages/Default@3x.png 133 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/README.txt 134 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/android.txt 135 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/pictures.kv 136 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/shadow32.png 137 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/images/.empty 138 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/images/5509213687_ffd18df0b9_b.jpg 139 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/images/5552597274_de8b3fb5d2_b.jpg 140 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/YourApp/images/faust_github.jpg 141 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/{{ cookiecutter.project_name }}.xcodeproj/project.pbxproj 142 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/{{ cookiecutter.project_name }}.xcodeproj/project.xcworkspace/contents.xcworkspacedata 143 | kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/{{ cookiecutter.project_name }}/Images.xcassets/AppIcon.appiconset/Contents.json 144 | tests/test_libs/main.py 145 | tests/test_python3/main.m 146 | tests/test_python3/main.py -------------------------------------------------------------------------------- /tests/test_python3/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // test_python3 4 | // 5 | 6 | #import 7 | #import 8 | #include "/Users/tito/code/kivy-ios/dist/root/python3/include/python3.7m/Python.h" 9 | #include "/Users/tito/code/kivy-ios/dist/include/common/sdl2/SDL_main.h" 10 | #include 11 | 12 | void export_orientation(); 13 | void load_custom_builtin_importer(); 14 | 15 | int main(int argc, char *argv[]) { 16 | int ret = 0; 17 | 18 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 19 | 20 | // Change the executing path to YourApp 21 | chdir("YourApp"); 22 | 23 | // Special environment to prefer .pyo, and don't write bytecode if .py are found 24 | // because the process will not have a write attribute on the device. 25 | // putenv("PYTHONOPTIMIZE=2"); 26 | putenv("PYTHONDONTWRITEBYTECODE=1"); 27 | putenv("PYTHONNOUSERSITE=1"); 28 | putenv("PYTHONPATH=."); 29 | // putenv("PYTHONVERBOSE=1"); 30 | putenv("PYTHONUNBUFFERED=1"); 31 | // putenv("PYOBJUS_DEBUG=1"); 32 | 33 | // Kivy environment to prefer some implementation on iOS platform 34 | putenv("KIVY_BUILD=ios"); 35 | putenv("KIVY_NO_CONFIG=1"); 36 | putenv("KIVY_NO_FILELOG=1"); 37 | putenv("KIVY_WINDOW=sdl2"); 38 | putenv("KIVY_IMAGE=imageio,tex"); 39 | putenv("KIVY_AUDIO=sdl2"); 40 | putenv("KIVY_GL_BACKEND=sdl2"); 41 | #ifndef DEBUG 42 | putenv("KIVY_NO_CONSOLELOG=1"); 43 | #endif 44 | 45 | // Export orientation preferences for Kivy 46 | export_orientation(); 47 | 48 | NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; 49 | #if PY_MAJOR_VERSION == 2 50 | NSLog(@"PythonHome is: %s", (char *)[resourcePath UTF8String]); 51 | Py_SetPythonHome((char *)[resourcePath UTF8String]); 52 | #else 53 | NSString *python_home = [NSString stringWithFormat:@"PYTHONHOME=%@", resourcePath, nil]; 54 | putenv((char *)[python_home UTF8String]); 55 | 56 | NSString *python_path = [NSString stringWithFormat:@"PYTHONPATH=%@:%@/lib/python3.7/:%@/lib/python3.7/site-packages", resourcePath, resourcePath, resourcePath, nil]; 57 | putenv((char *)[python_path UTF8String]); 58 | 59 | NSString *tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; 60 | putenv((char *)[tmp_path UTF8String]); 61 | #endif 62 | 63 | NSLog(@"Initializing python"); 64 | Py_Initialize(); 65 | 66 | #if PY_MAJOR_VERSION == 2 67 | PySys_SetArgv(argc, argv); 68 | #else 69 | wchar_t** python_argv = PyMem_RawMalloc(sizeof(wchar_t *) *argc); 70 | for (int i = 0; i < argc; i++) 71 | python_argv[i] = Py_DecodeLocale(argv[i], NULL); 72 | PySys_SetArgv(argc, python_argv); 73 | #endif 74 | 75 | // If other modules are using the thread, we need to initialize them before. 76 | PyEval_InitThreads(); 77 | 78 | // Add an importer for builtin modules 79 | load_custom_builtin_importer(); 80 | 81 | // Search and start main.py 82 | #if PY_MAJOR_VERSION == 2 83 | #define MAIN_EXT @"pyo" 84 | #else 85 | #define MAIN_EXT @"py" 86 | #endif 87 | 88 | const char * prog = [ 89 | [[NSBundle mainBundle] pathForResource:@"YourApp/main" ofType:MAIN_EXT] cStringUsingEncoding: 90 | NSUTF8StringEncoding]; 91 | NSLog(@"Running main.py: %s", prog); 92 | FILE* fd = fopen(prog, "r"); 93 | if ( fd == NULL ) { 94 | ret = 1; 95 | NSLog(@"Unable to open main.py, abort."); 96 | } else { 97 | ret = PyRun_SimpleFileEx(fd, prog, 1); 98 | if (ret != 0) 99 | NSLog(@"Application quit abnormally!"); 100 | } 101 | 102 | Py_Finalize(); 103 | NSLog(@"Leaving"); 104 | 105 | [pool release]; 106 | 107 | // Look like the app still runs even when we left here. 108 | exit(ret); 109 | return ret; 110 | } 111 | 112 | // This method reads the available orientations from the Info.plist file and 113 | // shares them via an environment variable. Kivy will automatically set the 114 | // orientation according to this environment value, if it exists. To restrict 115 | // the allowed orientation, please see the comments inside. 116 | void export_orientation() { 117 | NSDictionary *info = [[NSBundle mainBundle] infoDictionary]; 118 | NSArray *orientations = [info objectForKey:@"UISupportedInterfaceOrientations"]; 119 | 120 | // Orientation restrictions 121 | // ======================== 122 | // Comment or uncomment blocks 1-3 in order the limit orientation support 123 | 124 | // 1. Landscape only 125 | // NSString *result = [[NSString alloc] initWithString:@"KIVY_ORIENTATION=LandscapeLeft LandscapeRight"]; 126 | 127 | // 2. Portrait only 128 | // NSString *result = [[NSString alloc] initWithString:@"KIVY_ORIENTATION=Portrait PortraitUpsideDown"]; 129 | 130 | // 3. All orientations 131 | NSString *result = [[NSString alloc] initWithString:@"KIVY_ORIENTATION="]; 132 | for (int i = 0; i < [orientations count]; i++) { 133 | NSString *item = [orientations objectAtIndex:i]; 134 | item = [item substringFromIndex:22]; 135 | if (i > 0) 136 | result = [result stringByAppendingString:@" "]; 137 | result = [result stringByAppendingString:item]; 138 | } 139 | // ======================== 140 | 141 | putenv((char *)[result UTF8String]); 142 | NSLog(@"Available orientation: %@", result); 143 | } 144 | 145 | void load_custom_builtin_importer() { 146 | static const char *custom_builtin_importer = \ 147 | "import sys, imp, types\n" \ 148 | "from os import environ\n" \ 149 | "from os.path import exists, join\n" \ 150 | "try:\n" \ 151 | " # python 3\n" 152 | " import _imp\n" \ 153 | " EXTS = _imp.extension_suffixes()\n" \ 154 | " sys.modules['subprocess'] = types.ModuleType(name='subprocess')\n" \ 155 | "except ImportError:\n" \ 156 | " EXTS = ['.so']\n" 157 | "# Fake redirection to supress console output\n" \ 158 | "if environ.get('KIVY_NO_CONSOLE', '0') == '1':\n" \ 159 | " class fakestd(object):\n" \ 160 | " def write(self, *args, **kw): pass\n" \ 161 | " def flush(self, *args, **kw): pass\n" \ 162 | " sys.stdout = fakestd()\n" \ 163 | " sys.stderr = fakestd()\n" \ 164 | "# Custom builtin importer for precompiled modules\n" \ 165 | "class CustomBuiltinImporter(object):\n" \ 166 | " def find_module(self, fullname, mpath=None):\n" \ 167 | " # print(f'find_module() fullname={fullname} mpath={mpath}')\n" \ 168 | " if '.' not in fullname:\n" \ 169 | " return\n" \ 170 | " if not mpath:\n" \ 171 | " return\n" \ 172 | " part = fullname.rsplit('.')[-1]\n" \ 173 | " for ext in EXTS:\n" \ 174 | " fn = join(list(mpath)[0], '{}{}'.format(part, ext))\n" \ 175 | " # print('find_module() {}'.format(fn))\n" \ 176 | " if exists(fn):\n" \ 177 | " return self\n" \ 178 | " return\n" \ 179 | " def load_module(self, fullname):\n" \ 180 | " f = fullname.replace('.', '_')\n" \ 181 | " mod = sys.modules.get(f)\n" \ 182 | " if mod is None:\n" \ 183 | " # print('LOAD DYNAMIC', f, sys.modules.keys())\n" \ 184 | " try:\n" \ 185 | " mod = imp.load_dynamic(f, f)\n" \ 186 | " except ImportError:\n" \ 187 | " import traceback; traceback.print_exc();\n" \ 188 | " # print('LOAD DYNAMIC FALLBACK', fullname)\n" \ 189 | " mod = imp.load_dynamic(fullname, fullname)\n" \ 190 | " sys.modules[fullname] = mod\n" \ 191 | " return mod\n" \ 192 | " return mod\n" \ 193 | "sys.meta_path.insert(0, CustomBuiltinImporter())"; 194 | PyRun_SimpleString(custom_builtin_importer); 195 | } 196 | -------------------------------------------------------------------------------- /kivy_ios/tools/templates/{{ cookiecutter.project_name }}-ios/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // {{ cookiecutter.project_name }} 4 | // 5 | 6 | #import 7 | #import 8 | #include "Python.h" 9 | #include "{{ cookiecutter.dist_dir }}/include/common/sdl2/SDL_main.h" 10 | #include 11 | 12 | void export_orientation(); 13 | void load_custom_builtin_importer(); 14 | 15 | int main(int argc, char *argv[]) { 16 | int ret = 0; 17 | 18 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 19 | 20 | // Change the executing path to YourApp 21 | chdir("YourApp"); 22 | 23 | // Special environment to prefer .pyo, and don't write bytecode if .py are found 24 | // because the process will not have a write attribute on the device. 25 | putenv("PYTHONOPTIMIZE=2"); 26 | putenv("PYTHONDONTWRITEBYTECODE=1"); 27 | putenv("PYTHONNOUSERSITE=1"); 28 | putenv("PYTHONPATH=."); 29 | putenv("PYTHONUNBUFFERED=1"); 30 | putenv("LC_CTYPE=UTF-8"); 31 | // putenv("PYTHONVERBOSE=1"); 32 | // putenv("PYOBJUS_DEBUG=1"); 33 | 34 | // Kivy environment to prefer some implementation on iOS platform 35 | putenv("KIVY_BUILD=ios"); 36 | putenv("KIVY_WINDOW=sdl2"); 37 | putenv("KIVY_IMAGE=imageio,tex,gif,sdl2"); 38 | putenv("KIVY_AUDIO=sdl2"); 39 | putenv("KIVY_GL_BACKEND=sdl2"); 40 | 41 | // IOS_IS_WINDOWED=True disables fullscreen and then statusbar is shown 42 | putenv("IOS_IS_WINDOWED=False"); 43 | 44 | #ifndef DEBUG 45 | putenv("KIVY_NO_CONSOLELOG=1"); 46 | #endif 47 | 48 | // Export orientation preferences for Kivy 49 | export_orientation(); 50 | 51 | NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; 52 | NSString *python_home = [NSString stringWithFormat:@"PYTHONHOME=%@", resourcePath, nil]; 53 | putenv((char *)[python_home UTF8String]); 54 | 55 | NSString *python_path = [NSString stringWithFormat:@"PYTHONPATH=%@:%@/lib/python3.10/:%@/lib/python3.10/site-packages:.", resourcePath, resourcePath, resourcePath, nil]; 56 | putenv((char *)[python_path UTF8String]); 57 | 58 | NSString *tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; 59 | putenv((char *)[tmp_path UTF8String]); 60 | 61 | NSLog(@"Initializing python"); 62 | Py_Initialize(); 63 | 64 | wchar_t** python_argv = PyMem_RawMalloc(sizeof(wchar_t *) *argc); 65 | for (int i = 0; i < argc; i++) 66 | python_argv[i] = Py_DecodeLocale(argv[i], NULL); 67 | PySys_SetArgv(argc, python_argv); 68 | 69 | // If other modules are using the thread, we need to initialize them before. 70 | PyEval_InitThreads(); 71 | 72 | // Add an importer for builtin modules 73 | load_custom_builtin_importer(); 74 | 75 | // Search and start main.py 76 | #define MAIN_EXT @"pyc" 77 | 78 | const char * prog = [ 79 | [[NSBundle mainBundle] pathForResource:@"YourApp/main" ofType:MAIN_EXT] cStringUsingEncoding: 80 | NSUTF8StringEncoding]; 81 | NSLog(@"Running main.py: %s", prog); 82 | FILE* fd = fopen(prog, "r"); 83 | if ( fd == NULL ) { 84 | ret = 1; 85 | NSLog(@"Unable to open main.py, abort."); 86 | } else { 87 | ret = PyRun_SimpleFileEx(fd, prog, 1); 88 | if (ret != 0) 89 | NSLog(@"Application quit abnormally!"); 90 | } 91 | 92 | Py_Finalize(); 93 | NSLog(@"Leaving"); 94 | 95 | [pool release]; 96 | 97 | // Look like the app still runs even when we left here. 98 | exit(ret); 99 | return ret; 100 | } 101 | 102 | // This method reads the available orientations from the Info.plist file and 103 | // shares them via an environment variable. Kivy will automatically set the 104 | // orientation according to this environment value, if it exists. To restrict 105 | // the allowed orientation, please see the comments inside. 106 | void export_orientation() { 107 | NSDictionary *info = [[NSBundle mainBundle] infoDictionary]; 108 | NSArray *orientations = [info objectForKey:@"UISupportedInterfaceOrientations"]; 109 | 110 | // Orientation restrictions 111 | // ======================== 112 | // Comment or uncomment blocks 1-3 in order the limit orientation support 113 | 114 | // 1. Landscape only 115 | // NSString *result = [[NSString alloc] initWithString:@"KIVY_ORIENTATION=LandscapeLeft LandscapeRight"]; 116 | 117 | // 2. Portrait only 118 | // NSString *result = [[NSString alloc] initWithString:@"KIVY_ORIENTATION=Portrait PortraitUpsideDown"]; 119 | 120 | // 3. All orientations 121 | NSString *result = [[NSString alloc] initWithString:@"KIVY_ORIENTATION="]; 122 | for (int i = 0; i < [orientations count]; i++) { 123 | NSString *item = [orientations objectAtIndex:i]; 124 | item = [item substringFromIndex:22]; 125 | if (i > 0) 126 | result = [result stringByAppendingString:@" "]; 127 | result = [result stringByAppendingString:item]; 128 | } 129 | // ======================== 130 | 131 | putenv((char *)[result UTF8String]); 132 | NSLog(@"Available orientation: %@", result); 133 | } 134 | 135 | void load_custom_builtin_importer() { 136 | static const char *custom_builtin_importer = \ 137 | "import sys, imp, types\n" \ 138 | "from os import environ\n" \ 139 | "from os.path import exists, join\n" \ 140 | "try:\n" \ 141 | " # python 3\n" 142 | " import _imp\n" \ 143 | " EXTS = _imp.extension_suffixes()\n" \ 144 | " sys.modules['subprocess'] = types.ModuleType(name='subprocess')\n" \ 145 | " sys.modules['subprocess'].PIPE = None\n" \ 146 | " sys.modules['subprocess'].STDOUT = None\n" \ 147 | " sys.modules['subprocess'].DEVNULL = None\n" \ 148 | " sys.modules['subprocess'].CalledProcessError = Exception\n" \ 149 | " sys.modules['subprocess'].check_output = None\n" \ 150 | "except ImportError:\n" \ 151 | " EXTS = ['.so']\n" 152 | "# Fake redirection to supress console output\n" \ 153 | "if environ.get('KIVY_NO_CONSOLE', '0') == '1':\n" \ 154 | " class fakestd(object):\n" \ 155 | " def write(self, *args, **kw): pass\n" \ 156 | " def flush(self, *args, **kw): pass\n" \ 157 | " sys.stdout = fakestd()\n" \ 158 | " sys.stderr = fakestd()\n" \ 159 | "# Custom builtin importer for precompiled modules\n" \ 160 | "class CustomBuiltinImporter(object):\n" \ 161 | " def find_module(self, fullname, mpath=None):\n" \ 162 | " # print(f'find_module() fullname={fullname} mpath={mpath}')\n" \ 163 | " if '.' not in fullname:\n" \ 164 | " return\n" \ 165 | " if not mpath:\n" \ 166 | " return\n" \ 167 | " part = fullname.rsplit('.')[-1]\n" \ 168 | " for ext in EXTS:\n" \ 169 | " fn = join(list(mpath)[0], '{}{}'.format(part, ext))\n" \ 170 | " # print('find_module() {}'.format(fn))\n" \ 171 | " if exists(fn):\n" \ 172 | " return self\n" \ 173 | " return\n" \ 174 | " def load_module(self, fullname):\n" \ 175 | " f = fullname.replace('.', '_')\n" \ 176 | " mod = sys.modules.get(f)\n" \ 177 | " if mod is None:\n" \ 178 | " # print('LOAD DYNAMIC', f, sys.modules.keys())\n" \ 179 | " try:\n" \ 180 | " mod = imp.load_dynamic(f, f)\n" \ 181 | " except ImportError:\n" \ 182 | " # import traceback; traceback.print_exc();\n" \ 183 | " # print('LOAD DYNAMIC FALLBACK', fullname)\n" \ 184 | " mod = imp.load_dynamic(fullname, fullname)\n" \ 185 | " sys.modules[fullname] = mod\n" \ 186 | " return mod\n" \ 187 | " return mod\n" \ 188 | "sys.meta_path.insert(0, CustomBuiltinImporter())"; 189 | PyRun_SimpleString(custom_builtin_importer); 190 | } 191 | -------------------------------------------------------------------------------- /kivy_ios/recipes/python3/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy_ios.toolchain import Recipe, shprint 2 | from kivy_ios.context_managers import cd 3 | from os.path import join 4 | import sh 5 | import os 6 | import logging 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | 11 | class Python3Recipe(Recipe): 12 | version = "3.10.10" 13 | url = "https://www.python.org/ftp/python/{version}/Python-{version}.tgz" 14 | depends = ["hostpython3", "libffi", "openssl"] 15 | library = "libpython3.10.a" 16 | pbx_libraries = ["libz", "libbz2", "libsqlite3"] 17 | 18 | def init_with_ctx(self, ctx): 19 | super().init_with_ctx(ctx) 20 | self.set_python(self, "3.10") 21 | ctx.python_ver_dir = "python3.10" 22 | ctx.python_prefix = join(ctx.dist_dir, "root", "python3") 23 | ctx.site_packages_dir = join( 24 | ctx.python_prefix, "lib", ctx.python_ver_dir, "site-packages") 25 | 26 | def prebuild_arch(self, arch): 27 | # common to all archs 28 | if self.has_marker("patched"): 29 | return 30 | self.apply_patch("configure.patch") 31 | self.apply_patch("posixmodule.patch") 32 | self.apply_patch("dynload_shlib.patch") 33 | self.apply_patch("ctypes_duplicate.patch") 34 | self.copy_file("ModulesSetup", "Modules/Setup.local") 35 | self.append_file("ModulesSetup.mobile", "Modules/Setup.local") 36 | self.set_marker("patched") 37 | 38 | def postbuild_arch(self, arch): 39 | # We need to skip remove_junk, because we need to keep few files. 40 | # A cleanup will be done in the final step. 41 | return 42 | 43 | def get_build_env(self, arch): 44 | build_env = arch.get_env() 45 | build_env["PATH"] = "{}:{}".format( 46 | join(self.ctx.dist_dir, "hostpython3", "bin"), 47 | os.environ["PATH"]) 48 | build_env["CFLAGS"] += " --sysroot={}".format(arch.sysroot) 49 | return build_env 50 | 51 | def build_arch(self, arch): 52 | build_env = self.get_build_env(arch) 53 | configure = sh.Command(join(self.build_dir, "configure")) 54 | py_arch = arch.arch_arg() 55 | if py_arch == "arm64": 56 | py_arch = "aarch64" 57 | prefix = join(self.ctx.dist_dir, "root", "python3") 58 | shprint(configure, 59 | "CC={}".format(build_env["CC"]), 60 | "LD={}".format(build_env["LD"]), 61 | "CFLAGS={}".format(build_env["CFLAGS"].replace("-fembed-bitcode", "")), 62 | "LDFLAGS={} -undefined dynamic_lookup".format(build_env["LDFLAGS"]), 63 | "ac_cv_file__dev_ptmx=yes", 64 | "ac_cv_file__dev_ptc=no", 65 | "ac_cv_little_endian_double=yes", 66 | "ac_cv_func_memrchr=no", 67 | "ac_cv_func_getentropy=no", 68 | "ac_cv_func_getresuid=no", 69 | "ac_cv_func_getresgid=no", 70 | "ac_cv_func_setresgid=no", 71 | "ac_cv_func_setresuid=no", 72 | "ac_cv_func_plock=no", 73 | "ac_cv_func_dup3=no", 74 | "ac_cv_func_pipe2=no", 75 | "ac_cv_func_preadv=no", 76 | "ac_cv_func_pwritev=no", 77 | "ac_cv_func_preadv2=no", 78 | "ac_cv_func_pwritev2=no", 79 | "ac_cv_func_mkfifoat=no", 80 | "ac_cv_func_mknodat=no", 81 | "ac_cv_func_posix_fadvise=no", 82 | "ac_cv_func_posix_fallocate=no", 83 | "ac_cv_func_sigwaitinfo=no", 84 | "ac_cv_func_sigtimedwait=no", 85 | "ac_cv_func_clock_settime=no", 86 | "ac_cv_func_pthread_getcpuclockid=no", 87 | "ac_cv_func_sched_setscheduler=no", 88 | "ac_cv_func_sched_setparam=no", 89 | "ac_cv_func_clock_gettime=no", 90 | "ac_cv_func_rtpSpawn=no", 91 | "ac_cv_func_fdwalk=no", 92 | "ac_cv_func_futimesat=no", 93 | "ac_cv_func_copy_file_range=no", 94 | "ac_cv_func_fexecve=no", 95 | "ac_cv_func_execve=no", 96 | "ac_cv_func_sched_rr_get_interval=no", 97 | "ac_cv_func_explicit_bzero=no", 98 | "ac_cv_func_explicit_memset=no", 99 | "ac_cv_func_close_range=no", 100 | "ac_cv_func_splice=no", 101 | "ac_cv_func_mremap=no", 102 | "--host={}-apple-ios".format(py_arch), 103 | "--build=x86_64-apple-darwin", 104 | "--prefix={}".format(prefix), 105 | "--without-ensurepip", 106 | "--with-system-ffi", 107 | "--enable-ipv6", 108 | "PYTHON_FOR_BUILD=_PYTHON_PROJECT_BASE=$(abs_builddir) \ 109 | _PYTHON_HOST_PLATFORM=$(_PYTHON_HOST_PLATFORM) \ 110 | PYTHONPATH=$(shell test -f pybuilddir.txt && echo $(abs_builddir)/`cat pybuilddir.txt`:)$(srcdir)/Lib\ 111 | _PYTHON_SYSCONFIGDATA_NAME=_sysconfigdata_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH)\ 112 | {}".format(sh.Command(self.ctx.hostpython)), 113 | _env=build_env) 114 | shprint(sh.make, self.ctx.concurrent_make, "CFLAGS={}".format(build_env["CFLAGS"])) 115 | 116 | def install(self): 117 | arch = list(self.filtered_archs)[0] 118 | build_env = self.get_build_env(arch) 119 | build_dir = self.get_build_dir(arch.arch) 120 | shprint(sh.make, self.ctx.concurrent_make, 121 | "-C", build_dir, 122 | "install", 123 | "prefix={}".format(join(self.ctx.dist_dir, "root", "python3")), 124 | _env=build_env) 125 | self.reduce_python() 126 | 127 | def reduce_python(self): 128 | logger.info("Reduce python") 129 | logger.info("Remove files unlikely to be used") 130 | with cd(join(self.ctx.dist_dir, "root", "python3")): 131 | sh.rm("-rf", "bin", "share") 132 | # platform binaries and configuration 133 | with cd(join( 134 | self.ctx.dist_dir, "root", "python3", "lib", 135 | "python3.10", "config-3.10-darwin")): 136 | sh.rm( 137 | "libpython3.10.a", 138 | "python.o", 139 | "config.c.in", 140 | "makesetup", 141 | "install-sh", 142 | ) 143 | 144 | # cleanup pkgconfig and compiled lib 145 | with cd(join(self.ctx.dist_dir, "root", "python3", "lib")): 146 | sh.rm("-rf", "pkgconfig", "libpython3.10.a") 147 | 148 | # cleanup python libraries 149 | with cd(join( 150 | self.ctx.dist_dir, "root", "python3", "lib", "python3.10")): 151 | sh.rm("-rf", "wsgiref", "curses", "idlelib", "lib2to3", 152 | "ensurepip", "turtledemo", "lib-dynload", "venv", 153 | "pydoc_data") 154 | sh.find(".", "-path", "*/test*/*", "-delete") 155 | sh.find(".", "-name", "*.exe", "-type", "f", "-delete") 156 | sh.find(".", "-name", "test*", "-type", "d", "-delete") 157 | sh.find(".", "-iname", "*.pyc", "-delete") 158 | sh.find(".", "-path", "*/__pycache__/*", "-delete") 159 | sh.find(".", "-name", "__pycache__", "-type", "d", "-delete") 160 | 161 | # now precompile to Python bytecode 162 | hostpython = sh.Command(self.ctx.hostpython) 163 | shprint(hostpython, "-m", "compileall", "-f", "-b") 164 | # sh.find(".", "-iname", "*.py", "-delete") 165 | 166 | # some pycache are recreated after compileall 167 | sh.find(".", "-path", "*/__pycache__/*", "-delete") 168 | sh.find(".", "-name", "__pycache__", "-type", "d", "-delete") 169 | 170 | # create the lib zip 171 | logger.info("Create a python3.10.zip") 172 | sh.mv("config-3.10-darwin", "..") 173 | sh.mv("site-packages", "..") 174 | sh.zip("-r", "../python310.zip", sh.glob("*")) 175 | sh.rm("-rf", sh.glob("*")) 176 | sh.mv("../config-3.10-darwin", ".") 177 | sh.mv("../site-packages", ".") 178 | 179 | 180 | recipe = Python3Recipe() 181 | -------------------------------------------------------------------------------- /kivy_ios/recipes/ios/src/ios.pyx: -------------------------------------------------------------------------------- 1 | ''' 2 | IOS module 3 | ========== 4 | 5 | IOS module is wrapping some part of the IOS features. 6 | 7 | ''' 8 | 9 | from cpython cimport Py_INCREF, Py_DECREF 10 | from os.path import basename 11 | 12 | cdef extern from "ios_wrapper.h": 13 | ctypedef void (*ios_send_email_cb)(char *, void *) 14 | ctypedef struct padding: 15 | float top 16 | float bottom 17 | float right 18 | float left 19 | int ios_send_email(char *subject, char *text, char *mimetype, char 20 | *filename, char *filename_alias, ios_send_email_cb cb, void *userdata) 21 | void ios_open_url(char *url) 22 | void load_url_webview(char *url, int x, int y, int width, int height) 23 | float ios_uiscreen_get_scale() 24 | int ios_uiscreen_get_dpi() 25 | padding ios_get_safe_area() 26 | 27 | cdef void _send_email_done(char *status, void *data): 28 | cdef object callback = data 29 | callback(status) 30 | Py_DECREF(callback) 31 | 32 | # 33 | #Support for iOS webview 34 | # 35 | class IOSWebView(object): 36 | def open(self, url, x, y, width, height): 37 | open_url_wbv(url, x, y, width, height) 38 | 39 | 40 | def open_url_wbv(url, x, y, width, height): 41 | ''' 42 | OPEN URL in webview 43 | 44 | :Parameters: 45 | `url`: str 46 | URL string 47 | 48 | `height`: int 49 | Height of the window 50 | 51 | `width`: int 52 | Width of the window 53 | 54 | Example for opening up a web page in WKWebview:: 55 | 56 | import ios 57 | url = "http://www.google.com" 58 | ios.IOSWebView().open(url, x, y, width, height) 59 | ''' 60 | load_url_webview(url, x, y, width, height) 61 | 62 | # 63 | # Support for webbrowser module 64 | # 65 | 66 | class IosBrowser(object): 67 | def open(self, url, new=0, autoraise=True): 68 | open_url(url) 69 | def open_new(self, url): 70 | open_url(url) 71 | def open_new_tab(self, url): 72 | open_url(url) 73 | 74 | import webbrowser 75 | try: 76 | # python 2 77 | webbrowser.register('ios', IosBrowser, None, -1) 78 | except: 79 | # python 3 80 | webbrowser.register('ios', IosBrowser, None, preferred=True) 81 | # 82 | # API 83 | # 84 | 85 | __version__ = (1, 1, 0) 86 | 87 | def open_url(url): 88 | '''Open an URL in Safari 89 | 90 | :Parameters: 91 | `url`: str 92 | The url string 93 | ''' 94 | cdef char *j_url = NULL 95 | 96 | if url is not None: 97 | if type(url) is unicode: 98 | url = url.encode('UTF-8') 99 | j_url = url 100 | 101 | ios_open_url(j_url) 102 | 103 | 104 | def send_email(subject, text, mimetype=None, filename=None, filename_alias=None, callback=None): 105 | '''Send an email using the IOS api. 106 | 107 | :Parameters: 108 | `subject`: str 109 | Subject of the email 110 | `text`: str 111 | Content of the email 112 | `mimetype`: str 113 | Mimetype of the attachment if exist 114 | `filename`: str 115 | Full path of the filename to attach, must be used with mimetype. 116 | `filename_alias`: str 117 | Name of the file that will be shown to the user. If none is set, it 118 | will use the basename of filename. 119 | `callback`: func(status) 120 | Callback that can be called when the email interface have been 121 | removed. A status will be passed as the first argument: "cancelled", 122 | "saved", "sent", "failed", "unknown", "cannotsend". 123 | 124 | .. note:: 125 | 126 | The application must have the window created to be able to use that 127 | method. Trying to send an email without the application running will 128 | crash. 129 | 130 | Example for sending a simple hello world:: 131 | 132 | ios.send_email('This is my subject', 133 | 'Hello you!\n\nThis is an hello world.') 134 | 135 | Send a mail with an attachment:: 136 | 137 | from os.path import realpath 138 | ios.send_email('Mail with attachment', 139 | 'Your attachment will be just after this message.', 140 | mimetype='image/png', 141 | filename=realpath('mylogo.png')) 142 | 143 | Getting the status of the mail with the callback 144 | 145 | from kivy.app import App 146 | 147 | class EmailApp(App): 148 | def callback_email(self, status): 149 | print 'The email have been', status 150 | 151 | def send_email(self, *largs): 152 | print 'Sending an email' 153 | ios.send_email('Hello subject', 'World body', 154 | callback=self.callback_email) 155 | 156 | def build(self): 157 | btn = Button(text='Click me') 158 | btn.bind(on_release=self.send_email) 159 | return btn 160 | 161 | if __name__ == '__main__': 162 | EmailApp().run() 163 | 164 | ''' 165 | cdef char *j_mimetype = NULL 166 | cdef char *j_filename = NULL 167 | cdef char *j_subject = NULL 168 | cdef char *j_text = NULL 169 | cdef char *j_title = NULL 170 | cdef char *j_filename_alias = NULL 171 | 172 | if subject is not None: 173 | if type(subject) is unicode: 174 | subject = subject.encode('UTF-8') 175 | j_subject = subject 176 | if text is not None: 177 | if type(text) is unicode: 178 | text = text.encode('UTF-8') 179 | j_text = text 180 | if mimetype is not None: 181 | j_mimetype = mimetype 182 | if filename is not None: 183 | j_filename = filename 184 | 185 | if filename_alias is None: 186 | filename_alias = basename(filename) 187 | if type(filename_alias) is unicode: 188 | filename_alias = filename_alias.encode('UTF-8') 189 | j_filename_alias = filename_alias 190 | 191 | 192 | Py_INCREF(callback) 193 | 194 | ret = ios_send_email(j_subject, j_text, j_mimetype, j_filename, 195 | j_filename_alias, _send_email_done, callback) 196 | if ret == 0: 197 | callback('failed') 198 | return 0 199 | elif ret == -1: 200 | callback('cannotsend') 201 | return 0 202 | 203 | return 1 204 | 205 | def get_scale(): 206 | '''Return the UIScreen scale (1 on iPad, 2 on iPad 3) 207 | ''' 208 | return ios_uiscreen_get_scale() 209 | 210 | def get_dpi(): 211 | '''Return the approximate DPI of the screen 212 | ''' 213 | return ios_uiscreen_get_dpi() 214 | 215 | def get_safe_area(): 216 | '''Return the safe area bounds 217 | ''' 218 | return ios_get_safe_area() 219 | 220 | 221 | from pyobjus import autoclass, selector, protocol 222 | from pyobjus.protocols import protocols 223 | 224 | NSNotificationCenter = autoclass('NSNotificationCenter') 225 | 226 | protocols["KeyboardDelegates"] = { 227 | 'keyboardWillShow': ('v16@0:4@8', "v32@0:8@16"), 228 | 'keyboardDidHide': ('v16@0:4@8', "v32@0:8@16")} 229 | 230 | 231 | class IOSKeyboard(object): 232 | '''Get listener for keyboard height. 233 | ''' 234 | 235 | kheight = 0 236 | 237 | def __init__(self, **kwargs): 238 | super(IOSKeyboard, self).__init__() 239 | NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, selector("keyboardWillShow"), "UIKeyboardWillShowNotification", None) 240 | NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, selector("keyboardDidHide"), "UIKeyboardDidHideNotification", None) 241 | 242 | @protocol('KeyboardDelegates') 243 | def keyboardWillShow(self, notification): 244 | self.kheight = get_scale() * notification.userInfo().objectForKey_( 245 | 'UIKeyboardFrameEndUserInfoKey').CGRectValue().size.height 246 | from kivy.core.window import Window 247 | Window.trigger_keyboard_height() 248 | 249 | @protocol('KeyboardDelegates') 250 | def keyboardDidHide(self, notification): 251 | self.kheight = 0 252 | from kivy.core.window import Window 253 | Window.trigger_keyboard_height() 254 | 255 | iOSKeyboard = IOSKeyboard() 256 | 257 | def get_kheight(): 258 | return iOSKeyboard.kheight 259 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### This branch has modifications to build .xcframework supporting native arm64 iOS simulators on M1 macs. 2 | 3 | # Kivy for iOS 4 | 5 | [![kivy-ios](https://github.com/kivy/kivy-ios/workflows/kivy-ios/badge.svg)](https://github.com/kivy/kivy-ios/actions?query=workflow%3Akivy-ios) 6 | [![PyPI version](https://badge.fury.io/py/kivy-ios.svg)](https://badge.fury.io/py/kivy-ios) 7 | [![Backers on Open Collective](https://opencollective.com/kivy/backers/badge.svg)](#backers) 8 | [![Sponsors on Open Collective](https://opencollective.com/kivy/sponsors/badge.svg)](#sponsors) 9 | 10 | This toolchain is designed to compile the necessary libraries for iOS to run 11 | your application and manage the creation of the Xcode project. 12 | 13 | We do not provide any binary distributions of this toolchain. 14 | You do need to compile it at least once before creating your Xcode project. 15 | 16 | The toolchain supports: 17 | 18 | - iPhone Simulator (x86_64) 19 | - iPhone / iOS (arm64) 20 | 21 | These recipes are not ported to the new toolchain yet: 22 | 23 | - lxml 24 | 25 | 26 | ## Installation & requirements 27 | 28 | Before we start, we strongly advise using a Python virtual environment to install Python packages. 29 | 30 | python3 -m venv venv 31 | . venv/bin/activate 32 | 33 | Install [Kivy for iOS from PyPI](https://pypi.org/project/kivy-ios) with pip like any Python package. 34 | 35 | pip3 install kivy-ios 36 | 37 | Additionally, you would need a few system dependencies and configuration. 38 | 39 | - Xcode 13 or above, with an iOS SDK and command line tools installed: 40 | 41 | xcode-select --install 42 | 43 | - Using brew, you can install the following dependencies: 44 | 45 | brew install autoconf automake libtool pkg-config 46 | brew link libtool 47 | 48 | ## Using the toolchain 49 | 50 | Any Python extensions or C/C++ library must be compiled: you need to have what 51 | we call a `recipe` to compile it. For example, Python, libffi, SDL2, SDL_image, 52 | freetype... all the dependencies, compilation, and packaging instructions are 53 | contained in a `recipe`. 54 | 55 | You can list the available recipes and their versions with: 56 | 57 | $ toolchain recipes 58 | audiostream master 59 | click 7.1.2 60 | cymunk master 61 | ffmpeg n4.3.1 62 | ffpyplayer v3.2 63 | flask 1.1.2 64 | freetype 2.5.5 65 | hostlibffi 3.2.1 66 | hostopenssl 1.1.1g 67 | hostpython3 3.7.1 68 | ios master 69 | itsdangerous 1.1.0 70 | jinja2 2.11.2 71 | kivy 1.10.1 72 | libffi 3.2.1 73 | libjpeg v9a 74 | libpng 1.6.26 75 | markupsafe 1.1.1 76 | moodstocks 4.1.5 77 | numpy 1.16.4 78 | openssl 1.1.1g 79 | photolibrary master 80 | pillow 6.1.0 81 | plyer master 82 | pycrypto 2.6.1 83 | pykka 1.2.1 84 | pyobjus master 85 | python3 3.7.1 86 | pyyaml 3.11 87 | sdl2 2.0.8 88 | sdl2_image 2.0.0 89 | sdl2_mixer 2.0.0 90 | sdl2_ttf 2.0.12 91 | werkzeug 1.0.1 92 | 93 | Then, start the compilation with: 94 | 95 | $ toolchain build python3 kivy 96 | 97 | You can build recipes at the same time by adding them as parameters: 98 | 99 | $ toolchain build python3 openssl kivy 100 | 101 | Recipe builds can be removed via the clean command e.g.: 102 | 103 | $ toolchain clean openssl 104 | 105 | You can install package that don't require compilation with pip:: 106 | 107 | $ toolchain pip install plyer 108 | 109 | The Kivy recipe depends on several others, like the sdl\* and python recipes. 110 | These may, in turn, depend on others e.g. sdl2_ttf depends on freetype, etc. 111 | You can think of it as follows: the kivy recipe will compile everything 112 | necessary for a minimal working version of Kivy. 113 | 114 | Don't grab a coffee, just do dinner. Compiling all the libraries for the first 115 | time, 2x over (remember, 2 archs, x86_64, arm64) will take time. 116 | 117 | For a complete list of available commands, type: 118 | 119 | $ toolchain 120 | 121 | ## Create the Xcode project 122 | 123 | The `toolchain.py` can create the initial Xcode project for you:: 124 | 125 | $ toolchain create <app_directory> 126 | $ toolchain create Touchtracer ~/code/kivy/examples/demo/touchtracer 127 | 128 | Your app directory must contain a main.py. A directory named `<title>-ios` 129 | will be created, with an Xcode project in it. 130 | You can open the Xcode project using:: 131 | 132 | $ open touchtracer-ios/touchtracer.xcodeproj 133 | 134 | Then click on `Play`, and enjoy. 135 | 136 | > *Did you know?* 137 | > 138 | > Every time you press `Play`, your application directory will be synced to 139 | > the `<title>-ios/YourApp` directory. Don't make changes in the -ios 140 | > directory directly. 141 | 142 | 143 | ## Configuring your App 144 | 145 | You can configure and customize your app in various ways: 146 | 147 | - Set the icon and launch images in XCode. Note that XCode requires that you 148 | specify these assets per device or/and iOS version. 149 | 150 | - When you first build your XCode project, a 'main.m' file is created in your 151 | XCode project folder. This file configures your environment variables and 152 | controls your application startup. You can edit this file to customize your 153 | launch environment. 154 | 155 | - Kivy uses SDL, and as soon as the application starts the SDL main, the launch 156 | image will disappear. To prevent that, you need to have 2 files named 157 | `Default.png` and `Default-Landscape.png` and put them 158 | in the `Resources` folder in Xcode (not in your application folder) 159 | 160 | > *Did you know?* 161 | > 162 | > If you wish to restrict your app's orientation, you should do this via 163 | > the 'export_orientation' function in 'main.m'. The XCode orientation 164 | > settings should be set to support all. 165 | 166 | 167 | ## Using recipes 168 | 169 | Recipes are used to install and compile any libraries you may need to use. These 170 | recipes follow the same format as those used by the 171 | [Python-for-Android](https://github.com/kivy/python-for-android) sister project. 172 | Please refer to the 173 | [recipe documentation](https://python-for-android.readthedocs.io/en/latest/recipes/) 174 | there for more detail. 175 | 176 | 177 | ## Reducing the application size 178 | 179 | If you would like to reduce the size of your distributed app, there are a few 180 | things you can do to achieve this: 181 | 182 | - Minimize the `build/pythonX/lib/pythonXX.zip`: this contains all the python 183 | modules. You can edit the zip file and remove all the files you'll not use 184 | (reduce encodings, remove xml, email...) 185 | 186 | - Go to the settings `panel` > `build`, search for `"strip"` options, and 187 | triple-check that they are all set to `NO`. Stripping does not work with 188 | Python dynamic modules and will remove needed symbols. 189 | 190 | - By default, the iOS package compiles binaries for all processor 191 | architectures, namely x86_64 and arm64 as per the guidelines from 192 | Apple. You can reduce the size of your ipa significantly by removing the 193 | x86_64 architecture as they are used only for the emulator. 194 | 195 | The procedure is to first compile/build all the host recipes as is: 196 | 197 | toolchain build hostpython3 198 | 199 | Then build all the rest of the recipes using --arch=arm64 200 | arguments as follows: 201 | 202 | toolchain build python3 kivy --arch=arm64 203 | 204 | Note that these packages will not run in the iOS emulators, so use them 205 | only for deployment. 206 | 207 | ## Usage 208 | 209 | ``` 210 | toolchain <command> [<args>] 211 | 212 | Available commands: 213 | build Build a recipe (compile a library for the required target 214 | architecture) 215 | clean Clean the build of the specified recipe 216 | distclean Clean the build and the result 217 | recipes List all the available recipes 218 | status List all the recipes and their build status 219 | 220 | Xcode: 221 | create Create a new xcode project 222 | update Update an existing xcode project (frameworks, libraries..) 223 | launchimage Create Launch images for your xcode project 224 | icon Create Icons for your xcode project 225 | pip Install a pip dependency into the distribution 226 | pip3 Install a pip dependency into the python 3 distribution 227 | ``` 228 | 229 | ## Development 230 | 231 | Alternatively, it's also possible to clone the repository and use all the 232 | described commands in the above sections. 233 | Clone and install it to your local virtual environment: 234 | 235 | git clone https://github.com/kivy/kivy-ios.git 236 | cd kivy-ios/ 237 | python3 -m venv venv 238 | . venv/bin/activate 239 | pip install -e . 240 | 241 | Then use the `toolchain.py` script: 242 | 243 | python toolchain.py --help 244 | 245 | 246 | ## FAQ 247 | 248 | ### Fatal error: "stdio.h" file not found 249 | 250 | You need to install the Command line tools: `xcode-select --install` 251 | 252 | ### Error: SDK "iphonesimulator" cannot be located 253 | 254 | Xcode path is not set up correctly. Run the following command to fix this: `sudo xcode-select --switch <YOUR_XCODEAPP_PATH>` (Change `<YOUR_XCODEAPP_PATH>` to the path that reflects your XCode installation, usually is `/Applications/Xcode.app`) 255 | 256 | ### Bitcode is partially supported now (Xcode setting ENABLE_BITCODE can be set to Yes). 257 | 258 | * Supported recipes: python3, kivy, sdl2, sdl2_image, sdl2_mixer, and libffi 259 | 260 | ### You don't have permissions to run 261 | 262 | It is due to invalid archs, search for them and check it. Maybe you 263 | targetted a simulator but have only arm64. Maybe you want to target 264 | your iPad but it is only x86_64. 265 | 266 | ### Why does the python multiprocess/subprocess module not work? 267 | 268 | The iOS application model does not currently support multi-processing in a 269 | cross-platform compatible way. The application design focuses on minimizing 270 | processor usage (to minimize power consumption) and promotes an 271 | [alternative concurrency model](https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html). 272 | 273 | If you need to make use of multiple processes, you should consider using 274 | [PyObjus](https://github.com/kivy/pyobjus) to leverage native iOS 275 | functionals for this. 276 | 277 | ## Support 278 | 279 | If you need assistance, you can ask for help on our mailing list: 280 | 281 | * User Group : https://groups.google.com/group/kivy-users 282 | * Email : kivy-users@googlegroups.com 283 | 284 | We also have a Discord channel: 285 | 286 | * Server : https://chat.kivy.org 287 | * Channel : #support 288 | 289 | 290 | ## Contributing 291 | 292 | We love pull requests and discussing novel ideas. Check out our 293 | [contribution guide](http://kivy.org/docs/contribute.html) and 294 | feel free to improve Kivy for iOS. 295 | 296 | The following mailing list and IRC channel are used exclusively for 297 | discussions about developing the Kivy framework and its sister projects: 298 | 299 | * Dev Group : https://groups.google.com/group/kivy-dev 300 | * Email : kivy-dev@googlegroups.com 301 | 302 | Discord channel: 303 | 304 | * Server : https://chat.kivy.org 305 | * Channel : #dev 306 | 307 | ## License 308 | 309 | Kivy for iOS is released under the terms of the MIT License. Please refer to the 310 | LICENSE file. 311 | 312 | 313 | ## Backers 314 | 315 | Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/kivy#backer)] 316 | 317 | <a href="https://opencollective.com/kivy#backers" target="_blank"><img src="https://opencollective.com/kivy/backers.svg?width=890"></a> 318 | 319 | 320 | ## Sponsors 321 | 322 | Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/kivy#sponsor)] 323 | 324 | <a href="https://opencollective.com/kivy/sponsor/0/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/0/avatar.svg"></a> 325 | <a href="https://opencollective.com/kivy/sponsor/1/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/1/avatar.svg"></a> 326 | <a href="https://opencollective.com/kivy/sponsor/2/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/2/avatar.svg"></a> 327 | <a href="https://opencollective.com/kivy/sponsor/3/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/3/avatar.svg"></a> 328 | <a href="https://opencollective.com/kivy/sponsor/4/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/4/avatar.svg"></a> 329 | <a href="https://opencollective.com/kivy/sponsor/5/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/5/avatar.svg"></a> 330 | <a href="https://opencollective.com/kivy/sponsor/6/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/6/avatar.svg"></a> 331 | <a href="https://opencollective.com/kivy/sponsor/7/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/7/avatar.svg"></a> 332 | <a href="https://opencollective.com/kivy/sponsor/8/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/8/avatar.svg"></a> 333 | <a href="https://opencollective.com/kivy/sponsor/9/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/9/avatar.svg"></a> 334 | -------------------------------------------------------------------------------- /kivy_ios.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: kivy-ios 3 | Version: 1.3.0.dev0 4 | Summary: Kivy for iOS 5 | Home-page: https://github.com/kivy/kivy-ios 6 | Author: The Kivy team 7 | Author-email: kivy-dev@googlegroups.com 8 | License: MIT License 9 | Platform: UNKNOWN 10 | Description-Content-Type: text/markdown 11 | License-File: LICENSE 12 | 13 | # Kivy for iOS 14 | 15 | [![kivy-ios](https://github.com/kivy/kivy-ios/workflows/kivy-ios/badge.svg)](https://github.com/kivy/kivy-ios/actions?query=workflow%3Akivy-ios) 16 | [![PyPI version](https://badge.fury.io/py/kivy-ios.svg)](https://badge.fury.io/py/kivy-ios) 17 | [![Backers on Open Collective](https://opencollective.com/kivy/backers/badge.svg)](#backers) 18 | [![Sponsors on Open Collective](https://opencollective.com/kivy/sponsors/badge.svg)](#sponsors) 19 | 20 | This toolchain is designed to compile the necessary libraries for iOS to run 21 | your application and manage the creation of the Xcode project. 22 | 23 | We do not provide any binary distributions of this toolchain. 24 | You do need to compile it at least once before creating your Xcode project. 25 | 26 | The toolchain supports: 27 | 28 | - iPhone Simulator (x86_64) 29 | - iPhone / iOS (arm64) 30 | 31 | These recipes are not ported to the new toolchain yet: 32 | 33 | - lxml 34 | 35 | 36 | ## Installation & requirements 37 | 38 | Before we start, we strongly advise to use a Python virtual environment to install Python packages. 39 | 40 | python3 -m venv venv 41 | . venv/bin/activate 42 | 43 | Install [Kivy for iOS from PyPI](https://pypi.org/project/kivy-ios) with pip like any Python package. 44 | 45 | pip3 install kivy-ios 46 | 47 | Additionally you would need few system dependencies and configuration. 48 | 49 | - Xcode 10 or above, with an iOS SDK and command line tools installed: 50 | 51 | xcode-select --install 52 | 53 | - Using brew, you can install the following dependencies: 54 | 55 | brew install autoconf automake libtool pkg-config 56 | brew link libtool 57 | 58 | ## Using the toolchain 59 | 60 | Any Python extensions or C/C++ library must be compiled: you need to have what 61 | we call a `recipe` to compile it. For example, Python, libffi, SDL2, SDL_image, 62 | freetype... all the dependencies, compilation and packaging instructions are 63 | contained in a `recipe`. 64 | 65 | You can list the available recipes and their versions with: 66 | 67 | $ toolchain recipes 68 | audiostream master 69 | click 7.1.2 70 | cymunk master 71 | ffmpeg n4.3.1 72 | ffpyplayer v3.2 73 | flask 1.1.2 74 | freetype 2.5.5 75 | hostlibffi 3.2.1 76 | hostopenssl 1.1.1g 77 | hostpython3 3.7.1 78 | ios master 79 | itsdangerous 1.1.0 80 | jinja2 2.11.2 81 | kivy 1.10.1 82 | libffi 3.2.1 83 | libjpeg v9a 84 | libpng 1.6.26 85 | markupsafe 1.1.1 86 | moodstocks 4.1.5 87 | numpy 1.16.4 88 | openssl 1.1.1g 89 | photolibrary master 90 | pillow 6.1.0 91 | plyer master 92 | pycrypto 2.6.1 93 | pykka 1.2.1 94 | pyobjus master 95 | python3 3.7.1 96 | pyyaml 3.11 97 | sdl2 2.0.8 98 | sdl2_image 2.0.0 99 | sdl2_mixer 2.0.0 100 | sdl2_ttf 2.0.12 101 | werkzeug 1.0.1 102 | 103 | Then, start the compilation with: 104 | 105 | $ toolchain build python3 kivy 106 | 107 | You can build recipes at the same time by adding them as parameters: 108 | 109 | $ toolchain build python3 openssl kivy 110 | 111 | Recipe builds can be removed via the clean command e.g.: 112 | 113 | $ toolchain clean openssl 114 | 115 | You can install package that don't require compilation with pip:: 116 | 117 | $ toolchain pip install plyer 118 | 119 | The Kivy recipe depends on several others, like the sdl\* and python recipes. 120 | These may in turn depend on others e.g. sdl2_ttf depends on freetype, etc. 121 | You can think of it as follows: the kivy recipe will compile everything 122 | necessary for a minimal working version of Kivy. 123 | 124 | Don't grab a coffee, just do diner. Compiling all the libraries for the first 125 | time, 2x over (remember, 2 archs, x86_64, arm64) will take time. 126 | 127 | For a complete list of available commands, type: 128 | 129 | $ toolchain 130 | 131 | ## Create the Xcode project 132 | 133 | The `toolchain.py` can create the initial Xcode project for you:: 134 | 135 | $ toolchain create <title> <app_directory> 136 | $ toolchain create Touchtracer ~/code/kivy/examples/demo/touchtracer 137 | 138 | Your app directory must contain a main.py. A directory named `<title>-ios` 139 | will be created, with an Xcode project in it. 140 | You can open the Xcode project using:: 141 | 142 | $ open touchtracer-ios/touchtracer.xcodeproj 143 | 144 | Then click on `Play`, and enjoy. 145 | 146 | > *Did you know ?* 147 | > 148 | > Everytime you press `Play`, your application directory will be synced to 149 | > the `<title>-ios/YourApp` directory. Don't make changes in the -ios 150 | > directory directly. 151 | 152 | 153 | ## Configuring your App 154 | 155 | You can configure and customize your app in various ways: 156 | 157 | - Set the icon and launch images in XCode. Note that XCode requires that you 158 | specify these assests per device or/and iOS version. 159 | 160 | - When you first build your XCode project, a 'main.m' file is created in your 161 | XCode project folder. This file configures your environment variables and 162 | controls your application startup. You can edit this file to customize your 163 | launch environment. 164 | 165 | - Kivy uses SDL, and as soon as the application starts the SDL main, the launch 166 | image will disappear. To prevent that, you need to have 2 files named 167 | `Default.png` and `Default-Landscape.png`, and put them 168 | in the `Resources` folder in Xcode (not in your application folder) 169 | 170 | > *Did you know ?* 171 | > 172 | > If you wish to restrict your apps orientation, you should do this via 173 | > the 'export_orientation' function in 'main.m'. The XCode orientation 174 | > settings should be set to support all. 175 | 176 | 177 | ## Using recipes 178 | 179 | Recipes are used to install and compile any libraries you may need to use. These 180 | recipes follow the same format as those used by the 181 | [Python-for-Android](https://github.com/kivy/python-for-android) sister project. 182 | Please refer to the 183 | [recipe documentation](https://python-for-android.readthedocs.io/en/latest/recipes/) 184 | there for more detail. 185 | 186 | 187 | ## Reducing the application size 188 | 189 | If you would like to reduce the size of your distributed app, there are a few 190 | things you can do to achieve this: 191 | 192 | - Minimize the `build/pythonX/lib/pythonXX.zip`: this contains all the python 193 | modules. You can edit the zip file and remove all the files you'll not use 194 | (reduce encodings, remove xml, email...) 195 | 196 | - Go to the settings `panel` > `build`, search for `"strip"` options, and 197 | triple-check that they are all set to `NO`. Stripping does not work with 198 | Python dynamic modules and will remove needed symbols. 199 | 200 | - By default, the iOS package compiles binaries for all processor 201 | architectures, namely x86_64 and arm64 as per the guidelines from 202 | Apple. You can reduce the size of your ipa significantly by removing the 203 | x86_64 architecture as they are used only for the emulator. 204 | 205 | The procedure is to first compile/build all the host recipes as is: 206 | 207 | toolchain build hostpython3 208 | 209 | Then build all the rest of the recipes using --arch=arm64 210 | arguments as follows: 211 | 212 | toolchain build python3 kivy --arch=arm64 213 | 214 | Note that these packages will not run in the iOS emulators, so use them 215 | only for deployment. 216 | 217 | ## Usage 218 | 219 | ``` 220 | toolchain <command> [<args>] 221 | 222 | Available commands: 223 | build Build a recipe (compile a library for the required target 224 | architecture) 225 | clean Clean the build of the specified recipe 226 | distclean Clean the build and the result 227 | recipes List all the available recipes 228 | status List all the recipes and their build status 229 | 230 | Xcode: 231 | create Create a new xcode project 232 | update Update an existing xcode project (frameworks, libraries..) 233 | launchimage Create Launch images for your xcode project 234 | icon Create Icons for your xcode project 235 | pip Install a pip dependency into the distribution 236 | pip3 Install a pip dependency into the python 3 distribution 237 | ``` 238 | 239 | ## Development 240 | 241 | Alternatively, it's also possible to clone the repository and use all the 242 | described commands in the above sections. 243 | Clone and install it to your local virtual environment: 244 | 245 | git clone https://github.com/kivy/kivy-ios.git 246 | cd kivy-ios/ 247 | python3 -m venv venv 248 | . venv/bin/activate 249 | pip install -e . 250 | 251 | Then use the `toolchain.py` script: 252 | 253 | python toolchain.py --help 254 | 255 | 256 | ## FAQ 257 | 258 | ### Fatal error: "stdio.h" file not found 259 | 260 | You need to install the Command line tools: `xcode-select --install` 261 | 262 | ### Error: SDK "iphonesimulator" cannot be located 263 | 264 | Xcode path is not set up correctly. Run the following command to fix this: `sudo xcode-select --switch <YOUR_XCODEAPP_PATH>` (Change `<YOUR_XCODEAPP_PATH>` to the path that reflects your XCode installation, usually is `/Applications/Xcode.app`) 265 | 266 | ### Bitcode is partially supported now (Xcode setting ENABLE_BITCODE can be set to Yes). 267 | 268 | * Supported recipes: python3, kivy, sdl2, sdl2_image, sdl2_mixer and libffi 269 | 270 | ### You don't have permissions to run 271 | 272 | It is due to invalid archs, search for them and check it. Maybe you 273 | targetted a simulator but have only arm64. Maybe you want to target 274 | your iPad but it as only x86_64. 275 | 276 | ### Why does the python multiprocess/subprocess module not work? 277 | 278 | The iOS application model does not currently support multi-processing in a 279 | cross-platform compatible way. The application design focuses on minimizing 280 | processor usage (to minimize power consumption) and promotes an 281 | [alternative concurrency model](https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html). 282 | 283 | If you need to make use of multiple processes, you should consider using 284 | [PyObjus](https://github.com/kivy/pyobjus) to leverage native iOS 285 | functionals for this. 286 | 287 | ## Support 288 | 289 | If you need assistance, you can ask for help on our mailing list: 290 | 291 | * User Group : https://groups.google.com/group/kivy-users 292 | * Email : kivy-users@googlegroups.com 293 | 294 | We also have a Discord channel: 295 | 296 | * Server : https://chat.kivy.org 297 | * Channel : #support 298 | 299 | 300 | ## Contributing 301 | 302 | We love pull requests and discussing novel ideas. Check out our 303 | [contribution guide](http://kivy.org/docs/contribute.html) and 304 | feel free to improve Kivy for iOS. 305 | 306 | The following mailing list and IRC channel are used exclusively for 307 | discussions about developing the Kivy framework and its sister projects: 308 | 309 | * Dev Group : https://groups.google.com/group/kivy-dev 310 | * Email : kivy-dev@googlegroups.com 311 | 312 | Discord channel: 313 | 314 | * Server : https://chat.kivy.org 315 | * Channel : #dev 316 | 317 | ## License 318 | 319 | Kivy for iOS is released under the terms of the MIT License. Please refer to the 320 | LICENSE file. 321 | 322 | 323 | ## Backers 324 | 325 | Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/kivy#backer)] 326 | 327 | <a href="https://opencollective.com/kivy#backers" target="_blank"><img src="https://opencollective.com/kivy/backers.svg?width=890"></a> 328 | 329 | 330 | ## Sponsors 331 | 332 | Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/kivy#sponsor)] 333 | 334 | <a href="https://opencollective.com/kivy/sponsor/0/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/0/avatar.svg"></a> 335 | <a href="https://opencollective.com/kivy/sponsor/1/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/1/avatar.svg"></a> 336 | <a href="https://opencollective.com/kivy/sponsor/2/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/2/avatar.svg"></a> 337 | <a href="https://opencollective.com/kivy/sponsor/3/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/3/avatar.svg"></a> 338 | <a href="https://opencollective.com/kivy/sponsor/4/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/4/avatar.svg"></a> 339 | <a href="https://opencollective.com/kivy/sponsor/5/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/5/avatar.svg"></a> 340 | <a href="https://opencollective.com/kivy/sponsor/6/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/6/avatar.svg"></a> 341 | <a href="https://opencollective.com/kivy/sponsor/7/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/7/avatar.svg"></a> 342 | <a href="https://opencollective.com/kivy/sponsor/8/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/8/avatar.svg"></a> 343 | <a href="https://opencollective.com/kivy/sponsor/9/website" target="_blank"><img src="https://opencollective.com/kivy/sponsor/9/avatar.svg"></a> 344 | 345 | 346 | --------------------------------------------------------------------------------