diff options
author | Xavier Claessens <xavier.claessens@collabora.com> | 2021-11-10 10:57:41 -0500 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2021-11-14 22:07:17 +0200 |
commit | 364d951b347ab932d9a78cb6846d1288925cf32c (patch) | |
tree | f9a14c052dd5ba3f4d42b9097459b0a6fc55633f /mesonbuild/scripts | |
parent | ae3d495c370645957835bdf1dfa464d79e668420 (diff) | |
download | meson-364d951b347ab932d9a78cb6846d1288925cf32c.zip meson-364d951b347ab932d9a78cb6846d1288925cf32c.tar.gz meson-364d951b347ab932d9a78cb6846d1288925cf32c.tar.bz2 |
Share common code between clang tidy and format
Diffstat (limited to 'mesonbuild/scripts')
-rw-r--r-- | mesonbuild/scripts/clangformat.py | 55 | ||||
-rw-r--r-- | mesonbuild/scripts/clangtidy.py | 53 | ||||
-rw-r--r-- | mesonbuild/scripts/run_tool.py | 65 |
3 files changed, 84 insertions, 89 deletions
diff --git a/mesonbuild/scripts/clangformat.py b/mesonbuild/scripts/clangformat.py index ff0304a..9dc5466 100644 --- a/mesonbuild/scripts/clangformat.py +++ b/mesonbuild/scripts/clangformat.py @@ -14,34 +14,17 @@ import argparse import subprocess -import itertools -import fnmatch from pathlib import Path -from concurrent.futures import ThreadPoolExecutor +from .run_tool import run_tool from ..environment import detect_clangformat -from ..compilers import lang_suffixes -from ..mesonlib import Popen_safe import typing as T -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 run_clang_format(exelist: T.List[str], fname: Path, check: bool) -> subprocess.CompletedProcess: +def run_clang_format(fname: Path, exelist: T.List[str], check: bool) -> subprocess.CompletedProcess: if check: original = fname.read_bytes() before = fname.stat().st_mtime - args = ['-style=file', '-i', str(fname)] - ret = subprocess.run(exelist + args) + ret = subprocess.run(exelist + ['-style=file', '-i', str(fname)]) after = fname.stat().st_mtime if before != after: print('File reformatted: ', fname) @@ -51,36 +34,6 @@ def run_clang_format(exelist: T.List[str], fname: Path, check: bool) -> subproce ret.returncode = 1 return ret -def clangformat(exelist: T.List[str], srcdir: Path, builddir: Path, check: bool) -> int: - patterns = parse_pattern_file(srcdir / '.clang-format-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: - p, o, _ = Popen_safe(['git', 'ls-files'], cwd=srcdir) - if p.returncode == 0: - globs = [[Path(srcdir, f) for f in o.splitlines()]] - else: - globs = [srcdir.glob('**/*')] - patterns = parse_pattern_file(srcdir / '.clang-format-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} - futures = [] - returncode = 0 - with ThreadPoolExecutor() as e: - for f in itertools.chain(*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 - futures.append(e.submit(run_clang_format, exelist, f, check)) - if futures: - returncode = max(x.result().returncode for x in futures) - return returncode - def run(args: T.List[str]) -> int: parser = argparse.ArgumentParser() parser.add_argument('--check', action='store_true') @@ -96,4 +49,4 @@ def run(args: T.List[str]) -> int: print('Could not execute clang-format "%s"' % ' '.join(exelist)) return 1 - return clangformat(exelist, srcdir, builddir, options.check) + return run_tool('clang-format', srcdir, builddir, run_clang_format, exelist, options.check) diff --git a/mesonbuild/scripts/clangtidy.py b/mesonbuild/scripts/clangtidy.py index 9f48942..7364e27 100644 --- a/mesonbuild/scripts/clangtidy.py +++ b/mesonbuild/scripts/clangtidy.py @@ -12,46 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pathlib +import argparse import subprocess -import shutil -import os -import re -from concurrent.futures import ThreadPoolExecutor +from pathlib import Path + +from .run_tool import run_tool import typing as T -from ..compilers import lang_suffixes - -def manual_clangtidy(srcdir_name: str, builddir_name: str) -> int: - srcdir = pathlib.Path(srcdir_name) - suffixes = set(lang_suffixes['c']).union(set(lang_suffixes['cpp'])) - suffixes.add('h') - futures = [] - returncode = 0 - with ThreadPoolExecutor() as e: - for f in (x for suff in suffixes for x in srcdir.glob('**/*.' + suff)): - if f.is_dir(): - continue - strf = str(f) - if strf.startswith(builddir_name): - continue - futures.append(e.submit(subprocess.run, ['clang-tidy', '-p', builddir_name, strf])) - returncode = max(x.result().returncode for x in futures) - return returncode - -def clangtidy(srcdir_name: str, builddir_name: str) -> int: - run_clang_tidy = None - for rct in ('run-clang-tidy', 'run-clang-tidy.py'): - if shutil.which(rct): - run_clang_tidy = rct - break - if run_clang_tidy: - return subprocess.run([run_clang_tidy, '-p', builddir_name, '^(?!' + re.escape(builddir_name + os.path.sep) + ').*$']).returncode - else: - print('Could not find run-clang-tidy, running checks manually.') - return manual_clangtidy(srcdir_name, builddir_name) +def run_clang_tidy(fname: Path, builddir: Path) -> subprocess.CompletedProcess: + return subprocess.run(['clang-tidy', '-p', str(builddir), str(fname)]) def run(args: T.List[str]) -> int: - srcdir_name = args[0] - builddir_name = args[1] - return clangtidy(srcdir_name, builddir_name) + parser = argparse.ArgumentParser() + parser.add_argument('sourcedir') + parser.add_argument('builddir') + options = parser.parse_args(args) + + srcdir = Path(options.sourcedir) + builddir = Path(options.builddir) + + return run_tool('clang-tidy', srcdir, builddir, run_clang_tidy, builddir) diff --git a/mesonbuild/scripts/run_tool.py b/mesonbuild/scripts/run_tool.py new file mode 100644 index 0000000..700f459 --- /dev/null +++ b/mesonbuild/scripts/run_tool.py @@ -0,0 +1,65 @@ +# Copyright 2018 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import subprocess +import itertools +import fnmatch +from pathlib import Path +from concurrent.futures import ThreadPoolExecutor + +from ..compilers import lang_suffixes +from ..mesonlib import Popen_safe +import typing as T + +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 run_tool(name: str, srcdir: Path, builddir: Path, fn: T.Callable[..., subprocess.CompletedProcess], *args: T.Any) -> int: + 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: + p, o, _ = Popen_safe(['git', 'ls-files'], cwd=srcdir) + if p.returncode == 0: + 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} + futures = [] + returncode = 0 + with ThreadPoolExecutor() as e: + for f in itertools.chain(*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 + futures.append(e.submit(fn, f, *args)) + if futures: + returncode = max(x.result().returncode for x in futures) + return returncode |