博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python深入:Distutils发布Python模块--转载
阅读量:5235 次
发布时间:2019-06-14

本文共 19119 字,大约阅读时间需要 63 分钟。

https://blog.csdn.net/gqtcgq/article/details/49255995

Distutils可以用来在Python环境中构建和安装额外的模块。新的模块可以是纯Python的,也可以是用C/C++写的扩展模块,或者可以是Python包,包中包含了由C和Python编写的模块。

 

一:Distutils简介

1.1概念和术语

         对于模块开发者以及需要安装模块的使用者来说,Distutils的使用都很简单,作为一个开发者,除了编写源码之外,还需要:

编写setup脚本(一般是setup.py);

编写一个setup配置文件(可选);

创建一个源码发布;

创建一个或多个构建(二进制)发布(可选);

 

         有些模块开发者在开发时不会考虑多个平台发布,所以就有了packagers的角色,它们从模块开发者那取得源码发布,然后在多个平台上面进行构建,并发布多个平台的构建版本。

 

1.2简单例子

         由python编写的setup脚本一般都非常简单。作为autoconf类型的配置脚本,setup脚本可以在构建和安装模块发布时运行多次。

         比如,如果需要发布一个叫做foo的模块,它包含在一个文件foo.py,那setup脚本可以这样写:

 

[python]   
 
  1. from distutils.core import setup  
  2. setup(name='foo',  
  3.        version='1.0',  
  4.        py_modules=['foo'],  
  5.       )  

 

         setup函数的参数表示提供给Distutils的信息,这些参数分为两类:包的元数据(包名、版本号)以及包的信息(本例中是一个Python模块的列表);模块由模块名表示,而不是文件名(对于包和扩展而言也是这样);建议可以提供更多的元数据,比如你的名字,email地址和项目的URL地址。

 

         编写好setup.py之后,就可以创建该模块的源码发布了:

 

[plain]   
 
  1. python setup.py sdist  

 

        对于Windows而言,命令是: 

 

[plain]   
 
  1. setup.py sdist  

 

        sdist命令会创建一个archive 文件(比如Unix上的tar文件,Windows上的zip文件),它包含setup.py, foo.py。该archive文件命名为foo-1.0.tar.gz(zip),解压之后的目录名是foo-1.0。

 

         如果一个用户希望安装foo模块,他只需要下载foo-1.0.tar.gz,解压,进入foo-1.0目录,然后运行:

 

[plain]   
 
  1. python setup.py install  

 

         该命令最终会将foo.py复制到Python环境存放第三方模块的目录中。在linux环境下,运行该命令的输出是:

 

[plain]   
 
  1. # python setup.py install  
  2. running install  
  3. running build  
  4. running build_py  
  5. creating build  
  6. creating build/lib  
  7. copying foo.py -> build/lib  
  8. running install_lib  
  9. copying build/lib/foo.py -> /usr/lib/python2.7/site-packages  
  10. byte-compiling /usr/lib/python2.7/site-packages/foo.py to foo.pyc  
  11. running install_egg_info  
  12. Writing /usr/lib/python2.7/site-packages/foo-1.0-py2.7.egg-info  

 

 

         该命令生成的文件是:

/usr/lib/python2.7/site-packages/foo-1.0-py2.7.egg-info

/usr/lib/python2.7/site-packages/foo.py

/usr/lib/python2.7/site-packages/foo.pyc

 

         这个简单的例子展示了Distutils的基本概念。第一,开发者和安装者有同样的用户接口,也就是setup脚本,但他们使用的Distutils命令不同,sdist命令几乎只有开发者使用,而install对于安装者更常用。

 

         如果希望使用者的使用尽可能的简单,则可以创建多个构建发布。比如,如果在Windows中,可以使用bdist_wininst命令创建一个exe安装文件,下面的命令会在当前目录中创建foo-1.0.win32.exe文件:

 

[plain]   
 
  1. python setup.py bdist_wininst  

 

 

         其他的构建发布有RPM(由bdist_rpm命令实现),Solaris pkgtool(bdist_pkgtool),以及HP-UX swinstall (bdist_sdux)。

         比如,下面的命令将会创建RPM文件foo-1.0.noarch.rpm(bdist_rpm命令必须运行于基于RPM的系统,比如Red Hat Linux, SuSE Linux, Mandrake Linux):

 

[plain]   
 
  1. python setup.py bdist_rpm  

 

 

       可以通过下面的命令得到当前支持的发布格式:

 

[plain]   
 
  1. python setup.py bdist --help-formats  

 

 

