擁抱Android:編譯python搭建移動的無線服務器平臺

Android編譯系列篇:

1 - Python

2 - NodeJS

3 - Nginx

4 - MariaDB


你想用廢舊的Android手機作家庭服務器嘛?

其實並不難。以前,用Android SDK開發一個手機應用,安裝下apk就可以host服務了,而現在就直接native化吧。

這篇文章會帶你體驗編譯Python的過程,並用Python搭建可以帶着跑的服務器。


Android 6 ARM的預編譯版本可以直接下載:https://github.com/dna2github/dna2oslab/releases

Github: https://github.com/dna2github/dna2oslab/tree/master/android


Android Python運行SimpleHTTPServer:


Android python運行pip freeze查看安裝的python packages:



Android python跑Django和SocketIO服務:



GCC Plugin for C4Droid(Android上gcc編譯hello world):



首先,我們要開始在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

# 編譯ncurses
./configure --prefix=/YourPath --disable-home-terminfo --without-ada
make
make install
# 編譯readline
./configure --prefix=/YourPath --host=arm-linux --build=x86_64-linux \
            --enable-static --enable-shared --with-curses
make
make install
# 編譯sqlite
./configure --host=arm-linux --build=x86_64-linux
make
make install

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了。

這樣就可以configure 了:

./configure --host=arm-unknown-linux-gnu --build=x86_64-unknown-linux-gnu \
            --enable-ipv6

- 有一段編譯會報錯,仔細檢查,發現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) 解壓並安裝:

gzip -d setuptools-7.0.tar.gz
tar xf setuptools-7.0.tar
cd setuptools-7.0
python setup.py build
python setup.py install

gzip -d pip-1.5.6.tar.gz
tar xf pip-1.5.6.tar
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搬過來就無敵啦。買個USBminiB轉RJ45的頭就可以插網線了。

總體來說,可以搭建移動服務器了,以後寫一些網頁版小應用,想用的時候android開個熱點,電腦一連,開始enjoy!

讓服務器無處不在吧,從家裏開始~


2014.11.26

J.Y.Liu



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章