注意: 雖然 JavaScript 對於本網站不是必需的,但您與內容的互動將受到限制。請開啟 JavaScript 以獲得完整的體驗。

“擴充套件構建”會議紀要

“擴充套件構建被認為是痛苦的”:會議紀要

作者:Greg Ward

在 IPC7 舉行的“擴充套件構建被認為是痛苦的”會議非常富有成效,與會者就所需內容、適用於各種使用者類別的方法以及可以從其他相關係統(最接近的是 Red Hat 的 RPM 和 Perl 的 MakeMaker)借鑑的想法達成了良好共識。

已做出的決定

大家似乎都同意我提出的操作模式:使用者下載一個包,解包,然後執行一個暫定名為 setup.py 的 Python 指令碼(這個名字與 Python 發行版和許多大型模組發行版中的模組描述檔案有所重疊,這是故意的,可以被視為一個 bug 或一個功能)。關於 setup.py 的引數以及執行時的行為,請參見下文。

此外,沒有人不同意我的觀點,即該系統在“擴充套件”(即用 C 編寫的模組)或普通的“模組”(用 Python 編寫)上應該以相同的使用者介面工作。人們似乎也接受了將所有東西——C 模組、Python 模組,最終還有文件——“構建”到一個臨時“構建庫”目錄的想法,暫定名為 blib/。(這是 Perl 的 MakeMaker 少數幾個在會議中倖存下來的實現細節之一。)blib/ 目錄至少有兩個目的:它使安裝幾乎微不足道,並且它為執行測試指令碼提供了一個看起來真實的偽安裝樹。blib/ 樹的實際結構尚未確定(儘管我對它應該是什麼樣子有明確的想法!)

我們稍微討論了術語。這將是一個棘手的問題,必須是我們將在 SIG 上解決的首批問題之一。首先,moduleextension module(簡稱 extension)和 package 將保留其傳統的 Python 含義:一個旨在被其他模組或指令碼匯入的單個 .py 檔案、一個用 C 編寫的模組以及一個包含 __init__.py 檔案的目錄下分組的模組集合。

我建議使用 distribution(或在需要絕對清晰時使用 module distribution)來替代已經被佔用的 package。一個模組發行版包含一個或多個模組(包括擴充套件模組)、它們的文件、測試套件以及一個 setup.py 檔案。(文件和測試套件是可選的,但強烈推薦,特別是如果有人能想出一種標準的方法來文件化 Python 模組的話。但一個發行版必須至少有一個模組和一個 setup.py)。一個單獨的發行版將向世界呈現多種面貌:開發者的原始碼樹、原始碼發行版、各種二進位制發行版、最終安裝的產品等。(據我所知,原始碼和二進位制發行版是 Trove 將稱之為 resources 的東西。)

我們沒有時間決定的一個事情是這個傻東西的名字。對於一個長而正式的名字,我投票給 Module Distribution Utilities,簡稱 distutils。短名稱將用於將所有各種模組分組到一個包中:例如,我們計劃有 distutils.builddistutils.installdistutils.gen_make 等等。

setup.py 的作用

setup.py 的計劃介面和任務在整個會議期間迅速演變,主要由 Eric Raymond 確定的勞動分工和 Greg Stein 清晰記錄的,以及由???繪製並由我擴充套件的工作流程圖(見下文)驅動。

顯然,setup.py 必須包含 1) 有關包的元資料,以及 2) 為當前機器配置發行版所需的任何自定義程式碼。目前尚不清楚這些應該如何表達。第一個想法是借鑑 MakeMaker 的想法,呼叫一個帶有一堆命名引數的函式,然後該函式在幕後完成所有工作。使用此方案的 setup.py 示例如下:

    #!/usr/bin/env python

    import distutils

    distutils.setup (name = 'mymod',
                     version_from = 'mymod.py',
                     pyfiles = ['mymod.py', 'othermod.py'],
                     cfiles = ['myext.c'])
    
