python3: recipe with cross compilation patch + example of main.m that works
This commit is contained in:
parent
7ed286efa8
commit
6c043cf9f3
4 changed files with 480 additions and 0 deletions
150
recipes/python3/__init__.py
Normal file
150
recipes/python3/__init__.py
Normal 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()
|
145
recipes/python3/xcompile.patch
Normal file
145
recipes/python3/xcompile.patch
Normal 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
184
tests/test_python3/main.m
Normal 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);
|
||||
}
|
1
tests/test_python3/main.py
Normal file
1
tests/test_python3/main.py
Normal file
|
@ -0,0 +1 @@
|
|||
print("Python 3 running!")
|
Loading…
Reference in a new issue