python3: recipe with cross compilation patch + example of main.m that works

This commit is contained in:
Mathieu Virbel 2018-11-01 21:53:18 +01:00
parent 7ed286efa8
commit 6c043cf9f3
4 changed files with 480 additions and 0 deletions

150
recipes/python3/__init__.py Normal file
View file

@ -0,0 +1,150 @@
from toolchain import Recipe, shprint
from os.path import join
import sh
import shutil
import os
class Python3Recipe(Recipe):
version = "3.7.1"
url = "https://www.python.org/ftp/python/{version}/Python-{version}.tgz"
depends = ["hostpython3", "libffi", ]
optional_depends = ["openssl"]
library = "libpython3.7m.a"
pbx_libraries = ["libz", "libbz2", "libsqlite3"]
def init_with_ctx(self, ctx):
super(Python3Recipe, self).init_with_ctx(ctx)
self.ctx.python_ver_dir = "python3.7"
self.ctx.python_prefix = join(ctx.dist_dir, "root", "python")
self.ctx.site_packages_dir = join(
ctx.dist_dir, "root", "python", "lib", ctx.python_ver_dir,
"site-packages")
def prebuild_arch(self, arch):
# common to all archs
if self.has_marker("patched"):
return
# self.apply_patch("ssize-t-max.patch")
# self.apply_patch("dynload.patch")
# self.apply_patch("static-_sqlite3.patch")
shutil.copy("Modules/Setup.dist", "Modules/Setup")
self.apply_patch("xcompile.patch")
# self.copy_file("_scproxy.py", "Lib/_scproxy.py")
# self.apply_patch("xcompile.patch")
# self.apply_patch("setuppath.patch")
# self.append_file("ModulesSetup.mobile", "Modules/Setup.local")
# self.apply_patch("ipv6.patch")
# if "openssl.build_all" in self.ctx.state:
# self.append_file("ModulesSetup.openssl", "Modules/Setup.local")
# self.apply_patch("posixmodule.patch")
self.set_marker("patched")
def get_build_env(self, arch):
build_env = arch.get_env()
build_env["PATH"] = "{}:{}".format(
join(self.ctx.dist_dir, "hostpython3", "bin"),
os.environ["PATH"])
return build_env
def build_arch(self, arch):
build_env = self.get_build_env(arch)
configure = sh.Command(join(self.build_dir, "configure"))
py_arch = arch.arch
if py_arch == "armv7":
py_arch = "arm"
elif py_arch == "arm64":
py_arch = "aarch64"
prefix = join(self.ctx.dist_dir, "python3")
shprint(configure,
"CC={}".format(build_env["CC"]),
"LD={}".format(build_env["LD"]),
"CFLAGS={}".format(build_env["CFLAGS"]),
"LDFLAGS={} -undefined dynamic_lookup".format(build_env["LDFLAGS"]),
# "--without-pymalloc",
# "--disable-toolbox-glue",
"ac_cv_file__dev_ptmx=yes",
"ac_cv_file__dev_ptc=no",
"ac_cv_little_endian_double=yes",
"ac_cv_func_memrchr=no",
"ac_cv_func_getentropy=no",
"ac_cv_func_getresuid=no",
"ac_cv_func_getresgid=no",
"ac_cv_func_setresgid=no",
"ac_cv_func_setresuid=no",
"ac_cv_func_plock=no",
"ac_cv_func_dup3=no",
"ac_cv_func_pipe2=no",
"ac_cv_func_preadv=no",
"ac_cv_func_pwritev=no",
"ac_cv_func_preadv2=no",
"ac_cv_func_pwritev2=no",
"ac_cv_func_mkfifoat=no",
"ac_cv_func_mknodat=no",
"ac_cv_func_posix_fadvise=no",
"ac_cv_func_posix_fallocate=no",
"ac_cv_func_sigwaitinfo=no",
"ac_cv_func_sigtimedwait=no",
"ac_cv_func_clock_settime=no",
"ac_cv_func_pthread_getcpuclockid=no",
"ac_cv_func_sched_setscheduler=no",
"ac_cv_func_sched_setparam=no",
"ac_cv_func_clock_gettime=no",
"--host={}-apple-ios".format(py_arch),
"--build=x86_64-apple-darwin",
"--prefix={}".format(prefix),
"--exec-prefix={}".format(prefix),
"--without-ensurepip",
# "--with-system-ffi",
# "--without-doc-strings",
"--enable-ipv6",
_env=build_env)
# self._patch_pyconfig()
# self.apply_patch("ctypes_duplicate.patch")
# self.apply_patch("ctypes_duplicate_longdouble.patch")
shprint(sh.make, self.ctx.concurrent_make)
# "HOSTPYTHON={}".format(self.ctx.hostpython),
# "HOSTPGEN={}".format(self.ctx.hostpgen))
# "CROSS_COMPILE_TARGET=yes",
def install(self):
arch = list(self.filtered_archs)[0]
build_env = self.get_build_env(arch)
build_dir = self.get_build_dir(arch.arch)
shprint(sh.make, self.ctx.concurrent_make,
"-C", build_dir,
"install",
"prefix={}".format(join(self.ctx.dist_dir, "root", "python3")),
_env=build_env)
self.reduce_python()
def reduce_python(self):
print("Reduce python")
oldpwd = os.getcwd()
try:
print("Remove files unlikely to be used")
os.chdir(join(self.ctx.dist_dir, "root", "python3"))
sh.rm("-rf", "share")
os.chdir(join(
self.ctx.dist_dir, "root", "python3", "lib",
"python3.7", "config-3.7m-darwin"))
sh.rm("libpython3.7m.a")
sh.rm("python.o")
sh.rm("config.c.in")
sh.rm("makesetup")
sh.rm("install-sh")
os.chdir(join(self.ctx.dist_dir, "root", "python3", "lib", "python3.7"))
# sh.find(".", "-iname", "*.pyc", "-exec", "rm", "{}", ";")
# sh.find(".", "-iname", "*.py", "-exec", "rm", "{}", ";")
#sh.find(".", "-iname", "test*", "-exec", "rm", "-rf", "{}", ";")
sh.rm("-rf", "wsgiref", "curses", "idlelib", "lib2to3")
# now create the zip.
print("Create a stdlib.zip")
sh.zip("-r", "../stdlib.zip", sh.glob("*"))
finally:
os.chdir(oldpwd)
recipe = Python3Recipe()

