您的位置:首页 > 编程语言 > Python开发

交叉编译Python-2.7.13到ARM(aarch32)—— 支持sqlite3

2017-03-21 21:56 736 查看
作者:彭东林

邮箱:pengdonglin137@163.com

QQ: 405728433

环境

主机: ubuntu14.04 64bit

开发板: qemu + vexpress-a9 (参考: http://www.cnblogs.com/pengdonglin137/p/6442583.html

工具链: arm-none-linux-gnueabi-gcc (gcc version 4.8.3 20140320)

Python版本: Python-2.7.13

概述

前面一篇博文(交叉编译Python-2.7.13到ARM(aarch32)平台)介绍了移植python到aarch32上面,但是发现有很多模块都不能用,可以在板子上面执行下面的命令测试一下:

[root@vexpress ]# python /usr/lib/python2.7/test/test___all__.py
Traceback (most recent call last):
File "/usr/lib/python2.7/test/test___all__.py", line 3, in <module>
import unittest
File "/usr/lib/python2.7/unittest/__init__.py", line 58, in <module>
from .result import TestResult
File "/usr/lib/python2.7/unittest/result.py", line 9, in <module>
from . import util
File "/usr/lib/python2.7/unittest/util.py", line 2, in <module>
from collections import namedtuple, OrderedDict
File "/usr/lib/python2.7/collections.py", line 20, in <module>
from _collections import deque, defaultdict
ImportError: No module named _collections


可以看到这里找不到_collections模块。

对比x86_64的编译结果:

ls x86_64/build/lib.linux-x86_64-2.7/
array.so*       _codecs_hk.so*       cPickle.so*       _curses_panel.so*  future_builtins.so*  itertools.so*      mmap.so*              parser.so*    _socket.so*   _sysconfigdata.py   time.so*
audioop.so*     _codecs_iso2022.so*  crypt.so*         _curses.so*        grp.so*              _json.so*          _multibytecodec.so*   pyexpat.so*   spwd.so*      _sysconfigdata.pyc  unicodedata.so*
binascii.so*    _codecs_jp.so*       cStringIO.so*     datetime.so*       _hashlib.so*         linuxaudiodev.so*  _multiprocessing.so*  _random.so*   _sqlite3.so*  _sysconfigdata.pyo  zlib.so*
_bisect.so*     _codecs_kr.so*       _csv.so*          _elementtree.so*   _heapq.so*           _locale.so*        nis.so*               readline.so*  _ssl.so*      syslog.so*
cmath.so*       _codecs_tw.so*       _ctypes.so*       fcntl.so*          _hotshot.so*         _lsprof.so*        operator.so*          resource.so*  strop.so*     termios.so*
_codecs_cn.so*  _collections.so*     _ctypes_test.so*  _functools.so*     _io.so*              math.so*           ossaudiodev.so*       select.so*    _struct.so*   _testcapi.so*


而aarch32的编译结果:

$ls aarch32/build/lib.linux2-arm-2.7/
audioop.so*     _codecs_iso2022.so*  _codecs_tw.so*  _ctypes.so*       _elementtree.so*     _json.so*          mmap.so*              nis.so*      resource.so*        termios.so*
_codecs_cn.so*  _codecs_jp.so*       crypt.so*       _ctypes_test.so*  future_builtins.so*  linuxaudiodev.so*  _multibytecodec.so*   parser.so*   _sysconfigdata.py   _testcapi.so*
_codecs_hk.so*  _codecs_kr.so*       _csv.so*        datetime.so*      _hotshot.so*         _lsprof.so*        _multiprocessing.so*  pyexpat.so*  _sysconfigdata.pyc


可以看到,aarch32上面缺少了很多库, 比如_collections.so,将来这些库会被安装到/usr/lib/python2.7/lib-dynload下面, 所以下面要说的就是将缺少的这些库弄回来!

正文

1、通过分析setup.py发现问题

在函数build_extensions中刚开始self.extensions中存放的是需要编译库, 通过在加打印:

diff --git a/setup.py b/setup.py
index 54054c2..bc16bb1 100644
--- a/setup.py
+++ b/setup.py
@@ -178,6 +178,7 @@ class PyBuildExt(build_ext):

def build_extensions(self):

+        print "build_extensions enter."
# Detect which modules should be compiled
missing = self.detect_modules()

@@ -191,6 +192,9 @@ class PyBuildExt(build_ext):
extensions.append(ctypes)
self.extensions = extensions

+        for ext in self.extensions:
+            print "extensions: ", ext.name
+
# Fix up the autodetected modules, prefixing all the source files
# with Modules/ and adding Python's include directory to the path.
(srcdir,) = sysconfig.get_config_vars('srcdir')
@@ -217,6 +221,8 @@ class PyBuildExt(build_ext):
# Python header files
headers = [sysconfig.get_config_h_filename()]
headers += glob(os.path.join(sysconfig.get_path('include'), "*.h"))
+
+        print "builtin_module_names: ", sys.builtin_module_names
for ext in self.extensions[:]:
ext.sources = [ find_module_file(filename, moddirlist)
for filename in ext.sources ]
@@ -248,10 +254,15 @@ class PyBuildExt(build_ext):
remove_modules.append(line[0])
input.close()

+        print "remove_modules: ", remove_modules
+
for ext in self.extensions[:]:
if ext.name in remove_modules:
self.extensions.remove(ext)

+        for ext in self.extensions[:]:
+            print "extensions: ", ext.name
+
# When you run "make CC=altcc" or something similar, you really want
# those environment variables passed into the setup.py phase.  Here's
# a small set of useful ones.
@@ -1618,13 +1629,13 @@ class PyBuildExt(build_ext):

# Platform-specific libraries
-        if host_platform == 'linux2':
+        if host_platform == 'linux2' or host_platform == 'linux2-arm':
# Linux-specific modules
exts.append( Extension('linuxaudiodev', ['linuxaudiodev.c']) )
else:
missing.append('linuxaudiodev')

-        if (host_platform in ('linux2', 'freebsd4', 'freebsd5', 'freebsd6',
+        if (host_platform in ('linux2','linux2-arm' 'freebsd4', 'freebsd5', 'freebsd6',
'freebsd7', 'freebsd8')
or host_platform.startswith("gnukfreebsd")):
exts.append( Extension('ossaudiodev', ['ossaudiodev.c']) )
@@ -1755,6 +1766,10 @@ class PyBuildExt(build_ext):
##         ext = Extension('xx', ['xxmodule.c'])
##         self.extensions.append(ext)

+#        print "missing: ", missing
+#        for ext in self.extensions:
+#            print "extensions: ", ext.name
+
return missing

def detect_tkinter_explicitly(self):
@@ -2229,6 +2244,8 @@ Topic :: Software Development
"""

def main():
+    print "sys.path: ", sys.path
+    print "cross_compiling: ", cross_compiling
# turn off warnings when deprecated modules are imported
import warnings
warnings.filterwarnings("ignore",category=DeprecationWarning)


然后执行./mk2_make.sh可以看到:

sys.path:  ['/home/pengdonglin/src/qemu/python_cross_compile/Python-2.7.13', '/home/pengdonglin/src/qemu/python_cross_compile/aarch32/build/lib.linux2-arm-2.7', '/home/pengdonglin/src/qemu/python_cross_compile/Python-2.7.13/Lib', '/home/pengdonglin/src/qemu/python_cross_compile/Python-2.7.13/Lib/plat-linux2', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-x86_64-linux-gnu', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload']
cross_compiling:  True
build_extensions enter.
extensions:  _struct
extensions:  _ctypes_test
extensions:  array
extensions:  cmath
extensions:  math
extensions:  strop
extensions:  time
extensions:  datetime
extensions:  itertools
extensions:  future_builtins
extensions:  _random
extensions:  _collections
extensions:  _bisect
extensions:  _heapq
extensions:  operator
extensions:  _io
extensions:  _functools
extensions:  _json
extensions:  _testcapi
extensions:  _hotshot
extensions:  _lsprof
extensions:  unicodedata
extensions:  _locale
extensions:  fcntl
extensions:  pwd
extensions:  grp
extensions:  spwd
extensions:  select
extensions:  parser
extensions:  cStringIO
extensions:  cPickle
extensions:  mmap
extensions:  syslog
extensions:  audioop
extensions:  crypt
extensions:  _csv
extensions:  _socket
extensions:  _sha
extensions:  _md5
extensions:  _sha256
extensions:  _sha512
extensions:  termios
extensions:  resource
extensions:  nis
extensions:  binascii
extensions:  pyexpat
extensions:  _elementtree
extensions:  _multibytecodec
extensions:  _codecs_kr
extensions:  _codecs_jp
extensions:  _codecs_cn
extensions:  _codecs_tw
extensions:  _codecs_hk
extensions:  _codecs_iso2022
extensions:  _multiprocessing
extensions:  linuxaudiodev
extensions:  _ctypes
builtin_module_names:  ('__builtin__', '__main__', '_ast', '_bisect', '_codecs', '_collections', '_functools', '_heapq', '_io', '_locale', '_md5', '_random', '_sha', '_sha256', '_sha512', '_socket', '_sre', '_struct', '_symtable', '_warnings', '_weakref', 'array', 'binascii', 'cPickle', 'cStringIO', 'cmath', 'errno', 'exceptions', 'fcntl', 'gc', 'grp', 'imp', 'itertools', 'marshal', 'math', 'operator', 'posix', 'pwd', 'select', 'signal', 'spwd', 'strop', 'sys', 'syslog', 'thread', 'time', 'unicodedata', 'xxsubtype', 'zipimport', 'zlib')
remove_modules:  ['DESTLIB=$(LIBDEST)', 'MACHDESTLIB=$(BINLIBDEST)', 'DESTPATH=', 'SITEPATH=', 'TESTPATH=', 'MACHDEPPATH=:$(PLATDIR)', 'EXTRAMACHDEPPATH=', 'TKPATH=:lib-tk', 'OLDPATH=:lib-old', 'COREPYTHONPATH=$(DESTPATH)$(SITEPATH)$(TESTPATH)$(MACHDEPPATH)$(EXTRAMACHDEPPATH)$(TKPATH)$(OLDPATH)', 'PYTHONPATH=$(COREPYTHONPATH)', 'posix', 'errno', 'pwd', '_sre', '_codecs', '_weakref', 'zipimport', '_symtable', 'GLHACK=-Dclear=__GLclear', 'xxsubtype']
extensions:  _ctypes_test
extensions:  datetime
extensions:  future_builtins
extensions:  _json
extensions:  _testcapi
extensions:  _hotshot
extensions:  _lsprof
extensions:  parser
extensions:  mmap
extensions:  audioop
extensions:  crypt
extensions:  _csv
extensions:  termios
extensions:  resource
extensions:  nis
extensions:  pyexpat
extensions:  _elementtree
extensions:  _multibytecodec
extensions:  _codecs_kr
extensions:  _codecs_jp
extensions:  _codecs_cn
extensions:  _codecs_tw
extensions:  _codecs_hk
extensions:  _codecs_iso2022
extensions:  _multiprocessing
extensions:  linuxaudiodev
extensions:  _ctypes
Python build finished, but the necessary bits to build these modules were not found:
_bsddb             _curses            _curses_panel
_sqlite3           _ssl               _tkinter
bsddb185           bz2                dbm
dl                 gdbm               imageop
ossaudiodev        readline           sunaudiodev
zlib
To find the necessary bits, look in setup.py in detect_modules() for the module's name.


在刚开始的时候,self.extensions中还是全的,但是经过下面的处理后, 很多库都被remove了:

for ext in self.extensions[:]:
ext.sources = [ find_module_file(filename, moddirlist)
for filename in ext.sources ]
if ext.depends is not None:
ext.depends = [find_module_file(filename, moddirlist)
for filename in ext.depends]
else:
ext.depends = []
# re-compile extensions if a header file has been changed
ext.depends.extend(headers)
# platform specific include directories
ext.include_dirs.extend(incdirlist)
# If a module has already been built statically,
# don't build it here
if ext.name in sys.builtin_module_names:
self.extensions.remove(ext)


第15行的注释可以看到,如果sys.builtin_module_names中含有extensions中的库,那么这个库就会从extensions中remove。从目前的分析看,交叉编译的时候,setup.py的import sys导入的应该是PC机上面的环境,导致sys.builtin_module_names的值也是PC上面python运行环境的值(可以在PC的终端下输入python,查看sys.builtin_module_names的值)。

2、 解决

这里为了简单起见,我们只需把刚才出问题的判断注释掉,如下:

@@ -233,8 +239,8 @@ class PyBuildExt(build_ext):

# If a module has already been built statically,
# don't build it here
-            if ext.name in sys.builtin_module_names:
-                self.extensions.remove(ext)
+            #if ext.name in sys.builtin_module_names:
+            #    self.extensions.remove(ext)

# Parse Modules/Setup and Modules/Setup.local to figure out which
# modules are turned on in the file.


然后重新配置、编译、安装, 最后重新制作ramdisk文件,启动板子,重新执行下面的测试:

[root@vexpress ]# python /usr/lib/python2.7/test/test___all__.py
test_all (__main__.AllTest) ... BaseHTTPServer
Bastion
CGIHTTPServer
ConfigParser
Cookie
DocXMLRPCServer
HTMLParser
MimeWriter
Queue
SimpleHTTPServer
... ...
'xml.sax.xmlreader', 'xmllib', 'xmlrpclib']
Following modules failed to be imported: ['ctypes.wintypes', 'dbhash', 'gzip', 'idlelib.AutoComplete']
ok
----------------------------------------------------------------------
Ran 1 test in 9.345s
OK


可以看到,测试成功了。

下面开始一致sqlite3到板子上面,同时让python也增加多sqlite3的支持。

3、支持sqlite3

首先到http://www.sqlite.org/download.html 下载最新的sqlite3的源码,这里我用的是sqlite-autoconf-3170000.tar.gz,然后进行交叉编译,下面是交叉编译的脚本mk.sh:

#!/bin/bash
export PATH=/home/pengdonglin/src/qemu/aarch32/arm-2014.05/bin:$PATH

../sqlite-autoconf-3170000/configure --host=arm-none-linux-gnueabi \
--prefix=`pwd`

make -j4
make install


然后修改制作ramdisk的脚本:

#!/bin/bash

sudo rm -rf rootfs
sudo rm -rf tmpfs
sudo rm -rf ramdisk*

sudo mkdir rootfs
sudo cp ../busybox-1.24.2/_install/*  rootfs/ -raf

sudo mkdir -p rootfs/proc/
sudo mkdir -p rootfs/sys/
sudo mkdir -p rootfs/tmp/
sudo mkdir -p rootfs/root/
sudo mkdir -p rootfs/var/
sudo mkdir -p rootfs/mnt/

sudo cp etc rootfs/ -arf

sudo cp -arf ../arm-2014.05/arm-none-linux-gnueabi/libc/lib rootfs/

#python
sudo mkdir -p rootfs/usr
pushd rootfs/usr
sudo cp  -raf /home/pengdonglin/qemu/thiry_part/Python/aarch32/bin .
sudo cp  -raf /home/pengdonglin/qemu/thiry_part/Python/aarch32/lib .
sudo cp  -raf /home/pengdonglin/qemu/thiry_part/Python/aarch32/include .
sudo cp  -raf /home/pengdonglin/qemu/thiry_part/Python/aarch32/share .
sudo /home/pengdonglin/qemu/aarch32/arm-2014.05/bin/arm-none-linux-gnueabi-strip lib/python*
popd

31 #sqlite3
32 sudo cp -raf /home/pengdonglin/qemu/thiry_part/SQlite3/aarch32/bin/* rootfs/bin/
33 sudo cp -raf /home/pengdonglin/qemu/thiry_part/SQlite3/aarch32/include/* rootfs/include/
34 sudo cp -raf /home/pengdonglin/qemu/thiry_part/SQlite3/aarch32/lib/* rootfs/lib/
35 sudo cp -raf /home/pengdonglin/qemu/thiry_part/SQlite3/aarch32/share/* rootfs/usr/share


sudo mkdir -p rootfs/dev/
sudo mknod rootfs/dev/tty1 c 4 1
sudo mknod rootfs/dev/tty2 c 4 2
sudo mknod rootfs/dev/tty3 c 4 3
sudo mknod rootfs/dev/tty4 c 4 4
sudo mknod rootfs/dev/console c 5 1
sudo mknod rootfs/dev/null c 1 3

sudo rm -rf rootfs/lib/*.a
sudo rm -rf rootfs/lib/*.la
sudo ../arm-2014.05/bin/arm-none-linux-gnueabi-strip rootfs/lib/*

sudo dd if=/dev/zero of=ramdisk bs=1M count=100
sudo mkfs.ext4 -F ramdisk

sudo mkdir -p tmpfs
sudo mount -t ext4 ramdisk ./tmpfs/  -o loop
sudo cp -raf rootfs/*  tmpfs/
sudo umount tmpfs

sudo gzip --best -c ramdisk > ramdisk.gz
sudo mkimage -n "ramdisk" -A arm -O linux -T ramdisk -C gzip -d ramdisk.gz ramdisk.img


这样在板子上面就可以使用sqlite3了,但是此时python还无法使用,需要重新编译python,并指定sqlite3的lib和include的路径,修改mk1_config.sh如下:

#!/bin/bash

export PATH=/home/pengdonglin/qemu/aarch32/arm-2014.05/bin:$PATH

../Python-2.7.13/configure --prefix=`pwd` \
--host=arm-none-linux-gnueabi \
--build=x86_64-linux-gnu \
--enable-ipv6 \
--enable-shared \
ac_cv_file__dev_ptmx="yes" \
ac_cv_file__dev_ptc="no" \
    LDFLAGS="-L/home/pengdonglin/qemu/thiry_part/SQlite3/aarch32/lib" \
CPPFLAGS="-I/home/pengdonglin/qemu/thiry_part/SQlite3/aarch32/include"


这样在Makefile调用setup.py时就会将sqlite3相关的模块编译进来,然后再次执行mk2_make.sh和mk3_install.sh,然后我们可以检查一下:

$ls aarch32/build/lib.linux2-arm-2.7/
array.so*       _codecs_hk.so*       cPickle.so*       datetime.so*         _heapq.so*         _locale.so*          _multiprocessing.so*  _random.so*   _socket.so*        _sysconfigdata.pyc
audioop.so*     _codecs_iso2022.so*  crypt.so*         _elementtree.so*     _hotshot.so*       _lsprof.so*          nis.so*               resource.so*  spwd.so*           syslog.so*
binascii.so*    _codecs_jp.so*       cStringIO.so*     fcntl.so*            _io.so*            math.so*             operator.so*          select.so*    _sqlite3.so*       termios.so*
_bisect.so*     _codecs_kr.so*       _csv.so*          _functools.so*       itertools.so*      _md5.so*             ossaudiodev.so*       _sha256.so*   strop.so*          _testcapi.so*
cmath.so*       _codecs_tw.so*       _ctypes.so*       future_builtins.so*  _json.so*          mmap.so*             parser.so*            _sha512.so*   _struct.so*        time.so*
_codecs_cn.so*  _collections.so*     _ctypes_test.so*  grp.so*              linuxaudiodev.so*  _multibytecodec.so*  pyexpat.so*           _sha.so*      _sysconfigdata.py  unicodedata.so*


可以看到,库已经很全了。

4、测试

重新制作ramdisk文件,启动系统。

编写测试sqlite3的脚本sq_demo.py如下:

#!/usr/bin/python

import sqlite3

#open database
conn = sqlite3.connect('test.db')
print "Opened database successfully";

conn.execute('''CREATE TABLE COMPANY
(ID INT PRIMARY KEY     NOT NULL,
NAME           TEXT    NOT NULL,
AGE            INT     NOT NULL,
ADDRESS        CHAR(50),
SALARY         REAL);''')
print "Table created successfully";

#insert
conn.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
VALUES (1, 'Paul', 32, 'California', 20000.00 )");

conn.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
VALUES (2, 'Allen', 25, 'Texas', 15000.00 )");

conn.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
VALUES (3, 'Teddy', 23, 'Norway', 20000.00 )");

conn.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 )");

conn.commit()
print "Records created successfully";

#select
cursor = conn.execute("SELECT id, name, address, salary  from COMPANY")
for row in cursor:
print "ID = ", row[0]
print "NAME = ", row[1]
print "ADDRESS = ", row[2]
print "SALARY = ", row[3], "\n"

print "Operation done successfully";

#delect
conn.execute("DELETE from COMPANY where ID=2;")
conn.commit()
print "Total number of rows deleted :", conn.total_changes

cursor = conn.execute("SELECT id, name, address, salary  from COMPANY")
for row in cursor:
print "ID = ", row[0]
print "NAME = ", row[1]
print "ADDRESS = ", row[2]
print "SALARY = ", row[3], "\n"

print "Operation done successfully";

conn.close()


下面是输出结果:

[root@vexpress ]# python /tmp/sq_demo.py
Opened database successfully
Table created successfully
Records created successfully
ID =  1
NAME =  Paul
ADDRESS =  California
SALARY =  20000.0

ID =  2
NAME =  Allen
ADDRESS =  Texas
SALARY =  15000.0

ID =  3
NAME =  Teddy
ADDRESS =  Norway
SALARY =  20000.0

ID =  4
NAME =  Mark
ADDRESS =  Rich-Mond
SALARY =  65000.0

Operation done successfully
Total number of rows deleted : 5
ID =  1
NAME =  Paul
ADDRESS =  California
SALARY =  20000.0

ID =  3
NAME =  Teddy
ADDRESS =  Norway
SALARY =  20000.0

ID =  4
NAME =  Mark
ADDRESS =  Rich-Mond
SALARY =  65000.0

Operation done successfully


完。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: