-
-
Notifications
You must be signed in to change notification settings - Fork 766
/
lua.py
149 lines (124 loc) · 4.95 KB
/
lua.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
import contextlib
import os
import re
from typing import Generator
from typing import Sequence
from typing import Tuple
import pre_commit.constants as C
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var
from pre_commit.hook import Hook
from pre_commit.languages import helpers
from pre_commit.parse_shebang import find_executable
from pre_commit.prefix import Prefix
from pre_commit.util import clean_path_on_failure
from pre_commit.util import cmd_output
ENVIRONMENT_DIR = 'lua_env'
get_default_version = helpers.basic_get_default_version
healthy = helpers.basic_healthy
def _find_lua(language_version: str) -> str: # pragma: win32 no cover
"""Find a lua executable.
Lua doesn't always have a plain `lua` executable.
Some OS vendors will ship the binary as `lua#.#` (e.g., lua5.3)
so discovery is needed to find a valid executable.
"""
if language_version == C.DEFAULT:
choices = ['lua']
for path in os.environ.get('PATH', '').split(os.pathsep):
try:
candidates = os.listdir(path)
except OSError:
# Invalid path on PATH or lacking permissions.
continue
for candidate in candidates:
# The Lua executable might look like `lua#.#` or `lua-#.#`.
if re.search(r'^lua[-]?\d+\.\d+', candidate):
choices.append(candidate)
else:
# Prefer version specific executables first if available.
# This should avoid the corner case where a user requests a language
# version, gets a `lua` executable, but that executable is actually
# for a different version and package.path would patch LUA_PATH
# incorrectly.
choices = [f'lua{language_version}', 'lua-{language_version}', 'lua']
found_exes = [exe for exe in choices if find_executable(exe)]
if found_exes:
return found_exes[0]
raise ValueError(
'No lua executable found on the system paths '
f'for {language_version} version.',
)
def _get_lua_path_version(
lua_executable: str,
) -> str: # pragma: win32 no cover
"""Get the Lua version used in file paths."""
# This could sniff out from _VERSION, but checking package.path should
# provide an answer for *exactly* where lua is looking for packages.
_, stdout, _ = cmd_output(lua_executable, '-e', 'print(package.path)')
match = re.search(fr'{os.sep}lua{os.sep}(.*?){os.sep}', stdout)
if match:
return match[1]
raise ValueError('Cannot determine lua version for file paths.')
def get_env_patch(
env: str, language_version: str,
) -> PatchesT: # pragma: win32 no cover
lua = _find_lua(language_version)
version = _get_lua_path_version(lua)
return (
('PATH', (os.path.join(env, 'bin'), os.pathsep, Var('PATH'))),
(
'LUA_PATH', (
os.path.join(env, 'share', 'lua', version, '?.lua;'),
os.path.join(env, 'share', 'lua', version, '?', 'init.lua;;'),
),
),
(
'LUA_CPATH', (
os.path.join(env, 'lib', 'lua', version, '?.so;;'),
),
),
)
def _envdir(prefix: Prefix, version: str) -> str: # pragma: win32 no cover
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
return prefix.path(directory)
@contextlib.contextmanager # pragma: win32 no cover
def in_env(
prefix: Prefix,
language_version: str,
) -> Generator[None, None, None]:
with envcontext(
get_env_patch(
_envdir(prefix, language_version), language_version,
),
):
yield
def install_environment(
prefix: Prefix,
version: str,
additional_dependencies: Sequence[str],
) -> None: # pragma: win32 no cover
helpers.assert_version_default('lua', version)
envdir = _envdir(prefix, version)
with clean_path_on_failure(envdir):
with in_env(prefix, version):
# luarocks doesn't bootstrap a tree prior to installing
# so ensure the directory exists.
os.makedirs(envdir, exist_ok=True)
make_cmd = ['luarocks', '--tree', envdir, 'make']
# Older luarocks (e.g., 2.4.2) expect the rockspec as an argument.
filenames = prefix.star('.rockspec')
make_cmd.extend(filenames[:1])
helpers.run_setup_cmd(prefix, tuple(make_cmd))
# luarocks can't install multiple packages at once
# so install them individually.
for dependency in additional_dependencies:
cmd = ('luarocks', '--tree', envdir, 'install', dependency)
helpers.run_setup_cmd(prefix, cmd)
def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]: # pragma: win32 no cover
with in_env(hook.prefix, hook.language_version):
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)