python | setup.py里有什么?

setup.py里有什么?

C/C++扩展总结

对于C或C++ 代码,当我们在编译的时候,需要指定 包含的头文件目录,源代码目录,引用的库文件名称,引用的库文件的目录,这样编译器才能找到 我们自己编写的头文件及源文件的对应关系,主要是其中函数的对应关系,也能找到我们引用的外部库的头文件和库的对应符号关系。说白了,库就是打包后的源文件。如果没有指定某些库的库目录,编译器会默认从系统目录下查找。

gcc/g++的编译参数:

-I (i 的大写):指定头文件的所在的目录,可以使用相对路径。

-L :表示要链接的库所在的目录。 -L/usr/lib 表示要连接的库在/usr/lib下。一般情况下,编译器会自动搜索 /usr/lib 和 /usr/local/lib 目录,可以不用指明,自己定义的库需要格外指定路径;

-L. :表示要链接的库在当前目录

-l (L的小写):表示需要链接库的名称。注意不是库文件名称,Linux下的库文件有一个约定,全部以lib开头,因此可以省去lib,比如库文件为 librandy.so,那么库名称为randy;

-shared :指定生成动态链接库;

-fPIC: 表示编译为位置独立的代码,表示编译后的代码是位置不相关的,其他程序都可以独立使用。

-static : 表示指定使用静态链接库

// -I 参数指定头文件搜索目录
gcc/g++ hello.c -I /home/randy/include -o hello
    
// -L 参数指定库文件搜索路径, -l 指定库名称
gcc hello.c -L /home/randy/lib -l mylib -o hello

// -static 强制使用静态链接库
gcc hello.c -L /home/randy/lib -static -l mylib -o hello
    
gcc -o hello main.c -static -L. –lhellos

Windows Visual Studio

VC++目录:

包含目录:寻找#include中的randyx.h的搜索目录

库目录:寻找.lib文件的搜索目录

C/C++:

常规->附加包含目录:寻找#include中的randyx.h的搜索目录(每一项对应一个文件夹randyX,文件夹中包含了编译时所需的头文件,使用时直接#include即可)

链接器:

常规->附加库目录:寻找.lib文件的搜索目录

输入->附加依赖项:lib库(C++的库会把函数、类的声明放在*.h中,实现放在*.cpp或*.cc中。编译之后,.cpp,.cc,*.c会被打包成一个.lib文件,这样可以保护源代码)

在这里插入图片描述

Cmake