View file

@ -0,0 +1,145 @@
--- Python-3.7.1.orig/config.sub 2018-10-20 08:04:19.000000000 +0200
+++ Python-3.7.1/config.sub 2018-10-31 13:31:22.000000000 +0100
@@ -249,7 +249,7 @@
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
| am33_2.0 \
| arc | arceb \
- | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv6m | armv[78][arm] \
+ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv6m | armv[78][armk] \
| avr | avr32 \
| ba \
| be32 | be64 \
@@ -1524,7 +1524,11 @@
;;
-nacl*)
;;
- -ios)
+ -ios*)
+ ;;
+ -tvos*)
+ ;;
+ -watchos*)
;;
-none)
;;
--- Python-3.7.1.orig/configure 2018-10-20 08:04:19.000000000 +0200
+++ Python-3.7.1/configure 2018-10-31 13:41:38.000000000 +0100
@@ -3253,6 +3253,15 @@
*-*-cygwin*)
ac_sys_system=Cygwin
;;
+ *-apple-ios)
+ ac_sys_system=iOS
+ ;;
+ *-apple-tvos)
+ ac_sys_system=tvOS
+ ;;
+ *-apple-watchos)
+ ac_sys_system=watchOS
+ ;;
*)
# for now, limit cross builds to known configurations
MACHDEP="unknown"
@@ -3294,6 +3303,15 @@
_host_cpu=$host_cpu
esac
;;
+ *-apple-*)
+ case "$host_cpu" in
+ arm*)
+ _host_cpu=arm
+ ;;
+ *)
+ _host_cpu=$host_cpu
+ esac
+ ;;
*-*-cygwin*)
_host_cpu=
;;
@@ -3369,6 +3387,13 @@
define_xopen_source=no;;
Darwin/1[0-9].*)
define_xopen_source=no;;
+ # On iOS, defining _POSIX_C_SOURCE also disables platform specific features.
+ iOS/*)
+ define_xopen_source=no;;
+ tvOS/*)
+ define_xopen_source=no;;
+ watchOS/*)
+ define_xopen_source=no;;
# On AIX 4 and 5.1, mbstate_t is defined only when _XOPEN_SOURCE == 500 but
# used in wcsnrtombs() and mbsnrtowcs() even if _XOPEN_SOURCE is not defined
# or has another value. By not (re)defining it, the defaults come in place.
@@ -6176,11 +6201,17 @@
fi
if test "$cross_compiling" = yes; then
- case "$READELF" in
- readelf|:)
- as_fn_error $? "readelf for the host is required for cross builds" "$LINENO" 5
- ;;
- esac
+ case "$host" in
+ *-apple-*os)
+ # readelf not required for iOS cross builds.
+ ;;
+ *)
+ case "$READELF" in
+ readelf|:)
+ as_fn_error $? "readelf for the host is required for cross builds" "$LINENO" 5
+ ;;
+ esac
+ esac
fi
@@ -6803,8 +6834,6 @@
# tweak BASECFLAGS based on compiler and platform
case $GCC in
yes)
- CFLAGS_NODIST="$CFLAGS_NODIST -std=c99"
-
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -Wextra" >&5
$as_echo_n "checking for -Wextra... " >&6; }
ac_save_cc="$CC"
@@ -11281,6 +11310,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";;
*)
# use dynload_shlib.c and dlopen() if we have it; otherwise stub
# out any dynamic loading
@@ -18383,4 +18416,3 @@
echo "" >&6
echo "" >&6
fi
-
--- Python-3.7.1.orig/Modules/posixmodule.c 2018-10-20 08:04:19.000000000 +0200
+++ Python-3.7.1/Modules/posixmodule.c 2018-10-31 15:00:14.000000000 +0100
@@ -194,6 +194,22 @@
#endif /* _MSC_VER */
#endif /* ! __WATCOMC__ || __QNX__ */
+// iOS
+#undef HAVE_EXECV
+#undef HAVE_FORK
+#undef HAVE_FORK1
+#undef HAVE_FORKPTY
+#undef HAVE_GETGROUPS
+#undef HAVE_SCHED_H
+#undef HAVE_SENDFILE
+#undef HAVE_SETPRIORITY
+#undef HAVE_SPAWNV
+#undef HAVE_WAIT
+#undef HAVE_WAIT3
+#undef HAVE_WAIT4
+#undef HAVE_WAITPID
+#undef HAVE_SYSTEM
+#undef HAVE_FEXECVE
/*[clinic input]
# one of the few times we lie about this name!

184
tests/test_python3/main.m Normal file
View file

@ -0,0 +1,184 @@
//
// main.m
// test_python3
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#include "/Users/tito/code/kivy-ios/dist/root/python3/include/python3.7m/Python.h"
#include "/Users/tito/code/kivy-ios/dist/include/common/sdl2/SDL_main.h"
#include <dlfcn.h>
void export_orientation();
void load_custom_builtin_importer();
int main(int argc, char *argv[]) {
int ret = 0;
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Change the executing path to YourApp
chdir("YourApp");
// 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");
// Kivy environment to prefer some implementation on iOS platform
putenv("KIVY_BUILD=ios");
putenv("KIVY_NO_CONFIG=1");
putenv("KIVY_NO_FILELOG=1");
putenv("KIVY_WINDOW=sdl2");
putenv("KIVY_IMAGE=imageio,tex");
putenv("KIVY_AUDIO=sdl2");
putenv("KIVY_GL_BACKEND=sdl2");
#ifndef DEBUG
putenv("KIVY_NO_CONSOLELOG=1");
#endif
// Export orientation preferences for Kivy
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();
// Add an importer for builtin modules
load_custom_builtin_importer();
// Search and start main.py
#if PY_MAJOR_VERSION == 2
#define MAIN_EXT @"pyo"
#else
#define MAIN_EXT @"py"
#endif
const char * prog = [
[[NSBundle mainBundle] pathForResource:@"YourApp/main" ofType:MAIN_EXT] cStringUsingEncoding:
NSUTF8StringEncoding];
NSLog(@"Running main.py: %s", prog);
FILE* fd = fopen(prog, "r");
if ( fd == NULL ) {
ret = 1;
NSLog(@"Unable to open main.py, abort.");
} else {
ret = PyRun_SimpleFileEx(fd, prog, 1);
if (ret != 0)
NSLog(@"Application quit abnormally!");
}
Py_Finalize();
NSLog(@"Leaving");
[pool release];
// Look like the app still runs even when we left here.
exit(ret);
return ret;
}
// This method reads the available orientations from the Info.plist file and
// shares them via an environment variable. Kivy will automatically set the
// orientation according to this environment value, if it exists. To restrict
// the allowed orientation, please see the comments inside.
void export_orientation() {
NSDictionary *info = [[NSBundle mainBundle] infoDictionary];
NSArray *orientations = [info objectForKey:@"UISupportedInterfaceOrientations"];
// Orientation restrictions
// ========================
// Comment or uncomment blocks 1-3 in order the limit orientation support
// 1. Landscape only
// NSString *result = [[NSString alloc] initWithString:@"KIVY_ORIENTATION=LandscapeLeft LandscapeRight"];
// 2. Portrait only
// NSString *result = [[NSString alloc] initWithString:@"KIVY_ORIENTATION=Portrait PortraitUpsideDown"];
// 3. All orientations
NSString *result = [[NSString alloc] initWithString:@"KIVY_ORIENTATION="];
for (int i = 0; i < [orientations count]; i++) {
NSString *item = [orientations objectAtIndex:i];
item = [item substringFromIndex:22];
if (i > 0)
result = [result stringByAppendingString:@" "];
result = [result stringByAppendingString:item];
}
// ========================
putenv((char *)[result UTF8String]);
NSLog(@"Available orientation: %@", result);
}
void load_custom_builtin_importer() {
static const char *custom_builtin_importer = \
"import sys, imp\n" \
"from os import environ\n" \
"from os.path import exists, join\n" \
"# Fake redirection to supress console output\n" \
"if environ.get('KIVY_NO_CONSOLE', '0') == '1':\n" \
" class fakestd(object):\n" \
" def write(self, *args, **kw): pass\n" \
" def flush(self, *args, **kw): pass\n" \
" sys.stdout = fakestd()\n" \
" sys.stderr = fakestd()\n" \
"# Custom builtin importer for precompiled modules\n" \
"class CustomBuiltinImporter(object):\n" \
" def find_module(self, fullname, mpath=None):\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" \
" 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" \
" try:\n" \
" mod = imp.load_dynamic(f, f)\n" \
" except ImportError:\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())";
PyRun_SimpleString(custom_builtin_importer);
}

View file

@ -0,0 +1 @@
print("Python 3 running!")