拥抱Android:编译python搭建移动的无线服务器平台

你想用废旧的Android手机作家庭服务器嘛?

其实并不难。以前,用Android SDK开发一个手机应用,安装下apk就可以host服务了,而现在就直接native化吧。

这篇文章会带你体验编译Python的过程,并用Python搭建可以带着跑的服务器。


首先,我们要开始在Arm的Android平台上编译Python。当然,你需要先准备好一台Linux的机器,然后从Android的官方网站下载并安装好Android NDK(最好SDK也装了)。

下载一些必要的代码包:


openssl-1.0.1j: http://www.openssl.org/source/

ncurses-5.9: http://ftp.gnu.org/gnu/ncurses/

readline 6.3: http://ftp.gnu.org/gnu/readline/

sqlite-autoconf-3080701: http://www.sqlite.org/download.html

python-2.7.8: https://www.python.org/downloads/release/python-278/


我们需要一个一个编译这些包:

1. common.sh:这个文件里包含一些基础设置,比如选用的GCC,CFLAGS和LDFLAGS如何配置。

export NDKDIR="/你的NDK路径比如/android-ndk-r10c"
# GCC 版本选用,目前有4.6,4.8,4.9,选用时也注意Linux系统的类型,这里x86_64是六十四位
export COMPILER="$NDKDIR/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin"
export CC="$COMPILER/arm-linux-androideabi-gcc"
export CXX="$COMPILER/arm-linux-androideabi-g++"
export CPP="$COMPILER/arm-linux-androideabi-cpp"
export LD="$COMPILER/arm-linux-androideabi-ld"
export AS="$COMPILER/arm-linux-androideabi-as"
export AR="$COMPILER/arm-linux-androideabi-ar"
export STRIP="$COMPILER/arm-linux-androideabi-strip"
export OBJCOPY="$COMPILER/arm-linux-androideabi-objcopy"
export OBJDUMP="$COMPILER/arm-linux-androideabi-objdump"
export RANLIB="$COMPILER/arm-linux-androideabi-ranlib"
export NM="$COMPILER/arm-linux-androideabi-nm"
export STRINGS="$COMPILER/arm-linux-androideabi-strings"
export READELF="$COMPILER/arm-linux-androideabi-readelf"

# 选择要编译文件在哪个Android版本上使用,这里案例是在Android 4.2上使用,就是android 17;2.2对应Android 8,5.0对应Android 21
export ANDROID="$NDKDIR/platforms/android-17/arch-arm/usr"
# 配置系统头文件和库文件位置
export CFLAGS="-I$ANDROID/include --sysroot=$ANDROID"
export CXXFLAGS="-I$ANDROID/include --sysroot=$ANDROID"
export CPPFLAGS="-I$ANDROID/include"
export LDFLAGS="-L$ANDROID/lib"

2. 编译openssl:

tar zxf openssl-1.0.1j.tar.gz
cd openssl-1.0.1j
mkdir dist

source common.sh
MACHINE=armv7 SYSTEM=android ./config -fPIC --prefix=./dist

# 在Makefile里做一些补丁,以防error
sed -i "s|-m64||" Makefile
sed -i "s|-Wall|-Wall --sysroot=$ANDROID|" Makefile

# 编译并安装
make
make install

3. 编译ncurses,readline,sqlite

仿照openssl的方法,编译另外三个库;其实还有一个zlib需要编译,当然后面不让python支持bz2就可以忽略。

注意readline编译最好选择--with-curses,然后把编译好的ncurses链接上。库类文件编译,尽量都加-fPIC,这是什么,不从汇编说还真说不清楚,还是大家自己去看官方文档吧。

对于localeconv的问题,大家最好改写下那个locale.h,在里面把localeconv的struct里fix放上你要的字符,比如decimal_point是".",这样后面都不会出这类locale的问题了。

这里给出快捷的解决方案就是把localeconv干掉,直接hardcode:

# 在ncurses编译之前,需要打的补丁
sed -i "s/#define isDecimalPoint(c) .*/#define isDecimalPoint(c) ((c) == '.')/" form/fty_num.c
sed -i "s/localeconv()/NULL/" form/fty_num.c

4. 编译Python:

其实过程整体和openssl没有什么区别,细节上有一些注意事项。

- configure文件是需要手动fix的,打开文件,搜索 ac_cv_file__dev_ptmx 和 ac_cv_file__dev_ptc;删除对这两个变量的自动判断。手动去Android查看/dev文件夹里有没有ptmx和ptc设备,有就设置为yes没就no:

ac_cv_file__dev_ptmx=yes
ac_cv_file__dev_ptc=no

- 打开Modules/Setup.dist文件,把需要的python模块前面的#去掉,比如#_socket socketmodule.c timemodule.c,要python支持网络socket接口,需要把#去掉;建议尽量多加一些包;实在编译不过的包不要,有些模块需要额外下载开源软件库编译,就不只openssl,ncurses,readline,sqlite了。