1.3基本术语:

        模块(module):       Python中可复用的基本代码单元,可由其他代码import的一块代码,这里我们只关注三种类型的模块:纯python模块,扩展模块和包。

        纯python模块(pure Python module):      由python编写的模块,包含在单独的py文件中(或者是pyc/pyo文件)。

        扩展模块(extension module):由实现Python的底层语言编写的模块(C/C++ for Python, Java for Jython)。通常包含在单独的动态加载文件中,比如Unix中的so文件,windows中的DLL文件,或者是Jython扩展的java类文件。(注意,目前为止Distutils只能处理Python的C/C++扩展)

        包(package):包是含其他模块的模块,经常由包含__init__.py文件的目录发布。

        Root包(root package):       包层次关系中的根(它不是真正的包,因为它不包含__init__.py文件)。

 

1.4 Distutils术语

        模块发布(module distribution):一些Python模块的集合,它们将会被一起安装。一些常见的模块发布有Numeric Python,PyXML,PIL,mxBase。

        纯模块发布:一个只包含纯python模块和包的模块发布。

        非纯模块发布:至少包含一个扩展模块的模块发布。

        发布根:源码树的根目录;setup.py所在的目录。

 

二:编写setup脚本

       setup脚本是使用Distutils构建、发布和安装模块的核心。setup脚本的作用是向Distutils描述发布模块的信息。从上面那个简单的例子中可知,setup脚本主要是调用setup函数,而且模块开发者向Distutils提供的模块信息多数是由setup函数的关键字参数提供的。

       下面是一个更高级一些的例子:Distutils模块本身的setup脚本:

 

[python]   
 
  1. setup(name='Distutils',  
  2.       version='1.0',  
  3.       description='Python Distribution Utilities',  
  4.       author='Greg Ward',  
  5.       author_email='gward@python.net',  
  6.       url='https://www.python.org/sigs/distutils-sig/',  
  7.       packages=['distutils', 'distutils.command'],  
  8.      )  

 

 

       上面这个脚本有更多的元数据,列出的是两个包(packages),而不是列出每个模块。因为Distutils包含多个模块,这些模块分成了两个包;如果列出所有模块的话则是冗长且难以维护的。

       注意,在setup脚本中的路径必须以Unix形式来书写,也就是由”/”分割的。Distutils会在使用这些路径之前,将这种表示方法转换为适合当前平台的格式。

      

2.1列出整个包

         Setup函数的packages参数是一个列表,其中包含了Distutils需要处理(构建、发布、安装等)的所有包。要实现此目的,那么包名和目录名必须能够相互对应,比如包名是distutils,则意味着在发布的根目录(setup脚本所在目录)下存在distutils子目录;再比如在setup脚本中packages = ['foo'],意味着要在setup脚本所在目录下存在相应的foo目录和foo/__init__.py文件。

       比如如果setup脚本内容如下:

 

[python]   
 
  1. setup(name='foo',  
  2.        version='1.0',  
  3.        packages = ['foo']  
  4.      )  

 

       而setup脚本所在目录并没有foo目录(只有一个setup.py脚本),则在执行python setup.py bdist命令时,会打印如下错误:

 

[plain]   
 
  1. error: package directory 'foo' does not exist  

 

        如果创建了foo目录,但是没有foo/__init__.py文件,则Distutils会产生下面的警告,但是仍会处理该包:

 

[plain]   
 
  1. package init file 'foo/__init__.py' not found (or not a regular file)  

      

 

        可以使用package_dir选项来改变这种默认的对应规则。package_dir是个字典,其中的key是要安装的包名,如果为空,则表明是root package,value就是该包(key)对应的源码树的目录。

        比如如果setup.py内容如下:

 

[python]   
 
  1. setup(name='foo',  
  2.       version='1.0',  
  3.       package_dir = {
    '':'lib'},  
  4.       packages = ['foo']  
  5.      )  

 

 

        则必须在目录中存在lib子目录,lib/foo子目录,以及文件lib/foo/__init__.py。所以源码树如下:

 

[plain]   
 
  1. setup.py  
  2. lib/  
  3.     foo/  
  4.         __init__.py  
  5.         foo.py  

 

        最后生成的文件是:

\usr\local\lib\python2.7\dist-packages\ foo-1.0.egg-info

\usr\local\lib\python2.7\dist-packages\foo\__init__.py

\usr\local\lib\python2.7\dist-packages\foo\__init__.pyc

\usr\local\lib\python2.7\dist-packages\foo\foo.py

