Add a clang-format target.

pull/4691/head
Jussi Pakkanen 6 years ago
parent 3b495c397e
commit 1fca654055
  1. 11
      docs/markdown/snippets/clangformat.md
  2. 26
      mesonbuild/backend/ninjabackend.py
  3. 37
      mesonbuild/scripts/clangformat.py
  4. 35
      run_unittests.py
  5. 5
      test cases/unit/51 clang-format/.clang-format
  6. 4
      test cases/unit/51 clang-format/meson.build
  7. 6
      test cases/unit/51 clang-format/prog_expected_c
  8. 21
      test cases/unit/51 clang-format/prog_orig_c

@ -0,0 +1,11 @@
## A builtin target to run clang-format
If you have `clang-format` installed and there is a `.clang-format`
file in the root of your master project, Meson will generate a run
target called `clang-format` so you can reformat all files with one
command:
```meson
ninja clang-format
```

@ -2579,8 +2579,7 @@ rule FORTRAN_DEP_HACK%s
# Alias that runs the target defined above
self.create_target_alias('meson-dist', outfile)
# For things like scan-build and other helper tools we might have.
def generate_utils(self, outfile):
def generate_scanbuild(self, outfile):
cmd = self.environment.get_build_command() + \
['--internal', 'scanbuild', self.environment.source_dir, self.environment.build_dir] + \
self.environment.get_build_command() + self.get_user_option_args()
@ -2590,6 +2589,29 @@ rule FORTRAN_DEP_HACK%s
elem.write(outfile)
# Alias that runs the target defined above
self.create_target_alias('meson-scan-build', outfile)
def generate_clangformat(self, outfile):
import shutil
target_name = 'clang-format'
if shutil.which('clang-format') is None:
return
if not os.path.exists(os.path.join(self.environment.source_dir, '.clang-format')) and \
not os.path.exists(os.path.join(self.environment.source_dir, '_clang-format')):
return
if 'target_name' in self.all_outputs:
return
cmd = self.environment.get_build_command() + \
['--internal', 'clangformat', self.environment.source_dir, self.environment.build_dir]
elem = NinjaBuildElement(self.all_outputs, 'meson-' + target_name, 'CUSTOM_COMMAND', 'PHONY')
elem.add_item('COMMAND', cmd)
elem.add_item('pool', 'console')
elem.write(outfile)
self.create_target_alias('meson-' + target_name, outfile)
# For things like scan-build and other helper tools we might have.
def generate_utils(self, outfile):
self.generate_scanbuild(outfile)
self.generate_clangformat(outfile)
cmd = self.environment.get_build_command() + ['--internal', 'uninstall']
elem = NinjaBuildElement(self.all_outputs, 'meson-uninstall', 'CUSTOM_COMMAND', 'PHONY')
elem.add_item('COMMAND', cmd)

@ -0,0 +1,37 @@
# 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 pathlib
import subprocess
from concurrent.futures import ThreadPoolExecutor
from ..compilers import lang_suffixes
def clangformat(srcdir_name, builddir_name):
srcdir = pathlib.Path(srcdir_name)
suffixes = set(lang_suffixes['c']).union(set(lang_suffixes['cpp']))
futures = []
with ThreadPoolExecutor() as e:
for f in (x for suff in suffixes for x in srcdir.glob('**/*.' + suff)):
strf = str(f)
if strf.startswith(builddir_name):
continue
futures.append(e.submit(subprocess.check_call, ['clang-format', '-style=file', '-i', strf]))
[x.result() for x in futures]
return 0
def run(args):
srcdir_name = args[0]
builddir_name = args[1]
return clangformat(srcdir_name, builddir_name)

@ -102,6 +102,19 @@ def _git_init(project_dir):
subprocess.check_call(['git', 'commit', '-a', '-m', 'I am a project'], cwd=project_dir,
stdout=subprocess.DEVNULL)
def skipIfNoExecutable(exename):
'''
Skip this test if the given executable is not found.
'''
def wrapper(func):
@functools.wraps(func)
def wrapped(*args, **kwargs):
if shutil.which(exename) is None:
raise unittest.SkipTest(exename + ' not found')
return func(*args, **kwargs)
return wrapped
return wrapper
def skipIfNoPkgconfig(f):
'''
Skip this test if no pkg-config is found, unless we're on CI.
@ -3053,6 +3066,28 @@ recommended as it is not supported on some platforms''')
}
self.assertDictEqual(res, expected)
@skipIfNoExecutable('clang-format')
def test_clang_format(self):
if self.backend is not Backend.ninja:
raise unittest.SkipTest('Clang-format is for now only supported on Ninja, not {}'.format(self.backend.name))
testdir = os.path.join(self.unit_test_dir, '51 clang-format')
testfile = os.path.join(testdir, 'prog.c')
badfile = os.path.join(testdir, 'prog_orig_c')
goodfile = os.path.join(testdir, 'prog_expected_c')
try:
self.run_clangformat(testdir, testfile, badfile, goodfile)
finally:
if os.path.exists(testfile):
os.unlink(testfile)
def run_clangformat(self, testdir, testfile, badfile, goodfile):
shutil.copyfile(badfile, testfile)
self.init(testdir)
self.assertNotEqual(Path(testfile).read_text(),
Path(goodfile).read_text())
self.run_target('clang-format')
self.assertEqual(Path(testfile).read_text(),
Path(goodfile).read_text())
class FailureTests(BasePlatformTests):
'''

@ -0,0 +1,5 @@
---
BasedOnStyle: LLVM
IndentWidth: 4
UseTab: Never
---

@ -0,0 +1,4 @@
project('clangformat', 'c')
executable('prog', 'prog.c')

@ -0,0 +1,6 @@
#include <stdio.h>
int main(int argc, char **argv) {
printf("Awful.\n");
return 0;
}

@ -0,0 +1,21 @@
#include <stdio.h>
int
main(
int
argc,
char**
argv)
{
printf(
"Awful.\n"
)
;
return
0
;
}
Loading…
Cancel
Save