我的项目结构中有多个子模块,这些子模块本质上也是项目本身。
对于给定的python项目,每个项目都包含子项目。我希望能够编写一个宏,它可以创建一组要在给定子项目上执行的操作。
基本上是WORKSPACE中的这些内容:
load("@io_bazel_rules_python//:python/pip.bzl",
"pip_import"
)
pip_import(
name = "foo",
requirements = "@subprojectname//:requirements.txt",
)
load("@foo//:requirements.bzl", "pip_install")
pip_install()
我想为上述行动做多个子项目。但是,如果我将所有代码放入bzl文件中的函数中。
pyrules.bzl
load("@io_bazel_rules_python//:python/pip.bzl",
"pip_import"
)
def doit(name):
pip_import(
name = "foo",
requirements = "@{repo}//:requirements.txt".format(repo=name)
)
load("@foo//:requirements.bzl", "pip_install")
pip_install()
由于"加载':期望表达式"的语法错误,我无法使用第二个加载命令。如果没有多个加载到父WORKSPACE的bzl文件,还有其他方法可以创建可重用的逻辑块吗?
更新1。 评论要求有关工作流程的更多信息。警告,我有一个非标准的布局,如何使用Bazel其他地方,即不是monorepo。但它应该采用类似的实用布局。
项目布局如下:
projectname/
WORKSPACE
BUILD
src/
stuff
submodule/ # < git modules that are checked out into folder
subproject1/
BUILD
src/
stuff
subproject2/
BUILD
src/
stuff
首先,我有一个在WORKSPACE中加载的repository_rule,它找到子模块文件夹中的所有项目并将它们添加为存储库。在子项目中,其中一些是python。我想加载其requirements.txt文件。因此,总体问题是这些requirements.txt文件在哪里,并在它们上执行pip安装。理想情况下,我希望子项目BUILD文件中的py_library定义知道子项目的需求文件存在依赖性,但这可能并不重要,因为父BUILD文件是唯一创建par_binaries的东西,所以on,所以只要pip_install和设置依赖项发生,项目本身就应该可用。
Bazel似乎不允许子项目定义他们自己的存储库操作,如上面的pip_install。我假设这是因为您无法在BUILD文件中拥有存储库操作,并且子WORKSPACE文件似乎没有任何效果。所以我最终不得不将其添加到父WORKSPACE文件中。
如果它在父WORKSPACE中,我必须复制粘贴我想要使用的每个子项目的pip操作。但是,我更愿意设置一个通用规则来定位需求文件并将其安装到pip中。但是,尝试从此创建宏意味着我无法在其中使用加载调用。所有pip操作似乎都需要与存储库操作进行交互,然后需要仅从父WORKSPACE文件调用这些操作。
答案 0 :(得分:0)
对于此方案,看起来不存在任何合理的解决方案。但是,我认为如果您愿意在内部支持它们,那么实现您所依赖的规则是很重要的。我最终修改了rules_python存储库中的piptool.py,以便在一次调用中添加对多个requirements.txt文件的支持。然后将以下规则添加到我在内部使用的规则库中。
#
# Python
#
def _pip_import_requirements_impl(repository_ctx):
"""Core implementation of pip_import."""
# Add an empty top-level BUILD file.
# This is because Bazel requires BUILD files along all paths accessed
# via //this/sort/of:path and we wouldn't be able to load our generated
# requirements.bzl without it.
repository_ctx.file("BUILD", "")
# To see the output, pass: quiet=False
result = repository_ctx.execute([
"python3", repository_ctx.path(repository_ctx.attr._script),
"--name", repository_ctx.attr.name,
"--input"
] +
[repository_ctx.path(r) for r in repository_ctx.attr.requirements] +
[
"--output", repository_ctx.path("requirements.bzl"),
"--directory", repository_ctx.path("")
],
quiet = repository_ctx.attr.quiet)
if result.return_code:
fail("pip_import failed: %s (%s)" % (result.stdout, result.stderr))
pip_import_requirements = repository_rule(
attrs = {
"quiet" : attr.bool(default = False),
"requirements": attr.label_list(
allow_files = True,
mandatory = True,
),
"_script": attr.label(
executable = True,
default = Label("@io_bazel_rules_python//tools:piptool.par"),
cfg = "host",
),
},
implementation = _pip_import_requirements_impl,
)
然后我可以在我的WORKSPACE中执行以下操作。
pip_import_requirements(
name = "py_requirements",
requirements = [
"@mycore//:requirements.txt",
"@myother//:requirements.txt"
]
)
load(
"@py_requirements//:requirements.bzl",
"pip_install",
)
pip_install()
以下在我碰巧需要的任何BUILD文件中。请注意,py_requirements引用随后将始终可用于项目中的任何BUILD文件。
load(
"@py_requirements//:requirements.bzl",
"all_requirements"
)
par_binary(
name = "funkyserver",
main = "src/main.py",
srcs = glob(["src/**/*.py"]),
deps = [
"@mycore//:core",
"@myother//:other"
] + all_requirements,
)
对于piptool.py。您需要使用rules_python repo中的update_tools.sh重建piptool.par。
diff --git a/rules_python/piptool.py b/rules_python/piptool.py
index f5d504a..ab520d8 100644
--- a/rules_python/piptool.py
+++ b/rules_python/piptool.py
@@ -87,7 +87,7 @@ def pip_main(argv):
parser.add_argument('--name', action='store',
help=('The namespace of the import.'))
-parser.add_argument('--input', action='store',
+parser.add_argument('--input', action='store', nargs='+',
help=('The requirements.txt file to import.'))
parser.add_argument('--output', action='store',
@@ -154,7 +154,8 @@ def main():
args = parser.parse_args()
# https://github.com/pypa/pip/blob/9.0.1/pip/__init__.py#L209
- if pip_main(["wheel", "-w", args.directory, "-r", args.input]):
+ if pip_main(["wheel", "-w", args.directory] + [p for x in [
+ ('-r', i) for i in args.input] for p in x]):
sys.exit(1)
# Enumerate the .whl files we downloaded.
@@ -219,7 +220,7 @@ def requirement(name):
if name_key not in _requirements:
fail("Could not find pip-provided dependency: '%s'" % name)
return _requirements[name_key]
-""".format(input=args.input,
+""".format(input=" ".join(args.input),
whl_libraries='\n'.join(map(whl_library, whls)) if whls else "pass",
mappings=whl_targets))