\usr\local\lib\python2.7\dist-packages\foo\foo.pyc

 

       另外一个例子,foo包对应lib目录,所以,foo.bar包就对应着lib/bar子目录。所以如果在setup.py中这么写:

 

[python]   
 
  1. package_dir = {
    'foo':'lib'},  
  2. packages = ['foo',’foo.bar’]  

 

 

       则必须存在lib/__init__.py,  lib/bar/__init__.py文件。源码树如下:

 

[plain]   
 
  1. setup.py  
  2. lib/  
  3.     __init__.py  
  4.     foo.py  
  5.     bar/  
  6.         __init__.py  
  7.         bar.py  

 

       最后生成的文件是:

\usr\local\lib\python2.7\dist-packages\ foo-1.0.egg-info

\usr\local\lib\python2.7\dist-packages\foo\__init__.py

\usr\local\lib\python2.7\dist-packages\foo\__init__.pyc

\usr\local\lib\python2.7\dist-packages\foo\foo.py

\usr\local\lib\python2.7\dist-packages\foo\foo.pyc

\usr\local\lib\python2.7\dist-packages\foo\bar\__init__.py

\usr\local\lib\python2.7\dist-packages\foo\bar\__init__.pyc

\usr\local\lib\python2.7\dist-packages\foo\bar\bar.py

\usr\local\lib\python2.7\dist-packages\foo\bar\bar.pyc

 

2.2列出单独的模块

       如果发布中仅包含较少的模块,你可能更喜欢列出所有模块,而不是列出包,特别是在root package中存在单一模块的情况(或者根本就没有包)。可以使用py_modules参数,比如下面的例子:

 

[python]   
 
  1. setup(name='foo',  
  2.       version='1.0',  
  3.       py_modules = ['mod1', 'pkg.mod2']  
  4.      )  

 

 

         它描述了两个模块,一个在root package中,另一个在pkg包中。根据默认的包/目录对应规则,这两个模块存在于文件mod1.py和pkg/mod2.py中,并且要存在pkg/__init__.py文件(不存在的话,会产生报警:package init file 'pkg/__init__.py' not found (or not a regular file))。当然,也可以使用package_dir选项改变这种对应关系。所以,源码树如下:

 

[plain]   
 
  1. setup.py  
  2. mod1.py  
  3. pkg/  
  4.     __init__.py  
  5.     mod2.py  

 

         最终生成的文件是:

\usr\local\lib\python2.7\dist-packages\foo-1.0.egg-info

\usr\local\lib\python2.7\dist-packages\mod1.py

\usr\local\lib\python2.7\dist-packages\mod1.pyc

\usr\local\lib\python2.7\dist-packages\pkg\__init__.py

\usr\local\lib\python2.7\dist-packages\pkg\__init__.pyc

\usr\local\lib\python2.7\dist-packages\pkg\mod2.py

\usr\local\lib\python2.7\dist-packages\pkg\mod2.pyc

 

2.3扩展模块

         在Distutils中描述扩展模块较描述纯python模块要复杂一些。对于纯python模块,仅需要列出模块或包,然后Distutils就会去寻找合适的文件,这对于扩展模块来说是不够的,你还需要指定扩展名、源码文件以及其他编译/链接需要的参数(需要包含的目录,需要连接的库等等)

         描述扩展模块可以由setup函数的关键字参数ext_modules实现。ext_modules是Extension实例的列表,每一个Extension实例描述了一个独立的扩展模块。比如发布中包含一个独立的扩展模块称为foo,由foo.c实现,且无需其他编译链接指令,那么下面的语句就可以描述该扩展模块:

 

[python]   
 
  1. Extension('foo', ['foo.c'])  

 

         Extension可以从distutils.core中随setup一起引入。因此,对于仅包含一个扩展模块的发布来说,setup脚本如下:

 

[python]   
 
  1. from distutils.core import setup, Extension  
  2. setup(name='foo',  
  3.       version='1.0',  
  4.       ext_modules=[Extension('foo', ['foo.c'])],  
  5.       )  

 

        底层的扩展构建机制是由build_ext命令实现的。Extension类在描述Python扩展时具有很大的灵活性。

 

2.3.1 扩展名和包

         通常,Extension类的构造函数的第一个参数都是扩展的名字,比如下面的语句:

 

[python]   
 
  1. Extension('foo', ['src/foo1.c', 'src/foo2.c'])  

 

         如果执行python  setup.py bdist,就会调用相应的编译器和连接器命令,最终根据生成foo.so文件,存放在发布包的根目录中,最终生成的文件是:

