• 用C扩展Python2


    参考

    python扩展实现方法--python与c混和编程

    编写Python扩展(Extending Python with C or C++)

    https://docs.python.org/2.7/extending/embedding.html

     

    环境

    主机: ubuntu14.04 64bit

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

     

    工具链: aarch64-linux-gnu-gcc  (gcc version 4.9.1 20140529)

    Python版本: Python-2.7.13

     

    概述

    上面参考列表中的文章已经说的很全了,这里仅作一些补充。分为三个:

    1、交叉编译扩展模块到aarch64上面

    2、编译扩展模块到Qemu模拟的x86_64上面

    3、编译扩展模块到PC(x86_64)上面

    采用的测试模块是Extend_wrap.c,这个在python扩展实现方法--python与c混和编程中有说明,源码如下:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <string.h>
     4 #include <Python.h>
     5 
     6 #define BUFSIZE 10
     7 
     8 int fac(int n) {
     9     if (n < 2)
    10         return 1;
    11     return n * fac(n - 1);
    12 }
    13 
    14 static PyObject * Extest_fac(PyObject *self, PyObject *args) {
    15     int res;//计算结果值
    16     int num;//参数
    17     PyObject* retval;//返回值
    18 
    19     //i表示需要传递进来的参数类型为整型,如果是,就赋值给num,如果不是,返回NULL;
    20     res = PyArg_ParseTuple(args, "i", &num);
    21     if (!res) {
    22         //包装函数返回NULL,就会在Python调用中产生一个TypeError的异常
    23         return NULL;
    24     }
    25     res = fac(num);
    26     //需要把c中计算的结果转成python对象,i代表整数对象类型。
    27     retval = (PyObject *)Py_BuildValue("i", res);
    28     return retval;
    29 }
    30 
    31 char *reverse(char *s) {
    32     register char t;
    33     char *p = s;
    34     char *q = (s + (strlen(s) - 1));
    35     while (p < q) {
    36         t = *p;
    37         *p++ = *q;
    38         *q-- = t;
    39     }
    40     return s;
    41 }
    42 
    43 static PyObject *
    44 Extest_reverse(PyObject *self, PyObject *args) {
    45     char *orignal;
    46     if (!(PyArg_ParseTuple(args, "s", &orignal))) {
    47         return NULL;
    48     }
    49     return (PyObject *)Py_BuildValue("s", reverse(orignal));
    50 }
    51 
    52 static PyObject *
    53 Extest_doppel(PyObject *self, PyObject *args) {
    54     char *orignal;
    55     char *reversed;
    56     PyObject * retval;
    57     if (!(PyArg_ParseTuple(args, "s", &orignal))) {
    58         return NULL;
    59     }
    60     retval = (PyObject *)Py_BuildValue("ss", orignal, reversed=reverse(strdup(orignal)));
    61     free(reversed);
    62     return retval;
    63 }
    64 
    65 static PyMethodDef
    66 ExtestMethods[] = {
    67     {"fac", Extest_fac, METH_VARARGS},
    68     {"doppel", Extest_doppel, METH_VARARGS},
    69     {"reverse", Extest_reverse, METH_VARARGS},
    70     {NULL, NULL},
    71 };
    72 
    73 void initExtest() {
    74     Py_InitModule("Extest", ExtestMethods);
    75 }
    76 
    77 int main() {
    78     char s[BUFSIZE];
    79     printf("4! == %d
    ", fac(4));
    80     printf("8! == %d
    ", fac(8));
    81     printf("12! == %d
    ", fac(12));
    82     strcpy(s, "abcdef");
    83     printf("reversing 'abcdef', we get '%s'
    ", reverse(s));
    84     strcpy(s, "madam");
    85     printf("reversing 'madam', we get '%s'
    ", reverse(s));
    86     return 0;
    87 }

    关于这段代码的解释,请参考python扩展实现方法--python与c混和编程

    正文

    1、交叉编译扩展模块到aarch64上面

    这里介绍两种方法:

    第一种: 将这个文件拷贝到Python2.7.3的Modules目录下面编译

    拷贝:

        cp Extest_wrap.c ../../Python-2.7.13/Modules/

    修改Python-2.7.13/setup.py,添加模块:

     1 diff --git a/setup.py b/setup.py
     2 index 81355c7..5083c3d 100644
     3 --- a/setup.py
     4 +++ b/setup.py
     5 @@ -1743,6 +1743,7 @@ class PyBuildExt(build_ext):
     6                                       '-framework', 'Carbon']) )
     7  
     8  
     9 +        exts.append(Extension('Extest', ['Extest_wrap.c']))
    10          self.extensions.extend(exts)
    11  
    12          # Call the method for detecting whether _tkinter can be compiled

    然后执行aarch64/mk2_make.sh,可以看到build/lib.linux2-aarch64-2.7/下面已经有Extest.so了:

    1 $ls build/lib.linux2-aarch64-2.7/Extest.so -l
    2 -rwxrwxr-x 1 pengdonglin pengdonglin 22121 Mar 22 14:47 build/lib.linux2-aarch64-2.7/Extest.so*

    然后执行aarch64/mk3_install.sh,就会将Extest.so安装到lib/python2.7/lib-dynload/下面。

    最后重新制作ramdisk文件,重启板子,测试Extest.so能否使用:

     1 [root@aarch64 root]# python
     2 Python 2.7.13 (default, Mar 22 2017, 10:39:43) 
     3 [GCC 4.9.1 20140529 (prerelease)] on linux2
     4 Type "help", "copyright", "credits" or "license" for more information.
     5 >>> import Extest
     6 >>> Extest.fac(4)
     7 24
     8 >>> Extest.reverse("abc") 
     9 'cba'
    10 >>> Extest.doppel("abc")
    11 ('abc', 'cba')
    12 >>> 

    第二种: 手动编译

    我们需要指定编译用的库以及头文件的搜索路径即可,下面是编译命令:

     1 #!/bin/bash
     2 export PATH=/home/pengdonglin/src/qemu/aarch64/gcc-linaro-aarch64-linux-gnu-4.9-2014.07_linux/bin:$PATH
     3 
     4 CFLAGS="-I/home/pengdonglin/src/qemu/python_cross_compile/Python2/aarch64/include/python2.7 -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes"
     5 
     6 LDFLAGS="-L/home/pengdonglin/src/qemu/python_cross_compile/Python2/aarch64/lib -lpython2.7 -lpthread -ldl -lutil -lm -Xlinker -export-dynamic"
     7 
     8 aarch64-linux-gnu-gcc -c ../Extest_wrap.c ${CFLAGS} -o Extest.o
     9 
    10 aarch64-linux-gnu-gcc --shared Extest.o ${LDFLAGS} -o Extest.so

    其中CFLAGS和LDFLAGS的值可以用下面的命令获得

    1 $/usr/local/bin/python2-config --cflags
    2 -I/usr/local/include/python2.7 -I/usr/local/include/python2.7 -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
    3 
    4 $/usr/local/bin/python2-config --ldflags
    5 -L/usr/local/lib/python2.7/config -lpython2.7 -lpthread -ldl -lutil -lm -Xlinker -export-dynamic

    然后编译,就会在当前目录下面生成一个Extest.so,然后拷贝到板子的/usr/lib/python2.7/site-packages/下面,这个目录下存放的是一些第三方的扩展模块,而/usr/lib/python2.7/lib-dynload/存放的一般是内建模块。

    或者可以写成Makefile:

    CFLAGS = -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
    CFLAGS += -fPIC -I/home/pengdonglin/qemu/thiry_part/Python2/aarch32/include/python2.7
    CC = arm-none-linux-gnueabi-gcc
    
    all:Extest.so
    
    Extest.o: Extest_wrap.c
        $(CC) $(CFLAGS) -c $^ -o $@
    
    Extest.so: Extest.o
        $(CC) -pthread -shared $^ -o $@
        cp $@ /home/pengdonglin/qemu/thiry_part/Python2/aarch32/lib/python2.7/site-packages/
    
    clean:
        $(RM) *.o *.so
    
    .PHONY: clean all

    将Extest_wrap.c跟Makefile放到统一目录下执行make命令即可

    2、编译扩展模块到Qemu模拟的x86_64上面

    这里也有三种方法:

    第一种:将Extend_wrap.c拷贝到Python源码的Modules目录下,这个前面说过,不再重复

    第二种:手动编译,编译命令如下

    1 #!/bin/bash
    2 CFLAGS="-I/home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/include/python2.7 -fPIC"
    3 
    4 LDFLAGS="-L/home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/lib -fPIC"
    5 
    6 gcc -c ../Extest_wrap.c ${CFLAGS} -o Extest.o
    7 
    8 gcc --shared Extest.o ${LDFLAGS} -o Extest.so

    编译完成后,将Extest.so拷贝到板子上面的相应目录下即可(如/usr/lib/python2.7/site-packages

    第三种:手动编写setup.py

    setup.py:

    1 #!/usr/bin/env python
    2 
    3 from distutils.core import setup, Extension
    4 
    5 MOD = 'Extest_x86_64'
    6 setup(name=MOD, ext_modules=[Extension(MOD, sources=['Extest_wrap.c'])])

    这里模块名是Extest_x86_64,同时需要注意的是需要将setup.py跟Extest_wrap.c放到同一个目录下面。

    编译:

    /home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/bin/python ./setup.py build

    从log看执行的其实就是下面两条命令:

    gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/include/python2.7 -c Extest_wrap.c -o build/temp.linux-x86_64-2.7/Extest_wrap.o
    
    creating build/lib.linux-x86_64-2.7
    gcc -pthread -shared build/temp.linux-x86_64-2.7/Extest_wrap.o -o build/lib.linux-x86_64-2.7/Extest_x86_64.so

    安装:

    /home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/bin/python ./setup.py  install

    从log看,Extest_x86_64.so会被安装到/home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/lib/python2.7/site-packages下面

    1 running install
    2 running build
    3 running build_ext
    4 running install_lib
    5 copying build/lib.linux-x86_64-2.7/Extest_x86_64.so -> /home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/lib/python2.7/site-packages
    6 running install_egg_info
    7 Writing /home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/lib/python2.7/site-packages/Extest_x86_64-0.0.0-py2.7.egg-info

    然后从新制作ramdisk就可以了

    3、编译扩展模块到PC(x86_64)上面

    在操作之前PC上面应该用Python源码编译安装一次,方法很简单:

    #!/bin/bash
    ../Python-2.7.13/configure
    make -j8
    sudo make install

    默认会被安装到/usr/local下面

    方法一: 将Extend_wrap.c拷贝到Python源码的Modules目录下,这个前面说过,不再重复

    方法二: 手动编译,编译命令如下

    1 #!/bin/bash
    2 CFLAGS="-I/usr/local/include/python2.7 -fPIC"
    3 LDFLAGS="-L/usr/local/lib -fPIC"
    4 gcc -c ../Extest_wrap.c ${CFLAGS} -o Extest.o
    5 gcc --shared Extest.o ${LDFLAGS} -o Extest.so

    将生成的Extest.so拷贝到/usr/local/lib/python2.7/site-packages/即可

    测试:

    1 $sudo cp Extest.so /usr/local/lib/python2.7/site-packages/
    2 $/usr/local/bin/python
    3 Python 2.7.13 (default, Mar 22 2017, 13:18:43) 
    4 [GCC 4.8.4] on linux2
    5 Type "help", "copyright", "credits" or "license" for more information.
    6 >>> import Extest
    7 >>> Extest.reverse("peng")
    8 'gnep'

    方法三: 编写setup.py

    setup.py:

    1 $cat setup.py 
    2 #!/usr/bin/env python
    3 from distutils.core import setup, Extension
    4 MOD = 'Extest'
    5 setup(name=MOD, ext_modules=[Extension(MOD, sources=['Extest_wrap.c'])])

    编译:

    /usr/local/bin/python ./setup.py build

    从log看,执行的是下面的命令:

    1 running build
    2 running build_ext
    3 building 'Extest' extension
    4 creating build
    5 creating build/temp.linux-x86_64-2.7
    6 gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/usr/local/include/python2.7 -c Extest_wrap.c -o build/temp.linux-x86_64-2.7/Extest_wrap.o
    7 gcc -pthread -shared build/temp.linux-x86_64-2.7/Extest_wrap.o -o build/lib.linux-x86_64-2.7/Extest.so

    安装:

    sudo /usr/local/bin/python ./setup.py install
    从log看,Extest.so被安装到了/usr/local/lib/python2.7/site-packages下面
    1 running install
    2 running build
    3 running build_ext
    4 running install_lib
    5 copying build/lib.linux-x86_64-2.7/Extest.so -> /usr/local/lib/python2.7/site-packages
    6 running install_egg_info
    7 Removing /usr/local/lib/python2.7/site-packages/Extest-0.0.0-py2.7.egg-info
    8 Writing /usr/local/lib/python2.7/site-packages/Extest-0.0.0-py2.7.egg-info

  • 相关阅读:
    用原生js实现ajax、jsonp
    $.ajax()方法详解
    用原生js实现ajax
    escape()、encodeURI()、encodeURIComponent()区别详解
    stylus项目知识点
    vue之给a标签赋值
    iOS—网络实用技术OC篇&网络爬虫-使用java语言抓取网络数据
    iOS开发——高级语法篇&继承、实现、依赖、关联、聚合、组合的联系与区别
    iOS-性能优化4
    iOS-性能优化3
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/6601431.html
Copyright © 2020-2023  润新知