forked from bazel-contrib/rules_python
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathenv_marker_setting.bzl
More file actions
141 lines (122 loc) · 4.54 KB
/
env_marker_setting.bzl
File metadata and controls
141 lines (122 loc) · 4.54 KB
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
"""Implement a flag for matching the dependency specifiers at analysis time."""
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load("//python/private:common_labels.bzl", "labels")
load("//python/private:toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE")
load(":env_marker_info.bzl", "EnvMarkerInfo")
load(":pep508_env.bzl", "create_env", "set_missing_env_defaults")
load(":pep508_evaluate.bzl", "evaluate")
# Use capitals to hint its not an actual boolean type.
_ENV_MARKER_TRUE = "TRUE"
_ENV_MARKER_FALSE = "FALSE"
def env_marker_setting(*, name, expression, **kwargs):
"""Creates an env_marker setting.
Generated targets:
* `is_{name}_true`: config_setting that matches when the expression is true.
* `{name}`: env marker target that evalutes the expression.
Args:
name: {type}`str` target name
expression: {type}`str` the environment marker string to evaluate
**kwargs: {type}`dict` additional common kwargs.
"""
native.config_setting(
name = "is_{}_true".format(name),
flag_values = {
":{}".format(name): _ENV_MARKER_TRUE,
},
**kwargs
)
_env_marker_setting(
name = name,
expression = expression,
**kwargs
)
def _env_marker_setting_impl(ctx):
env = create_env()
env.update(
ctx.attr._env_marker_config_flag[EnvMarkerInfo].env,
)
runtime = ctx.toolchains[TARGET_TOOLCHAIN_TYPE].py3_runtime
if "python_version" not in env:
if runtime.interpreter_version_info:
version_info = runtime.interpreter_version_info
env["python_version"] = "{major}.{minor}".format(
major = version_info.major,
minor = version_info.minor,
)
full_version = _format_full_version(version_info)
env["python_full_version"] = full_version
env["implementation_version"] = full_version
else:
env["python_version"] = _get_flag(ctx.attr._python_version_major_minor_flag)
full_version = _get_flag(ctx.attr._python_full_version_flag)
env["python_full_version"] = full_version
env["implementation_version"] = full_version
if "implementation_name" not in env and runtime.implementation_name:
env["implementation_name"] = runtime.implementation_name
set_missing_env_defaults(env)
if evaluate(ctx.attr.expression, env = env):
value = _ENV_MARKER_TRUE
else:
value = _ENV_MARKER_FALSE
return [config_common.FeatureFlagInfo(value = value)]
_env_marker_setting = rule(
doc = """
Evaluates an environment marker expression using target configuration info.
See
https://packaging.python.org/en/latest/specifications/dependency-specifiers
for the specification of behavior.
""",
implementation = _env_marker_setting_impl,
attrs = {
"expression": attr.string(
mandatory = True,
doc = "Environment marker expression to evaluate.",
),
"_env_marker_config_flag": attr.label(
default = labels.PIP_ENV_MARKER_CONFIG,
providers = [EnvMarkerInfo],
),
"_python_full_version_flag": attr.label(
default = labels.PYTHON_VERSION,
providers = [config_common.FeatureFlagInfo],
),
"_python_version_major_minor_flag": attr.label(
default = labels.PYTHON_VERSION_MAJOR_MINOR,
providers = [config_common.FeatureFlagInfo],
),
},
provides = [config_common.FeatureFlagInfo],
toolchains = [
TARGET_TOOLCHAIN_TYPE,
],
)
def _format_full_version(info):
"""Format the full python interpreter version.
Adapted from spec code at:
https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers
Args:
info: The provider from the Python runtime.
Returns:
a {type}`str` with the version
"""
kind = info.releaselevel
if kind == "final":
kind = ""
serial = ""
else:
kind = kind[0] if kind else ""
serial = str(info.serial) if info.serial else ""
return "{major}.{minor}.{micro}{kind}{serial}".format(
v = info,
major = info.major,
minor = info.minor,
micro = info.micro,
kind = kind,
serial = serial,
)
def _get_flag(t):
if config_common.FeatureFlagInfo in t:
return t[config_common.FeatureFlagInfo].value
if BuildSettingInfo in t:
return t[BuildSettingInfo].value
fail("Should not occur: {} does not have necessary providers")