如何将调用其他脚本的Bazel生成脚本打包为Pip Wheel
尝试为Bazel生成的py_binary脚本创建wheel包时遇到路径问题,简化示例如下:
目录结构
. ├── MANIFEST.in ├── bin │ ├── __init__.py │ ├── mytest │ └── mytest.lib │ ├── used1 │ └── used2 └── setup.py
关键文件内容
bin/mytest(入口脚本,无法修改):
#!/bin/bash bash $0.lib/used1 bash $0.lib/used2
该脚本依赖$0.lib寻找与自身同目录的mytest.lib子目录脚本。
- 原
setup.py:
import setuptools setuptools.setup( name="mytest", version="0.0.1", maintainer="", maintainer_email="", packages=setuptools.find_packages(), install_requires=[], scripts=["bin/mytest"], include_package_data=True, )
MANIFEST.in:
graft bin
安装后问题
执行python setup.py bdist_wheel和pip install dist/mytest-0.0.1-py3-none-any.whl后,运行mytest报错:MY_ENV/bin/mytest.lib/used1不存在,核心问题:
mytest.lib被安装到MY_ENV/lib/python3.8/site-packages/bin,而非脚本所在的MY_ENV/bin;- 原脚本
mytest要求mytest.lib与自身同目录,但安装后两者路径分离。
限制:无法修改Bazel生成的mytest和mytest.lib文件,但可添加其他文件或修改打包配置。
方案一:把mytest.lib直接装到脚本同目录
修改setup.py,用data_files指定将mytest.lib目录安装到环境的bin文件夹,和mytest脚本放在一起,原脚本的路径逻辑就能正常工作:
import setuptools import os # 遍历mytest.lib下的所有文件,整理成data_files需要的格式 lib_files = [] lib_root = "bin/mytest.lib" for root, _, files in os.walk(lib_root): for file in files: src_path = os.path.join(root, file) # 目标路径为bin/mytest.lib/对应子路径 dest_path = os.path.join("bin", os.path.relpath(root, "bin")) lib_files.append((dest_path, [src_path])) setuptools.setup( name="mytest", version="0.0.1", maintainer="", maintainer_email="", packages=setuptools.find_packages(), install_requires=[], scripts=["bin/mytest"], data_files=lib_files, # 关闭include_package_data避免重复打包 # include_package_data=True, )
同时修改MANIFEST.in确保文件被正确包含:
include bin/mytest graft bin/mytest.lib
安装后,mytest和mytest.lib都会出现在MY_ENV/bin目录,原脚本的$0.lib就能找到依赖文件。
方案二:加个包装脚本做入口
创建一个新的bash脚本bin/mytest_wrapper,代替原mytest作为wheel的入口,让它自动找到mytest.lib的实际安装路径:
#!/bin/bash # 自动获取site-packages下的bin目录路径 SITE_PACKAGES_BIN=$(python -c "import site; print(site.getsitepackages()[0])")/bin # 直接执行mytest.lib下的脚本,绕开原脚本的路径限制 bash "$SITE_PACKAGES_BIN/mytest.lib/used1" bash "$SITE_PACKAGES_BIN/mytest.lib/used2"
然后修改setup.py,把包装脚本作为入口,并且安装后命名为mytest:
import setuptools setuptools.setup( name="mytest", version="0.0.1", maintainer="", maintainer_email="", packages=setuptools.find_packages(), install_requires=[], # 将包装脚本安装为mytest命令 scripts=[("mytest", "bin/mytest_wrapper")], include_package_data=True, )
方案三:用Python入口脚本动态找路径
利用bin目录下的__init__.py将其作为Python子包,创建bin/mytest_entry.py脚本:
import os import subprocess import site # 获取site-packages下的bin目录路径 site_bin = os.path.join(site.getsitepackages()[0], "bin") # 执行mytest.lib下的脚本 used1_path = os.path.join(site_bin, "mytest.lib", "used1") used2_path = os.path.join(site_bin, "mytest.lib", "used2") subprocess.run(["bash", used1_path], check=True) subprocess.run(["bash", used2_path], check=True)
修改setup.py,用console_scripts生成mytest命令:
import setuptools setuptools.setup( name="mytest", version="0.0.1", maintainer="", maintainer_email="", packages=setuptools.find_packages(), install_requires=[], entry_points={ 'console_scripts': [ 'mytest = bin.mytest_entry:main', ], }, include_package_data=True, )
同时确保MANIFEST.in包含所有文件:
graft bin
安装后,mytest命令会调用这个Python脚本,自动找到mytest.lib的位置并执行脚本。
内容的提问来源于stack exchange,提问作者XuanInsr

