summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2018-12-23 16:19:45 +0200
committerJussi Pakkanen <jpakkane@gmail.com>2018-12-30 00:50:00 +0200
commit1fca654055d3502d2db9c5aad66a522beaa1df19 (patch)
treebaf97e14e15d57482ed73adba96ab98180ca13d6
parent3b495c397ec1baffe99d63031f2b03301b7f6ba5 (diff)
downloadmeson-1fca654055d3502d2db9c5aad66a522beaa1df19.tar.gz
Add a clang-format target.
-rw-r--r--docs/markdown/snippets/clangformat.md11
-rw-r--r--mesonbuild/backend/ninjabackend.py26
-rw-r--r--mesonbuild/scripts/clangformat.py37
-rwxr-xr-xrun_unittests.py35
-rw-r--r--test cases/unit/51 clang-format/.clang-format5
-rw-r--r--test cases/unit/51 clang-format/meson.build4
-rw-r--r--test cases/unit/51 clang-format/prog_expected_c6
-rw-r--r--test cases/unit/51 clang-format/prog_orig_c21
8 files changed, 143 insertions, 2 deletions
diff --git a/docs/markdown/snippets/clangformat.md b/docs/markdown/snippets/clangformat.md
new file mode 100644
index 000000000..89832432d
--- /dev/null
+++ b/docs/markdown/snippets/clangformat.md
@@ -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
+```
+
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index f49649be4..5d59fa975 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -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)
diff --git a/mesonbuild/scripts/clangformat.py b/mesonbuild/scripts/clangformat.py
new file mode 100644
index 000000000..fcdf5af74
--- /dev/null
+++ b/mesonbuild/scripts/clangformat.py
@@ -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)
diff --git a/run_unittests.py b/run_unittests.py
index 91daa1b2f..f8ede9b89 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -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):
'''
diff --git a/test cases/unit/51 clang-format/.clang-format b/test cases/unit/51 clang-format/.clang-format
new file mode 100644
index 000000000..5c60ac9f3
--- /dev/null
+++ b/test cases/unit/51 clang-format/.clang-format
@@ -0,0 +1,5 @@
+---
+BasedOnStyle: LLVM
+IndentWidth: 4
+UseTab: Never
+---
diff --git a/test cases/unit/51 clang-format/meson.build b/test cases/unit/51 clang-format/meson.build
new file mode 100644
index 000000000..1b93cd54d
--- /dev/null
+++ b/test cases/unit/51 clang-format/meson.build
@@ -0,0 +1,4 @@
+project('clangformat', 'c')
+
+executable('prog', 'prog.c')
+
diff --git a/test cases/unit/51 clang-format/prog_expected_c b/test cases/unit/51 clang-format/prog_expected_c
new file mode 100644
index 000000000..a045966a7
--- /dev/null
+++ b/test cases/unit/51 clang-format/prog_expected_c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main(int argc, char **argv) {
+ printf("Awful.\n");
+ return 0;
+}
diff --git a/test cases/unit/51 clang-format/prog_orig_c b/test cases/unit/51 clang-format/prog_orig_c
new file mode 100644
index 000000000..f098bbcce
--- /dev/null
+++ b/test cases/unit/51 clang-format/prog_orig_c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+
+
+
+
+int
+main(
+int
+argc,
+char**
+argv)
+{
+printf(
+"Awful.\n"
+)
+;
+return
+0
+;
+}
+