Skip to content

Commit ec22580

Browse files
committed
Improve compatibility with rules_pycross
Adds support for recursing into directory deps, and improves site packages path determination.
1 parent da0a91b commit ec22580

File tree

2 files changed

+58
-23
lines changed

2 files changed

+58
-23
lines changed

build_env.py

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
# limitations under the License.
1414

1515
import collections
16-
import functools
1716
import itertools
1817
import json
1918
import os
@@ -22,7 +21,7 @@
2221
import sys
2322
import textwrap
2423
import venv
25-
from typing import Dict, List, Optional, Union
24+
from typing import Dict, List, Optional
2625

2726
import importlib_metadata
2827

@@ -45,25 +44,40 @@ def console_script(env_path: pathlib.Path, module: str, func: str) -> str:
4544
)
4645

4746

48-
def get_site_packages_path(path: str, imports: List[str]) -> Optional[str]:
49-
if not path.startswith("../"):
50-
return path
47+
def path_starts_with(path: pathlib.Path, prefix: pathlib.Path) -> bool:
48+
return path.parts[: len(prefix.parts)] == prefix.parts
49+
50+
51+
def get_site_packages_path(
52+
workspace: str, path: pathlib.Path, imports: List[pathlib.Path]
53+
) -> Optional[pathlib.Path]:
54+
# Import prefixes start with the workspace name, which might be the local workspace.
55+
# We first normalize the given path so that it starts with its workspace name.
56+
if path.parts[0] == "..":
57+
wspath = path.relative_to("..")
58+
is_external = True
59+
else:
60+
wspath = pathlib.Path(workspace) / path
61+
is_external = False
5162

5263
for imp in imports:
53-
# Newer versions of rules_python use a site-packages/ prefix, so we also check for that.
54-
prefixes = [f"../{imp}/site-packages/", f"../{imp}/"]
55-
for prefix in prefixes:
56-
if path.startswith(prefix):
57-
return path[len(prefix) :]
64+
if path_starts_with(wspath, imp):
65+
return wspath.relative_to(imp)
66+
67+
if not is_external:
68+
# If the input wasn't an external path and it didn't match any import prefixes,
69+
# just return it as given.
70+
return path
5871

5972
# External file that didn't match imports. Include but warn.
60-
parts = path.split("/", maxsplit=2)
61-
include_path = parts[2]
73+
# We include it as relative to its workspace directory, so strip the first component
74+
# off wspath.
75+
include_path = wspath.relative_to(wspath.parts[0])
6276
print(f"Warning: [{path}] didn't match any imports. Including as [{include_path}]")
6377

6478

65-
def is_external(file_: str) -> bool:
66-
return file_.startswith("../")
79+
def is_external(file_: pathlib.Path) -> bool:
80+
return file_.parts[0] == ".."
6781

6882

6983
def find_site_packages(env_path: pathlib.Path) -> pathlib.Path:
@@ -82,21 +96,32 @@ def find_site_packages(env_path: pathlib.Path) -> pathlib.Path:
8296
def get_files(build_env_input: Dict) -> List[EnvFile]:
8397
files = []
8498

85-
imports = build_env_input["imports"]
99+
imports = [pathlib.Path(imp) for imp in build_env_input["imports"]]
100+
print(imports)
101+
workspace = build_env_input["workspace"]
86102
for depfile in build_env_input["files"]:
87103
# Bucket files into external and workspace groups.
88104
# Only generated workspace files are kept.
89105
type_ = depfile["t"]
90-
path = depfile["p"]
106+
input_path = pathlib.Path(depfile["p"])
91107

92-
site_packages_path = get_site_packages_path(path, imports)
93-
if not site_packages_path:
108+
# Only add external and generated files
109+
if not (is_external(input_path) or type_ == "G"):
94110
continue
95111

96-
if is_external(path):
97-
files.append(EnvFile(pathlib.Path(path), pathlib.Path(site_packages_path)))
98-
elif type_ == "G":
99-
files.append(EnvFile(pathlib.Path(path), pathlib.Path(site_packages_path)))
112+
# If this is a directory, expand to each recursive child.
113+
if input_path.is_dir():
114+
paths = input_path.glob("**/*")
115+
paths = [p for p in paths if not p.is_dir()]
116+
else:
117+
paths = [input_path]
118+
119+
for path in paths:
120+
site_packages_path = get_site_packages_path(workspace, path, imports)
121+
if not site_packages_path:
122+
continue
123+
124+
files.append(EnvFile(path, site_packages_path))
100125

101126
return files
102127

venv.bzl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@
1414

1515
load("@rules_python//python:defs.bzl", "py_binary")
1616

17+
PYTHON_TOOLCHAIN_TYPE = "@bazel_tools//tools/python:toolchain_type"
18+
1719
def _py_venv_deps_impl(ctx):
20+
toolchain_depset = ctx.toolchains[PYTHON_TOOLCHAIN_TYPE].py3_runtime.files or depset()
21+
toolchain_files = {f: None for f in toolchain_depset.to_list()}
22+
1823
imports = []
1924
for dep in ctx.attr.deps:
2025
if PyInfo not in dep:
@@ -26,12 +31,16 @@ def _py_venv_deps_impl(ctx):
2631

2732
files = []
2833
for dep in deps.to_list():
29-
if dep.is_directory:
34+
# Skip files that are provided by the python toolchain.
35+
# They don't need to be in the venv.
36+
if dep in toolchain_files:
3037
continue
38+
3139
typ = "S" if dep.is_source else "G"
3240
files.append({"t": typ, "p": dep.short_path})
3341

3442
doc = {
43+
"workspace": ctx.workspace_name,
3544
"imports": imports,
3645
"files": files,
3746
"commands": ctx.attr.commands,
@@ -47,6 +56,7 @@ _py_venv_deps = rule(
4756
"commands": attr.string_list(),
4857
"output": attr.output(),
4958
},
59+
toolchains = [PYTHON_TOOLCHAIN_TYPE],
5060
)
5161

5262
def py_venv(name, deps = None, extra_pip_commands = None):

0 commit comments

Comments
 (0)