The Meson Build System http://mesonbuild.com/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

138 lines
4.9 KiB

# SPDX-License-Identifier: Apache-2.0
# Copyright 2018 The Meson development team
from __future__ import annotations
import asyncio.subprocess
import fnmatch
import itertools
import json
import signal
import sys
from pathlib import Path
from .. import mlog
from ..compilers import lang_suffixes
from ..mesonlib import quiet_git, join_args, determine_worker_count
from ..mtest import complete_all
import typing as T
Info = T.TypeVar("Info")
async def run_with_buffered_output(cmdlist: T.List[str]) -> int:
"""Run the command in cmdlist, buffering the output so that it is
not mixed for multiple child processes. Kill the child on
cancellation."""
quoted_cmdline = join_args(cmdlist)
p: T.Optional[asyncio.subprocess.Process] = None
try:
p = await asyncio.create_subprocess_exec(*cmdlist,
stdin=asyncio.subprocess.DEVNULL,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT)
stdo, _ = await p.communicate()
except FileNotFoundError as e:
print(mlog.blue('>>>'), quoted_cmdline, file=sys.stderr)
print(mlog.red('not found:'), e.filename, file=sys.stderr)
return 1
except asyncio.CancelledError:
if p:
p.kill()
await p.wait()
return p.returncode or 1
else:
return 0
if stdo:
print(mlog.blue('>>>'), quoted_cmdline, flush=True)
sys.stdout.buffer.write(stdo)
return p.returncode
async def _run_workers(infos: T.Iterable[Info],
fn: T.Callable[[Info], T.Iterable[T.Coroutine[None, None, int]]]) -> int:
futures: T.List[asyncio.Future[int]] = []
semaphore = asyncio.Semaphore(determine_worker_count())
async def run_one(worker_coro: T.Coroutine[None, None, int]) -> int:
try:
async with semaphore:
return await worker_coro
except asyncio.CancelledError as e:
worker_coro.throw(e)
return await worker_coro
def sigterm_handler() -> None:
for f in futures:
f.cancel()
if sys.platform != 'win32':
loop = asyncio.get_running_loop()
loop.add_signal_handler(signal.SIGINT, sigterm_handler)
loop.add_signal_handler(signal.SIGTERM, sigterm_handler)
for i in infos:
futures.extend((asyncio.ensure_future(run_one(x)) for x in fn(i)))
if not futures:
return 0
try:
await complete_all(futures)
except BaseException:
for f in futures:
f.cancel()
raise
return max(f.result() for f in futures if f.done() and not f.cancelled())
def parse_pattern_file(fname: Path) -> T.List[str]:
patterns = []
try:
with fname.open(encoding='utf-8') as f:
for line in f:
pattern = line.strip()
if pattern and not pattern.startswith('#'):
patterns.append(pattern)
except FileNotFoundError:
pass
return patterns
def all_clike_files(name: str, srcdir: Path, builddir: Path) -> T.Iterable[Path]:
patterns = parse_pattern_file(srcdir / f'.{name}-include')
globs: T.Union[T.List[T.List[Path]], T.List[T.Generator[Path, None, None]]]
if patterns:
globs = [srcdir.glob(p) for p in patterns]
else:
r, o = quiet_git(['ls-files'], srcdir)
if r:
globs = [[Path(srcdir, f) for f in o.splitlines()]]
else:
globs = [srcdir.glob('**/*')]
patterns = parse_pattern_file(srcdir / f'.{name}-ignore')
ignore = [str(builddir / '*')]
ignore.extend([str(srcdir / p) for p in patterns])
suffixes = set(lang_suffixes['c']).union(set(lang_suffixes['cpp']))
suffixes.add('h')
suffixes = {f'.{s}' for s in suffixes}
for f in itertools.chain.from_iterable(globs):
strf = str(f)
if f.is_dir() or f.suffix not in suffixes or \
any(fnmatch.fnmatch(strf, i) for i in ignore):
continue
yield f
def run_clang_tool(name: str, srcdir: Path, builddir: Path, fn: T.Callable[..., T.Coroutine[None, None, int]], *args: T.Any) -> int:
if sys.platform == 'win32':
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
def wrapper(path: Path) -> T.Iterable[T.Coroutine[None, None, int]]:
yield fn(path, *args)
return asyncio.run(_run_workers(all_clike_files(name, srcdir, builddir), wrapper))
def run_tool_on_targets(fn: T.Callable[[T.Dict[str, T.Any]],
T.Iterable[T.Coroutine[None, None, int]]]) -> int:
if sys.platform == 'win32':
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
with open('meson-info/intro-targets.json', encoding='utf-8') as fp:
targets = json.load(fp)
return asyncio.run(_run_workers(targets, fn))