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

作者:彭东林

邮箱: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上面,但是发现有很多模块都不能用,可以在板子上面执行下面的命令测试一下:

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

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

对比x86_64的编译结果:

1 ls x86_64/build/lib.linux-x86_64-2.7/
2 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*
3 audioop.so*     _codecs_iso2022.so*  crypt.so*         _curses.so*        grp.so*              _json.so*          _multibytecodec.so*   pyexpat.so*   spwd.so*      _sysconfigdata.pyc  unicodedata.so*
4 binascii.so*    _codecs_jp.so*       cStringIO.so*     datetime.so*       _hashlib.so*         linuxaudiodev.so*  _multiprocessing.so*  _random.so*   _sqlite3.so*  _sysconfigdata.pyo  zlib.so*
5 _bisect.so*     _codecs_kr.so*       _csv.so*          _elementtree.so*   _heapq.so*           _locale.so*        nis.so*               readline.so*  _ssl.so*      syslog.so*
6 cmath.so*       _codecs_tw.so*       _ctypes.so*       fcntl.so*          _hotshot.so*         _lsprof.so*        operator.so*          resource.so*  strop.so*     termios.so*
7 _codecs_cn.so*  _collections.so*     _ctypes_test.so*  _functools.so*     _io.so*              math.so*           ossaudiodev.so*       select.so*    _struct.so*   _testcapi.so*

而aarch32的编译结果:

1 $ls aarch32/build/lib.linux2-arm-2.7/
2 audioop.so*     _codecs_iso2022.so*  _codecs_tw.so*  _ctypes.so*       _elementtree.so*     _json.so*          mmap.so*              nis.so*      resource.so*        termios.so*
3 _codecs_cn.so*  _codecs_jp.so*       crypt.so*       _ctypes_test.so*  future_builtins.so*  linuxaudiodev.so*  _multibytecodec.so*   parser.so*   _sysconfigdata.py   _testcapi.so*
4 _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中存放的是需要编译库, 通过在加打印:

 1 diff --git a/setup.py b/setup.py
 2 index 54054c2..bc16bb1 100644
 3 --- a/setup.py
 4 +++ b/setup.py
 5 @@ -178,6 +178,7 @@ class PyBuildExt(build_ext):
 6  
 7      def build_extensions(self):
 8  
 9 +        print "build_extensions enter."
