參考 Python的Distutils模塊 - 雲+社區 - 騰訊雲
目錄
一、Distutils簡介
1.1、概念和術語
1.2、簡單例子
1.3、基本術語
1.4、Distutils術語
二、編寫setup腳本
2.1、列出整個包
2.2、列出單獨的模塊
2.3、擴展模塊
2.3.1、擴展名和包
2.3.2、擴展的源碼文件
2.3.3、其他選項
2.4、發布和包的關系
2.5、安裝腳本
2.6、安裝package data
2.7、安裝其他文件
2.8、元數據
2.9、調試setup腳本
三、配置文件
四、源碼發布
4.1、指定發布的文件
4.2、Manifest相關選項
4.3、MANIFEST.in模板
五、構建發布(Built Distributions)
六、Distutils與PYPI
七、簡單示例
7.1、純Python發布(包)
7.3、單獨的擴展模塊
八、其他
Distutils可以用來在Python環境中構建和安裝額外的模塊。新的模塊可以是純Python的,也可以是用C/C++寫的擴展模塊,或者可以是Python包,包中包含了由C和Python編寫的模塊。
對於模塊開發者以及需要安裝模塊的使用者來說,Distutils的使用都很簡單,作為一個開發者,除了編寫源碼之外,還需要:
有些模塊開發者在開發時不會考慮多個平台發布,所以就有了packagers的角色,它們從模塊開發者那取得源碼發布,然後在多個平台上面進行構建,並發布多個平台的構建版本。
由python編寫的setup腳本一般都非常簡單。作為autoconf類型的配置腳本,setup腳本可以在構建和安裝模塊發布時運行多次。比如,如果需要發布一個叫做foo的模塊,它包含在一個文件foo.py,那setup腳本可以這樣寫:
from distutils.core import setup
setup(name='foo',
version='1.0',
py_modules=['foo'],
)
setup函數的參數表示提供給Distutils的信息,這些參數分為兩類:包的元數據(包名、版本號)以及包的信息(本例中是一個Python模塊的列表);模塊由模塊名表示,而不是文件名(對於包和擴展而言也是這樣);建議可以提供更多的元數據,比如你的名字,email地址和項目的URL地址。編寫好setup.py之後,就可以創建該模塊的源碼發布了:
python setup.py sdist
對於Windows而言,命令是:
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目錄,然後運行:
python setup.py install
該命令最終會將foo.py復制到Python環境存放第三方模塊的目錄中。在linux環境下,運行該命令的輸出是:
# 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
這個簡單的例子展示了Distutils的基本概念。第一,開發者和安裝者有同樣的用戶接口,也就是setup腳本,但他們使用的Distutils命令不同,sdist命令幾乎只有開發者使用,而install對於安裝者更常用。如果希望使用者的使用盡可能的簡單,則可以創建多個構建發布。比如,如果在Windows中,可以使用bdist_wininst命令創建一個exe安裝文件,下面的命令會在當前目錄中創建foo-1.0.win32.exe文件:
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):
python setup.py bdist_rpm
可以通過下面的命令得到當前支持的發布格式:
python setup.py bdist --help-formats
setup腳本是使用Distutils構建、發布和安裝模塊的核心。setup腳本的作用是向Distutils描述發布模塊的信息。從上面那個簡單的例子中可知,setup腳本主要是調用setup函數,而且模塊開發者向Distutils提供的模塊信息多數是由setup函數的關鍵字參數提供的。下面是一個更高級一些的例子:Distutils模塊本身的setup腳本:
setup(name='Distutils',
version='1.0',
description='Python Distribution Utilities',
author='Greg Ward',
author_email='[email protected]',
url='https://www.python.org/sigs/distutils-sig/',
packages=['distutils', 'distutils.command'],
)
上面這個腳本有更多的元數據,列出的是兩個包(packages),而不是列出每個模塊。因為Distutils包含多個模塊,這些模塊分成了兩個包;如果列出所有模塊的話則是冗長且難以維護的。注意,在setup腳本中的路徑必須以Unix形式來書寫,也就是由”/”分割的。Distutils會在使用這些路徑之前,將這種表示方法轉換為適合當前平台的格式。
Setup函數的packages參數是一個列表,其中包含了Distutils需要處理(構建、發布、安裝等)的所有包。要實現此目的,那麼包名和目錄名必須能夠相互對應,比如包名是distutils,則意味著在發布的根目錄(setup腳本所在目錄)下存在distutils子目錄;再比如在setup腳本中packages = ['foo'],意味著要在setup腳本所在目錄下存在相應的foo目錄和foo/__init__.py文件。比如如果setup腳本內容如下:
setup(name='foo',
version='1.0',
packages = ['foo']
)
而setup腳本所在目錄並沒有foo目錄(只有一個setup.py腳本),則在執行python setup.py bdist命令時,會打印如下錯誤:
error: package directory 'foo' does not exist
如果創建了foo目錄,但是沒有foo/__init__.py文件,則Distutils會產生下面的警告,但是仍會處理該包:
package init file 'foo/__init__.py' not found (or not a regular file)
可以使用package_dir選項來改變這種默認的對應規則。package_dir是個字典,其中的key是要安裝的包名,如果為空,則表明是root package,value就是該包(key)對應的源碼樹的目錄。比如如果setup.py內容如下:
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
另外一個例子,foo包對應lib目錄,所以,foo.bar包就對應著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
如果發布中僅包含較少的模塊,你可能更喜歡列出所有模塊,而不是列出包,特別是在root package中存在單一模塊的情況(或者根本就沒有包)。可以使用py_modules參數,比如下面的例子:
setup(name='foo',
version='1.0',
py_modules = ['mod1', 'pkg.mod2']
)
它描述了兩個模塊,一個在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選項改變這種對應關系。所以,源碼樹如下:
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
在Distutils中描述擴展模塊較描述純python模塊要復雜一些。對於純python模塊,僅需要列出模塊或包,然後Distutils就會去尋找合適的文件,這對於擴展模塊來說是不夠的,你還需要指定擴展名、源碼文件以及其他編譯/鏈接需要的參數(需要包含的目錄,需要連接的庫等等)。描述擴展模塊可以由setup函數的關鍵字參數ext_modules實現。ext_modules是Extension實例的列表,每一個Extension實例描述了一個獨立的擴展模塊。比如發布中包含一個獨立的擴展模塊稱為foo,由foo.c實現,且無需其他編譯鏈接指令,那麼下面的語句就可以描述該擴展模塊:
Extension('foo', ['foo.c'])
Extension可以從distutils.core中隨setup一起引入。因此,對於僅包含一個擴展模塊的發布來說,setup腳本如下:
from distutils.core import setup, Extension
setup(name='foo',
version='1.0',
ext_modules=[Extension('foo', ['foo.c'])],
)
底層的擴展構建機制是由build_ext命令實現的。Extension類在描述Python擴展時具有很大的靈活性。
通常,Extension類的構造函數的第一個參數都是擴展的名字,比如下面的語句:
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
又比如下面的語句:
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關鍵字,比如下面的語句:
setup(...,
ext_package='pkg',
ext_modules=[Extension('foo', ['src/foo.c']),
Extension('subpkg.bar', ['src/bar.c'])]
)
上面的描述將會編譯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
Extension構建函數的第二個參數是源文件的列表。目前Distutils僅支持C、C++和Objective-C擴展,所以這些源碼文件就是C、C++和Objective-C的源碼文件。(C++源碼文件的擴展名可以是.cc和.cpp,Unix和Windows編譯器都支持)。不過還可以在列表中包含SWIG接口文件(.i文件),build_ext命令知道如何處理SWIG接口文件。盡管會發生報警,但是可以像下面這樣傳遞SWIG選項:
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"
在一些系統上,該列表中還可以包含能由編譯器處理的非源碼文件。當前只支持Windows message 文本文件(.mc)和Visual C++的資源定義文件(.rc)。它們將會編譯為二進制文件.res並且鏈接進可執行文件中。
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
發布和包有三種關系:它依賴其他包,它服務於其他包,它淘汰其他包。這些關系可以分別用setup函數的參數requires ,provides 和obsoletes 來指定,具體參閱:https://docs.python.org/2/distutils/setupscript.html#relationships-between-distributions-and-packages。
模塊通常不自己運行,而是由腳本引入。除了可以安裝模塊之外,還可以安裝能直接運行的腳本,具體參閱https://docs.python.org/2/distutils/setupscript.html#installing-scripts。
有時包中還需要安裝其他文件,這些文件與包的實現密切相關,或者是包含文檔信息的文本文件等,這些文件就叫做package data。使用setup函數中的package_data參數可以向packages中添加package data。該參數的值必須是個字典,字典的key就是package name,value是個list,其中包含了需要復制到package中的一系列路徑。這些路徑都是相對於包目錄而言的(比如package_dir),所以,這些文件必須存在於包的源碼目錄中。在安裝時,也會創建相應的目錄。比如,如果包中有一個包含數據文件的子目錄,源碼樹如下:
setup.py
src/
mypkg/
__init__.py
module.py
data/
tables.dat
spoons.dat
forks.dat
相應的setup函數可以這樣寫:
setup(...,
packages=['mypkg'],
package_dir={'mypkg': 'src/mypkg'},
package_data={'mypkg': ['data/*.dat']},
)
可以通過data_files選項來安裝除了上面提到過的文件之外的其他文件,比如配置文件,數據文件等。data_files是個列表,列表中的元素是(directory, files),比如:
setup(...,
data_files=[('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
('config', ['cfg/data.cfg']),
('/etc/init.d', ['init-script'])]
)
(directory, files)中,directory表示文件最終要被安裝到的地方,如果它是相對路徑的話,則是相對於installation prefix而言(對於純python包而言,就是sys.prefix;對於擴展包,則是sys.exec_prefix)。files是要安裝的文件,其中的目錄信息(安裝前)是相對於setup.py所在目錄而言的,安裝時,setup.py根據files的信息找到該文件,然後將其安裝到directory中。
Setup腳本可以包含很多發布的元數據,比如名稱、版本、作者等信息,具體列表和注意信息,參閱https://docs.python.org/2/distutils/setupscript.html#additional-meta-data
如果在運行setup腳本是發生了錯誤,則Distutils會打印出簡單的錯誤信息,對於開發者而言這些錯誤信息可能不足以找到錯誤的原因。所以可以通過設置環境變量DISTUTILS_DEBUG,將其置為任意值(不能是空字符串),Distutils就會打印其執行過程的詳細信息,並且在發生異常時打印全部的traceback,並且在像C編譯器這樣的外部程序發生錯誤時,打印整個命令行。
一般情況下,在構建發布時無法將所有的選項都確定下來,有些選項的值可能來自於用戶,或者用戶的系統。這也就是配置文件setup.cfg存在的目的,用戶可以通過修改該配置文件進行選項的配置。在構建時,選項的處理順序是setup腳本、配置文件,命令行。所以,安裝者可以通過修改setup.cfg文件來覆蓋setup.py中的選項;也可以通過運行setup.py時的命令行選項,來覆蓋setup.cfg。
配置文件的基本語法如下:
[command]
option=value
...
command就是Distutils的命令(比如build_py,install等),option就是命令支持的選項。配置文件中的空行、注釋(以’#’開頭,直到行尾)會被忽略。可以通過--help選項得到某個命令支持的選項,比如:
> 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
[...]
注意,命令行中的選項”--foo-bar”,在配置文件中要寫成”foo_bar”。比如,運行以下命令:
python setup.py build_ext --inplace
如果不希望每次執行命令時都輸入”--inplace”選項,則可以在配置文件中寫明:
[build_ext]
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選項指定用戶和群組。比如:
python setup.py sdist --owner=root --group=root
如果沒有明確的列出需要發布的文件,則sdist命令默認在源碼發布中包含下列文件:
如果還需要發布其他額外的文件,典型的做法是編寫一個叫做MANIFEST.in的manifest模板。manifest模板包含如何創建MANIFEST文件的一系列指令,sdist命令會解析該模板,根據模板中的指令,以及找到的文件生成MANIFEST。文件MANIFEST中明確的列出了包含在源碼發布中的所有文件。比如下面就是一個MANIFEST文件的內容:
# file GENERATED by distutils, do NOT edit
setup.py
lib/__init__.py
lib/foo.py
lib/bar/__init__.py
lib/bar/bar.py
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文件,則可以使用如下命令:
python setup.py sdist --manifest-only
如果存在MANIFEST.in文件,則sdist命令就會根據該文件生成MANIFEST。在MANIFEST.in文件中,一行一個命令,每一個命令指定了源碼發布中需要包含或者需要排除的文件,比如下面的例子:
include *.txt
recursive-include examples *.txt *.py
prune examples/sample?/build
很容易看出,上面的命令的意思是:包含所有的.txt文件;包含examples目錄下的所有.txt或者.py文件;排除所有匹配examples/sample?/build的目錄。所有這些過程,都是在標准規則執行之後執行的,所以可以在模板文件中排除標准集合中的文件。關於MANIFEST文件的其他內容,參閱https://docs.python.org/2/distutils/sourcedist.html
所謂的構建發布(built distribution),即是指二進制包,或是指安裝文件。當然它未必真的是二進制,而有可能包含Python源碼和字節碼。構建發布是為了方便安裝者而創建的,比如對於基於RPM的Linux用戶來說,它可以是二進制RPM包,而對於Windows用戶來說,它可以是一個可執行的安裝文件等。創建包的構建發布,是前面介紹的packager的主要職責。它們拿到包的源碼發布之後,使用setup腳本以及bdist命令來生成構建發布。比如,在包的源碼樹中運行下面的命令:
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命令,該選項可用於指定生成的構建發布的格式,比如命令:
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
具體的子命令信息,可以參閱5. Creating Built Distributions — Python 2.7.18 documentation
7.1純Python發布(模塊)
如果只是發布幾個模塊,這些模塊沒有放在包中,可是使用py_modules選項。比如源碼樹如下:
setup.py
foo.py
bar.py
setup腳本如下:
from distutils.core import setup
setup(name='foobar',
version='1.0',
py_modules=['foo', 'bar'],
)
安裝之後,會生成以下文件:
\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
如果有很多模塊需要發布,則可以將這些模塊放到統一的包中,然後在setup腳本中指明要發布的包,而不是列出所有的模塊。即使模塊沒有放到包中,也可以通過向setup腳本聲明root包的方法來發布,與實際的包不同,根目錄下可以沒有__init__.py文件。比如上面的例子,源碼樹保持不變,setup腳本也可以這樣寫:
from distutils.core import setup
setup(name='foobar',
version='1.0',
packages=[''],
)
空字符串就意味著root包。安裝之後,生成的文件跟上面是一樣的。如果將源文件放到發布根目錄下的子目錄中,比如源碼樹:
setup.py
src/
foo.py
bar.py
這種情況依然可以用聲明root包的方式來發布,只不過需要使用package_dir選項來指明包和目錄的關系:
from distutils.core import setup
setup(name='foobar',
version='1.0',
package_dir={'': 'src'},
packages=[''],
)
安裝之後生成的文件跟之前是一樣的。更常見的做法是將多個模塊組織在同一個包中,比如在包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'],
)
安裝之後,會生成以下文件:
\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選項,比如源碼樹如下:
setup.py
src/
__init__.py
foo.py
bar.py
則相應的setup腳本如下:
from distutils.core import setup
setup(name='foobar',
version='1.0',
package_dir={'foobar': 'src'},
packages=['foobar'],
)
安裝之後生成的文件與上面的例子是一樣的。或者,直接將所有模塊放到發布的根目錄下:
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選項中明確的指出。不過,package_dir中的值卻會自動擴展到其子目錄。比如源碼樹如下:
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
擴展模塊由選項ext_modules指定。package_dir選項對擴展模塊的源碼文件沒有作用,它只影響純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
運行install命令,會首先運行build命令,然後運行子命令install_lib,install_data和install_scripts。