\usr\local\lib\python2.7\dist-packages\foo.so

\usr\local\lib\python2.7\dist-packages\foo-1.0.egg-info

 

        又比如下面的语句:

 

[python]   
 
  1. Extension('pkg.foo', ['src/foo1.c', 'src/foo2.c'])  

 

        使用的源文件是一样的,最终生成的结果文件也是一样的foo.so,唯一的不同是最终的结果文件存放的目录,是在发布包的根目录下的pkg目录下。因此最终生成的文件是:

\usr\local\lib\python2.7\dist-packages\pkg\foo.so

\usr\local\lib\python2.7\dist-packages\foo-1.0.egg-info

 

        如果一个包下有多个扩展,而且要把这些扩展都放在统一的目录下,则可以使用ext_package关键字,比如下面的语句:

 

[python]   
 
  1. setup(...,  
  2.       ext_package='pkg',  
  3.       ext_modules=[Extension('foo', ['src/foo.c']),  
  4.                    Extension('subpkg.bar', ['src/bar.c'])]  
  5.      )  

 

 

         上面的描述将会编译src/foo.c为pkg.foo,将src/bar.c编译为pkg.subpkg.bar。因此源码树如下:

 

[plain]   
 
  1. setup.py  
  2. src/  
  3.     foo.c  
  4.     bar.c  

 

         最终生成的文件是:

\usr\local\lib\python2.7\dist-packages\foo-1.0.egg-info

\usr\local\lib\python2.7\dist-packages\pkg\foo.so

\usr\local\lib\python2.7\dist-packages\pkg\subpkg\bar.so

 

2.3.2 扩展的源码文件

         Extension构建函数的第二个参数是源文件的列表。目前Distutils仅支持C、C++和Objective-C扩展,所以这些源码文件就是C、C++和Objective-C的源码文件。(C++源码文件的扩展名可以是.cc和.cpp,Unix和Windows编译器都支持)

         不过还可以在列表中包含SWIG接口文件(.i文件),build_ext命令知道如何处理SWIG接口文件。尽管会发生报警,但是可以像下面这样传递SWIG选项:

 

[python]   
 
  1. setup(...,  
  2.       ext_modules=[Extension('_foo', ['foo.i'],  
  3.                              swig_opts=['-modern', '-I../include'])],  
  4.       py_modules=['foo'],  
  5.      )  

 

         或者是使用如下命令:

 

[plain]   
 
  1. > python setup.py build_ext --swig-opts="-modern -I../include"  

 

 

         在一些系统上,该列表中还可以包含能由编译器处理的非源码文件。当前只支持Windows message 文本文件(.mc)和Visual C++的资源定义文件(.rc)。它们将会编译为二进制文件.res并且链接进可执行文件中。

        

2.3.3其他选项

         Extension还可以指定其他选项,比如可以指定头文件目录,define或undefine宏、需要链接的库,链接时和运行时搜索库的路径等等。具体可参阅:

https://docs.python.org/2/distutils/setupscript.html#preprocessor-options

https://docs.python.org/2/distutils/setupscript.html#library-options

https://docs.python.org/2/distutils/setupscript.html#other-options

 

2.4发布和包的关系

         发布和包有三种关系:它依赖其他包,它服务于其他包,它淘汰其他包。这些关系可以分别用setup函数的参数requires ,provides 和obsoletes 来指定,具体参阅:https://docs.python.org/2/distutils/setupscript.html#relationships-between-distributions-and-packages

 

2.5安装脚本

         模块通常不自己运行,而是由脚本引入。除了可以安装模块之外,还可以安装能直接运行的脚本,具体参阅https://docs.python.org/2/distutils/setupscript.html#installing-scripts

 

2.6安装package data

         有时包中还需要安装其他文件,这些文件与包的实现密切相关,或者是包含文档信息的文本文件等,这些文件就叫做package data。

         使用setup函数中的package_data参数可以向packages中添加package data。该参数的值必须是个字典,字典的key就是package name,value是个list,其中包含了需要复制到package中的一系列路径。这些路径都是相对于包目录而言的(比如package_dir),所以,这些文件必须存在于包的源码目录中。在安装时,也会创建相应的目录。

         比如,如果包中有一个包含数据文件的子目录,源码树如下:

 

[plain]   
 
  1. setup.py  
  2. src/  
  3.     mypkg/  
  4.         __init__.py  
  5.         module.py  
  6.         data/  
  7.             tables.dat  
  8.             spoons.dat  
  9.             forks.dat  

 

         相应的setup函数可以这样写:

 

