Python源文件编译为动态链接库文件

2019-10-23  本文已影响0人  Bug2Coder

Windows:pyd文件
Linux:so文件

import subprocess, re
import sys, os, shutil, time
from distutils.core import setup
from Cython.Build import cythonize
import platform

starttime = time.time()  
currdir = os.path.abspath('.')  # 当前目录
parentpath = sys.argv[1] if len(sys.argv) > 1 else ""
setupfile = os.path.join(os.path.abspath('.'), __file__)
build_dir = currdir + "/build"  # 构建目录
build_tmp_dir = build_dir + "/temp" # 临时目录


def getpy(basepath=os.path.abspath('.'), parentpath='', name='', excepts=(), copyOther=False, delC=False):
    """
    获取py文件的路径
    :param basepath: 根路径
    :param parentpath: 父路径
    :param name: 文件/夹
    :param excepts: 排除文件
    :param copy: 是否copy其他文件
    :return: py文件的迭代器
    """
    fullpath = os.path.join(basepath, parentpath, name)
    for fname in os.listdir(fullpath):
        ffile = os.path.join(fullpath, fname)
        if os.path.isdir(ffile) and fname != build_dir and not fname.startswith('.'):
            for f in getpy(basepath, os.path.join(parentpath, name), fname, excepts, copyOther, delC):
                yield f
        elif os.path.isfile(ffile):
            ext = os.path.splitext(fname)[1]
            if ext == ".c":
                if delC and os.stat(ffile).st_mtime > starttime:
                    os.remove(ffile)
            elif os.path.realpath(ffile) != os.path.realpath(excepts) and os.path.splitext(fname)[1] not in (
                    '.pyc', '.pyx'):
                if os.path.splitext(fname)[1] in ('.py', '.pyx') and not fname.startswith('__'):
                    yield os.path.join(parentpath, name, fname)
                elif copyOther:
                    dstdir = os.path.join(basepath, build_dir, parentpath, name)
                    if not os.path.isdir(dstdir):
                        os.makedirs(dstdir)
                    shutil.copyfile(ffile, os.path.join(dstdir, fname))
        else:
            pass


def getc(basepath=os.path.abspath('.'), parentpath='', name=''):
    """
    获取c文件的路径
    :param basepath: 根路径
    :param parentpath: 父路径
    :param name: 文件/夹
    :return: py文件的迭代器
    """
    fullpath = os.path.join(basepath, parentpath, name)  # 本级目录
    for fname in os.listdir(fullpath):  # fname 所有文件
        ffile = os.path.join(fullpath, fname)  # 所有文件及绝对路径
        if os.path.isdir(ffile):  # 获得不是build目录和.开头的目录
            for f in getc(basepath, os.path.join(parentpath, name), fname):
                yield f
        elif os.path.isfile(ffile):  # 同级的文件
            yield os.path.join(basepath, parentpath, name, fname)
        else:
            pass


def GetDefaultPath():
    """
    获取默认的python路径,目前只使用与windows
    """
    path = os.environ['path']
    lst = path.split(';')
    for dir in lst:
        try:
            files = os.listdir(dir)
            for file in files:
                if file == "python.exe":
                    return dir
        except:
            pass


def chkCython(path):
    '''
    检查是否安装了cython
    '''
    path += "\Scripts"
    files = os.listdir(path)
    for file in files:
        if file == "cython.exe":
            return True
    return False


def getLibName(path):
    """
    得到python的库名(如 python36)
    """
    path += '\libs'
    for fp in os.listdir(path):
        rtn = re.match(r'python...lib', fp)
        if rtn is not None:
            return fp[:len(fp) - 4]


def check_system():
    system_name = platform.system()
    return system_name


def packPydFiles():
    """
    打包pyd文件
    """
    sys_name = check_system()
    module_list = list(getpy(basepath=currdir, parentpath=parentpath, excepts=(setupfile)))
    if sys_name == "Windows":
        pythonPath = GetDefaultPath()
        res = (cythonize(module_list, build_dir=build_dir))
        cfile_list = list(getc(basepath=build_dir))
        if chkCython(pythonPath):
            for i in cfile_list:
                str_c = i
                str_pyd = os.path.splitext(i)[0] + '.pyd'
                libname = getLibName(pythonPath)
                cmd = 'gcc {} -o {} -shared -DMS_WIN64 -I "{}\include" -L "{}\libs" -l {}'.format(str_c, str_pyd,
                                                                                                  pythonPath,
                                                                                                  pythonPath,
                                                                                                  libname)
                rtn2 = subprocess.run(cmd, stdin=subprocess.PIPE,
                                      stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
                os.remove(str_c)
        else:
            print('请安装cython模块')
    elif sys_name == "Linux":
        try:
            setup(ext_modules=cythonize(module_list, build_dir=build_dir),
                  script_args=["build_ext", "-b", build_dir, "-t", build_tmp_dir])
        except Exception as ex:
            print("error:", ex)

        module_list = list(getpy(basepath=currdir, parentpath=parentpath, excepts=(setupfile), delC=True))
        if os.path.exists(build_tmp_dir):
            shutil.rmtree(build_tmp_dir)


packPydFiles()
上一篇下一篇

猜你喜欢

热点阅读