顯然,自定義配置程式碼將在呼叫 distutils.setup 之前或之後用 Python 編寫;目前尚不清楚如何使此程式碼與 distutils.setup 背後的一切進行互動。

主要的競爭想法是以更面向物件的方式做事,透過定義一個子類並覆蓋各種屬性和方法。這是一個關於該概念的即興說明:

    #!/usr/bin/env python

    from distutils import Setup

    class MySetup (Setup):
        name = 'mydist
        version_from = 'mymod.py'
        pyfiles = ['mymod.py', 'othermod.py']
        cfiles = ['myext.c']
    
在這種情況下,覆蓋所有 distutils 類的特定行為會更清楚一些:只需根據需要子類化並覆蓋即可。顯然,所有類都必須有良好的文件!

模組分發的工作流程

下圖說明了開發、打包、構建和安裝模組分發所涉及的工作流程(但不包括測試或文件,這些最終也應是計劃的一部分)。

Diagram of module distribution workflow

請注意圖中出現的這三種人

開發者
基礎原始碼樹的建立者/維護者
打包者
a) 某人(可能是開發者,戴著他的“打包者”帽子),他將基礎原始碼樹轉換為原始碼分發;或 b) 某人(可能是開發者,也可能是一個擁有執行 X 機器的朋友,也可能是一個可以訪問執行 X 機器的歸檔機器人),他構建原始碼分發以建立架構 X 的“構建分發”
使用者
那個可憐的傢伙,他想在他的機器上安裝分發;不一定是瞭解(或想了解)任何 Python 知識的人
(這些是 Eric Raymond 和 Greg Stein 確定的勞動分工。)還要注意,開發者、打包者和使用者都面帶微笑。此功能已計劃,但尚未實施。

開發者工具

顯然,工作流程從頂部的開發者原始碼樹開始。當開發者努力工作時,他可能需要一個瞭解如何構建 Python 模組和擴充套件模組(尤其是後者)的 Makefile。他無需自己編寫,而是可以要求 setup.py 為他生成一個(大概使用 distutils.gen_make 模組)

    ./setup.py gen_make
    
然後開發者可以像他可能習慣做的那樣執行 make, make test 等等(假設他是一個老式的 Unix 程式設計師!)。如果他不喜歡 Makefiles,或者因為這是一個微不足道的小專案而不需要一個,他可以直接要求 setup.py 構建、測試等。
    ./setup.py build
    ./setup.py test
    
(我們的想法是 setup.py 將支援“命令”——buildtest 等——這些命令與 Makefile 目標相對應。這樣,沒有人需要依賴 Makefile,但可以為開發者的方便和效率生成一個(尤其是在處理包含大量待編譯擴充套件模組的大型發行版時)。)

打包者工具

當開發者對他的模組的當前狀態滿意併到了釋出的時候,他戴上他的“打包者”帽子並建立了一個原始碼釋出

    make dist
    # or, equivalently
    ./setup.py dist
    
這會將發行版中的所有檔案(如 MANIFEST 檔案中列出的)打包成某種存檔檔案——可能在 Unix 下是 .tar.gz,在 Windows 下是 .zip 等。存檔檔案的名稱將源自模組發行版的名稱和版本:例如 mydist-1.2.3.tar.gz

如果他願意,開發者可以就此止步,並將他的原始碼釋出上傳到存檔。或者,他可以為他可以訪問的所有架構建立構建發行版。(請注意,我明確避免使用更熟悉的術語二進位制發行版。這是因為模組發行版可能只包含 .py 檔案及其相關文件。但即使在這些情況下,也有理由需要一個可以立即安裝的可下載資源。主要原因是保持一致性:如果新手使用者只需要處理一種檔案來分發 Python 模組(例如,Red Hat Linux 使用者可以下載並安裝一批 RPM;無論這些 RPM 包含 .py 檔案還是 .so 檔案或兩者都有,這都無關緊要),那就很好了。其次,可能有一些非二進位制檔案是從原始碼釋出中的檔案生成的,例如從 SGML 源生成的 man 頁面。Unix 平臺的構建發行版可能包含已準備好安裝的 man 頁面,因此不需要進行文件處理。)

