用Python写了个使用pyusb库的模仿JLink Commander的应用:https://github.com/XIVN1987/DAPCmdr
为了方便同事使用,于是用“pyinstaller -F path/to/DAPCmdr.py”将其打包成单个exe可执行文件,,
可是打包后程序执行报错“No backend available”,,无法发现和连接DAPLink,,根据之前调试pyusb的经验,,这是pyusb库找不到libusb.dll导致的
可是源码形式下可以找到libusb.dll,,为啥打包后就不行了呢??经过搜索和阅读源码发现,,原来pyinstaller修改了pyusb搜索libusb.dll的方式。。
pyusb原本的libusb.dll搜索代码执行流程如下:
_load_library() # usb\backend\libusb1.py
usb.libloader.load_locate_library('libusb-1.0')
load_locate_library(name) # usb\libloader.py
ctypes.util.find_library(name + '.dll')
find_library(name) # ctypes\util.py
for directory in os.environ['PATH'].split(os.pathsep):
fname = os.path.join(directory, name)
if os.path.isfile(fname):
return fname
简单来说pyusb最终是通过ctypes.util.find_library搜索libusb.dll的,,而ctypes.util.find_library是在环境变量PATH包含的路径下搜索libusb.dll的,,
我为了不修改系统变量PATH依然能够执行DAPCmdr.py,所以在DAPCmdr.py的起始处添加了下面一行代码:
os.environ['PATH'] = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'libusb-1.0.24/MinGW64/dll') + os.pathsep + os.environ['PATH']
也就是把DAPCmdr.py所在目录下的 libusb-1.0.24/MinGW64/dll 目录路径加入环境变量 PATH,这样 ctypes.util.find_library 就能从 DAPCmdr.py 所在路径下搜索到 libusb.dll,而且不管 DAPCmdr 目录拷贝到哪里,DAPCmdr.py 都能正常启动执行。。
而 Pyinstaller 打包使用 pyusb 库的程序时,会用到 site-packages\\_pyinstaller_hooks_contrib\\hooks\\rthooks\\pyi_rth_usb.py 文件,其内容如下:
def get_load_func(type, candidates):
def _load_library(find_library=None):
exec_path = sys._MEIPASS
l = None
for candidate in candidates:
# Do linker's path lookup work to force load bundled copy.
if sys.platform == 'win32' or sys.platform == 'cygwin':
libs = glob.glob("%s\\%s*.dll" % (exec_path, candidate))
else:
libs = glob.glob("%s/%s*.so*" % (exec_path, candidate))
for libname in libs:
try:
l = ctypes.WinDLL(libname)
if l is not None:
break
except:
l = None
if l is not None:
break
else:
raise OSError('USB library could not be found')
return l
return _load_library
import usb.backend.libusb1 as libusb10
libusb10._load_library = get_load_func('libusb10', ('usb-1.0', 'libusb-1.0', 'usb'))
可以看到,它用自己编写的 _load_library 函数替代了usb.backend.libusb1 自身的 _load_library 函数,,而它自己编写的 _load_library 是在打包生成的 DAPCmdr.exe 文件所在的目录下查找 libusb.dll 的。。
所以我按照源码目录结构把 libusb-1.0.24 文件夹拷贝到 DAPCmdr.exe 所在目录,,DAPCmdr.exe 执行时是无法搜索到 libusb.dll 的。。只有将 libusb-1.0.24 文件夹下的 libusb-1.0.dll 文件拷贝到 DAPCmdr.exe 所在目录,,DAPCmdr.exe 执行时才能搜索到 libusb.dll
离线
@小智
我主要是分析为什么打包后pyusb无法定位到libusb.dll,,知道了原因应对方法就很简单了,,修改spec文件或者使用--add-binary命令行参数都可以。。
离线