Skip to content

Commit dd2e947

Browse files
UebelAndregroodt
andauthored
Fix missing python includes for Unix hosts (#809)
* Fix missing python includes for Unix hosts * Update python/pip_install/pip_repository.bzl Co-authored-by: Greg Roodt <[email protected]>
1 parent 4999f63 commit dd2e947

File tree

2 files changed

+95
-28
lines changed

2 files changed

+95
-28
lines changed

python/pip_install/pip_repository.bzl

Lines changed: 71 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
""
22

3-
load("//python:repositories.bzl", "STANDALONE_INTERPRETER_FILENAME")
3+
load("//python:repositories.bzl", "is_standalone_interpreter")
44
load("//python/pip_install:repositories.bzl", "all_requirements")
55
load("//python/pip_install/private:srcs.bzl", "PIP_INSTALL_PY_SRCS")
66

@@ -66,35 +66,68 @@ def _resolve_python_interpreter(rctx):
6666
fail("python interpreter `{}` not found in PATH".format(python_interpreter))
6767
return python_interpreter
6868

69-
def _maybe_set_xcode_location_cflags(rctx, environment):
70-
"""Patch environment with CPPFLAGS of xcode sdk location.
69+
def _get_xcode_location_cflags(rctx):
70+
"""Query the xcode sdk location to update cflags
7171
7272
Figure out if this interpreter target comes from rules_python, and patch the xcode sdk location if so.
7373
Pip won't be able to compile c extensions from sdists with the pre built python distributions from indygreg
7474
otherwise. See https://github.com/indygreg/python-build-standalone/issues/103
7575
"""
76-
if (
77-
rctx.os.name.lower().startswith("mac os") and
78-
rctx.attr.python_interpreter_target != None and
79-
# This is a rules_python provided toolchain.
80-
rctx.execute([
81-
"ls",
82-
"{}/{}".format(
83-
rctx.path(Label("@{}//:WORKSPACE".format(rctx.attr.python_interpreter_target.workspace_name))).dirname,
84-
STANDALONE_INTERPRETER_FILENAME,
85-
),
86-
]).return_code == 0 and
87-
not environment.get(CPPFLAGS)
88-
):
89-
xcode_sdk_location = rctx.execute(["xcode-select", "--print-path"])
90-
if xcode_sdk_location.return_code == 0:
91-
xcode_root = xcode_sdk_location.stdout.strip()
92-
if COMMAND_LINE_TOOLS_PATH_SLUG not in xcode_root.lower():
93-
# This is a full xcode installation somewhere like /Applications/Xcode13.0.app/Contents/Developer
94-
# so we need to change the path to to the macos specific tools which are in a different relative
95-
# path than xcode installed command line tools.
96-
xcode_root = "{}/Platforms/MacOSX.platform/Developer".format(xcode_root)
97-
environment[CPPFLAGS] = "-isysroot {}/SDKs/MacOSX.sdk".format(xcode_root)
76+
77+
# Only run on MacOS hosts
78+
if not rctx.os.name.lower().startswith("mac os"):
79+
return []
80+
81+
# Only update the location when using a hermetic toolchain.
82+
if not is_standalone_interpreter(rctx, rctx.attr.python_interpreter_target):
83+
return []
84+
85+
# Locate xcode-select
86+
xcode_select = rctx.which("xcode-select")
87+
88+
xcode_sdk_location = rctx.execute([xcode_select, "--print-path"])
89+
if xcode_sdk_location.return_code != 0:
90+
return []
91+
92+
xcode_root = xcode_sdk_location.stdout.strip()
93+
if COMMAND_LINE_TOOLS_PATH_SLUG not in xcode_root.lower():
94+
# This is a full xcode installation somewhere like /Applications/Xcode13.0.app/Contents/Developer
95+
# so we need to change the path to to the macos specific tools which are in a different relative
96+
# path than xcode installed command line tools.
97+
xcode_root = "{}/Platforms/MacOSX.platform/Developer".format(xcode_root)
98+
return [
99+
"-isysroot {}/SDKs/MacOSX.sdk".format(xcode_root),
100+
]
101+
102+
def _get_toolchain_unix_cflags(rctx):
103+
"""Gather cflags from a standalone toolchain for unix systems.
104+
105+
Pip won't be able to compile c extensions from sdists with the pre built python distributions from indygreg
106+
otherwise. See https://github.com/indygreg/python-build-standalone/issues/103
107+
"""
108+
109+
# Only run on Unix systems
110+
if not rctx.os.name.lower().startswith(("mac os", "linux")):
111+
return []
112+
113+
# Only update the location when using a standalone toolchain.
114+
if not is_standalone_interpreter(rctx, rctx.attr.python_interpreter_target):
115+
return []
116+
117+
er = rctx.execute([
118+
rctx.path(rctx.attr.python_interpreter_target).realpath,
119+
"-c",
120+
"import sys; print(f'{sys.version_info[0]}.{sys.version_info[1]}')",
121+
])
122+
if er.return_code != 0:
123+
fail("could not get python version from interpreter (status {}): {}".format(er.return_code, er.stderr))
124+
_python_version = er.stdout
125+
include_path = "{}/include/python{}".format(
126+
rctx.path(Label("@{}//:WORKSPACE".format(rctx.attr.python_interpreter_target.workspace_name))).dirname.realpath,
127+
_python_version,
128+
)
129+
130+
return ["-isystem {}".format(include_path)]
98131

99132
def _parse_optional_attrs(rctx, args):
100133
"""Helper function to parse common attributes of pip_repository and whl_library repository rules.
@@ -152,10 +185,20 @@ def _create_repository_execution_environment(rctx):
152185
153186
Args:
154187
rctx: The repository context.
155-
Returns: Dictionary of envrionment variable suitable to pass to rctx.execute.
188+
Returns:
189+
Dictionary of environment variable suitable to pass to rctx.execute.
156190
"""
157-
env = {"PYTHONPATH": _construct_pypath(rctx)}
158-
_maybe_set_xcode_location_cflags(rctx, env)
191+
192+
# Gather any available CPPFLAGS values
193+
cppflags = []
194+
cppflags.extend(_get_xcode_location_cflags(rctx))
195+
cppflags.extend(_get_toolchain_unix_cflags(rctx))
196+
197+
env = {
198+
"PYTHONPATH": _construct_pypath(rctx),
199+
CPPFLAGS: " ".join(cppflags),
200+
}
201+
159202
return env
160203

161204
_BUILD_FILE_CONTENTS = """\

python/repositories.bzl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,30 @@ def py_repositories():
3737

3838
STANDALONE_INTERPRETER_FILENAME = "STANDALONE_INTERPRETER"
3939

40+
def is_standalone_interpreter(rctx, python_interpreter_target):
41+
"""Query a python interpreter target for whether or not it's a rules_rust provided toolchain
42+
43+
Args:
44+
rctx (repository_ctx): The repository rule's context object.
45+
python_interpreter_target (Target): A target representing a python interpreter.
46+
47+
Returns:
48+
bool: Whether or not the target is from a rules_python generated toolchain.
49+
"""
50+
51+
# Only update the location when using a hermetic toolchain.
52+
if not python_interpreter_target:
53+
return False
54+
55+
# This is a rules_python provided toolchain.
56+
return rctx.execute([
57+
"ls",
58+
"{}/{}".format(
59+
rctx.path(Label("@{}//:WORKSPACE".format(rctx.attr.python_interpreter_target.workspace_name))).dirname,
60+
STANDALONE_INTERPRETER_FILENAME,
61+
),
62+
]).return_code == 0
63+
4064
def _python_repository_impl(rctx):
4165
if rctx.attr.distutils and rctx.attr.distutils_content:
4266
fail("Only one of (distutils, distutils_content) should be set.")

0 commit comments

Comments
 (0)