- 有一段编译会报错,仔细检查,发现python需要编译一个程序,这个程序跑在host上,但gcc是arm的,host linux是x86_64的,所以我们需要复制一份解压好的python代码,然后用本地原有的gcc编译;当然编译时直接./configure && make就可以了,直到Parser文件夹下出现了pgen这个可执行文件;把它拿出来,复制到另一个python源码的Parser文件夹中,修改Makefile:

sed -i "s|\$(PGEN):.*|\$(PGEN):|" Makefile
sed -i "s|\$(CC) \$(OPT) \$(LDFLAGS) \$(PGENOBJS) \$(LIBS) -o \$(PGEN)|echo \"fake Parser/pgen\"|" Makefile

- 解决locale的问题,还有一些常量问题,笨方法hardcode:

sed -i "s|.*localeconv().*||" Objects/stringlib/localeutil.h
sed -i "s|locale_data->grouping|\"\"|" Objects/stringlib/localeutil.h
sed -i "s|locale_data->thousands_sep|\"\"|" Objects/stringlib/localeutil.h
sed -i "s|.*localeconv().*||" Objects/stringlib/formatter.h
sed -i "s|locale_data->grouping|\"\"|" Objects/stringlib/formatter.h
sed -i "s|locale_data->thousands_sep|\"\"|" Objects/stringlib/formatter.h
sed -i "s|locale_data->decimal_point|\".\"|" Objects/stringlib/formatter.h
sed -i "s|.*localeconv().*||" Python/pystrtod.c
sed -i "s|locale_data->decimal_point|\".\"|" Python/pystrtod.c
sed -i "s|I_PUSH|0x5302|" Modules/posixmodule.c
sed -i "s|p->pw_gecos|\"\"|" Modules/pwdmodule.c

- Modules/socketmodule.c: 需要去掉一些#if,不然头文件里没有定义,或者直接去$ANDROID的include文件夹把相应.h文件补充完整也可以。

...
    Py_BEGIN_ALLOW_THREADS
#ifdef USE_GETHOSTBYNAME_LOCK
    PyThread_acquire_lock(netdb_lock, 1);
#endif
    h = gethostbyaddr(ap, al, af);
    Py_END_ALLOW_THREADS
    ret = gethost_common(h, (struct sockaddr *)&addr, sizeof(addr), af);
#ifdef USE_GETHOSTBYNAME_LOCK
    PyThread_release_lock(netdb_lock);
#endif
    return ret;
...

make然后make -i install,好啦,python编译出来啦!


下面就是放到android上跑了。

需要Android是root的,不root也可以,就是得找个地方放。

需要把python编译好的文件夹打包放到android上,还有sqlite里的那个so文件。

root的话可以在/system/bin里软链接一个python。当然,sqlite.so.3要放在/system/lib里。

其实sqlite是可以不编译的,但是我们的Django需要它,所以还是弄出来吧,ssl也可以不用,但是为了服务器支持https,还是编译下吧。

这样就可以运行python了。

# python
>>> 1+2
3
然后下载setuptools (https://pypi.python.org/pypi/setuptools/7.0) 和 pip (https://pypi.python.org/pypi/pip/1.5.6) 解压并安装:

tar zxf setuptools-7.0.tar.gz
cd setuptools-7.0
python setup.py build
python setup.py install

tar zxf pip-1.5.6.tar.gz
cd pip-1.5.6
python setup.py build
python setup.py install
把pip软链接到/system/bin。好了,python有了pip,哈哈,随心安装包吧。先来个pip install virtualenv
接下去可以安装django django-sslserver,把django-admin软链接到/system/bin,就可以写网站啦:

django-admin startproject test001
cd test001
python manage.py migrate
python manage.py runserver 0.0.0.0:8000
不安装django也可以直接对一个文件夹提供http服务:

python -m SimpleHTTPServer

有了server,在家庭里就可以搭建平台啦,如果有多个手机,连上wifi,就可以不用接线,完成无线分布式服务器,赶快练习loadbalance吧。

嗯嗯,看看需不需要用手机服务器随时监控家里的活动,然后插上SIM卡还能自动给我发短信,嘿嘿。


后面我们来想象怎么解决pip install有时需要编译c文件的问题。其实有团队已经解决了这个问题。

下载Droid for GCC plugin的apk:http://www.liqucn.com/rj/228351.shtml (这个不是官网,最好去google play下载)

把apk解压,然后找到gcc的压缩包,里面就有gcc了,把它放到Android上:

#include <stdio.h>
int main() {
  printf("hello world!\n");
  return 0;
}
然后gcc -o test test.c,并运行./test,完美输出hello world。赶紧软链接到/system/bin里吧。
好了,这样numpy都可以编译安装了。还可以编译下erl,把rabbitmq编译下,弄个分布式也不是问题。最好移植一下lxc,然后把raspberry里的arm版java搬过来就无敌啦。

总体来说,可以搭建移动服务器了,以后写一些网页版小应用,想用的时候android开个热点,电脑一连,开始enjoy!

让服务器无处不在吧,从家里开始~


2014.11.26

J.Y.Liu



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。