最近有一個(gè)小項(xiàng)目,有如下的需求:
將某幾個(gè)源碼文件夾進(jìn)行打包,文件夾內(nèi)有py文件、dll文件、exe文件等各種文件類(lèi)型
打包生成的安裝包,在進(jìn)行安裝的時(shí)候,應(yīng)該能夠帶有參數(shù),對(duì)配置文件進(jìn)行修改配置
安裝過(guò)程中,可以配置系統(tǒng)環(huán)境變量
能夠檢測(cè)環(huán)境,提示安裝依賴(lài)包
整個(gè)過(guò)程要可以自動(dòng)化,能夠大量部署
綜合考慮后,決定以下幾個(gè)步驟完成:
用setup.py將源碼文件夾都打包成msi安裝包,這樣可以使用msiexec進(jìn)行靜默安裝
setup.py可以提示用戶安裝依賴(lài)包,否則安裝失敗
再編寫(xiě)一個(gè)py文件,用來(lái)靜默安裝msi安裝包,并配置系統(tǒng)環(huán)境變量,接受安裝參數(shù)去修改配置文件的屬性
最后使用pyinstaller將所有都打包成exe文件
先來(lái)編寫(xiě)setup.py文件:
# coding=utf-8 from distutils.core import setup import os def get_all_dir(path): """ 獲取指定路徑下的所有文件 """ all_file = [] for dirpath, dirnames, filenames in os.walk(path): for filename in filenames: all_file.append(dirpath) return all_file if __name__ == '__main__': all_file = get_all_dir('A') + get_all_dir('B') # 獲取相對(duì)路徑下A和B兩個(gè)文件夾下的所有文件 setup(name='Example', # 所要安裝的軟件名 version="1.0", # 版本 description="This is example", # 對(duì)所安裝軟件的描述 author="author", # 作者 author_email='my email', # 郵箱 packages=all_file, # 要打包的文件 package_data={'': ['*.*']}, # 所有文件類(lèi)型都打包 classifiers=[ 'Development Status :: 5 - Production/Stable', 'Operating System :: Microsoft :: Windows', 'Natural Language :: Chinese (Simplified)', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Topic :: Software Development :: Libraries :: Python Modules' ], # 需要參照https://pypi.python.org/pypi?%3Aaction=list_classifiers,用于發(fā)布在PYPI上 install_requires=[ 'pyserial==3.2.1' ], # 依賴(lài)包,如果沒(méi)有安裝,會(huì)提示缺少,并安裝失敗 )
然后打開(kāi)setup.py所在目錄,并將A和B兩個(gè)文件夾復(fù)制過(guò)來(lái)
打開(kāi)dos窗口,并運(yùn)行
python setup.py bdist_msi
運(yùn)行結(jié)果如下圖:
build我們不關(guān)注,直接看dist,里面有一個(gè)Example-1.0.win32.msi,這就是我們生成的msi安裝包。
我們?cè)倬帉?xiě)一個(gè)Example.py用來(lái)配置系統(tǒng)環(huán)境變量,并接受安裝參數(shù)修改配置文件:
# coding=utf-8 import os import sys import subprocess config_file = r"C:\Python27\Lib\site-packages\B\lib\configuration\config.cfg" import sys from subprocess import check_call ### 設(shè)置系統(tǒng)環(huán)境變量所需代碼 if sys.hexversion > 0x03000000: import winreg else: import _winreg as winreg ENV_VARAIABLE = 'Result_Path' class Win32Environment: def __init__(self, scope): assert scope in ('user', 'system') self.scope = scope if scope == 'user': self.root = winreg.HKEY_CURRENT_USER self.subkey = 'Environment' else: self.root = winreg.HKEY_LOCAL_MACHINE self.subkey = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment' def getenv(self, name): key = winreg.OpenKey(self.root, self.subkey, 0, winreg.KEY_READ) try: value, _ = winreg.QueryValueEx(key, name) except WindowsError: value = '' return value def setenv(self, name, value): key = winreg.OpenKey(self.root, self.subkey, 0, winreg.KEY_ALL_ACCESS) winreg.SetValueEx(key, name, 0, winreg.REG_EXPAND_SZ, value) winreg.CloseKey(key) try: check_call('''\ "%s" -c "import win32api, win32con; assert win32api.SendMessage(win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment')"''' % sys.executable) except Exception as e: print e.message ### 設(shè)置系統(tǒng)環(huán)境變量所需代碼 end def search_content(str, lists): """ 查找str是否存在于lists中,不存在就退出程序 """ for i in lists: if str in i: return lists.index(i) print "The section not found" os._exit(1) def run_command_line(command_line): """ 運(yùn)行command line """ print("run:" + command_line) p = subprocess.Popen(command_line, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) (stdout, stderr) = p.communicate() try: print("stdout:" + stdout) print("stderr:" + stderr) except: pass def main(): # 靜默安裝MSI安裝包 run_command_line("msiexec /i " + sys.path[0] + r"\Example-1.0.win32.msi /qb REBOOT=SUPPRESS") # 接受參數(shù) section = sys.argv[1] attribute = sys.argv[2] change = sys.argv[3] # 讀取配置文件內(nèi)容 file = open(config_file, 'r') content = file.readlines() file.close() # 修改配置文件的某個(gè)屬性值 index = search_content(section, content) is_change = False for change_str in content[index + 1:]: if "[" in change_str: if not is_change: print "Property does not exist or not in this section" break if attribute in change_str: content[content.index(change_str)] = change_str[:change_str.index("=") + 1] + change + "\n" is_change = True break # 把修改后的內(nèi)容寫(xiě)入配置文件 file = open(config_file, 'w') for i in content: file.write(i) file.close() if __name__ == "__main__": # 如果沒(méi)有參數(shù),就默認(rèn)直接安裝MSI安裝包 # 如果有參數(shù),但是參數(shù)個(gè)數(shù)不足,直接報(bào)錯(cuò)退出 if len(sys.argv) == 1 and sys.argv[0] == "commonlib.exe": run_command_line("msiexec /i " + sys.path[0] + r"\Example-1.0.win32.msi /qb REBOOT=SUPPRESS") elif len(sys.argv) != 4: print "Usage: commonlib.py" sys.exit(1) else: main() # 設(shè)置系統(tǒng)環(huán)境變量 e = Win32Environment(scope="system") e.setenv(ENV_VARAIABLE, r'C:\Local') print "Setup Success!"
現(xiàn)在我們用Pyinstaller來(lái)進(jìn)行最后的打包。
先看一個(gè)重要的文件Example.spec
spec文件是Pyinstaller打包成EXE的配置文件,是自動(dòng)生成的,這里我直接拿以前的進(jìn)行修改,剛開(kāi)始沒(méi)有的,可以直接隨便運(yùn)行一次Pyinstaller來(lái)獲得,直接復(fù)制我的也可以。
# -*- mode: python -*- block_cipher = None a = Analysis(['Example.py'], # 主要打包的主py文件 pathex=['C:\\Users\\abc\\Documents'], # 打包路徑 binaries=None, datas=None, hiddenimports=[], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) a.datas+= [('Exmaple.msi', r'C:\Users\abc\Documents\Example-1.0.win32.msi', 'DATA'),]# 附加文件,打包時(shí)加入到EXE文件中,讓我們可以在py文件中調(diào)用 exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, # 打包文件列表 name='examlpe',# exe文件的名字 debug=False, strip=False, upx=True, console=True )
打開(kāi)Example.spec所在的路徑,復(fù)制MSI安裝包到這里,在dos窗口中運(yùn)行
pyinstaller Example.spec
運(yùn)行成功后,會(huì)生成build和dist兩個(gè)文件夾,我們依然只看dist文件夾,里面example.exe就是我們所需要的
以上這篇Python封裝成可帶參數(shù)的EXE安裝包實(shí)例就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
