Support GN build language

GN is a meta-build system that generates build files for Ninja
See also https://gn.googlesource.com/gn/
This commit is contained in:
Arkadii Shapkin 2023-12-21 01:17:12 +03:00
parent 9c9983dba0
commit 7d323a5c29
2 changed files with 138 additions and 0 deletions

101
pre_commit/languages/gn.py Normal file
View file

@ -0,0 +1,101 @@
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)

View file

@ -0,0 +1,37 @@
from __future__ import annotations
import re
from pre_commit.languages import gn
from testing.language_helpers import run_language
def test_gn_format(tmp_path):
# Create a GN file with content that needs formatting
gn_file_content = """\
source_set("hello_world") {
sources = [ "hello_world.cc", ]
}
"""
gn_file = tmp_path.joinpath('test.gn')
gn_file.write_text(gn_file_content)
# Run gn format on the created file
ret, out = run_language(tmp_path, gn, f'gn format {gn_file}')
# Read the formatted file
formatted_content = gn_file.read_text()
assert ret == 0
assert gn_file_content != formatted_content
def test_gn_version(tmp_path):
ret, out = run_language(
tmp_path,
gn,
'gn --version',
)
assert ret == 0
version_regex = re.compile(rb'\d+ \([a-f0-9]+\)\n')
assert version_regex.search(out) is not None