forked from bazel-contrib/rules_python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsite_init_template.py
199 lines (158 loc) · 6.91 KB
/
site_init_template.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# Copyright 2024 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""site initialization logic for Bazel-built py_binary targets."""
import os
import os.path
import sys
# Colon-delimited string of runfiles-relative import paths to add
_IMPORTS_STR = "%imports%"
# Though the import all value is the correct literal, we quote it
# so this file is parsable by tools.
_IMPORT_ALL = "%import_all%" == "True"
_WORKSPACE_NAME = "%workspace_name%"
# runfiles-relative path to this file
_SELF_RUNFILES_RELATIVE_PATH = "%site_init_runfiles_path%"
# Runfiles-relative path to the coverage tool entry point, if any.
_COVERAGE_TOOL = "%coverage_tool%"
def _is_verbose():
return bool(os.environ.get("RULES_PYTHON_BOOTSTRAP_VERBOSE"))
def _print_verbose_coverage(*args):
if os.environ.get("VERBOSE_COVERAGE") or _is_verbose():
_print_verbose(*args)
def _print_verbose(*args, mapping=None, values=None):
if not _is_verbose():
return
print("bazel_site_init:", *args, file=sys.stderr, flush=True)
_print_verbose("imports_str:", _IMPORTS_STR)
_print_verbose("import_all:", _IMPORT_ALL)
_print_verbose("workspace_name:", _WORKSPACE_NAME)
_print_verbose("self_runfiles_path:", _SELF_RUNFILES_RELATIVE_PATH)
_print_verbose("coverage_tool:", _COVERAGE_TOOL)
def _find_runfiles_root():
# Give preference to the environment variables
runfiles_dir = os.environ.get("RUNFILES_DIR", None)
if not runfiles_dir:
runfiles_manifest_file = os.environ.get("RUNFILES_MANIFEST_FILE", "")
if runfiles_manifest_file.endswith(
".runfiles_manifest"
) or runfiles_manifest_file.endswith(".runfiles/MANIFEST"):
runfiles_dir = runfiles_manifest_file[:-9]
# Be defensive: the runfiles dir should contain ourselves. If it doesn't,
# then it must not be our runfiles directory.
if runfiles_dir and os.path.exists(
os.path.join(runfiles_dir, _SELF_RUNFILES_RELATIVE_PATH)
):
return runfiles_dir
num_dirs_to_runfiles_root = _SELF_RUNFILES_RELATIVE_PATH.count("/") + 1
runfiles_root = os.path.dirname(__file__)
for _ in range(num_dirs_to_runfiles_root):
runfiles_root = os.path.dirname(runfiles_root)
return runfiles_root
_RUNFILES_ROOT = _find_runfiles_root()
_print_verbose("runfiles_root:", _RUNFILES_ROOT)
def _is_windows():
return os.name == "nt"
def _get_windows_path_with_unc_prefix(path):
path = path.strip()
# No need to add prefix for non-Windows platforms.
if not _is_windows() or sys.version_info[0] < 3:
return path
# Starting in Windows 10, version 1607(OS build 14393), MAX_PATH limitations have been
# removed from common Win32 file and directory functions.
# Related doc: https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later
import platform
if platform.win32_ver()[1] >= "10.0.14393":
return path
# import sysconfig only now to maintain python 2.6 compatibility
import sysconfig
if sysconfig.get_platform() == "mingw":
return path
# Lets start the unicode fun
unicode_prefix = "\\\\?\\"
if path.startswith(unicode_prefix):
return path
# os.path.abspath returns a normalized absolute path
return unicode_prefix + os.path.abspath(path)
def _search_path(name):
"""Finds a file in a given search path."""
search_path = os.getenv("PATH", os.defpath).split(os.pathsep)
for directory in search_path:
if directory:
path = os.path.join(directory, name)
if os.path.isfile(path) and os.access(path, os.X_OK):
return path
return None
def _setup_sys_path():
seen = set(sys.path)
python_path_entries = []
def _maybe_add_path(path):
if path in seen:
return
path = _get_windows_path_with_unc_prefix(path)
if _is_windows():
path = path.replace("/", os.sep)
_print_verbose("append sys.path:", path)
sys.path.append(path)
seen.add(path)
for rel_path in _IMPORTS_STR.split(":"):
abs_path = os.path.join(_RUNFILES_ROOT, rel_path)
_maybe_add_path(abs_path)
if _IMPORT_ALL:
repo_dirs = sorted(
os.path.join(_RUNFILES_ROOT, d) for d in os.listdir(_RUNFILES_ROOT)
)
for d in repo_dirs:
if os.path.isdir(d):
_maybe_add_path(d)
else:
_maybe_add_path(os.path.join(_RUNFILES_ROOT, _WORKSPACE_NAME))
# COVERAGE_DIR is set if coverage is enabled and instrumentation is configured
# for something, though it could be another program executing this one or
# one executed by this one (e.g. an extension module).
# NOTE: Coverage is added last to allow user dependencies to override it.
coverage_setup = False
if os.environ.get("COVERAGE_DIR"):
cov_tool = _COVERAGE_TOOL
if cov_tool:
_print_verbose_coverage(f"Using toolchain coverage_tool {cov_tool}")
elif cov_tool := os.environ.get("PYTHON_COVERAGE"):
_print_verbose_coverage(
f"Using env var coverage: PYTHON_COVERAGE={cov_tool}"
)
if cov_tool:
if os.path.isabs(cov_tool):
pass
elif os.sep in os.path.normpath(cov_tool):
cov_tool = os.path.join(_RUNFILES_ROOT, cov_tool)
else:
cov_tool = _search_path(cov_tool)
if cov_tool:
# The coverage entry point is `<dir>/coverage/coverage_main.py`, so
# we need to do twice the dirname so that `import coverage` works
coverage_dir = os.path.dirname(os.path.dirname(cov_tool))
# coverage library expects sys.path[0] to contain the library, and replaces
# it with the directory of the program it starts. Our actual sys.path[0] is
# the runfiles directory, which must not be replaced.
# CoverageScript.do_execute() undoes this sys.path[0] setting.
_maybe_add_path(coverage_dir)
coverage_setup = True
else:
_print_verbose_coverage(
"Coverage was enabled, but the coverage tool was not found or valid. "
+ "To enable coverage, consult the docs at "
+ "https://rules-python.readthedocs.io/en/latest/coverage.html"
)
return coverage_setup
COVERAGE_SETUP = _setup_sys_path()
_print_verbose("DONE")