重要的是要強調打包者作為一個獨立於開發者的概念。這對於支援多個平臺的構建發行版是必要的,因為沒有多少開發者能夠訪問多種 Unix 變體、Windows 和 Mac——他們可能需要一些幫助來為一個或多個平臺製作構建發行版。這種幫助可能來自一個朋友(在走廊對面或世界各地)他確實可以訪問特定平臺;它可能來自一個自願為某些平臺保持某些發行版最新的人;或者它可能採取自動化此過程的存檔機器人的形式。在遍歷這個列表時,安全問題變得越來越重要。

我設想了幾種可能的介面來建立構建發行版;此外,自開發者日以來,“啞”與“智慧”構建發行版的想法一直在我的腦海中形成。(因此,它可能不屬於這裡,因為這是開發者日會議的摘要。所以起訴我吧。)首先,考慮建立一個傳統的 Unix 構建發行版:一個要解包到 /usr/local 下的 .tar.gz 檔案(或者,在 Python 庫上下文中,更可能是 /usr/local/lib/python1.x)。這可以透過以下方式實現:

    ./setup.py bdist
    
這將進行一次構建(將模擬安裝樹放入 ./blib/),並將構建樹打包到一個以發行版名稱、版本號和當前平臺命名的存檔檔案,例如 mydist-1.2.3-sunos5.tar.gzmydist-1.2.3-win32.zip

然而,人們對 Red Hat 的 RPM 等“智慧”安裝程式很感興趣(我得到的印象是 Windows 世界有幾個相互競爭的可能性——來自黑暗面的人必須向我補充這一點)。我目前的想法是,對於每個這些安裝程式,都應該有一個單獨的命令(或 Makefile 目標),因此你可以在 Red Hat Linux 機器上執行

    make rpm
    
在 Red Hat Linux 機器上,以及
    setup.py xxx
    
在 Windows 機器上(其中 xxx 是 Windows 某種智慧安裝程式的縮寫名稱)。然而,支援老式的“啞”構建發行版模型很重要——不是每個人都會擁有那個花哨的新安裝程式(或者他們可能擁有不同的智慧安裝程式)。

使用者工具

最後,也許最重要的是,我們必須考慮希望在他的計算機上安裝 Python 模組發行版的幸運使用者。使用者形形色色,但我們主要關注兩個區別:

  • 構建發行版使用者:任何在流行平臺上可獲得(或必需:許多 Mac 和 Windows 使用者沒有編譯器)構建發行版的使用者
  • 原始碼發行版使用者:在不太流行的平臺上,很可能可以獲得編譯器(以及其他可能必要的工具)的使用者
顯然,對於只想安裝一些模組(可能是純 Python,也可能是擴充套件——這無關緊要!)以使其他功能正常工作的初級使用者來說,事情應該完全無痛且簡單。像 RPM 這樣的智慧安裝程式會有所幫助,但從“啞”構建發行版或原始碼發行版開始也應該幾乎同樣容易。我們還必須記住,許多必須使用原始碼發行版的人不一定是程式設計師,他們只是想讓這個傻東西安裝並工作——所以使用原始碼發行版應該像使用構建發行版一樣容易(儘管它會需要更多的機器時間和更多的命令)。即使是經驗豐富的駭客,他們可以深入原始碼並進行修改,或者修補 Makefile,或者提供所需庫的位置,但他們很少願意做這些事情。

下一步去哪裡

首先,我認為這個話題足夠大,值得一個新的 SIG,我暫定稱之為 distutils-sig。該 {將要|已} 釋出的章程已提交給 meta-sig,所以如果你認為整個概念是無望的,並且想在我開始之前就將我扼殺,或者如果你認為這個名字很糟糕,就去那裡吧。

SIG 成立後,我想花一些時間討論元問題:有人強烈反對這個想法嗎?“distutils”這個名字足夠好嗎?第一階段應該有哪些功能,以及完整發布需要哪些功能?然後我們就可以深入研究具體的設計和實現問題。