Showing posts with label Python. Show all posts
Showing posts with label Python. Show all posts

Monday, June 1, 2015

Simplified script to cross-compile Python-4-Android for both ARM NEON and x86

I've created a simple script to cross-compile Python-4-Android for both the ARM NEON AND x86 architectures. This script only works with the patched Python-4-Android archive you can download from this post. I successfully ran the script on a Ubuntu 13.04 machine using the Android NDK version r9c.

First download both the make.sh script and python-for-android.tar.bz2 files in the same directory:




Cross-compiling Python-4-Android for ARM NEON

To cross-compile for ARM NEON, run make.sh with the following options:

$ make.sh -x -n /path/to/android-ndk-r9c -a armeabi-v7a-NEON -t arm-linux-androideabi-4.8


Cross-compiling Python-4-Android for x86

To cross-compile for x86, run make.sh with the following options:

$ make.sh -x -n /path/to/android-ndk-r9c -a x86 -t x86-4.6

Thursday, January 31, 2013

How to build Python-4-Android for the ARM Neon

Currently the Py4A project does not compile for the ARM Neon architecture. If you try to run ndk-build on the project by setting the APP_ABI to armeabi-v7a and setting the LOCAL_ARM_NEON variable to true, you get the following error:





libffi/src/arm/sysv.S: Assembler messages:
libffi/src/arm/sysv.S:203: Error: selected processor does not support ARM mode `stfeqs f0,[r2]'
libffi/src/arm/sysv.S:208: Error: selected processor does not support ARM mode `stfeqd f0,[r2]'
libffi/src/arm/sysv.S:283: Error: selected processor does not support ARM mode `ldfs f0,[sp]'
libffi/src/arm/sysv.S:286: Error: selected processor does not support ARM mode `ldfd f0,[sp]'
libffi/src/arm/sysv.S:289: Error: selected processor does not support ARM mode `ldfd f0,[sp]'
make: *** [obj/local/armeabi-v7a/objs/ffi/src/arm/sysv.o] Error 1


The following patch for the python-build/libffi/src/asm/sysv.S file allows you to cross-compile Py4A for the armeabi-v7a ABI and also for the Neon instruction set. The patch consists in appropriately changing the conditionals containing __SOFTFP__ and adding __SOFTFP__ || __ARM_EABI__.
For example to build for the ARM Neon instruction set, the ndk-build command line in build.sh should be the following:

ndk-build APP_ABI:=armeabi-v7a LOCAL_ARM_NEON:=true


--- libffi/src/arm/a/sysv.S    2013-01-30 14:49:29.711595414 -0500
+++ libffi/src/arm/sysv.S    2013-01-31 18:05:07.376178842 -0500
@@ -189,7 +189,7 @@ ARM_FUNC_START ffi_call_SYSV
 
 @ return INT
     cmp    r3, #FFI_TYPE_INT
-#ifdef __SOFTFP__
+#if defined(__SOFTFP__) || defined(__ARM_EABI__)
     cmpne    r3, #FFI_TYPE_FLOAT
 #endif
     streq    r0, [r2]
@@ -197,12 +197,12 @@ ARM_FUNC_START ffi_call_SYSV
 
     @ return INT64
     cmp    r3, #FFI_TYPE_SINT64
-#ifdef __SOFTFP__
+#if defined(__SOFTFP__) || defined(__ARM_EABI__)
     cmpne    r3, #FFI_TYPE_DOUBLE
 #endif
     stmeqia    r2, {r0, r1}
 
-#ifndef __SOFTFP__
+#if !defined(__SOFTFP__) && !defined(__ARM_EABI__)
     beq    LSYM(Lepilogue)
 
 @ return FLOAT
@@ -245,21 +245,21 @@ ARM_FUNC_START ffi_closure_SYSV
     beq    .Lretint
 
     cmp    r0, #FFI_TYPE_FLOAT
-#ifdef __SOFTFP__
+#if defined(__SOFTFP__) || defined(__ARM_EABI__)
     beq    .Lretint
 #else
     beq    .Lretfloat
 #endif
 
     cmp    r0, #FFI_TYPE_DOUBLE
-#ifdef __SOFTFP__
+#if defined(__SOFTFP__) || defined(__ARM_EABI__)
     beq    .Lretlonglong
 #else
     beq    .Lretdouble
 #endif
 
     cmp    r0, #FFI_TYPE_LONGDOUBLE
-#ifdef __SOFTFP__
+#if defined(__SOFTFP__) || defined(__ARM_EABI__)
     beq    .Lretlonglong
 #else
     beq    .Lretlongdouble
@@ -278,7 +278,7 @@ ARM_FUNC_START ffi_closure_SYSV
     ldr    r1, [sp, #4]
     b    .Lclosure_epilogue
 
-#ifndef __SOFTFP__
+#if !defined(__SOFTFP__) && !defined(__ARM_EABI__)
 .Lretfloat:
     ldfs    f0, [sp]
     b    .Lclosure_epilogue


Tuesday, January 29, 2013

How to build Python-4-Android for the x86

Currently the Py4A project only compiles for the ARM architecture.

The following patch for the python-build sub-directory allows you to cross-compile Py4A for the x86 architecture. The patch consists in:
  1. Adding a set of new files to support the x86 architecture (including creating a new libffi/linux-x86/ directory):

    libffi/linux-x86/ffi.h
    libffi/linux-x86/fficonfig.h
    setup-x86.cfg
  2. changing the build.sh script to accept a new parameter - the Android ABI (for example 'armeabi' or 'x86') - so that the script can call

    ndk-build APP_ABI:=x86

    to trigger the cross-compilation for the x86 processor;
  3. changing the build.py script to parametrize the GCC strip command and the selection of the setup.cfg configuration file based on the input ABI;
  4. changing the libffi/Android.mk makefile to select the right set of source files based on the input ABI;
  5. changing the libffi/include/ffi_real.h header file to redefine FFI_TYPE_LONGDOUBLE to 4;
  6. changing the openssl/crypto/Android.mk and openssl/ssl/Android.mk makefiles to strip down the openssl library with only the bare minimum set that allows you to cross-compile the source code;
  7. changing the  python/jni/modules.mk makefile to add the -fno-stack-protector compiler and linker option when building the _socket module and exclude the openssl module.
After applying the patch, to cross-compile Py4A for the x86 architecture you should run the following:

$ cd python-build
$ ./build.sh x86

Binaries for the x86 can be downloaded here:





--- orig/python/jni/modules.mk    2013-01-21 14:39:56.000000000 -0500
+++ python/jni/modules.mk    2013-01-29 14:58:52.922096000 -0500
@@ -58,7 +58,7 @@ $(call build-module,  syslog ,  Modules/
 $(call build-module,  audioop ,  Modules/audioop.c )
 $(call build-module,  imageop ,  Modules/imageop.c )
 $(call build-module,  _csv ,  Modules/_csv.c )
-$(call build-module,  _socket ,  Modules/socketmodule.c, libc, -lc, -nostdlib )
+$(call build-module,  _socket ,  Modules/socketmodule.c, libc, -lc -fno-stack-protector, -nostdlib -fno-stack-protector )
 $(call build-module,  _sha ,  Modules/shamodule.c )
 $(call build-module,  _md5 ,  Modules/md5module.c Modules/md5.c )
 $(call build-module,  _sha256 ,  Modules/sha256module.c )
@@ -125,6 +125,7 @@ include $(BUILD_SHARED_LIBRARY)
 
 #$(call build-module,  _ssl ,  Modules/_ssl.c, ssl crypto )
 
+ifneq ($(APP_ABI), x86)
 $(call import-module, openssl)
 LOCAL_PATH :=  $(PYTHON_SRC_PATH)
 LOCAL_C_INCLUDES += $(PYTHON_SRC_PATH) $(PYTHON_SRC_PATH)/Include $(OPENSSL)/include $(OPENSSL)
@@ -133,6 +134,7 @@ LOCAL_MODULE_FILENAME := _ssl
 LOCAL_SRC_FILES := Modules/_ssl.c
 LOCAL_SHARED_LIBRARIES := libpython2.6 libcrypto libssl
 include $(BUILD_SHARED_LIBRARY)
+endif
 
 
 $(call import-module, libffi)
--- orig/setup-x86.cfg    1969-12-31 19:00:00.000000000 -0500
+++ setup-x86.cfg    2013-01-28 16:09:49.004463929 -0500
@@ -0,0 +1,2 @@
+[bdist_egg]
+plat-name=linux-x86
--- orig/openssl/ssl/Android.mk    2013-01-21 14:42:17.000000000 -0500
+++ openssl/ssl/Android.mk    2013-01-28 16:33:35.563643087 -0500
@@ -7,6 +7,10 @@ local_c_includes := \
 
 include $(CLEAR_VARS)
 
+ifeq ($(APP_ABI), x86)
+LOCAL_SRC_FILES:= \
+    kssl.c
+else
 LOCAL_SRC_FILES:= \
     s2_meth.c \
     s2_srvr.c \
@@ -45,6 +49,7 @@ LOCAL_SRC_FILES:= \
     bio_ssl.c \
     ssl_err.c \
     kssl.c
+endif
 
 include $(LOCAL_PATH)/../android-config.mk
 
--- orig/openssl/crypto/Android.mk    2013-01-21 14:42:08.000000000 -0500
+++ openssl/crypto/Android.mk    2013-01-29 14:49:00.334324159 -0500
@@ -2,27 +2,17 @@ LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
 LOCAL_CFLAGS += -DOPENSSL_BN_ASM_MONT -DAES_ASM -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DOPENSSL_NO_STATIC_ENGINE
+ifneq ($(APP_ABI), x86)
 LOCAL_SRC_FILES:= 0.9.9-dev/bn/armv4-mont.s \
                   0.9.9-dev/aes/aes-armv4.s \
                   0.9.9-dev/sha/sha1-armv4-large.s \
                   0.9.9-dev/sha/sha256-armv4.s \
                   0.9.9-dev/sha/sha512-armv4.s
-
 LOCAL_SRC_FILES+= \
-    cryptlib.c \
-    mem.c \
-    mem_clr.c \
     mem_dbg.c \
-    cversion.c \
     dyn_lck.c \
     ex_data.c \
-    tmdiff.c \
     cpt_err.c \
-    ebcdic.c \
-    uid.c \
-    o_time.c \
-    o_str.c \
-    o_dir.c \
     aes/aes_misc.c \
     aes/aes_ecb.c \
     aes/aes_cbc.c \
@@ -474,6 +464,19 @@ LOCAL_SRC_FILES+= \
     ripemd/rmd_dgst.c \
     ripemd/rmd_one.c \
     evp/m_ripemd.c
+endif
+
+LOCAL_SRC_FILES+= \
+    cryptlib.c \
+    mem.c \
+    mem_clr.c \
+    cversion.c \
+    tmdiff.c \
+    ebcdic.c \
+    uid.c \
+    o_time.c \
+    o_str.c \
+    o_dir.c
 
 LOCAL_CFLAGS += -DNO_WINDOWS_BRAINDEATH
 
--- orig/build.py    2013-01-21 14:44:17.000000000 -0500
+++ build.py    2013-01-28 16:19:35.088015089 -0500
@@ -82,6 +82,10 @@ def rm(path):
 
 
 def strip(path):
+  if len(sys.argv) > 1:
+    if sys.argv[1] == "x86":
+      run('i686-linux-android-strip %s' % path)
+    else:
   run('arm-linux-androideabi-strip %s' % path)
 
 
@@ -153,6 +157,12 @@ map(rm, find('output.temp/usr/lib/python
 map(rm, find('output.temp', 'python$', exclude=['setuptools', 'distutils'])[0])
 run("mkdir python", cwd="output.temp/usr")
 run("cp -r %s/python-libs/py4a python" % pwd, cwd="output.temp/usr")
+if len(sys.argv) > 1:
+    if sys.argv[1] == "x86":
+        run("cp %s/setup-x86.cfg ." % pwd, cwd="output.temp/usr")
+    else:
+        run("cp %s/setup.cfg ." % pwd, cwd="output.temp/usr")
+else:
 run("cp %s/setup.cfg ." % pwd, cwd="output.temp/usr")
 run("cp %s/prepare_setuptools.sh setup.sh" % pwd, cwd="output.temp/usr")
 run("cp %s/standalone_python.sh python.sh" % pwd, cwd="output.temp/usr")
--- orig/libffi/include/ffi_real.h    2013-01-21 14:42:35.000000000 -0500
+++ libffi/include/ffi_real.h    2013-01-29 10:27:30.664550823 -0500
@@ -371,8 +371,12 @@ void ffi_call(ffi_cif *cif,
 #if CONF_HAVE_LONG_DOUBLE       // android changed
 #define FFI_TYPE_LONGDOUBLE 4
 #else
+#ifdef x86
+#define FFI_TYPE_LONGDOUBLE 4
+#else
 #define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE
 #endif
+#endif
 #define FFI_TYPE_UINT8      5  
 #define FFI_TYPE_SINT8      6
 #define FFI_TYPE_UINT16     7
--- orig/libffi/Android.mk    2013-01-21 14:42:42.000000000 -0500
+++ libffi/Android.mk    2013-01-28 17:00:21.190897854 -0500
@@ -21,6 +21,17 @@ include $(CLEAR_VARS)
 
 LOCAL_MODULE := ffi
 LOCAL_MODULE_FILENAME :=
+ifeq ($(APP_ABI), x86)
+    LOCAL_SRC_FILES := src/x86/sysv.S \
+    src/x86/ffi.c \
+    src/debug.c \
+    src/java_raw_api.c \
+    src/prep_cif.c \
+    src/raw_api.c \
+    src/types.c
+
+    LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/linux-x86
+else
 LOCAL_SRC_FILES := src/arm/sysv.S \
     src/arm/ffi.c \
     src/debug.c \
@@ -30,6 +41,7 @@ LOCAL_SRC_FILES := src/arm/sysv.S \
     src/types.c
 
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/linux-arm
+endif
 LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
 
 $(call __ndk_info, Building libffi)
--- orig/libffi/linux-x86/ffi.h    1969-12-31 19:00:00.000000000 -0500
+++ libffi/linux-x86/ffi.h    2013-01-28 16:24:28.975890002 -0500
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2008 The Android Open Source Project
+ */
+#ifndef LIBFFI_H
+
+#define x86
+#include "../src/x86/ffitarget.h"
+#include "../include/ffi_real.h"
+
+#endif
--- orig/libffi/linux-x86/fficonfig.h    1969-12-31 19:00:00.000000000 -0500
+++ libffi/linux-x86/fficonfig.h    2013-01-28 16:23:04.047926730 -0500
@@ -0,0 +1,160 @@
+/* fficonfig.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+   systems. This function is required for `alloca.c' support on those systems.
+   */
+#undef CRAY_STACKSEG_END
+
+/* Define to 1 if using `alloca.c'. */
+#undef C_ALLOCA
+
+/* Define to the flags needed for the .section .eh_frame directive. */
+#define EH_FRAME_FLAGS "aw"
+
+/* Define this if you want extra debugging. */
+#undef FFI_DEBUG
+
+/* Define this is you do not want support for the raw API. */
+#undef FFI_NO_RAW_API
+
+/* Define this is you do not want support for aggregate types. */
+#undef FFI_NO_STRUCTS
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+#define HAVE_ALLOCA 1
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+   */
+#define HAVE_ALLOCA_H 1
+
+/* Define if your assembler supports .cfi_* directives. */
+#undef HAVE_AS_CFI_PSEUDO_OP
+
+/* Define if your assembler supports .register. */
+#undef HAVE_AS_REGISTER_PSEUDO_OP
+
+/* Define if your assembler and linker support unaligned PC relative relocs.
+   */
+#undef HAVE_AS_SPARC_UA_PCREL
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define if __attribute__((visibility("hidden"))) is supported. */
+#undef HAVE_HIDDEN_VISIBILITY_ATTRIBUTE
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define if you have the long double type and it is bigger than a double */
+#undef HAVE_LONG_DOUBLE
+
+/* Define to 1 if you have the `memcpy' function. */
+#define HAVE_MEMCPY 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `mmap' function. */
+#define HAVE_MMAP 1
+
+/* Define if mmap with MAP_ANON(YMOUS) works. */
+#define HAVE_MMAP_ANON 1
+
+/* Define if mmap of /dev/zero works. */
+#define HAVE_MMAP_DEV_ZERO 1
+
+/* Define if read-only mmap of a plain file works. */
+#define HAVE_MMAP_FILE 1
+
+/* Define if .eh_frame sections should be read-only. */
+#undef HAVE_RO_EH_FRAME
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#define HAVE_SYS_MMAN_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+#undef NO_MINUS_C_MINUS_O
+
+/* Name of package */
+#define PACKAGE "libffi"
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* The size of `double', as computed by sizeof. */
+#define SIZEOF_DOUBLE 8
+
+/* The size of `long double', as computed by sizeof. */
+#undef SIZEOF_LONG_DOUBLE
+
+/* If using the C implementation of alloca, define if you know the
+   direction of stack growth for your system; otherwise it will be
+   automatically deduced at runtime.
+    STACK_DIRECTION > 0 => grows toward higher addresses
+    STACK_DIRECTION < 0 => grows toward lower addresses
+    STACK_DIRECTION = 0 => direction of growth unknown */
+#undef STACK_DIRECTION
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define this if you are using Purify and want to suppress spurious messages.
+   */
+#undef USING_PURIFY
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to 1 if your processor stores words with the most significant byte
+   first (like Motorola and SPARC, unlike Intel and VAX). */
+#undef WORDS_BIGENDIAN
+
+
+#ifdef HAVE_HIDDEN_VISIBILITY_ATTRIBUTE
+#ifdef LIBFFI_ASM
+#define FFI_HIDDEN(name) .hidden name
+#else
+#define FFI_HIDDEN __attribute__ ((visibility ("hidden")))
+#endif
+#else
+#ifdef LIBFFI_ASM
+#define FFI_HIDDEN(name)
+#else
+#define FFI_HIDDEN
+#endif
+#endif
+