在编写CMakeLists.txt 文件时,也是同样要指明上述路径及文件名称:

  • include_directories : 添加路径到头文件的搜索路径

    include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
    include_directories(${PROJECT_SOURCE_DIR}/testFunc/inc
    
  • add_executable : 利用源码文件生成目标可执行程序

    add_executable(<name> [WIN32] [MACOSX_BUNDLE]
                   [EXCLUDE_FROM_ALL]
                   source1 [source2 ...])
    
  • link_libraries : 将库链接到以后添加的所有目标

    link_libraries([item1 [item2 [...]]] [[debug|optimized|general] <item>] ...)
    
  • add_library : 生成动态库或静态库

    add_library(<name> [STATIC | SHARED | MODULE] [source1] [source2 ...])
    add_library(<指定库的名字> [STATIC静态 | SHARED动态 | MODULE] [source1源文件] [source2源文件 ...])
    
    add_library(${PROJECT_NAME} SHARED
      ${CMAKE_CURRENT_SOURCE_DIR}/source/
    )
    
  • find_library :在指定路径下查找库,并把库的绝对路径存放到变量里

    find_library(变量名称 库名称  提示 路径 ${PROJECT_SOURCE_DIR}/testFunc/lib)
     find_library(CUDNN_STATIC_LIBRARY NAMES ${CUDNN_STATIC_LIB_NAME}
    
  • target_link_libraries : 把目标文件与库文件进行链接

    target_link_libraries(${PROJECT_NAME} ${catkin_LIBRARIES} )
    

setup.py C/C++扩展模块

setup.py 添加C或C++的扩展模块的时候,同样需要制定 源文件目录 sources 、 头文件目录 include_dirs 、库名称 libraries 及 库目录 library_dirs。

from distutils.core import setup, Extension

module1 = Extension('demo',
                    define_macros = [('MAJOR_VERSION', '1'),
                                     ('MINOR_VERSION', '0')],
                    include_dirs = ['/usr/local/include'],
                    libraries = ['tcl83'],
                    library_dirs = ['/usr/local/lib'],
                    sources = ['demo.c'])

setup (name = 'PackageName',
       version = '1.0',
       description = 'This is a demo package',
       author = 'Randy',
       author_email = 'randy@jeff',
       url = 'https://docs.python.org/extending/building',
       long_description = '''
This is really just a demo package.
''',
       ext_modules = [module1])

进行 python setup.py develop 或者 python setup.py install的时候,python 会调用 g++ 进行编译,将对应参数传递过去即可,本质上是一样的。

g++ -pthread -B /home/randy/anaconda3/envs/randy-v1.0/compiler_compat -Wl,--sysroot=/ -pthread -shared -B /home/randy/anaconda3/envs/randy-v1.0/compiler_compat -L/home/randy/anaconda3/envs/randy-v1.0/lib -Wl,-rpath=/home/randy/anaconda3/envs/randy-v1.0/lib -Wl,--no-as-needed -Wl,--sysroot=/ /home/randy/codes/lidarops/build/temp.linux-x86_64-cpython-38/lidarops/rslabel/src/RSLabelGenerator.o -L/home/randy/anaconda3/envs/randy-v1.0/lib/python3.8/site-packages/torch/lib -L/home/randy/anaconda3/envs/randy-v1.0/lib/python3.8/site-packages/torch/lib -L/home/randy/anaconda3/envs/randy-v1.0/lib/python3.8/site-packages/torch/lib -lc10 -ltorch -ltorch_cpu -ltorch_python -lc10 -ltorch -ltorch_cpu -ltorch_python -lc10 -ltorch -ltorch_cpu -ltorch_python -o build/lib.linux-x86_64-cpython-38/lidarops/rslabel/label_generator.cpython-38-x86_64-linux-gnu.so

为什么需要分发打包?

平常我们习惯了使用 pip 来安装一些第三方模块,有点像我们在 windows 系统上安装 exe 程序,方便后面直接点击快捷键使用一样,也像 linux 系统里面,我们通过 apt-get install 各类软件包一样,这些 exe 程序或者安装的软件包,都是别人替我们封装好的程序或者库,封装的过程就是 打包

打包,就是将源代码或者库文件进行封装,安装后释放到指定位置,这样使用者可以即装即用,直接引用接口或者调用程序即可,不用关心其具体的实现,同时也有利于开发者封装自己的源代码,保护源码。

Distutils

distutils (distribute utils)是 Python 的一个标准库,即分发工具,所有后续的打包工具,全部都是基于它进行开发的。

Distutils 用起来非常简单,对于模块开发者或安装第三方模块的用户/管理员均是如此。开发者的责任(当然还有编写可靠、良好文档和经过良好测试的代码!)就是:

  • 编写一个设置脚本 (setup.py by convention)
  • (可选)编写设置脚本的配置文件
  • 创建源码的发行版
  • (可选)创建一个或多个编译好(二进制)的发行版

一个简单的例子

setup 脚本通常很简单,尽管是用 Python 编写的,它能干的事情没有限制。

如果只想发布一个名为 foo 的模块,位于 foo.py 文件中,那么 setup 脚本可以如此简单:

from distutils.core import setup
setup(name='foo',
      version='1.0',
      py_modules=['foo'],
      )

注意:

  • 提供给 Distutils 的大部分信息将作为关键字参数发给 setup() 函数。
  • 这些关键字参数分为两类:包的元数据(名称、版本号)和包中内容的描述信息(本例中是纯 Python 模块的列表)。
  • 模块由模块名指定,而不是文件名(包和扩展也是如此)。
  • 建议多提供一些元数据,特别是开发者姓名、电子邮件地址和项目的URL。

为该模块创建一个源码发布版本:

python setup.py sdist

sdist 将创建一个归档文件(例如在 Unix 中为 tarball,在 Windows 中为 ZIP 文件),其中包含你的配置脚本 setup.py 以及你的模块 foo.py。 此归档文件将被命名为 foo-1.0.tar.gz (或 .zip),并将解包到目录 foo-1.0 当中。

如果最终用户希望安装 foo 模块,只需下载 foo-1.0.tar.gz (或 .zip )并解压,进入 foo-1.0 目录运行:

python setup.py install

这会把 foo.py 复制到 Python 安装环境的第三方模块目录中。

如果想让用户真正轻松使用,你可以为他们创建一个或多个内置发行版。例如,如果您在 Windows 计算机上运行,并且想让其他 Windows 用户轻松使用,则可以使用 bdist_wininst 命令创建可执行安装程序(最适合此平台的内置发行版类型)。例如:

python setup.py bdist_wininst

将在当前目录中创建一个可执行安装程序 foo-1.0.win32.exe。

linux 系统可以使用下述命令,将源码编译成 .whl 文件:

python setup.py bdist_wheel

.whl文件就像压缩包一样,可以点击打开,如果源文件包含python 代码,则python 代码仍然可见源码,而 C/C++ 文件则编译成 so 库文件

其他有用的内置分发格式是 RPM,可由 bdist_rpm 、Solaris pkgtool(:command:bdist_pkgtool)和 HP-UX swinstall(:command:bdist_sdux)实现。比如,以下命令将创建一个名为 foo-1.0.noarch.rpm 的RPM文件:

python setup.py bdist_rpm

bdist_rpm 命令用到了 rpm 可执行文件,因此必须运行在基于 RPM 的系统中,如 Red Hat Linux 、 SuSE Linux 或 Mandrake Linux)。

可以随时运行以下命令,以便了解当前可用的分发格式:

python setup.py bdist --help-formats

List of available distribution formats:
  --formats=rpm    RPM distribution
  --formats=gztar  gzip'ed tar file
  --formats=bztar  bzip2'ed tar file
  --formats=xztar  xz'ed tar file
  --formats=ztar   compressed tar file
  --formats=tar    tar file
  --formats=zip    ZIP file
  --formats=egg    Python .egg file

通用的 Python 术语

  • module

    实现 Python 代码重用的基本单位:可被其他代码导入的一段代码。有三种类型的模块与本文有关:纯 Python 模块、扩展模块和包。

  • 纯 Python 模块

    用 Python 编写的模块,包含在某 .py 文件中(可能还会有相关的 .pyc 文件)。有时被称为 “纯模块”。

  • extension module – 扩展模块

    用低级语言编写的 Python 模块。Python 用 C/C++ ,而 Jython 则用Java。

    通常包含在一个可动态加载的预编译文件中,比如 Unix 中的 Python 扩展是一个共享对象(.so)文件,Windows 中的 Python 扩展则是一个 DLL (扩展名为 .pyd ),而 Jython 的扩展是个 Java class 文件。(注意,目前,Distutils 只处理 Python 的 C/C++ 扩展。)

  • package(包)

    包含其他模块的模块;

    通常位于文件系统的某个目录中,区别于其他目录的标记就是存在一个 __init__.py 文件,如

  packages=find_packages(exclude=("configs", "tests",))

find_packages(exclude=("configs", "tests",))是递归地包含当前目录下除了configs和tests外所有文件夹的包。

  • root package (根包)

    包的层次结构的根。(其并非一个真正的包,因为没有 __init__.py 文件。但总得给它起个名字)。 绝大多数标准库都在根包中,还有许多不属于任何大型模块的小型、独立的第三方模块。与普通的包不同,根包中的模块可能会在很多目录中出现:事实上,sys.path 列出的每个目录都会为根包提供模块。

使用 Setuptools 构建和分发软件包

Setuptools 是 Python distutils 的增强功能集合,可让开发人员更轻松地构建和分发 Python 包,尤其是那些依赖于其他包的包。

使用 setuptools 构建和分发的包在用户看来就像基于 distutils 的普通 Python 包一样。

源码包与二进制包

  1. 以源码包的方式发布

    源码包安装的过程,是先解压,再编译,最后才安装,可以跨平台。本质是一个压缩包,其常见的格式有:

    格式 后缀
    zip .zip
    gztar .tar.gz
    bztar .tar.bz2
    ztar .tar.Z
    tar .tar
  2. 以二进制包形式发布

二进制包的安装过程省去了编译的过程,直接进行解压安装,所以安装速度较源码包来说更快。

由于不同平台的编译出来的包无法通用,所以在发布时,需事先编译好多个平台的包。

二进制包的常见格式有:

格式 后缀
egg egg
wheel .whl

eggs VS wheels

Wheel 的出现是为了替代 Egg,实际上是一个zip包,其现在被认为是 Python 的二进制包的标准格式。

Wheel 和 Egg 的主要区别:

  • Wheel 有一个官方的 PEP427 来定义,而 Egg 没有 PEP 定义
  • Wheel 是一种分发格式,即打包格式。而 Egg 既是一种分发格式,也是一种运行时安装的格式(如果保持压缩状态),并且是可以被直接 import
  • Wheel 文件不会包含 .pyc 文件。因此,当发行版仅包含 Python 文件(即没有编译的扩展)并且与 Python 2 和 3 兼容时,wheel 可能是“通用”的,类似于 sdist。
  • Wheel 使用和 PEP376 兼容的 .dist-info 目录,而 Egg 使用 .egg-info 目录
  • Wheel 有着更丰富的命名规则。单个 wheel 存档可以指示它与许多 Python 语言版本和实现、ABI 和系统架构的兼容性。
  • Wheel 已进行版本控制。每个 Wheel 文件都包含 wheel 规范的版本和打包它的实现。
  • Wheel 在内部被 sysconfig 路径类型进行组织,因此可以更轻松地转换为其他格式。
  • Egg其实是对源文件的一种引用,文件内是源码的地址,本身不包含源码。
(base) qiancj@Randy-HP-ZBook
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值