diff --git a/docs/markdown/Code-formatting.md b/docs/markdown/Code-formatting.md index 386c78725..8191aa218 100644 --- a/docs/markdown/Code-formatting.md +++ b/docs/markdown/Code-formatting.md @@ -56,3 +56,8 @@ Note that `.clang-format-ignore` has the same format as used by A new target `clang-format-check` has been added. It returns an error code if any file needs to be reformatted. This is intended to be used by CI. + +*Since 0.60.0* + +If `.clang-format-include` file is missing and source files are in a git +repository, only files tracked by git will be included. diff --git a/mesonbuild/scripts/clangformat.py b/mesonbuild/scripts/clangformat.py index 518d44ced..ff0304a4b 100644 --- a/mesonbuild/scripts/clangformat.py +++ b/mesonbuild/scripts/clangformat.py @@ -21,6 +21,7 @@ from concurrent.futures import ThreadPoolExecutor 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]: @@ -52,9 +53,15 @@ def run_clang_format(exelist: T.List[str], fname: Path, check: bool) -> subproce def clangformat(exelist: T.List[str], srcdir: Path, builddir: Path, check: bool) -> int: patterns = parse_pattern_file(srcdir / '.clang-format-include') - if not patterns: - patterns = ['**/*'] - globs = [srcdir.glob(p) for p in patterns] + 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]) @@ -70,7 +77,8 @@ def clangformat(exelist: T.List[str], srcdir: Path, builddir: Path, check: bool) any(fnmatch.fnmatch(strf, i) for i in ignore): continue futures.append(e.submit(run_clang_format, exelist, f, check)) - returncode = max(x.result().returncode for x in futures) + if futures: + returncode = max(x.result().returncode for x in futures) return returncode def run(args: T.List[str]) -> int: diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 9468fd544..12aefa018 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -2539,6 +2539,7 @@ class AllPlatformTests(BasePlatformTests): testheader = os.path.join(testdir, 'header.h') badheader = os.path.join(testdir, 'header_orig_h') goodheader = os.path.join(testdir, 'header_expected_h') + includefile = os.path.join(testdir, '.clang-format-include') try: shutil.copyfile(badfile, testfile) shutil.copyfile(badheader, testheader) @@ -2547,6 +2548,17 @@ class AllPlatformTests(BasePlatformTests): Path(goodfile).read_text(encoding='utf-8')) self.assertNotEqual(Path(testheader).read_text(encoding='utf-8'), Path(goodheader).read_text(encoding='utf-8')) + + # test files are not in git so this should do nothing + self.run_target('clang-format') + self.assertNotEqual(Path(testfile).read_text(encoding='utf-8'), + Path(goodfile).read_text(encoding='utf-8')) + self.assertNotEqual(Path(testheader).read_text(encoding='utf-8'), + Path(goodheader).read_text(encoding='utf-8')) + + # Add an include file to reformat everything + with open(includefile, 'w', encoding='utf-8') as f: + f.write('*') self.run_target('clang-format') self.assertEqual(Path(testheader).read_text(encoding='utf-8'), Path(goodheader).read_text(encoding='utf-8')) @@ -2555,6 +2567,8 @@ class AllPlatformTests(BasePlatformTests): os.unlink(testfile) if os.path.exists(testheader): os.unlink(testheader) + if os.path.exists(includefile): + os.unlink(includefile) @skipIfNoExecutable('clang-tidy') def test_clang_tidy(self):