--- build.sh (orig/build.sh)
+++ build.sh (build.sh)
@@ -5,6 +5,7 @@
 set -ex
 CWD=$(pwd)
 DEBUG=no
+android_abi="$1"

 RELEASE_VERSION=$(cat LATEST_VERSION)
 echo "Building Python VM For Android Release ${RELEASE_VERSION}"
@@ -78,12 +79,12 @@
 ${CWD}/host/pgen ${CWD}/python-src/Grammar/Grammar \
  ${CWD}/python-src/Include/graminit.h \
  ${CWD}/python-src/Python/graminit.c
-ndk-build
+ndk-build APP_ABI:=$android_abi

 # copy out all the needed files
-mv obj/local/armeabi/python ${OUT}/usr/bin
-mv obj/local/armeabi/lib*.so ${OUT}/usr/lib
-mv obj/local/armeabi/*.so ${OUT}/usr/lib/python2.6/lib-dynload
+mv obj/local/$android_abi/python ${OUT}/usr/bin
+mv obj/local/$android_abi/lib*.so ${OUT}/usr/lib
+mv obj/local/$android_abi/*.so ${OUT}/usr/lib/python2.6/lib-dynload
 popd

 pushd ${CWD}/python-libs
@@ -91,7 +92,7 @@
 popd

 ${CWD}/host/bin/python ${OUT}/usr/lib/python2.6/compileall.py ${OUT}/usr/lib/python2.6
-${CWD}/host/bin/python build.py
+${CWD}/host/bin/python build.py $android_abi

 if [ "$DEBUG" != "yes" ]; then
     rm -rf output*

Tuesday, November 20, 2012

Python For Android (Py4A)

A better solution for cross-compiling Python for Android is to use the Py4A project which is made to be used together with SL4A (Scripting Layer For Android). If you are only interested in the Python interpreter and the runtime Python library, you can also use it standalone.
Get a local copy of the source code using  the following command:

   $ hg clone https://code.google.com/p/python-for-android/

Just focus on the python-build subdirectory and make sure the  python-build/python-src subdirectory is not present (remove it if it came with the Mercurial repository, or else the compilation will fail).
Set up your environment so that the python-for-android build script can pick up the ndk-build script from the Android NDK:

  $ export ANDROID_NDK_ROOT=/home/<your-directory>/android-ndk-r8
  $ export PATH=$ANDROID_NDK_ROOT:$PATH

Finally build Python for Android by issuing the following command:

  $ cd python-for-android/python-build
  $ rm -rf python-src
  $ bash build.sh

Note that on my Ubuntu 12.04 machine I had initially the following compilation error:

Traceback (most recent call last):
  File "build.py", line 161, in <module>
    os.path.join(pwd, 'output.temp', 'usr'))
  File "build.py", line 89, in zipup
    zip_file = zipfile.ZipFile(out_path, 'w', compression=zipfile.ZIP_DEFLATED)
  File "/home/danilo/python-for-android/python-build/host/lib/python2.6/zipfile.py", line 660, in __init__
    "Compression requires the (missing) zlib module"
RuntimeError: Compression requires the (missing) zlib module


I identified the problem in having the zlib library in my system installed under /lib/x86_64-linux-gnu/ instead of one of the traditional lib directories covered by the Python setup.py script. Also on my system I only had libz.so.1 and not libz.so. So to fix both problems I just created a symlink in the standard /usr/lib directory as follows:

  $ cd /usr/lib
  $ sudo ln -s /lib/x86_64-linux-gnu/libz.so.1 libz.so

With this fix the build.sh script was able to successfully build the zlib module for the host environment and create the following zipped files:

  • python_extras_r14.zip
  • python-lib_r16.zip
  • python_r16.zip
  • python_scripts_r13.zip

Of these I only used the python_r16.zip which contains the stripped python interpreter and the runtime libraries, and the python-lib_r16.zip which contains the include header files such as Python.h that can be used to compile Python bindings at development time.


Friday, October 26, 2012

How to cross-compile Python for Android

When it comes to cross-compiling Python for Android, I've followed Gabriel's blog post @ http://mdqinc.com/blog/2011/09/cross-compiling-python-for-android/ and I was successful in creating an Arm-based Python executable (and related libraries) in little or no time.
Gabriel has a lot of the initial steps just verbally described, but I've come up with a shell script that allows you to automate the entire process. Of course you need to replace <path-to-android-ndk> with the directory where you have installed the NDK.
Here it goes.

ANDROID_NDK=<path-to-android-ndk>
ANDROID_ABI="armeabi-v7a"
ANDROID_NATIVE_API_LEVEL="android-8"
PYTHON_VERSION="2.6.2"


output_dir=$1
mkdir -p $output_dir
cd $output_dir
# get Python source tarball
wget http://www.python.org/ftp/python/$PYTHON_VERSION/Python-$PYTHON_VERSION.tgz

# create Python Host version
tar zxvf Python-$PYTHON_VERSION.tgz
mv Python-$PYTHON_VERSION Host-Python-$PYTHON_VERSION-src
cd Host-Python-$PYTHON_VERSION-src
./configure --prefix=$output_dir/Host-Python-$PYTHON_VERSION
make
make install

cd $output_dir
# create Python Cross-compiled version for Android
tar zxvf Python-$PYTHON_VERSION.tgz
mv Python-$PYTHON_VERSION Android-Python-$PYTHON_VERSION-src
cd Android-Python-$PYTHON_VERSION-src

# get and apply Python patch
wget -o 
Python-2.6.2-android.patch https://sites.google.com/site/dgtechblogscripts/Python-2.6.2-android.patch
patch -p0 < Python-2.6.2-android.patch
# fix setup.py
mv setup.py setup.py.orig
cat setup.py.orig | awk '{ if (NR==316) {print "    " $0} else {print $0}}' > setup.py

MY_HOSTPYTHON=$output_dir/Host-Python-$PYTHON_VERSION/bin/python
MY_HOSTPGEN=$output_dir/Host-Python-$PYTHON_VERSION-src/Parser/pgen

export ANDROID_NDK
export PATH="$ANDROID_NDK/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin/:$ANDROID_NDK:$ANDROID_NDK/tools:/usr/local/bin:/usr/bin:/bin"
export ARCH=$ANDROID_ABI
export CFLAGS="-DANDROID -mandroid -fomit-frame-pointer --sysroot $ANDROID_NDK/platforms/$ANDROID_NATIVE_API_LEVEL/arch-arm"
export CXXFLAGS="$CFLAGS"
export CC="arm-linux-androideabi-gcc $CFLAGS"
export CXX="arm-linux-androideabi-g++ $CXXFLAGS"
export AR="arm-linux-androideabi-ar"
export RANLIB="arm-linux-androideabi-ranlib"
export STRIP="arm-linux-androideabi-strip --strip-unneeded"
export MAKE="make -j4 install HOSTPYTHON=$MY_HOSTPYTHON HOSTPGEN=$MY_HOSTPGEN CROSS_COMPILE=arm-eabi- CROSS_COMPILE_TARGET=yes"

./configure LDFLAGS="-Wl,--allow-shlib-undefined" CFLAGS="-mandroid -fomit-frame-pointer --sysroot $ANDROID_NDK/platforms/$ANDROID_NATIVE_API_LEVEL/arch-arm" HOSTPYTHON=$MY_HOSTPYTHON HOSTPGEN=$MY_HOSTPGEN --host=arm-eabi --build=i686-pc-linux-gnu --enable-shared --prefix="$output_dir/Android-Python-$PYTHON_VERSION"
sed -i "s|^INSTSONAME=\(.*.so\).*|INSTSONAME=\\1|g" Makefile
$MAKE