diff --git a/.gitignore b/.gitignore index 229be10..445896d 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ src/ios/iosbuild/ src/ios/ios.c *.DS_Store* *-ios/ +__pycache__ diff --git a/recipes/python3/xcompile.patch b/recipes/python3/xcompile.patch index f3c2ec9..f5e0e9f 100644 --- a/recipes/python3/xcompile.patch +++ b/recipes/python3/xcompile.patch @@ -106,10 +106,10 @@ fi ;; hp*|HP*) DYNLOADFILE="dynload_hpux.o";; -+ # Disable dynamic loading on iOS -+ iOS/*) DYNLOADFILE="dynload_stub.o";; -+ tvOS/*) DYNLOADFILE="dynload_stub.o";; -+ watchOS/*) DYNLOADFILE="dynload_stub.o";; ++ # Dynamic loading on iOS ++ iOS/*) DYNLOADFILE="dynload_shlib.o";; ++ tvOS/*) DYNLOADFILE="dynload_shlib.o";; ++ watchOS/*) DYNLOADFILE="dynload_shlib.o";; *) # use dynload_shlib.c and dlopen() if we have it; otherwise stub # out any dynamic loading diff --git a/toolchain.py b/toolchain.py index 6804164..08e8b52 100755 --- a/toolchain.py +++ b/toolchain.py @@ -1096,6 +1096,15 @@ def ensure_dir(filename): makedirs(filename) +def ensure_recipes_loaded(ctx): + for recipe in Recipe.list_recipes(): + key = "{}.build_all".format(recipe) + if key not in ctx.state: + continue + recipe = Recipe.get_recipe(recipe, ctx) + recipe.init_with_ctx(ctx) + + def update_pbxproj(filename): # list all the compiled recipes ctx = Context() @@ -1318,6 +1327,7 @@ Xcode: from cookiecutter.main import cookiecutter ctx = Context() + ensure_recipes_loaded(ctx) template_dir = join(curdir, "tools", "templates") context = { "title": args.name, @@ -1327,6 +1337,8 @@ Xcode: "project_dir": realpath(args.directory), "version": "1.0.0", "dist_dir": ctx.dist_dir, + "python_version": ctx.python_ver, + "python_major": ctx.python_major } cookiecutter(template_dir, no_input=True, extra_context=context) filename = join( diff --git a/tools/templates/{{ cookiecutter.project_name }}-ios/main.m b/tools/templates/{{ cookiecutter.project_name }}-ios/main.m index ba91039..10ba0d3 100644 --- a/tools/templates/{{ cookiecutter.project_name }}-ios/main.m +++ b/tools/templates/{{ cookiecutter.project_name }}-ios/main.m @@ -5,7 +5,11 @@ #import #import -#include "{{ cookiecutter.kivy_dir }}/dist/root/python/include/python2.7/Python.h" +{%- if cookiecutter.python_major == 2 %} +#include "{{ cookiecutter.kivy_dir }}/dist/root/python2/include/python2.7/Python.h" +{%- else %} +#include "{{ cookiecutter.kivy_dir }}/dist/root/python3/include/python3.7m/Python.h" +{%- endif %} #include "{{ cookiecutter.kivy_dir }}/dist/include/common/sdl2/SDL_main.h" #include @@ -22,11 +26,12 @@ int main(int argc, char *argv[]) { // Special environment to prefer .pyo, and don't write bytecode if .py are found // because the process will not have a write attribute on the device. - putenv("PYTHONOPTIMIZE=2"); putenv("PYTHONDONTWRITEBYTECODE=1"); putenv("PYTHONNOUSERSITE=1"); putenv("PYTHONPATH=."); - //putenv("PYTHONVERBOSE=1"); + putenv("PYTHONUNBUFFERED=1"); + // putenv("PYTHONVERBOSE=1"); + // putenv("PYOBJUS_DEBUG=1"); // Kivy environment to prefer some implementation on iOS platform putenv("KIVY_BUILD=ios"); @@ -44,12 +49,31 @@ int main(int argc, char *argv[]) { export_orientation(); NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; +#if PY_MAJOR_VERSION == 2 NSLog(@"PythonHome is: %s", (char *)[resourcePath UTF8String]); Py_SetPythonHome((char *)[resourcePath UTF8String]); +#else + NSString *python_home = [NSString stringWithFormat:@"PYTHONHOME=%@", resourcePath, nil]; + putenv((char *)[python_home UTF8String]); + + NSString *python_path = [NSString stringWithFormat:@"PYTHONPATH=%@:%@/lib/python3.7/:%@/lib/python3.7/site-packages", resourcePath, resourcePath, resourcePath, nil]; + putenv((char *)[python_path UTF8String]); + + NSString *tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; + putenv((char *)[tmp_path UTF8String]); +#endif NSLog(@"Initializing python"); Py_Initialize(); + +#if PY_MAJOR_VERSION == 2 PySys_SetArgv(argc, argv); +#else + wchar_t** python_argv = PyMem_RawMalloc(sizeof(wchar_t *) *argc); + for (int i = 0; i < argc; i++) + python_argv[i] = Py_DecodeLocale(argv[i], NULL); + PySys_SetArgv(argc, python_argv); +#endif // If other modules are using the thread, we need to initialize them before. PyEval_InitThreads(); @@ -58,14 +82,20 @@ int main(int argc, char *argv[]) { load_custom_builtin_importer(); // Search and start main.py +#if PY_MAJOR_VERSION == 2 +#define MAIN_EXT @"pyo" +#else +#define MAIN_EXT @"pyc" +#endif + const char * prog = [ - [[NSBundle mainBundle] pathForResource:@"YourApp/main" ofType:@"pyo"] cStringUsingEncoding: + [[NSBundle mainBundle] pathForResource:@"YourApp/main" ofType:MAIN_EXT] cStringUsingEncoding: NSUTF8StringEncoding]; - NSLog(@"Running main.pyo: %s", prog); + NSLog(@"Running main.py: %s", prog); FILE* fd = fopen(prog, "r"); if ( fd == NULL ) { ret = 1; - NSLog(@"Unable to open main.pyo, abort."); + NSLog(@"Unable to open main.py, abort."); } else { ret = PyRun_SimpleFileEx(fd, prog, 1); if (ret != 0) @@ -117,9 +147,16 @@ void export_orientation() { void load_custom_builtin_importer() { static const char *custom_builtin_importer = \ - "import sys, imp\n" \ + "import sys, imp, types\n" \ "from os import environ\n" \ "from os.path import exists, join\n" \ + "try:\n" \ + " # python 3\n" + " import _imp\n" \ + " EXTS = _imp.extension_suffixes()\n" \ + " sys.modules['subprocess'] = types.ModuleType(name='subprocess')\n" \ + "except ImportError:\n" \ + " EXTS = ['.so']\n" "# Fake redirection to supress console output\n" \ "if environ.get('KIVY_NO_CONSOLE', '0') == '1':\n" \ " class fakestd(object):\n" \ @@ -130,29 +167,32 @@ void load_custom_builtin_importer() { "# Custom builtin importer for precompiled modules\n" \ "class CustomBuiltinImporter(object):\n" \ " def find_module(self, fullname, mpath=None):\n" \ + " # print(f'find_module() fullname={fullname} mpath={mpath}')\n" \ " if '.' not in fullname:\n" \ " return\n" \ " if not mpath:\n" \ " return\n" \ " part = fullname.rsplit('.')[-1]\n" \ - " fn = join(mpath[0], '{}.so'.format(part))\n" \ - " if exists(fn):\n" \ - " return self\n" \ + " for ext in EXTS:\n" \ + " fn = join(list(mpath)[0], '{}{}'.format(part, ext))\n" \ + " # print('find_module() {}'.format(fn))\n" \ + " if exists(fn):\n" \ + " return self\n" \ " return\n" \ " def load_module(self, fullname):\n" \ " f = fullname.replace('.', '_')\n" \ " mod = sys.modules.get(f)\n" \ " if mod is None:\n" \ - " # print 'LOAD DYNAMIC', f, sys.modules.keys()\n" \ + " # print('LOAD DYNAMIC', f, sys.modules.keys())\n" \ " try:\n" \ " mod = imp.load_dynamic(f, f)\n" \ " except ImportError:\n" \ - " # import traceback; traceback.print_exc();\n" \ - " # print 'LOAD DYNAMIC FALLBACK', fullname\n" \ + " import traceback; traceback.print_exc();\n" \ + " # print('LOAD DYNAMIC FALLBACK', fullname)\n" \ " mod = imp.load_dynamic(fullname, fullname)\n" \ " sys.modules[fullname] = mod\n" \ " return mod\n" \ " return mod\n" \ - "sys.meta_path.append(CustomBuiltinImporter())"; + "sys.meta_path.insert(0, CustomBuiltinImporter())"; PyRun_SimpleString(custom_builtin_importer); } diff --git a/tools/templates/{{ cookiecutter.project_name }}-ios/{{ cookiecutter.project_name }}.xcodeproj/project.pbxproj b/tools/templates/{{ cookiecutter.project_name }}-ios/{{ cookiecutter.project_name }}.xcodeproj/project.pbxproj index cb58807..94571f6 100755 --- a/tools/templates/{{ cookiecutter.project_name }}-ios/{{ cookiecutter.project_name }}.xcodeproj/project.pbxproj +++ b/tools/templates/{{ cookiecutter.project_name }}-ios/{{ cookiecutter.project_name }}.xcodeproj/project.pbxproj @@ -33,7 +33,7 @@ 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 2CB5F34517D5233A006187AB /* bridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bridge.h; sourceTree = SOURCE_ROOT; }; 2CB5F34617D5233A006187AB /* bridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = bridge.m; sourceTree = SOURCE_ROOT; }; - 59738AB41A8BB5D8001B2C0C /* lib */ = {isa = PBXFileReference; lastKnownFileType = folder; name = lib; path = {{ cookiecutter.dist_dir }}/root/python/lib; sourceTree = ""; }; + 59738AB41A8BB5D8001B2C0C /* lib */ = {isa = PBXFileReference; lastKnownFileType = folder; name = lib; path = {{ cookiecutter.dist_dir }}/root/python{{ cookiecutter.python_major }}/lib; sourceTree = ""; }; 59738AB61A8BB71F001B2C0C /* include */ = {isa = PBXFileReference; lastKnownFileType = folder; name = include; path = {{ cookiecutter.dist_dir }}/hostpython/include; sourceTree = ""; }; 59738ABA1A8E19AA001B2C0C /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = {{ cookiecutter.project_name }}/Images.xcassets; sourceTree = ""; }; 59738AD01A8E62D6001B2C0C /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "LaunchImages/Default-568h@2x.png"; sourceTree = ""; }; @@ -234,7 +234,11 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/bash; - shellScript = "{{ cookiecutter.dist_dir }}/hostpython/bin/python -OO -m compileall \"$PROJECT_DIR\"/YourApp"; + {%- if cookiecutter.python_major == 2 %} + shellScript = "{{ cookiecutter.dist_dir }}/hostpython2/bin/python -OO -m compileall \"$PROJECT_DIR\"/YourApp"; + {%- else %} + shellScript = "{{ cookiecutter.dist_dir }}/hostpython3/bin/python -m compileall -f -b \"$PROJECT_DIR\"/YourApp"; + {%- endif %} }; /* End PBXShellScriptBuildPhase section */ @@ -250,7 +254,11 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/bash; - shellScript = "find \"$PROJECT_DIR\"/YourApp/ -iname '*.py' -exec rm {} \\; -or -iname '*.pyc' -exec rm {} \\;"; + {%- if cookiecutter.python_major == 2 %} + shellScript = "find \"$PROJECT_DIR\"/YourApp/ -regex '.*\\.py[c]*' -delete"; + {%- else %} + shellScript = "find \"$PROJECT_DIR\"/YourApp/ -regex '.*\\.py' -delete"; + {%- endif %} }; /* End PBXShellScriptBuildPhase section */ @@ -343,8 +351,8 @@ isa = XCBuildConfiguration; buildSettings = { DEAD_CODE_STRIPPING = NO; + ENABLE_BITCODE = NO; USER_HEADER_SEARCH_PATHS = {{ cookiecutter.dist_dir }}/root/include/; - VALID_ARCHS = x86_64; }; name = Debug; }; @@ -352,9 +360,9 @@ isa = XCBuildConfiguration; buildSettings = { DEAD_CODE_STRIPPING = NO; + ENABLE_BITCODE = NO; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; USER_HEADER_SEARCH_PATHS = {{ cookiecutter.dist_dir }}/root/include/; - VALID_ARCHS = x86_64; }; name = Release; };