Nuitka簡(jiǎn)介:編譯和分發(fā)Python的更好方法
譯文譯者 | 李睿
審校 | 孫淑娟
隨著Python越來(lái)越受歡迎,其局限性也越來(lái)越明顯。一方面,編寫(xiě)Python應(yīng)用程序并將其分發(fā)給沒(méi)有安裝Python的人員可能非常困難。
解決這一問(wèn)題的最常見(jiàn)方法是將程序與其所有支持庫(kù)和文件以及Python運(yùn)行時(shí)打包在一起。有一些工具可以做到這一點(diǎn),例如PyInstaller,但它們需要大量的緩存才能正常工作。更重要的是,通??梢詮纳傻陌刑崛ython程序的源代碼。在某些情況下,這會(huì)破壞交易。
第三方項(xiàng)目Nuitka提供了一個(gè)激進(jìn)的解決方案。它將Python程序編譯為C語(yǔ)言二進(jìn)制文件——不是通過(guò)將CPython運(yùn)行時(shí)與程序字節(jié)碼打包,而是通過(guò)將Python指令翻譯成C語(yǔ)言。其結(jié)果能夠以壓縮包的形式分發(fā),也可以與其他第三方產(chǎn)品一起打包到安裝程序中。
Nuitka還試圖保持與Python生態(tài)系統(tǒng)的最大兼容性,因此NumPy等第三方庫(kù)可以可靠地工作。Nuitka還盡可能地對(duì)編譯后的Python程序進(jìn)行性能改進(jìn),但同樣不會(huì)犧牲整體兼容性。但不能保證加快速度,因此它們?cè)诠ぷ髫?fù)載之間變化很大,并且某些程序可能不會(huì)體驗(yàn)到任何顯著的性能改進(jìn)。一般來(lái)說(shuō),最好不要依賴(lài)Nuitka來(lái)提高性能,而是作為捆綁解決方案。
安裝Nuitka
Nuitka可以與Python 2.6到2.7和Python3.3到3.10配合使用。它可以為Microsoft Windows、macOS、Linux和FreeBSD/NetBSD編譯二進(jìn)制文件。需要注意的是,開(kāi)發(fā)人員必須在目標(biāo)平臺(tái)上構(gòu)建二進(jìn)制文件;不能交叉編譯。
對(duì)于每個(gè)平臺(tái),除了需要Python運(yùn)行時(shí)外,還需要一個(gè)C編譯器。在Microsoft Windows上,建議使用Visual Studio 2022或更高版本,但也可以使用MinGW-w64 C11(gcc 11.2或更高)。對(duì)于其他平臺(tái),可以在Visual Studio下的Windows上使用gcc 5.1或更高版本g++4.4或更高級(jí)別、clang或clang cl。
需要注意的是,如果使用Python 3.3或Python 3.4,由于工具依賴(lài)性,將需要Python 2.7。如果可以的話(huà),所有這些都應(yīng)該成為使用最新版本Python的理由。
最好將Nuitka與項(xiàng)目一起安裝在虛擬環(huán)境中,作為開(kāi)發(fā)依賴(lài)項(xiàng)而不是分發(fā)依賴(lài)項(xiàng)。Nuitka本身并未與項(xiàng)目捆綁在一起或被其項(xiàng)目使用;它執(zhí)行捆綁。
首次使用Nuitka
在安裝Nuitka之后,使用Nuitka或python-m nuitka調(diào)用它。
開(kāi)發(fā)人員想要使用Nuitka做的第一件事是驗(yàn)證整個(gè)工具鏈?zhǔn)欠裾9ぷ?,包括C編譯器。要對(duì)此進(jìn)行測(cè)試,可以編譯一個(gè)簡(jiǎn)單的“Hello world”P(pán)ython程序,將其命名為main.py:
使用Nuitka編譯Python程序時(shí),將入口點(diǎn)模塊的名稱(chēng)作為參數(shù)傳遞給Nuitka,例如Nuitka main.py。當(dāng)這樣調(diào)用時(shí),Nuitka將接收main.py并從中構(gòu)建一個(gè)可執(zhí)行文件。
需要注意的是,因?yàn)橹皇窃跍y(cè)試Nuitka的功能,所以它只會(huì)將該P(yáng)ython文件編譯為可執(zhí)行文件。它不會(huì)編譯任何其他內(nèi)容,也不會(huì)捆綁任何內(nèi)容以進(jìn)行重新分發(fā)。但是編譯一個(gè)文件應(yīng)該足以確定Nuitka的工具鏈?zhǔn)欠裨O(shè)置正確。
在編譯完成后,應(yīng)該會(huì)看到與Python程序位于同一目錄中的二進(jìn)制可執(zhí)行文件。運(yùn)行可執(zhí)行文件以確保其正常工作。
還可以通過(guò)將--run作為命令行標(biāo)志傳遞自動(dòng)運(yùn)行Nuitka編譯的應(yīng)用程序。
如果“Hello world”測(cè)試可執(zhí)行文件有效,可以嘗試將其打包為可再發(fā)行文件。以下解釋這個(gè)過(guò)程。
需要注意的是,當(dāng)使用Nuitka運(yùn)行第一個(gè)測(cè)試編譯時(shí),它可能會(huì)在幾秒鐘內(nèi)完成。而這只編譯一個(gè)模塊,而不是整個(gè)程序。使用Nuitka編譯完整的程序可能需要幾分鐘或更長(zhǎng)時(shí)間,具體取決于程序使用的模塊數(shù)量。
使用Nuitka編譯Python程序
在默認(rèn)情況下,Nuitka只編譯指定的模塊。如果模塊有來(lái)自程序中其他地方、標(biāo)準(zhǔn)庫(kù)或第三方包的導(dǎo)入,則需要指定也應(yīng)該編譯這些導(dǎo)入。
考慮修改后的“Hello world”程序,其中有一個(gè)名為greet.py的相鄰模塊:
和修改后的main.py:
要編譯這兩個(gè)模塊,可以使用--follow-imports開(kāi)關(guān):
該開(kāi)關(guān)確保整個(gè)程序所需的所有導(dǎo)入都從導(dǎo)入語(yǔ)句中跟蹤并一起編譯。
另一個(gè)選項(xiàng)--nofollow-import-to允許從導(dǎo)入過(guò)程中排除特定的子目錄。這一選項(xiàng)對(duì)于篩選出知道從未使用過(guò)的測(cè)試套件或模塊很有用。它還允許提供通配符作為參數(shù)。
圖1.使用Nuitka編譯大型復(fù)雜程序。這個(gè)示例涉及編譯Pyglet模塊以及標(biāo)準(zhǔn)庫(kù)中的許多模塊,這需要幾分鐘的時(shí)間
(1)包括動(dòng)態(tài)導(dǎo)入
現(xiàn)在出現(xiàn)了Python用戶(hù)在嘗試打包Python應(yīng)用程序以進(jìn)行分發(fā)時(shí)經(jīng)常遇到的問(wèn)題之一。--follow-imports選項(xiàng)僅遵循通過(guò)import語(yǔ)句在代碼中顯式聲明的導(dǎo)入。它不處理動(dòng)態(tài)導(dǎo)入。
為了解決這個(gè)問(wèn)題,可以使用--include-plugin-directory開(kāi)關(guān)為動(dòng)態(tài)導(dǎo)入的模塊提供一個(gè)或多個(gè)路徑。例如,對(duì)于包含動(dòng)態(tài)導(dǎo)入代碼的名為mods的目錄,可以使用:
(2)包括數(shù)據(jù)文件和目錄
如果Python程序使用在運(yùn)行時(shí)加載的數(shù)據(jù)文件,Nuitka也無(wú)法自動(dòng)檢測(cè)這些文件。要將單個(gè)文件和目錄包含在Nuitka打包程序中,可能使用--include-data-files和--include-data-dir。
--include-data-files允許為要復(fù)制的文件指定通配符以及要將它們復(fù)制到的位置。例如,--include-data dir=/path/to/data=data會(huì)將/path.to/data中的所有內(nèi)容復(fù)制到分發(fā)目錄中的匹配目錄數(shù)據(jù)。
-include-data-dir的工作方式大致相同,只是它不使用通配符;它只允許傳遞要復(fù)制的路徑和要將其復(fù)制到的分發(fā)文件夾中的目標(biāo)。例如,--include-data dir=/path/to/data=data會(huì)將/path.to/data中的所有內(nèi)容復(fù)制到分發(fā)目錄中的匹配目錄數(shù)據(jù)。
(3)包括Python包和模塊
指定導(dǎo)入的另一種方法是使用Python樣式的包命名空間而不是文件路徑,使用--include-package選項(xiàng)。例如,以下命令將包括mypackage,它在磁盤(pán)上的任何位置(假設(shè)Python可以找到它),以及它下面的所有內(nèi)容:
如果包需要自己的數(shù)據(jù)文件,可以使用--include-package-data選項(xiàng)包含這些文件:
該命令告訴Nuitka獲取包目錄中實(shí)際上不是代碼的所有文件。
如果只想包含單個(gè)模塊,可以使用--include-module:
該命令告訴Nuitka只包含mypackage.mymodule,而不包含其他內(nèi)容。
編譯Python程序進(jìn)行重新分發(fā)
當(dāng)想用Nuitka編譯Python程序以進(jìn)行重新分發(fā)時(shí),可以使用命令行開(kāi)關(guān)--standalone來(lái)處理大部分工作。此開(kāi)關(guān)自動(dòng)跟隨所有導(dǎo)入并生成一個(gè)dist文件夾,其中包含已編譯的可執(zhí)行文件和所需的任何支持文件。要重新分發(fā)程序,只需要復(fù)制此目錄即可。
不要指望--standalone的程序在第一次運(yùn)行時(shí)就可以工作。Python程序的一般動(dòng)態(tài)性幾乎保證了需要使用上述其他一些選項(xiàng)來(lái)確保編譯的程序正常運(yùn)行。例如,如果有一個(gè)需要特定字體的GUI應(yīng)用程序,可能必須使用--include-data-files或--include-data-dir將它們復(fù)制到發(fā)行版中。
此外,如上所述,--standalone應(yīng)用程序的編譯時(shí)間可能比測(cè)試編譯長(zhǎng)得多。一旦對(duì)測(cè)試獨(dú)立構(gòu)建的應(yīng)用程序需要多長(zhǎng)時(shí)間有所了解,就為測(cè)試獨(dú)立構(gòu)建的應(yīng)用程序所需的構(gòu)建時(shí)間進(jìn)行預(yù)算。
最后,Nuitka提供了另一個(gè)構(gòu)建選項(xiàng)--onefile。對(duì)于那些熟悉PyInstaller的人來(lái)說(shuō),--onefile的工作方式與該程序中的相同選項(xiàng)相同:它將整個(gè)應(yīng)用程序(包括其所有依賴(lài)文件)壓縮為單個(gè)可執(zhí)行文件,無(wú)需重新分發(fā)其他文件。但是,重要的是要知道--onefile在Linux和Microsoft Windows上的工作方式不同。在Linux上,它使用存檔的內(nèi)容安裝一個(gè)虛擬文件系統(tǒng)。在Windows上,它會(huì)將文件解壓縮到一個(gè)臨時(shí)目錄中并從那里運(yùn)行它們,它必須為程序的每次運(yùn)行執(zhí)行這一操作。在Windows上使用--onefile可能會(huì)顯著降低啟動(dòng)程序所需的時(shí)間。
原文標(biāo)題:??Intro to Nuitka: A better way to compile and distribute Python??,作者:Serdar Yegulalp