10          # Detect which modules should be compiled
11          missing = self.detect_modules()
12  
13 @@ -191,6 +192,9 @@ class PyBuildExt(build_ext):
14              extensions.append(ctypes)
15          self.extensions = extensions
16  
17 +        for ext in self.extensions:
18 +            print "extensions: ", ext.name
19 +
20          # Fix up the autodetected modules, prefixing all the source files
21          # with Modules/ and adding Python's include directory to the path.
22          (srcdir,) = sysconfig.get_config_vars('srcdir')
23 @@ -217,6 +221,8 @@ class PyBuildExt(build_ext):
24          # Python header files
25          headers = [sysconfig.get_config_h_filename()]
26          headers += glob(os.path.join(sysconfig.get_path('include'), "*.h"))
27 +
28 +        print "builtin_module_names: ", sys.builtin_module_names
29          for ext in self.extensions[:]:
30              ext.sources = [ find_module_file(filename, moddirlist)
31                              for filename in ext.sources ]
32 @@ -248,10 +254,15 @@ class PyBuildExt(build_ext):
33                  remove_modules.append(line[0])
34              input.close()
35  
36 +        print "remove_modules: ", remove_modules
37 +
38          for ext in self.extensions[:]:
39              if ext.name in remove_modules:
40                  self.extensions.remove(ext)
41  
42 +        for ext in self.extensions[:]:
43 +            print "extensions: ", ext.name
44 +
45          # When you run "make CC=altcc" or something similar, you really want
46          # those environment variables passed into the setup.py phase.  Here's
47          # a small set of useful ones.
48 @@ -1618,13 +1629,13 @@ class PyBuildExt(build_ext):
49  
50  
51          # Platform-specific libraries
52 -        if host_platform == 'linux2':
53 +        if host_platform == 'linux2' or host_platform == 'linux2-arm':
54              # Linux-specific modules
55              exts.append( Extension('linuxaudiodev', ['linuxaudiodev.c']) )
56          else:
57              missing.append('linuxaudiodev')
58  
59 -        if (host_platform in ('linux2', 'freebsd4', 'freebsd5', 'freebsd6',
60 +        if (host_platform in ('linux2','linux2-arm' 'freebsd4', 'freebsd5', 'freebsd6',
61                          'freebsd7', 'freebsd8')
62              or host_platform.startswith("gnukfreebsd")):
63              exts.append( Extension('ossaudiodev', ['ossaudiodev.c']) )
64 @@ -1755,6 +1766,10 @@ class PyBuildExt(build_ext):
65  ##         ext = Extension('xx', ['xxmodule.c'])
66  ##         self.extensions.append(ext)
67  
68 +#        print "missing: ", missing
69 +#        for ext in self.extensions:
70 +#            print "extensions: ", ext.name
71 +
72          return missing
73  
74      def detect_tkinter_explicitly(self):
75 @@ -2229,6 +2244,8 @@ Topic :: Software Development
76  """
77  
78  def main():
79 +    print "sys.path: ", sys.path
80 +    print "cross_compiling: ", cross_compiling
81      # turn off warnings when deprecated modules are imported
82      import warnings
83      warnings.filterwarnings("ignore",category=DeprecationWarning)

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

 1 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']
 2 cross_compiling:  True
 3 build_extensions enter.
 4 extensions:  _struct
 5 extensions:  _ctypes_test
 6 extensions:  array
 7 extensions:  cmath
 8 extensions:  math
 9 extensions:  strop
10 extensions:  time
11 extensions:  datetime
12 extensions:  itertools
13 extensions:  future_builtins
14 extensions:  _random
15 extensions:  _collections
16 extensions:  _bisect
17 extensions:  _heapq
18 extensions:  operator
19 extensions:  _io
20 extensions:  _functools
21 extensions:  _json
22 extensions:  _testcapi
23 extensions:  _hotshot
24 extensions:  _lsprof
25 extensions:  unicodedata
26 extensions:  _locale
27 extensions:  fcntl
28 extensions:  pwd
29 extensions:  grp
30 extensions:  spwd
31 extensions:  select
32 extensions:  parser
33 extensions:  cStringIO
34 extensions:  cPickle
35 extensions:  mmap
36 extensions:  syslog
37 extensions:  audioop
38 extensions:  crypt
39 extensions:  _csv
40 extensions:  _socket
41 extensions:  _sha
42 extensions:  _md5
43 extensions:  _sha256
44 extensions:  _sha512
45 extensions:  termios
46 extensions:  resource
47 extensions:  nis
48 extensions:  binascii
49 extensions:  pyexpat
50 extensions:  _elementtree
51 extensions:  _multibytecodec
52 extensions:  _codecs_kr
53 extensions:  _codecs_jp
54 extensions:  _codecs_cn
55 extensions:  _codecs_tw
56 extensions:  _codecs_hk
57 extensions:  _codecs_iso2022
58 extensions:  _multiprocessing
59 extensions:  linuxaudiodev
60 extensions:  _ctypes
61 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')
62 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']
63 extensions:  _ctypes_test
64 extensions:  datetime
65 extensions:  future_builtins
66 extensions:  _json
67 extensions:  _testcapi
68 extensions:  _hotshot
69 extensions:  _lsprof
70 extensions:  parser
71 extensions:  mmap
72 extensions:  audioop
73 extensions:  crypt
74 extensions:  _csv
75 extensions:  termios
76 extensions:  resource
77 extensions:  nis
78 extensions:  pyexpat
79 extensions:  _elementtree
80 extensions:  _multibytecodec
81 extensions:  _codecs_kr
82 extensions:  _codecs_jp
83 extensions:  _codecs_cn
84 extensions:  _codecs_tw
85 extensions:  _codecs_hk
86 extensions:  _codecs_iso2022
87 extensions:  _multiprocessing
88 extensions:  linuxaudiodev
89 extensions:  _ctypes
90 Python build finished, but the necessary bits to build these modules were not found:
91 _bsddb             _curses            _curses_panel   
92 _sqlite3           _ssl               _tkinter        
93 bsddb185           bz2                dbm             
94 dl                 gdbm               imageop         
95 ossaudiodev        readline           sunaudiodev     
96 zlib                                                  
97 To find the necessary bits, look in setup.py in detect_modules() for the module's name.

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

 1         for ext in self.extensions[:]:
 2             ext.sources = [ find_module_file(filename, moddirlist)
 3                             for filename in ext.sources ]
 4             if ext.depends is not None:
 5                 ext.depends = [find_module_file(filename, moddirlist)
 6                                for filename in ext.depends]
 7             else:
 8                 ext.depends = []
 9             # re-compile extensions if a header file has been changed
10             ext.depends.extend(headers)
11             # platform specific include directories
12             ext.include_dirs.extend(incdirlist)
13             # If a module has already been built statically,
14             # don't build it here
15             if ext.name in sys.builtin_module_names:
16                 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、 解决

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

 1 @@ -233,8 +239,8 @@ class PyBuildExt(build_ext):
 2  
 3              # If a module has already been built statically,
 4              # don't build it here
 5 -            if ext.name in sys.builtin_module_names:
 6 -                self.extensions.remove(ext)
 7 +            #if ext.name in sys.builtin_module_names:
 8 +            #    self.extensions.remove(ext)
 9  
10          # Parse Modules/Setup and Modules/Setup.local to figure out which
11          # modules are turned on in the file.

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

 1 [root@vexpress ]# python /usr/lib/python2.7/test/test___all__.py
 2 test_all (__main__.AllTest) ... BaseHTTPServer
 3 Bastion
 4 CGIHTTPServer
 5 ConfigParser
 6 Cookie
 7 DocXMLRPCServer
 8 HTMLParser
 9 MimeWriter
10 Queue
11 SimpleHTTPServer
12 ... ...
13 'xml.sax.xmlreader', 'xmllib', 'xmlrpclib']
14 Following modules failed to be imported: ['ctypes.wintypes', 'dbhash', 'gzip', 'idlelib.AutoComplete']
15 ok
16 ----------------------------------------------------------------------
17 Ran 1 test in 9.345s
18 OK

可以看到,测试成功了。

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

3、支持sqlite3

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

1 #!/bin/bash
2 export PATH=/home/pengdonglin/src/qemu/aarch32/arm-2014.05/bin:$PATH
3 
4 ../sqlite-autoconf-3170000/configure --host=arm-none-linux-gnueabi \
5     --prefix=`pwd`
6 
7 make -j4
8 make install

然后修改制作ramdisk的脚本:

 1 #!/bin/bash
 2 
 3 sudo rm -rf rootfs
 4 sudo rm -rf tmpfs
 5 sudo rm -rf ramdisk*
 6 
 7 sudo mkdir rootfs
 8 sudo cp ../busybox-1.24.2/_install/*  rootfs/ -raf
 9 
10 sudo mkdir -p rootfs/proc/
11 sudo mkdir -p rootfs/sys/
12 sudo mkdir -p rootfs/tmp/
13 sudo mkdir -p rootfs/root/
14 sudo mkdir -p rootfs/var/
15 sudo mkdir -p rootfs/mnt/
16 
17 sudo cp etc rootfs/ -arf
18 
19 sudo cp -arf ../arm-2014.05/arm-none-linux-gnueabi/libc/lib rootfs/
20 
21 #python
22 sudo mkdir -p rootfs/usr
23 pushd rootfs/usr
24 sudo cp  -raf /home/pengdonglin/qemu/thiry_part/Python/aarch32/bin .
25 sudo cp  -raf /home/pengdonglin/qemu/thiry_part/Python/aarch32/lib .
26 sudo cp  -raf /home/pengdonglin/qemu/thiry_part/Python/aarch32/include .
27 sudo cp  -raf /home/pengdonglin/qemu/thiry_part/Python/aarch32/share .
28 sudo /home/pengdonglin/qemu/aarch32/arm-2014.05/bin/arm-none-linux-gnueabi-strip lib/python*
29 popd
30 
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
36 
37 
38 sudo mkdir -p rootfs/dev/
39 sudo mknod rootfs/dev/tty1 c 4 1
40 sudo mknod rootfs/dev/tty2 c 4 2
41 sudo mknod rootfs/dev/tty3 c 4 3
42 sudo mknod rootfs/dev/tty4 c 4 4
43 sudo mknod rootfs/dev/console c 5 1
44 sudo mknod rootfs/dev/null c 1 3
45 
46 sudo rm -rf rootfs/lib/*.a
47 sudo rm -rf rootfs/lib/*.la
48 sudo ../arm-2014.05/bin/arm-none-linux-gnueabi-strip rootfs/lib/*
49 
50 sudo dd if=/dev/zero of=ramdisk bs=1M count=100
51 sudo mkfs.ext4 -F ramdisk
52 
53 sudo mkdir -p tmpfs
54 sudo mount -t ext4 ramdisk ./tmpfs/  -o loop
55 sudo cp -raf rootfs/*  tmpfs/
56 sudo umount tmpfs
57 
58 sudo gzip --best -c ramdisk > ramdisk.gz
59 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,然后我们可以检查一下:

1 $ls aarch32/build/lib.linux2-arm-2.7/
2 array.so*       _codecs_hk.so*       cPickle.so*       datetime.so*         _heapq.so*         _locale.so*          _multiprocessing.so*  _random.so*   _socket.so*        _sysconfigdata.pyc
3 audioop.so*     _codecs_iso2022.so*  crypt.so*         _elementtree.so*     _hotshot.so*       _lsprof.so*          nis.so*               resource.so*  spwd.so*           syslog.so*
4 binascii.so*    _codecs_jp.so*       cStringIO.so*     fcntl.so*            _io.so*            math.so*             operator.so*          select.so*    _sqlite3.so*       termios.so*
5 _bisect.so*     _codecs_kr.so*       _csv.so*          _functools.so*       itertools.so*      _md5.so*             ossaudiodev.so*       _sha256.so*   strop.so*          _testcapi.so*
6 cmath.so*       _codecs_tw.so*       _ctypes.so*       future_builtins.so*  _json.so*          mmap.so*             parser.so*            _sha512.so*   _struct.so*        time.so*
7 _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如下:

 1 #!/usr/bin/python
 2               
 3 import sqlite3                   
 4                                      
 5 #open database                     
 6 conn = sqlite3.connect('test.db')
 7 print "Opened database successfully";
 8                                      
 9 conn.execute('''CREATE TABLE COMPANY 
10        (ID INT PRIMARY KEY     NOT NULL,
11        NAME           TEXT    NOT NULL, 
12        AGE            INT     NOT NULL, 
13        ADDRESS        CHAR(50),         
14        SALARY         REAL);''')       
15 print "Table created successfully";    
16                                    
17 #insert                            
18 conn.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
19       VALUES (1, 'Paul', 32, 'California', 20000.00 )");        
20                                                                 
21 conn.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
22       VALUES (2, 'Allen', 25, 'Texas', 15000.00 )");            
23                                                                 
24 conn.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
25       VALUES (3, 'Teddy', 23, 'Norway', 20000.00 )");           
26                                                                 
27 conn.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
28       VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 )");        
29                                                                 
30 conn.commit()                                                   
31 print "Records created successfully";                   
32                                      
33 #select                              
34 cursor = conn.execute("SELECT id, name, address, salary  from COMPANY")
35 for row in cursor:                                                     
36    print "ID = ", row[0]                                               
37    print "NAME = ", row[1]                                             
38    print "ADDRESS = ", row[2]
39    print "SALARY = ", row[3], "\n"
40                                   
41 print "Operation done successfully";
42                                     
43 #delect                             
44 conn.execute("DELETE from COMPANY where ID=2;")
45 conn.commit()                                  
46 print "Total number of rows deleted :", conn.total_changes
47                                                           
48 cursor = conn.execute("SELECT id, name, address, salary  from COMPANY")
49 for row in cursor:                                                     
50    print "ID = ", row[0]                                               
51    print "NAME = ", row[1]                                             
52    print "ADDRESS = ", row[2]
53    print "SALARY = ", row[3], "\n"
54                                   
55 print "Operation done successfully";
56                                     
57 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

完。

posted @ 2017-03-21 21:56  摩斯电码  阅读(3585)  评论(0编辑  收藏  举报