/
isolated.py
128 lines (101 loc) · 4.47 KB
/
isolated.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
from __future__ import unicode_literals
import json
from collections import namedtuple
import six
from packaging.requirements import Requirement
from packaging.utils import canonicalize_name
from tox import reporter
from tox.config import DepConfig, get_py_project_toml
from tox.constants import BUILD_ISOLATED, BUILD_REQUIRE_SCRIPT
BuildInfo = namedtuple("BuildInfo", ["requires", "backend_module", "backend_object"])
def build(config, session):
build_info = get_build_info(config.setupdir)
package_venv = session.getvenv(config.isolated_build_env)
package_venv.envconfig.deps_matches_subset = True
# we allow user specified dependencies so the users can write extensions to
# install additional type of dependencies (e.g. binary)
user_specified_deps = package_venv.envconfig.deps
package_venv.envconfig.deps = [DepConfig(r, None) for r in build_info.requires]
package_venv.envconfig.deps.extend(user_specified_deps)
if package_venv.setupenv():
package_venv.finishvenv()
if isinstance(package_venv.status, Exception):
raise package_venv.status
build_requires = get_build_requires(build_info, package_venv, config.setupdir)
# we need to filter out requirements already specified in pyproject.toml or user deps
base_build_deps = {
canonicalize_name(Requirement(r.name).name) for r in package_venv.envconfig.deps
}
build_requires_dep = [
DepConfig(r, None)
for r in build_requires
if canonicalize_name(Requirement(r).name) not in base_build_deps
]
if build_requires_dep:
with package_venv.new_action("build_requires", package_venv.envconfig.envdir) as action:
package_venv.run_install_command(packages=build_requires_dep, action=action)
package_venv.finishvenv()
return perform_isolated_build(build_info, package_venv, config.distdir, config.setupdir)
def get_build_info(folder):
toml_file = folder.join("pyproject.toml")
# as per https://www.python.org/dev/peps/pep-0517/
def abort(message):
reporter.error("{} inside {}".format(message, toml_file))
raise SystemExit(1)
if not toml_file.exists():
reporter.error("missing {}".format(toml_file))
raise SystemExit(1)
config_data = get_py_project_toml(toml_file)
if "build-system" not in config_data:
abort("build-system section missing")
build_system = config_data["build-system"]
if "requires" not in build_system:
abort("missing requires key at build-system section")
if "build-backend" not in build_system:
abort("missing build-backend key at build-system section")
requires = build_system["requires"]
if not isinstance(requires, list) or not all(isinstance(i, six.text_type) for i in requires):
abort("requires key at build-system section must be a list of string")
backend = build_system["build-backend"]
if not isinstance(backend, six.text_type):
abort("build-backend key at build-system section must be a string")
args = backend.split(":")
module = args[0]
obj = args[1] if len(args) > 1 else ""
return BuildInfo(requires, module, obj)
def perform_isolated_build(build_info, package_venv, dist_dir, setup_dir):
with package_venv.new_action(
"perform-isolated-build", package_venv.envconfig.envdir
) as action:
# need to start with an empty (but existing) source distribution folder
if dist_dir.exists():
dist_dir.remove(rec=1, ignore_errors=True)
dist_dir.ensure_dir()
result = package_venv._pcall(
[
package_venv.envconfig.envpython,
BUILD_ISOLATED,
str(dist_dir),
build_info.backend_module,
build_info.backend_object,
],
returnout=True,
action=action,
cwd=setup_dir,
)
reporter.verbosity2(result)
return dist_dir.join(result.split("\n")[-2])
def get_build_requires(build_info, package_venv, setup_dir):
with package_venv.new_action("get-build-requires", package_venv.envconfig.envdir) as action:
result = package_venv._pcall(
[
package_venv.envconfig.envpython,
BUILD_REQUIRE_SCRIPT,
build_info.backend_module,
build_info.backend_object,
],
returnout=True,
action=action,
cwd=setup_dir,
)
return json.loads(result.split("\n")[-2])