pre-commit/pre_commit/languages/gn.py
Arkadii Shapkin 7d323a5c29 Support GN build language
GN is a meta-build system that generates build files for Ninja
See also https://gn.googlesource.com/gn/
2023-12-21 01:23:50 +03:00

101 lines
2.7 KiB
Python

from __future__ import annotations
import contextlib
import functools
import os.path
import platform
import shutil
import tempfile
import urllib.error
import urllib.request
import zipfile
from collections.abc import Generator
from collections.abc import Sequence
from typing import ContextManager
from typing import IO
import pre_commit.constants as C
from pre_commit import lang_base
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var
from pre_commit.prefix import Prefix
from pre_commit.util import make_executable
ENVIRONMENT_DIR = 'gn_env'
health_check = lang_base.basic_health_check
run_hook = lang_base.basic_run_hook
_ARCH_ALIASES = {
'x86_64': 'amd64',
'aarch64': 'arm64',
'armv8': 'arm64',
}
_ARCH = platform.machine().lower()
_ARCH = _ARCH_ALIASES.get(_ARCH, _ARCH)
def _open_archive(bio: IO[bytes]) -> ContextManager[zipfile.ZipFile]:
return zipfile.ZipFile(bio)
def _get_gn_url() -> str:
os_name = platform.system().lower()
if os_name == 'linux':
os_name = 'linux-' + _ARCH
elif os_name == 'darwin':
os_name = 'mac-' + _ARCH
elif os_name == 'windows':
os_name = 'windows-' + _ARCH
return 'https://chrome-infra-packages.appspot.com/dl/gn/gn/' + \
f'{os_name}/+/latest'
def _install_gn(dest: str) -> None:
try:
resp = urllib.request.urlopen(_get_gn_url())
except urllib.error.HTTPError as e: # pragma: no cover
os_name = platform.system().lower()
raise ValueError(
f'Could not find GN for your system (os={os_name}; arch={_ARCH})',
) from e
else:
with tempfile.TemporaryFile() as f:
shutil.copyfileobj(resp, f)
f.seek(0)
with _open_archive(f) as archive:
archive.extractall(dest)
make_executable(f'{dest}/gn')
@functools.lru_cache(maxsize=1)
def get_default_version() -> str:
if lang_base.exe_exists('gn'):
return 'system'
else:
return C.DEFAULT
def get_env_patch(venv: str, version: str) -> PatchesT:
if version == 'system':
return ()
return (('PATH', (venv, os.pathsep, Var('PATH'))),)
@contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
env_dir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(env_dir, version)):
yield
def install_environment(
prefix: Prefix,
version: str,
additional_dependencies: Sequence[str],
) -> None:
if version == 'system':
return
env_dir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
_install_gn(env_dir)