? ? ? ? ?Distutils可以用來在Python環(huán)境中構(gòu)建和安裝額外的模塊单默。新的模塊可以是純Python的,也可以是用C/C++寫的擴(kuò)展模塊,或者可以是Python包,包中包含了由C和Python編寫的模塊丑孩。
一:Distutils簡介
1.1概念和術(shù)語
???????? 對(duì)于模塊開發(fā)者以及需要安裝模塊的使用者來說,Distutils的使用都很簡單灭贷,作為一個(gè)開發(fā)者温学,除了編寫源碼之外,還需要:
編寫setup腳本(一般是setup.py)甚疟;
編寫一個(gè)setup配置文件(可選)仗岖;
創(chuàng)建一個(gè)源碼發(fā)布;
創(chuàng)建一個(gè)或多個(gè)構(gòu)建(二進(jìn)制)發(fā)布(可選);
???????? 有些模塊開發(fā)者在開發(fā)時(shí)不會(huì)考慮多個(gè)平臺(tái)發(fā)布览妖,所以就有了packagers的角色轧拄,它們從模塊開發(fā)者那取得源碼發(fā)布,然后在多個(gè)平臺(tái)上面進(jìn)行構(gòu)建讽膏,并發(fā)布多個(gè)平臺(tái)的構(gòu)建版本檩电。
1.2簡單例子
???????? 由python編寫的setup腳本一般都非常簡單。作為autoconf類型的配置腳本府树,setup腳本可以在構(gòu)建和安裝模塊發(fā)布時(shí)運(yùn)行多次俐末。
? ? ? ? ?比如,如果需要發(fā)布一個(gè)叫做foo的模塊挺尾,它包含在一個(gè)文件foo.py,那setup腳本可以這樣寫:
from distutils.core import setup
setup(name='foo',
? ? ? version='1.0',
? ? ? py_modules=['foo'],
? ? ? )
?setup函數(shù)的參數(shù)表示提供給Distutils的信息站绪,這些參數(shù)分為兩類:包的元數(shù)據(jù)(包名遭铺、版本號(hào))以及包的信息(本例中是一個(gè)Python模塊的列表);模塊由模塊名表示,而不是文件名(對(duì)于包和擴(kuò)展而言也是這樣)魂挂;建議可以提供更多的元數(shù)據(jù)甫题,比如你的名字,email地址和項(xiàng)目的URL地址涂召。
???????? 編寫好setup.py之后坠非,就可以創(chuàng)建該模塊的源碼發(fā)布了:
python setup.py sdist
對(duì)于Windows而言,命令是:?
setup.py sdist
sdist命令會(huì)創(chuàng)建一個(gè)archive?文件(比如Unix上的tar文件果正,Windows上的zip文件)炎码,它包含setup.py, foo.py秋泳。該archive文件命名為foo-1.0.tar.gz(zip)潦闲,解壓之后的目錄名是foo-1.0。
???????? 如果一個(gè)用戶希望安裝foo模塊迫皱,他只需要下載foo-1.0.tar.gz歉闰,解壓,進(jìn)入foo-1.0目錄卓起,然后運(yùn)行:
python setup.py install
?該命令最終會(huì)將foo.py復(fù)制到Python環(huán)境存放第三方模塊的目錄中和敬。在linux環(huán)境下,運(yùn)行該命令的輸出是:
# python setup.py install
running install
running build
running build_py
creating build
creating build/lib
copying foo.py -> build/lib
running install_lib
copying build/lib/foo.py -> /usr/lib/python2.7/site-packages
byte-compiling /usr/lib/python2.7/site-packages/foo.py to foo.pyc
running install_egg_info
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
???????? 這個(gè)簡單的例子展示了Distutils的基本概念戏阅。第一昼弟,開發(fā)者和安裝者有同樣的用戶接口,也就是setup腳本饲握,但他們使用的Distutils命令不同私杜,sdist命令幾乎只有開發(fā)者使用,而install對(duì)于安裝者更常用救欧。
???????? 如果希望使用者的使用盡可能的簡單衰粹,則可以創(chuàng)建多個(gè)構(gòu)建發(fā)布。比如笆怠,如果在Windows中铝耻,可以使用bdist_wininst命令創(chuàng)建一個(gè)exe安裝文件,下面的命令會(huì)在當(dāng)前目錄中創(chuàng)建foo-1.0.win32.exe文件:
python setup.py bdist_wininst
其他的構(gòu)建發(fā)布有RPM(由bdist_rpm命令實(shí)現(xiàn))蹬刷,Solaris pkgtool(bdist_pkgtool)瓢捉,以及HP-UX?swinstall?(bdist_sdux)。
? ? ? ? ?比如办成,下面的命令將會(huì)創(chuàng)建RPM文件foo-1.0.noarch.rpm(bdist_rpm命令必須運(yùn)行于基于RPM的系統(tǒng)泡态,比如Red Hat Linux, SuSE Linux, Mandrake Linux):
python setup.py bdist_rpm
可以通過下面的命令得到當(dāng)前支持的發(fā)布格式:
python setup.py bdist --help-formats
1.3基本術(shù)語:
? ? ? ? 模塊(module):?????? Python中可復(fù)用的基本代碼單元,可由其他代碼import的一塊代碼迂卢,這里我們只關(guān)注三種類型的模塊:純python模塊某弦,擴(kuò)展模塊和包桐汤。
? ? ? ??純python模塊(pure Python module):????? 由python編寫的模塊,包含在單獨(dú)的py文件中(或者是pyc/pyo文件)靶壮。
? ? ? ??擴(kuò)展模塊(extension module):由實(shí)現(xiàn)Python的底層語言編寫的模塊(C/C++ for Python, Java for Jython)怔毛。通常包含在單獨(dú)的動(dòng)態(tài)加載文件中,比如Unix中的so文件腾降,windows中的DLL文件拣度,或者是Jython擴(kuò)展的java類文件。(注意螃壤,目前為止Distutils只能處理Python的C/C++擴(kuò)展)
? ? ? ??包(package):包是含其他模塊的模塊抗果,經(jīng)常由包含__init__.py文件的目錄發(fā)布。
? ? ? ??Root包(root package):?????? 包層次關(guān)系中的根(它不是真正的包映穗,因?yàn)樗话琠_init__.py文件)窖张。
1.4 Distutils術(shù)語
? ? ? ??模塊發(fā)布(module distribution):一些Python模塊的集合,它們將會(huì)被一起安裝蚁滋。一些常見的模塊發(fā)布有Numeric Python宿接,PyXML,PIL辕录,mxBase睦霎。
? ? ? ??純模塊發(fā)布:一個(gè)只包含純python模塊和包的模塊發(fā)布。
? ? ? ??非純模塊發(fā)布:至少包含一個(gè)擴(kuò)展模塊的模塊發(fā)布走诞。
? ? ? ??發(fā)布根:源碼樹的根目錄副女;setup.py所在的目錄。
二:編寫setup腳本
?????? setup腳本是使用Distutils構(gòu)建蚣旱、發(fā)布和安裝模塊的核心碑幅。setup腳本的作用是向Distutils描述發(fā)布模塊的信息。從上面那個(gè)簡單的例子中可知塞绿,setup腳本主要是調(diào)用setup函數(shù)沟涨,而且模塊開發(fā)者向Distutils提供的模塊信息多數(shù)是由setup函數(shù)的關(guān)鍵字參數(shù)提供的。
?????? 下面是一個(gè)更高級(jí)一些的例子:Distutils模塊本身的setup腳本:
setup(name='Distutils',
? ? ? version='1.0',
? ? ? description='Python Distribution Utilities',
? ? ? author='Greg Ward',
? ? ? author_email='gward@python.net',
? ? ? url='https://www.python.org/sigs/distutils-sig/',
? ? ? packages=['distutils', 'distutils.command'],
? ? )
?上面這個(gè)腳本有更多的元數(shù)據(jù)异吻,列出的是兩個(gè)包(packages)裹赴,而不是列出每個(gè)模塊。因?yàn)镈istutils包含多個(gè)模塊诀浪,這些模塊分成了兩個(gè)包棋返;如果列出所有模塊的話則是冗長且難以維護(hù)的。
?????? 注意雷猪,在setup腳本中的路徑必須以Unix形式來書寫睛竣,也就是由”/”分割的。Distutils會(huì)在使用這些路徑之前求摇,將這種表示方法轉(zhuǎn)換為適合當(dāng)前平臺(tái)的格式射沟。
2.1列出整個(gè)包
???????? Setup函數(shù)的packages參數(shù)是一個(gè)列表嫉你,其中包含了Distutils需要處理(構(gòu)建、發(fā)布躏惋、安裝等)的所有包。要實(shí)現(xiàn)此目的嚷辅,那么包名和目錄名必須能夠相互對(duì)應(yīng)簿姨,比如包名是distutils,則意味著在發(fā)布的根目錄(setup腳本所在目錄)下存在distutils子目錄簸搞;再比如在setup腳本中packages?=?['foo']扁位,意味著要在setup腳本所在目錄下存在相應(yīng)的foo目錄和foo/__init__.py文件。
?????? 比如如果setup腳本內(nèi)容如下:
setup(name='foo',
? ? ? version='1.0',
? ? ? packages = ['foo']
? ? )
而setup腳本所在目錄并沒有foo目錄(只有一個(gè)setup.py腳本)趁俊,則在執(zhí)行python setup.py bdist命令時(shí)域仇,會(huì)打印如下錯(cuò)誤:
error: package directory 'foo' does not exist
? ? ? ? 如果創(chuàng)建了foo目錄,但是沒有foo/__init__.py文件寺擂,則Distutils會(huì)產(chǎn)生下面的警告暇务,但是仍會(huì)處理該包:
package init file 'foo/__init__.py' not found (or not a regular file)
? 可以使用package_dir選項(xiàng)來改變這種默認(rèn)的對(duì)應(yīng)規(guī)則。package_dir是個(gè)字典怔软,其中的key是要安裝的包名垦细,如果為空,則表明是root package挡逼,value就是該包(key)對(duì)應(yīng)的源碼樹的目錄括改。
? ? ? ? 比如如果setup.py內(nèi)容如下:
setup(name='foo',
? ? ? version='1.0',
? ? ? package_dir = {'':'lib'},
? ? ? packages = ['foo']
? ? )
? ? 則必須在目錄中存在lib子目錄,lib/foo子目錄家坎,以及文件lib/foo/__init__.py嘱能。所以源碼樹如下:
setup.py
lib/
? ? foo/
? ? ? ? __init__.py
? ? ? ? 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
?????? 另外一個(gè)例子,foo包對(duì)應(yīng)lib目錄虱疏,所以惹骂,foo.bar包就對(duì)應(yīng)著lib/bar子目錄。所以如果在setup.py中這么寫:
? ? ?package_dir = {'foo':'lib'},
? ? ? packages = ['foo',’foo.bar’]
?????? 則必須存在lib/__init__.py, ?lib/bar/__init__.py文件订框。源碼樹如下:
setup.py
lib/
? ? __init__.py
? ? foo.py
? ? bar/
? ? ? ? __init__.py
? ? ? ? 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列出單獨(dú)的模塊
?????? 如果發(fā)布中僅包含較少的模塊析苫,你可能更喜歡列出所有模塊,而不是列出包穿扳,特別是在root package中存在單一模塊的情況(或者根本就沒有包)衩侥。可以使用py_modules參數(shù)矛物,比如下面的例子:
setup(name='foo',
? ? ? version='1.0',
? ? ? py_modules = ['mod1', 'pkg.mod2']
? ? )
???????? 它描述了兩個(gè)模塊茫死,一個(gè)在root package中,另一個(gè)在pkg包中履羞。根據(jù)默認(rèn)的包/目錄對(duì)應(yīng)規(guī)則峦萎,這兩個(gè)模塊存在于文件mod1.py和pkg/mod2.py中屡久,并且要存在pkg/__init__.py文件(不存在的話,會(huì)產(chǎn)生報(bào)警:package init file 'pkg/__init__.py' not found (or not a regular file))爱榔。當(dāng)然被环,也可以使用package_dir選項(xiàng)改變這種對(duì)應(yīng)關(guān)系。所以详幽,源碼樹如下:
setup.py
mod1.py
pkg/
? ? __init__.py
? ? 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擴(kuò)展模塊
???????? 在Distutils中描述擴(kuò)展模塊較描述純python模塊要復(fù)雜一些筛欢。對(duì)于純python模塊,僅需要列出模塊或包唇聘,然后Distutils就會(huì)去尋找合適的文件版姑,這對(duì)于擴(kuò)展模塊來說是不夠的,你還需要指定擴(kuò)展名迟郎、源碼文件以及其他編譯/鏈接需要的參數(shù)(需要包含的目錄剥险,需要連接的庫等等)
? ? ? ? 描述擴(kuò)展模塊可以由setup函數(shù)的關(guān)鍵字參數(shù)ext_modules實(shí)現(xiàn)。ext_modules是Extension實(shí)例的列表宪肖,每一個(gè)Extension實(shí)例描述了一個(gè)獨(dú)立的擴(kuò)展模塊表制。比如發(fā)布中包含一個(gè)獨(dú)立的擴(kuò)展模塊稱為foo,由foo.c實(shí)現(xiàn)控乾,且無需其他編譯鏈接指令夫凸,那么下面的語句就可以描述該擴(kuò)展模塊:
Extension('foo', ['foo.c'])
???????? Extension可以從distutils.core中隨setup一起引入。因此阱持,對(duì)于僅包含一個(gè)擴(kuò)展模塊的發(fā)布來說夭拌,setup腳本如下:
from distutils.core import setup, Extension
setup(name='foo',
? ? ? version='1.0',
? ? ? ext_modules=[Extension('foo', ['foo.c'])],
? ? ? )
? ? ? ? 底層的擴(kuò)展構(gòu)建機(jī)制是由build_ext命令實(shí)現(xiàn)的蒂胞。Extension類在描述Python擴(kuò)展時(shí)具有很大的靈活性变秦。
2.3.1 擴(kuò)展名和包
???????? 通常盆昙,Extension類的構(gòu)造函數(shù)的第一個(gè)參數(shù)都是擴(kuò)展的名字褥实,比如下面的語句:
Extension('foo', ['src/foo1.c', 'src/foo2.c'])
???????? 如果執(zhí)行python ?setup.py?bdist司顿,就會(huì)調(diào)用相應(yīng)的編譯器和連接器命令赃春,最終根據(jù)生成foo.so文件燕酷,存放在發(fā)布包的根目錄中喊巍,最終生成的文件是:
\usr\local\lib\python2.7\dist-packages\foo.so
\usr\local\lib\python2.7\dist-packages\foo-1.0.egg-info
? ? ? ? 又比如下面的語句:
Extension('pkg.foo', ['src/foo1.c', 'src/foo2.c'])
? ? ? ? 使用的源文件是一樣的鼎姊,最終生成的結(jié)果文件也是一樣的foo.so骡和,唯一的不同是最終的結(jié)果文件存放的目錄,是在發(fā)布包的根目錄下的pkg目錄下相寇。因此最終生成的文件是:
\usr\local\lib\python2.7\dist-packages\pkg\foo.so
\usr\local\lib\python2.7\dist-packages\foo-1.0.egg-info
? ? ? ? 如果一個(gè)包下有多個(gè)擴(kuò)展慰于,而且要把這些擴(kuò)展都放在統(tǒng)一的目錄下,則可以使用ext_package關(guān)鍵字唤衫,比如下面的語句:
setup(...,
? ? ? ext_package='pkg',
? ? ? ext_modules=[Extension('foo', ['src/foo.c']),
? ? ? ? ? ? ? ? ? Extension('subpkg.bar', ['src/bar.c'])]
? ? )
???????? 上面的描述將會(huì)編譯src/foo.c為pkg.foo婆赠,將src/bar.c編譯為pkg.subpkg.bar。因此源碼樹如下:
setup.py
src/
? ? foo.c
? ? 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 擴(kuò)展的源碼文件
???????? Extension構(gòu)建函數(shù)的第二個(gè)參數(shù)是源文件的列表佳励。目前Distutils僅支持C休里、C++和Objective-C擴(kuò)展蛆挫,所以這些源碼文件就是C、C++和Objective-C的源碼文件妙黍。(C++源碼文件的擴(kuò)展名可以是.cc和.cpp悴侵,Unix和Windows編譯器都支持)
???????? 不過還可以在列表中包含SWIG接口文件(.i文件),build_ext命令知道如何處理SWIG接口文件拭嫁。盡管會(huì)發(fā)生報(bào)警畜挨,但是可以像下面這樣傳遞SWIG選項(xiàng):
setup(...,
? ? ? ext_modules=[Extension('_foo', ['foo.i'],
? ? ? ? ? ? ? ? ? ? ? ? ? ? swig_opts=['-modern', '-I../include'])],
? ? ? py_modules=['foo'],
? ? )
???????? 或者是使用如下命令:
> python setup.py build_ext --swig-opts="-modern -I../include"
???????? 在一些系統(tǒng)上,該列表中還可以包含能由編譯器處理的非源碼文件噩凹。當(dāng)前只支持Windows message 文本文件(.mc)和Visual C++的資源定義文件(.rc)。它們將會(huì)編譯為二進(jìn)制文件.res并且鏈接進(jìn)可執(zhí)行文件中毡咏。
2.3.3其他選項(xiàng)
???????? Extension還可以指定其他選項(xiàng)驮宴,比如可以指定頭文件目錄,define或undefine宏呕缭、需要鏈接的庫堵泽,鏈接時(shí)和運(yùn)行時(shí)搜索庫的路徑等等。具體可參閱:
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發(fā)布和包的關(guān)系
???????? 發(fā)布和包有三種關(guān)系:它依賴其他包恢总,它服務(wù)于其他包迎罗,它淘汰其他包。這些關(guān)系可以分別用setup函數(shù)的參數(shù)requires?片仿,provides?和obsoletes?來指定纹安,具體參閱:https://docs.python.org/2/distutils/setupscript.html#relationships-between-distributions-and-packages
2.5安裝腳本
???????? 模塊通常不自己運(yùn)行,而是由腳本引入砂豌。除了可以安裝模塊之外厢岂,還可以安裝能直接運(yùn)行的腳本,具體參閱https://docs.python.org/2/distutils/setupscript.html#installing-scripts
2.6安裝package data
???????? 有時(shí)包中還需要安裝其他文件阳距,這些文件與包的實(shí)現(xiàn)密切相關(guān)塔粒,或者是包含文檔信息的文本文件等,這些文件就叫做package data筐摘。
???????? 使用setup函數(shù)中的package_data參數(shù)可以向packages中添加package data卒茬。該參數(shù)的值必須是個(gè)字典,字典的key就是package name咖熟,value是個(gè)list圃酵,其中包含了需要復(fù)制到package中的一系列路徑。這些路徑都是相對(duì)于包目錄而言的(比如package_dir)馍管,所以辜昵,這些文件必須存在于包的源碼目錄中。在安裝時(shí)咽斧,也會(huì)創(chuàng)建相應(yīng)的目錄堪置。
???????? 比如躬存,如果包中有一個(gè)包含數(shù)據(jù)文件的子目錄,源碼樹如下:
setup.py
src/
? ? mypkg/
? ? ? ? __init__.py
? ? ? ? module.py
? ? ? ? data/
? ? ? ? ? ? tables.dat
? ? ? ? ? ? spoons.dat
? ? ? ? ? ? forks.dat
???????? 相應(yīng)的setup函數(shù)可以這樣寫:
setup(...,
? ? ? packages=['mypkg'],
? ? ? package_dir={'mypkg': 'src/mypkg'},
? ? ? package_data={'mypkg': ['data/*.dat']},
? ? ? )
2.7安裝其他文件
???????? 可以通過data_files選項(xiàng)來安裝除了上面提到過的文件之外的其他文件舀锨,比如配置文件岭洲,數(shù)據(jù)文件等。data_files是個(gè)列表坎匿,列表中的元素是(directory,?files)盾剩,比如:
setup(...,
? ? ? data_files=[('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
? ? ? ? ? ? ? ? ? ('config', ['cfg/data.cfg']),
? ? ? ? ? ? ? ? ? ('/etc/init.d', ['init-script'])]
? ? )
???????? (directory,?files)中,directory表示文件最終要被安裝到的地方替蔬,如果它是相對(duì)路徑的話告私,則是相對(duì)于installation prefix而言(對(duì)于純python包而言,就是sys.prefix承桥;對(duì)于擴(kuò)展包驻粟,則是sys.exec_prefix)。files是要安裝的文件凶异,其中的目錄信息(安裝前)是相對(duì)于setup.py所在目錄而言的蜀撑,安裝時(shí),setup.py根據(jù)files的信息找到該文件剩彬,然后將其安裝到directory中酷麦。
2.8元數(shù)據(jù)
???????? Setup腳本可以包含很多發(fā)布的元數(shù)據(jù),比如名稱喉恋、版本沃饶、作者等信息,具體列表和注意信息轻黑,參閱https://docs.python.org/2/distutils/setupscript.html#additional-meta-data
2.9調(diào)試setup腳本
???????? 如果在運(yùn)行setup腳本是發(fā)生了錯(cuò)誤绍坝,則Distutils會(huì)打印出簡單的錯(cuò)誤信息,對(duì)于開發(fā)者而言這些錯(cuò)誤信息可能不足以找到錯(cuò)誤的原因苔悦。所以可以通過設(shè)置環(huán)境變量DISTUTILS_DEBUG轩褐,將其置為任意值(不能是空字符串),Distutils就會(huì)打印其執(zhí)行過程的詳細(xì)信息玖详,并且在發(fā)生異常時(shí)打印全部的traceback把介,并且在像C編譯器這樣的外部程序發(fā)生錯(cuò)誤時(shí),打印整個(gè)命令行蟋座。
?
三:配置文件
???????? 一般情況下拗踢,在構(gòu)建發(fā)布時(shí)無法將所有的選項(xiàng)都確定下來,有些選項(xiàng)的值可能來自于用戶向臀,或者用戶的系統(tǒng)巢墅。這也就是配置文件setup.cfg存在的目的,用戶可以通過修改該配置文件進(jìn)行選項(xiàng)的配置。
???????? 在構(gòu)建時(shí)君纫,選項(xiàng)的處理順序是setup腳本驯遇、配置文件,命令行蓄髓。所以叉庐,安裝者可以通過修改setup.cfg文件來覆蓋setup.py中的選項(xiàng);也可以通過運(yùn)行setup.py時(shí)的命令行選項(xiàng)会喝,來覆蓋setup.cfg陡叠。
???????? 配置文件的基本語法如下:
[command]
option=value
...
???????? command就是Distutils的命令(比如build_py,install等)肢执,option就是命令支持的選項(xiàng)枉阵。配置文件中的空行、注釋(以’#’開頭预茄,直到行尾)會(huì)被忽略兴溜。
???????? 可以通過--help選項(xiàng)得到某個(gè)命令支持的選項(xiàng),比如:
> python setup.py --help build_ext
[...]
Options for 'build_ext' command:
? --build-lib (-b)? ? directory for compiled extension modules
? --build-temp (-t)? ? directory for temporary files (build by-products)
? --inplace (-i)? ? ? ignore build-lib and put compiled extensions into the
? ? ? ? ? ? ? ? ? ? ? source directory alongside your pure Python modules
? --include-dirs (-I)? list of directories to search for header files
? --define (-D)? ? ? ? C preprocessor macros to define
? --undef (-U)? ? ? ? C preprocessor macros to undefine
? --swig-opts? ? ? ? ? list of SWIG command line options
[...]
? ? ? ? 注意反璃,命令行中的選項(xiàng)”--foo-bar”,在配置文件中要寫成”foo_bar”假夺。
? ? ? ? 比如淮蜈,運(yùn)行以下命令:
python setup.py build_ext --inplace
? ? ? ? 如果不希望每次執(zhí)行命令時(shí)都輸入”--inplace”選項(xiàng),則可以在配置文件中寫明:
[build_ext]
inplace=1
? ? ? ? 其他例子和注意事項(xiàng)已卷,可以參閱https://docs.python.org/2/distutils/configfile.html
四:源碼發(fā)布
? ? ? ? 之前已經(jīng)提到過梧田,使用sdist命令可以創(chuàng)建包的源碼發(fā)布,該命令最終生成一個(gè)archive文件侧蘸。Unix上默認(rèn)的文件格式是.tar.gz裁眯,在Windows上的是ZIP文件』浒可以使用”--formats”選項(xiàng)指定生成的格式穿稳,比如:python setup.py sdist --formats=gztar,zip,執(zhí)行該命令后晌坤,就會(huì)生成兩個(gè)文件foo-1.0.tar.gz 和foo-1.0.zip逢艘。
? ? ? ? 支持的格式有:
Format? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Description
zipzip? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?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)
? ? 當(dāng)在Unix上使用tar格式時(shí)(gztar,bztar骤菠,ztar或tar)它改,可以通過owner和group選項(xiàng)指定用戶和群組。比如:
python setup.py sdist --owner=root --group=root
4.1指定發(fā)布的文件
???????? 如果沒有明確的列出需要發(fā)布的文件商乎,則sdist命令默認(rèn)在源碼發(fā)布中包含下列文件:
由py_modules和packages選項(xiàng)指定的所有python源碼文件央拖;
由ext_modules或libraries選項(xiàng)指定的所有C源碼文件;
由scripts指定的腳本;
測(cè)試腳本:test/test*.py鲜戒;
README.txt?(或者README)专控,?setup.py?和setup.cfg;
package_data指定的所有文件袍啡;
data_files指定的所有文件踩官。
? ? ? ? 如果還需要發(fā)布其他額外的文件,典型的做法是編寫一個(gè)叫做MANIFEST.in的manifest模板境输。manifest模板包含如何創(chuàng)建MANIFEST文件的一系列指令蔗牡,sdist命令會(huì)解析該模板,根據(jù)模板中的指令嗅剖,以及找到的文件生成MANIFEST辩越。
? ? ? ? 文件MANIFEST中明確的列出了包含在源碼發(fā)布中的所有文件。比如下面就是一個(gè)MANIFEST文件的內(nèi)容:
# file GENERATED by distutils, do NOT edit
setup.py
lib/__init__.py
lib/foo.py
lib/bar/__init__.py
lib/bar/bar.py
4.2 Manifest相關(guān)選項(xiàng)
? ? ? ? sdist命令的執(zhí)行步驟如下:
? ? ? ? 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).
? ? ? ? 如果僅僅需要(重新)創(chuàng)建MANIFEST文件信粮,則可以使用如下命令:
python setup.py sdist --manifest-only
4.3 MANIFEST.in模板
???????? 如果存在MANIFEST.in文件黔攒,則sdist命令就會(huì)根據(jù)該文件生成MANIFEST。在MANIFEST.in文件中强缘,一行一個(gè)命令督惰,每一個(gè)命令指定了源碼發(fā)布中需要包含或者需要排除的文件,比如下面的例子:
include *.txt
recursive-include examples *.txt *.py
prune examples/sample?/build
???????? 很容易看出旅掂,上面的命令的意思是:包含所有的.txt文件赏胚;包含examples目錄下的所有.txt或者.py文件;排除所有匹配examples/sample?/build的目錄商虐。所有這些過程觉阅,都是在標(biāo)準(zhǔn)規(guī)則執(zhí)行之后執(zhí)行的,所以可以在模板文件中排除標(biāo)準(zhǔn)集合中的文件秘车。關(guān)于MANIFEST文件的其他內(nèi)容典勇,參閱https://docs.python.org/2/distutils/sourcedist.html
五:構(gòu)建發(fā)布(Built Distributions)
???????? 所謂的構(gòu)建發(fā)布(built distribution),即是指二進(jìn)制包叮趴,或是指安裝文件割笙。當(dāng)然它未必真的是二進(jìn)制,而有可能包含Python源碼和字節(jié)碼眯亦。
???????? 構(gòu)建發(fā)布是為了方便安裝者而創(chuàng)建的咳蔚,比如對(duì)于基于RPM的Linux用戶來說,它可以是二進(jìn)制RPM包搔驼,而對(duì)于Windows用戶來說谈火,它可以是一個(gè)可執(zhí)行的安裝文件等。
???????? 創(chuàng)建包的構(gòu)建發(fā)布舌涨,是前面介紹的packager的主要職責(zé)糯耍。它們拿到包的源碼發(fā)布之后扔字,使用setup腳本以及bdist命令來生成構(gòu)建發(fā)布。比如温技,在包的源碼樹中運(yùn)行下面的命令:
python setup.py bdist
? ? ? ? ?Distutils就會(huì)創(chuàng)建發(fā)布革为,執(zhí)行“偽”安裝(在build目錄中),并且創(chuàng)建當(dāng)前平臺(tái)下的默認(rèn)格式的構(gòu)建發(fā)布舵鳞。構(gòu)建發(fā)布在Unix中的默認(rèn)格式是一個(gè)”dumb”的tar文件(之所以稱之為”dumb”震檩,是因?yàn)樵搕ar文件只有解壓到特定的目錄下才能工作),而在Windows上是一個(gè)簡單可執(zhí)行安裝文件蜓堕。
???????? 所以抛虏,在Unix上運(yùn)行上面的命令之后,就會(huì)在dist目錄中生成foo-1.0.linux-i686.tar.gz文件套才,在合適的位置解壓該文件迂猴,就安裝了foo模塊,等同于下載了該模塊的源碼發(fā)布之后運(yùn)行python setup.py install命令背伴。所謂合適的位置沸毁,要么是文件系統(tǒng)的根目錄,要么是Python的prefix目錄傻寂,這取決于bdist_dump的命令選項(xiàng)息尺。
???????? bdist命令有一個(gè)--formats選項(xiàng),類似于sdist命令疾掰,該選項(xiàng)可用于指定生成的構(gòu)建發(fā)布的格式搂誉,比如命令:
python setup.py bdist --format=zip
???????? 在Unix上運(yùn)行該命令,就會(huì)創(chuàng)建foo-1.0.linux-i686.zip文件个绍,在根目錄下解壓該文件就安裝了foo模塊勒葱。構(gòu)建發(fā)布支持的格式如下:
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.
??????? 具體的子命令信息浪汪,可以參閱https://docs.python.org/2/distutils/builtdist.html
六:Distutils與PYPI
???????? PYPI巴柿,也就是Python Package Index,它是Python第三方模塊的集中營死遭,Python開發(fā)者可以向PYPI上傳自己的Python模塊广恢。PYPI中存放了發(fā)布文件以及發(fā)布的元數(shù)據(jù)。
???????? Distutils提供了register和upload命令呀潭,來直接向PYPI推送元數(shù)據(jù)和發(fā)布文件钉迷,詳細(xì)內(nèi)容可以參閱https://docs.python.org/2/distutils/packageindex.html
七:簡單示例
7.1純Python發(fā)布(模塊)
???????? 如果只是發(fā)布幾個(gè)模塊,這些模塊沒有放在包中钠署,可是使用py_modules選項(xiàng)糠聪。比如源碼樹如下:
setup.py
foo.py
bar.py
???????? setup腳本如下:
from distutils.core import setup
setup(name='foobar',
? ? ? version='1.0',
? ? ? py_modules=['foo', 'bar'],
? ? ? )
???????? 安裝之后,會(huì)生成以下文件:
\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發(fā)布(包)
???????? 如果有很多模塊需要發(fā)布谐鼎,則可以將這些模塊放到統(tǒng)一的包中舰蟆,然后在setup腳本中指明要發(fā)布的包,而不是列出所有的模塊。
???????? 即使模塊沒有放到包中身害,也可以通過向setup腳本聲明root包的方法來發(fā)布味悄,與實(shí)際的包不同,根目錄下可以沒有__init__.py文件塌鸯。比如上面的例子侍瑟,源碼樹保持不變,setup腳本也可以這樣寫:
from distutils.core import setup
setup(name='foobar',
? ? ? version='1.0',
? ? ? packages=[''],
? ? ? )
???????? 空字符串就意味著root包丙猬。安裝之后涨颜,生成的文件跟上面是一樣的。
???????? 如果將源文件放到發(fā)布根目錄下的子目錄中淮悼,比如源碼樹:
setup.py
src/? ? ?
? ? ? ? foo.py
? ? ? ? bar.py
???????? 這種情況依然可以用聲明root包的方式來發(fā)布咐低,只不過需要使用package_dir選項(xiàng)來指明包和目錄的關(guān)系:
from distutils.core import setup
setup(name='foobar',
? ? ? version='1.0',
? ? ? package_dir={'': 'src'},
? ? ? packages=[''],
? ? ? )
???????? 安裝之后生成的文件跟之前是一樣的。
???????? 更常見的做法是將多個(gè)模塊組織在同一個(gè)包中袜腥,比如在包foobar中包含foo和bar模塊见擦,源碼樹如下
setup.py
foobar/
? ? ? ? __init__.py
? ? ? ? foo.py
? ? ? ? bar.py
???????? setup腳本如下:
from distutils.core import setup
setup(name='foobar',
? ? ? version='1.0',
? ? ? packages=['foobar'],
? ? ? )
???????? 安裝之后,會(huì)生成以下文件:
\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選項(xiàng)鲤屡,比如源碼樹如下:
setup.py
src/
? ? ? ? __init__.py
? ? ? ? foo.py
? ? ? ? bar.py
???????? 則相應(yīng)的setup腳本如下:
from distutils.core import setup
setup(name='foobar',
? ? ? version='1.0',
? ? ? package_dir={'foobar': 'src'},
? ? ? packages=['foobar'],
? ? ? )
???????? 安裝之后生成的文件與上面的例子是一樣的。
???????? 或者福侈,直接將所有模塊放到發(fā)布的根目錄下:
setup.py
__init__.py
foo.py
bar.py
???????? setup腳本如下:
from distutils.core import setup
setup(name='foobar',
? ? ? version='1.0',
? ? ? package_dir={'foobar': ''},
? ? ? packages=['foobar'],
? ? ? )
? ? ? ? 安裝之后生成的文件與上面的例子是一樣的酒来。
???????? 如果涉及到子包的話,則必須在packages選項(xiàng)中明確的指出肪凛。不過堰汉,package_dir中的值卻會(huì)自動(dòng)擴(kuò)展到其子目錄。比如源碼樹如下:
setup.py
src/
__init__.py
? ? ? ? foo.py
? ? ? ? bar.py
? ? ? ? subfoo/
? ? ? ? ? ? ? ? __init__.py
? ? ? ? ? ? ? ? blah.py
???????? setup腳本如下:
from distutils.core import setup
setup(name='foobar',
? ? ? version='1.0',
? ? ? package_dir = {'foobar':'src'},
? ? ? packages=['foobar', 'foobar.subfoo'],
? ? ? )
???????? 安裝之后伟墙,生成文件如下:
\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單獨(dú)的擴(kuò)展模塊
???????? 擴(kuò)展模塊由選項(xiàng)ext_modules指定翘鸭。package_dir選項(xiàng)對(duì)擴(kuò)展模塊的源碼文件沒有作用,它只影響純Python模塊戳葵。比如源碼樹如下:
setup.py
foo.c
???????? 如果setup腳本如下:
from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
? ? ? version='1.0',
? ? ? ext_modules=[Extension('foo', ['foo.c'])],
? ? ? )
???????? 則生成的文件是:
\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info
\usr\lib\python2.7\site-packages\ foo.so
???????? 如果源碼樹不變就乓,setup腳本如下:
from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
? ? ? version='1.0',
? ? ? ext_modules=[Extension('foopkg.foo', ['foo.c'])],
? ? ? )
???????? 則生成的文件是:
\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info
\usr\lib\python2.7\site-packages\foopkg\ foo.so
八:其他
???????? 運(yùn)行install命令,會(huì)首先運(yùn)行build命令拱烁,然后運(yùn)行子命令install_lib,install_data和install_scripts生蚁。
???????? Distutils可以進(jìn)行擴(kuò)展,比如增加新的命令戏自、修改現(xiàn)有的命令邦投。可參閱https://docs.python.org/2/distutils/extending.html
???????? Distutils的API參閱https://docs.python.org/2/distutils/apiref.html
原文參考:https://blog.csdn.net/gqtcgq/article/details/49255995