关于这个问题,在网上看了很多帖子,都无法解决我的问题,于是决定从源码入手,分析一波。
先罗列一下其它网友的解决方案,或许对某些朋友会有帮助,最后在写一下我自己的源码分析思路。如有不对的地方,还请各路大神指出。
原创不易,如转载请注明出处!
报错:
6058 WARNING: lib not found: api-ms-win-core-path-l1-1-0.dll dependency of C:\Users\lvliang\AppData\Local\Programs\Python\Python39\python39.dll
报错意思是python39.dll动态库依赖于api-ms-win-core-path-l1-1-0.dll动态库文件,但是本地计算机上缺少这个文件,我用everything工具本地搜索了一下,确实是没有这个文件。
于是从官网下载了一个
https://cn.dll-files.com/api-ms-win-core-path-l1-1-0.dll.html
AppData\Local\Programs\Python\Python39和系统目录%windir%\SysWOW64下
-- 重新执行pyinstaller打包操作,还是报lib not found
C:\Windows\SysWOW64\downlevel
C:\windows\system32
C:\Windows\System32\downlevel
同时将AppData\Local\Programs\Python\Python39和以上目录加入path环境变量中
--重新执行pyinstaller打包操作,还是报lib not found
3.1 首先使用pyinstaller -F xxx.py文件打下包,会生成一个xxx.spec文件
3.2 删掉build和dist目录,修改spec文件,在pathex中添加上api-ms-win-core-path-l1-1-0.dll文件所在的目录
3.3 使用pyinstaller -F xxx.spec 命令打包
--还是报lib not found
使用regsrv32 xxx.dll命令执行了下发现注册失败
于是下载了一个depends.exe工具,打开这个dll文件看了下函数输出表,没有找到以下两个函数
DllRegisterServer
DllUnregisterServer
说明这个dll文件是不需要注册即可使用的
我使用的是python3.9
找到class类
class Analysis(Target):
方法:
def assemble(self):
跳转到def Dependencies(lTOC, xtrapath=None, manifest=None, redirects=None):方法
selectImports这个方法中会去查到python39.dll所需要的所有动态库dll,而且这边出现了lib not found的报错
def selectImports(pth, xtrapath=None):
rv = []
if xtrapath is None:
xtrapath = [os.path.dirname(pth)]
else:
assert isinstance(xtrapath, list)
xtrapath = [os.path.dirname(pth)] + xtrapath # make a copy
dlls = getImports(pth)
for lib in dlls:
if lib.upper() in seen:
continue
if not compat.is_win:
# all other platforms
npth = lib
lib = os.path.basename(lib)
else:
# plain win case
npth = getfullnameof(lib, xtrapath)
if lib == 'api-ms-win-crt-convert-l1-1-0.dll':
print(npth)
# now npth is a candidate lib if found
# check again for excludes but with regex FIXME: split the list
if npth:
candidatelib = npth
else:
candidatelib = lib
if not dylib.include_library(candidatelib):
if (candidatelib.find('libpython') < 0 and
candidatelib.find('Python.framework') < 0):
# skip libs not containing (libpython or Python.framework)
if npth.upper() not in seen:
logger.debug("Skipping %s dependency of %s",
lib, os.path.basename(pth))
continue
else:
pass
if npth:
if npth.upper() not in seen:
logger.debug("Adding %s dependency of %s from %s",
lib, os.path.basename(pth), npth)
rv.append((lib, npth))
elif dylib.warn_missing_lib(lib):
logger.warning("lib not found: %s dependency of %s", lib, pth)
return rv
结合这段代码,跳转到getfullnameof函数中
def getfullnameof(mod, xtrapath=None):
pywin32_paths = []
if compat.is_win:
pywin32_paths = [os.path.join(get_python_lib(), 'pywin32_system32')]
if compat.is_venv:
pywin32_paths.append(
os.path.join(compat.base_prefix, 'Lib', 'site-packages',
'pywin32_system32')
)
epath = (sys.path + # Search sys.path first!
pywin32_paths +
winutils.get_system_path() +
compat.getenv('PATH', '').split(os.pathsep))
if xtrapath is not None:
if type(xtrapath) == type(''):
epath.insert(0, xtrapath)
else:
epath = xtrapath + epath
for p in epath:
npth = os.path.join(p, mod)
if os.path.exists(npth) and matchDLLArch(npth):
return npth
return ''
从getfullnameof函数中可以得到很多信息,python会优先去python39安装目录找dll文件
然后还会从以下目录去找
'C:\\Windows\\SysWOW64\\downlevel',
'C:\\Windows\\System32\\downlevel',
'C:\\windows\\system32',
'C:\\windows',
'D:\\Program Files\\JetBrains\\PyCharm 2021.2\\bin'
'C:\\Users\\lys4989\\AppData\\Local\\Programs\\Python\\Python39\\DLLs',
'C:\\Users\\lys4989\\AppData\\Local\\Programs\\Python\\Python39\\lib',
'C:\\Users\\lys4989\\AppData\\Local\\Programs\\Python\\Python39\\lib\\site-packages',
那说明前面第1,2两步的做法没错,既然文件存在,那就一定是matchDLLArch(npth)判断失败了
看一下matchDLLArch方法
def matchDLLArch(filename):
# TODO: check machine type on other platforms?
if not compat.is_win:
return True
global _exe_machine_type
try:
if _exe_machine_type is None:
pefilename = compat.python_executable # for exception handling
exe_pe = pefile.PE(pefilename, fast_load=True)
_exe_machine_type = exe_pe.FILE_HEADER.Machine
exe_pe.close()
pefilename = filename # for exception handling
pe = pefile.PE(filename, fast_load=True)
match_arch = pe.FILE_HEADER.Machine == _exe_machine_type
pe.close()
except pefile.PEFormatError as exc:
raise SystemExit('Can not get architecture from file: %s\n'
' Reason: %s' % (pefilename, exc))
return match_arch
在源码中添加一些打印代码,发现matchDLLArch返回为false,说明我的dll文件是32位的,而操作系统版本是64位的
到这边,已经找到了一直报lib not found的原因了,虽然dll文件确实存在但是文件版本和操作系统版本不匹配,python也是一样输出lib not found。(我觉得这一块是不是可以优化一下源代码,这报错信息太折磨人了。)
在网上下载一个64位dll文件
使用以下python脚本测试一下
import pefile
pefile.fast_load = True
exe_pe = pefile.PE('D:\\api-ms-win-core-path-l1-1-0.dll', fast_load=True)
_exe_machine_type = exe_pe.FILE_HEADER.Machine
exe_pe.close()
print(_exe_machine_type)
输出结果为34404(如果是32位的dll文件,会输出332),表明是64位的,再次执行pyinstaller打包,问题解决。
总结一下:
对于我的这个问题,python3.9 + 64位操作系统,我只需要下载一个64位的dll文件,放到python安装目录下,就可以了。
由于官网上只有32位的文件,我在个人资源中添加了一个64位的dll文件。
原创不易,如转载请注明出处!