[python]   
 
  1. setup(...,  
  2.       packages=['mypkg'],  
  3.       package_dir={
    'mypkg': 'src/mypkg'},  
  4.       package_data={
    'mypkg': ['data/*.dat']},  
  5.       )  

 

 

2.7安装其他文件

         可以通过data_files选项来安装除了上面提到过的文件之外的其他文件,比如配置文件,数据文件等。data_files是个列表,列表中的元素是(directory, files),比如:

 

[python]   
 
  1. setup(...,  
  2.       data_files=[('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),  
  3.                   ('config', ['cfg/data.cfg']),  
  4.                   ('/etc/init.d', ['init-script'])]  
  5.      )  

 

         (directory, files)中,directory表示文件最终要被安装到的地方,如果它是相对路径的话,则是相对于installation prefix而言(对于纯python包而言,就是sys.prefix;对于扩展包,则是sys.exec_prefix)。files是要安装的文件,其中的目录信息(安装前)是相对于setup.py所在目录而言的,安装时,setup.py根据files的信息找到该文件,然后将其安装到directory中。

      

2.8元数据

         Setup脚本可以包含很多发布的元数据,比如名称、版本、作者等信息,具体列表和注意信息,参阅https://docs.python.org/2/distutils/setupscript.html#additional-meta-data

 

2.9调试setup脚本

         如果在运行setup脚本是发生了错误,则Distutils会打印出简单的错误信息,对于开发者而言这些错误信息可能不足以找到错误的原因。所以可以通过设置环境变量DISTUTILS_DEBUG,将其置为任意值(不能是空字符串),Distutils就会打印其执行过程的详细信息,并且在发生异常时打印全部的traceback,并且在像C编译器这样的外部程序发生错误时,打印整个命令行。

 

三:配置文件

         一般情况下,在构建发布时无法将所有的选项都确定下来,有些选项的值可能来自于用户,或者用户的系统。这也就是配置文件setup.cfg存在的目的,用户可以通过修改该配置文件进行选项的配置。

         在构建时,选项的处理顺序是setup脚本、配置文件,命令行。所以,安装者可以通过修改setup.cfg文件来覆盖setup.py中的选项;也可以通过运行setup.py时的命令行选项,来覆盖setup.cfg。

 

         配置文件的基本语法如下:

 

[plain]   
 
  1. [command]  
  2. option=value  
  3. ...  

 

         command就是Distutils的命令(比如build_py,install等),option就是命令支持的选项。配置文件中的空行、注释(以’#’开头,直到行尾)会被忽略。

 

         可以通过--help选项得到某个命令支持的选项,比如:

 

[plain]   
 
  1. > python setup.py --help build_ext  
  2. [...]  
  3. Options for 'build_ext' command:  
  4.   --build-lib (-b)     directory for compiled extension modules  
  5.   --build-temp (-t)    directory for temporary files (build by-products)  
  6.   --inplace (-i)       ignore build-lib and put compiled extensions into the  
  7.                        source directory alongside your pure Python modules  
  8.   --include-dirs (-I)  list of directories to search for header files  
  9.   --define (-D)        C preprocessor macros to define  
  10.   --undef (-U)         C preprocessor macros to undefine  
  11.   --swig-opts          list of SWIG command line options  
  12. [...]  

 

        注意,命令行中的选项”--foo-bar”,在配置文件中要写成”foo_bar”。

        

        比如,运行以下命令:

 

[plain]   
 
  1. python setup.py build_ext --inplace  

 

        如果不希望每次执行命令时都输入”--inplace”选项,则可以在配置文件中写明:

 

[plain]   
 
  1. [build_ext]  
  2. inplace=1  

 

        其他例子和注意事项,可以参阅https://docs.python.org/2/distutils/configfile.html

 

四:源码发布

        之前已经提到过,使用sdist命令可以创建包的源码发布,该命令最终生成一个archive文件。Unix上默认的文件格式是.tar.gz,在Windows上的是ZIP文件。可以使用”--formats”选项指定生成的格式,比如:python setup.py sdist --formats=gztar,zip,执行该命令后,就会生成两个文件foo-1.0.tar.gz 和foo-1.0.zip。

        支持的格式有:

Format

Description

zip

zip file (.zip)

gztar

gzip’ed tar file (.tar.gz)

bztar

bzip2’ed tar file (.tar.bz2)

ztar

compressed tar file (.tar.Z)

tar

tar file (.tar)

        当在Unix上使用tar格式时(gztar,bztar,ztar或tar),可以通过owner和group选项指定用户和群组。比如:

 

[plain]   
 
  1. python setup.py sdist --owner=root --group=root  

 

 

4.1指定发布的文件

         如果没有明确的列出需要发布的文件,则sdist命令默认在源码发布中包含下列文件:

由py_modules和packages选项指定的所有python源码文件;

由ext_modules或libraries选项指定的所有C源码文件;

由scripts指定的脚本;

测试脚本:test/test*.py;

README.txt (或者README), setup.py 和setup.cfg;

package_data指定的所有文件;

data_files指定的所有文件。

 

        如果还需要发布其他额外的文件,典型的做法是编写一个叫做MANIFEST.in的manifest模板。manifest模板包含如何创建MANIFEST文件的一系列指令,sdist命令会解析该模板,根据模板中的指令,以及找到的文件生成MANIFEST。

        文件MANIFEST中明确的列出了包含在源码发布中的所有文件。比如下面就是一个MANIFEST文件的内容:

 

[plain]   
 
  1. # file GENERATED by distutils, do NOT edit  
  2. setup.py  
  3. lib/__init__.py  
  4. lib/foo.py  
  5. lib/bar/__init__.py  
  6. lib/bar/bar.py  

 

 

4.2 Manifest相关选项

        sdist命令的执行步骤如下:

        if the manifest file (MANIFEST by default) exists and the first line does not have a comment indicating it is generated from MANIFEST.in, then it is used as is, unaltered;

        if the manifest file doesn’t exist or has been previously automatically generated, read MANIFEST.in and create the manifest;

        if neither MANIFEST nor MANIFEST.in exist, create a manifest with just the default file set;

        use the list of files now in MANIFEST (either just generated or read in) to create the source distribution archive(s).

        如果仅仅需要(重新)创建MANIFEST文件,则可以使用如下命令:

 

[plain]   
 
  1. python setup.py sdist --manifest-only  

 

 

4.3 MANIFEST.in模板

         如果存在MANIFEST.in文件,则sdist命令就会根据该文件生成MANIFEST。在MANIFEST.in文件中,一行一个命令,每一个命令指定了源码发布中需要包含或者需要排除的文件,比如下面的例子:

 

[plain]   
 
  1. include *.txt  
  2. recursive-include examples *.txt *.py  
  3. prune examples/sample?/build  

 

         很容易看出,上面的命令的意思是:包含所有的.txt文件;包含examples目录下的所有.txt或者.py文件;排除所有匹配examples/sample?/build的目录。所有这些过程,都是在标准规则执行之后执行的,所以可以在模板文件中排除标准集合中的文件。关于MANIFEST文件的其他内容,参阅https://docs.python.org/2/distutils/sourcedist.html

 

五:构建发布(Built Distributions)

         所谓的构建发布(built distribution),即是指二进制包,或是指安装文件。当然它未必真的是二进制,而有可能包含Python源码和字节码。

         构建发布是为了方便安装者而创建的,比如对于基于RPM的Linux用户来说,它可以是二进制RPM包,而对于Windows用户来说,它可以是一个可执行的安装文件等。

         创建包的构建发布,是前面介绍的packager的主要职责。它们拿到包的源码发布之后,使用setup脚本以及bdist命令来生成构建发布。比如,在包的源码树中运行下面的命令:

 

[plain]   
 
  1. python setup.py bdist  

 

         Distutils就会创建发布,执行“伪”安装(在build目录中),并且创建当前平台下的默认格式的构建发布。构建发布在Unix中的默认格式是一个”dumb”的tar文件(之所以称之为”dumb”,是因为该tar文件只有解压到特定的目录下才能工作),而在Windows上是一个简单可执行安装文件。

         所以,在Unix上运行上面的命令之后,就会在dist目录中生成foo-1.0.linux-i686.tar.gz文件,在合适的位置解压该文件,就安装了foo模块,等同于下载了该模块的源码发布之后运行python setup.py install命令。所谓合适的位置,要么是文件系统的根目录,要么是Python的prefix目录,这取决于bdist_dump的命令选项。

         bdist命令有一个--formats选项,类似于sdist命令,该选项可用于指定生成的构建发布的格式,比如命令:

 

[plain]   
 
  1. python setup.py bdist --format=zip  

 

 

         在Unix上运行该命令,就会创建foo-1.0.linux-i686.zip文件,在根目录下解压该文件就安装了foo模块。构建发布支持的格式如下:

gztar

gzipped tar file (.tar.gz)

ztar

compressed tar file (.tar.Z)

tar

tar file (.tar)

zip

zip file (.zip)

rpm

RPM

pkgtool

Solaris pkgtool

sdux

HP-UX swinstall

wininst

self-extracting ZIP file for Windows

msi

Microsoft Installer.

         当然,也可以不使用--formats选项,而是用bdist的子命令,直接创建相应的格式。比如使用bdist_dump命令可以生成所有的dumb archive格式(tar,ztar,gztar和zip),bdist_rpm会生成源码和二进制的RPM包,bdist的子命令如下表:

Command

Formats

bdist_dumb

tar, ztar, gztar, zip

bdist_rpm

rpm, srpm

bdist_wininst

wininst

bdist_msi

msi

         具体的子命令信息,可以参阅https://docs.python.org/2/distutils/builtdist.html

 

六:Distutils与PYPI

         PYPI,也就是Python Package Index,它是Python第三方模块的集中营,Python开发者可以向PYPI上传自己的Python模块。PYPI中存放了发布文件以及发布的元数据。

         Distutils提供了register和upload命令,来直接向PYPI推送元数据和发布文件,详细内容可以参阅https://docs.python.org/2/distutils/packageindex.html

 

七:简单示例

7.1纯Python发布(模块)

         如果只是发布几个模块,这些模块没有放在包中,可是使用py_modules选项。比如源码树如下:

 

[plain]   
 
  1. setup.py  
  2. foo.py  
  3. bar.py  

 

         setup脚本如下:

 

[python]   
 
  1. from distutils.core import setup  
  2. setup(name='foobar',  
  3.       version='1.0',  
  4.       py_modules=['foo', 'bar'],  
  5.       )  

 

         安装之后,会生成以下文件:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\ foo.py

\usr\lib\python2.7\site-packages\ foo.pyc

\usr\lib\python2.7\site-packages\ bar.py

\usr\lib\python2.7\site-packages\ bar.pyc

 

7.1纯Python发布(包)

         如果有很多模块需要发布,则可以将这些模块放到统一的包中,然后在setup脚本中指明要发布的包,而不是列出所有的模块。

         即使模块没有放到包中,也可以通过向setup脚本声明root包的方法来发布,与实际的包不同,根目录下可以没有__init__.py文件。比如上面的例子,源码树保持不变,setup脚本也可以这样写:

 

[python]   
 
  1. from distutils.core import setup  
  2. setup(name='foobar',  
  3.       version='1.0',  
  4.       packages=[''],  
  5.       )  

 

         空字符串就意味着root包。安装之后,生成的文件跟上面是一样的。

 

         如果将源文件放到发布根目录下的子目录中,比如源码树:

 

[plain]   
 
  1. setup.py  
  2. src/        
  3.         foo.py  
  4.         bar.py  

 

         这种情况依然可以用声明root包的方式来发布,只不过需要使用package_dir选项来指明包和目录的关系:

 

[python]   
 
  1. from distutils.core import setup  
  2. setup(name='foobar',  
  3.       version='1.0',  
  4.       package_dir={
    '': 'src'},  
  5.       packages=[''],  
  6.       )  

 

         安装之后生成的文件跟之前是一样的。

 

         更常见的做法是将多个模块组织在同一个包中,比如在包foobar中包含foo和bar模块,源码树如下:

 

[plain]   
 
  1. setup.py  
  2. foobar/  
  3.         __init__.py  
  4.         foo.py  
  5.         bar.py  

 

         setup脚本如下:

 

[python]   
 
  1. from distutils.core import setup  
  2. setup(name='foobar',  
  3.       version='1.0',  
  4.       packages=['foobar'],  
  5.       )  

 

         安装之后,会生成以下文件:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\foobar\ __init__.py

\usr\lib\python2.7\site-packages\foobar\ __init__.pyc

\usr\lib\python2.7\site-packages\foobar\foo.py

\usr\lib\python2.7\site-packages\foobar\foo.pyc

\usr\lib\python2.7\site-packages\foobar\bar.py

\usr\lib\python2.7\site-packages\foobar\bar.pyc

 

         如果不想以模块所在的子目录名来定义包名,则可以使用package_dir选项,比如源码树如下:

 

[plain]   
 
  1. setup.py  
  2. src/  
  3.         __init__.py  
  4.         foo.py  
  5.         bar.py  

 

         则相应的setup脚本如下:

 

[python]   
 
  1. from distutils.core import setup  
  2. setup(name='foobar',  
  3.       version='1.0',  
  4.       package_dir={
    'foobar': 'src'},  
  5.       packages=['foobar'],  
  6.       )  

 

         安装之后生成的文件与上面的例子是一样的。

 

         或者,直接将所有模块放到发布的根目录下:

 

[plain]   
 
  1. setup.py  
  2. __init__.py  
  3. foo.py  
  4. bar.py  

 

         setup脚本如下:

 

[python]   
 
  1. from distutils.core import setup  
  2. setup(name='foobar',  
  3.       version='1.0',  
  4.       package_dir={
    'foobar': ''},  
  5.       packages=['foobar'],  
  6.       )  

 

        安装之后生成的文件与上面的例子是一样的。

 

         如果涉及到子包的话,则必须在packages选项中明确的指出。不过,package_dir中的值却会自动扩展到其子目录。比如源码树如下:

 

[plain]   
 
  1. setup.py  
  2. src/  
  3.         __init__.py  
  4.         foo.py  
  5.         bar.py  
  6.         subfoo/  
  7.                 __init__.py  
  8.                 blah.py  

 

         setup脚本如下:

 

[python]   
 
  1. from distutils.core import setup  
  2. setup(name='foobar',  
  3.       version='1.0',  
  4.       package_dir = {
    'foobar':'src'},  
  5.       packages=['foobar', 'foobar.subfoo'],  
  6.       )  

 

         安装之后,生成文件如下:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\foobar\ __init__.py

\usr\lib\python2.7\site-packages\foobar\ __init__.pyc

\usr\lib\python2.7\site-packages\foobar\foo.py

\usr\lib\python2.7\site-packages\foobar\foo.pyc

\usr\lib\python2.7\site-packages\foobar\bar.py

\usr\lib\python2.7\site-packages\foobar\bar.pyc

\usr\lib\python2.7\site-packages\foobar\subfoo\__init__.py

\usr\lib\python2.7\site-packages\foobar\subfoo\__init__.pyc

\usr\lib\python2.7\site-packages\foobar\subfoo\blah.py

\usr\lib\python2.7\site-packages\foobar\subfoo\blah.pyc

 

7.3单独的扩展模块

         扩展模块由选项ext_modules指定。package_dir选项对扩展模块的源码文件没有作用,它只影响纯Python模块。比如源码树如下:

 

[plain]   
 
  1. setup.py  
  2. foo.c  

 

         如果setup脚本如下:

 

[python]   
 
  1. from distutils.core import setup  
  2. from distutils.extension import Extension  
  3. setup(name='foobar',  
  4.       version='1.0',  
  5.       ext_modules=[Extension('foo', ['foo.c'])],  
  6.       )  

 

         则生成的文件是:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\ foo.so

 

         如果源码树不变,setup脚本如下:

 

[python]   
 
  1. from distutils.core import setup  
  2. from distutils.extension import Extension  
  3. setup(name='foobar',  
  4.       version='1.0',  
  5.       ext_modules=[Extension('foopkg.foo', ['foo.c'])],  
  6.       )  

 

         则生成的文件是:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\foopkg\ foo.so

 

八:其他

         运行install命令,会首先运行build命令,然后运行子命令install_lib,install_data和install_scripts。

 

         Distutils可以进行扩展,比如增加新的命令、修改现有的命令。可参阅https://docs.python.org/2/distutils/extending.html

 

         Distutils的API参阅https://docs.python.org/2/distutils/apiref.html

转载于:https://www.cnblogs.com/nkwy2012/p/9071788.html

你可能感兴趣的文章
Kruskal基础最小生成树
查看>>
ubuntu 14.04 安装搜狗拼音输入法
查看>>
浅谈算法和数据结构: 一 栈和队列
查看>>
Java内部类详解
查看>>
【hdu 1429】胜利大逃亡(续)
查看>>
图论-次短路求法
查看>>
What's New for Visual C# 6.0
查看>>
ExtJs学习笔记之ComboBox组件
查看>>
关于收费软件
查看>>
getopt_long
查看>>
TensorFlow MNIST CNN 代码
查看>>
javascript之Style物
查看>>
JSON跨域解决方案收集
查看>>
SSH框架整合总结
查看>>
图的深度优先遍历
查看>>
C# 之 提高WebService性能大数据量网络传输处理
查看>>
md5sum命令详解
查看>>
[bzoj1004] [HNOI2008] Cards
查看>>
应该是实例化对象的没有对属性赋值时,自动赋值为null,但不是空指针对象引用...
查看>>
原生HttpClient详细使用示例
查看>>