aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--clang-tidy/meson.build13
-rw-r--r--doc/manual/rl-next/clang-tidy-sorta.md10
-rw-r--r--flake.nix4
-rw-r--r--justfile10
-rw-r--r--meson.build2
-rw-r--r--meson.options8
-rwxr-xr-xmeson/clang-tidy/build_required_targets.py21
-rwxr-xr-xmeson/clang-tidy/clean_compdb.py53
-rw-r--r--meson/clang-tidy/fake.cc0
-rw-r--r--meson/clang-tidy/meson.build98
-rw-r--r--subprojects/lix-clang-tidy/.clang-format (renamed from clang-tidy/.clang-format)0
-rw-r--r--subprojects/lix-clang-tidy/.editorconfig (renamed from clang-tidy/.editorconfig)0
-rw-r--r--subprojects/lix-clang-tidy/FixIncludes.cc (renamed from clang-tidy/FixIncludes.cc)0
-rw-r--r--subprojects/lix-clang-tidy/FixIncludes.hh (renamed from clang-tidy/FixIncludes.hh)0
-rw-r--r--subprojects/lix-clang-tidy/HasPrefixSuffix.cc (renamed from clang-tidy/HasPrefixSuffix.cc)0
-rw-r--r--subprojects/lix-clang-tidy/HasPrefixSuffix.hh (renamed from clang-tidy/HasPrefixSuffix.hh)0
-rw-r--r--subprojects/lix-clang-tidy/LixClangTidyChecks.cc (renamed from clang-tidy/LixClangTidyChecks.cc)0
-rw-r--r--subprojects/lix-clang-tidy/README.md (renamed from clang-tidy/README.md)0
-rw-r--r--subprojects/lix-clang-tidy/default.nix44
-rw-r--r--subprojects/lix-clang-tidy/meson.build18
-rw-r--r--subprojects/lix-clang-tidy/meson.options3
21 files changed, 269 insertions, 15 deletions
diff --git a/clang-tidy/meson.build b/clang-tidy/meson.build
deleted file mode 100644
index c59164a72..000000000
--- a/clang-tidy/meson.build
+++ /dev/null
@@ -1,13 +0,0 @@
-project('lix-clang-tidy', ['cpp', 'c'],
- version : '0.1',
- default_options : ['warning_level=3', 'cpp_std=c++20'])
-
-llvm = dependency('Clang', version: '>= 14', modules: ['libclang'])
-sources = files(
- 'HasPrefixSuffix.cc',
- 'LixClangTidyChecks.cc',
- 'FixIncludes.cc',
-)
-
-shared_module('lix-clang-tidy', sources,
- dependencies: llvm)
diff --git a/doc/manual/rl-next/clang-tidy-sorta.md b/doc/manual/rl-next/clang-tidy-sorta.md
new file mode 100644
index 000000000..5f3f84348
--- /dev/null
+++ b/doc/manual/rl-next/clang-tidy-sorta.md
@@ -0,0 +1,10 @@
+---
+synopsis: "clang-tidy support"
+cls: 1697
+issues: fj#147
+credits: jade
+category: Development
+---
+
+`clang-tidy` can be used to lint Lix with a limited set of lints using `ninja -C build clang-tidy` and `ninja -C build clang-tidy-fix`.
+In practice, this fixes the built-in meson rule that was used the same as above being broken ever since precompiled headers were introduced.
diff --git a/flake.nix b/flake.nix
index a1fc947b7..14fdf4147 100644
--- a/flake.nix
+++ b/flake.nix
@@ -197,6 +197,8 @@
busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell;
};
+ lix-clang-tidy = final.callPackage ./subprojects/lix-clang-tidy { };
+
# Export the patched version of boehmgc that Lix uses into the overlay
# for consumers of this flake.
boehmgc-nix = final.nix.passthru.boehmgc-nix;
@@ -384,6 +386,8 @@
rec {
inherit (nixpkgsFor.${system}.native) nix;
default = nix;
+
+ inherit (nixpkgsFor.${system}.native) lix-clang-tidy;
}
// (
lib.optionalAttrs (builtins.elem system linux64BitSystems) {
diff --git a/justfile b/justfile
index 6a93fa63f..1576c759d 100644
--- a/justfile
+++ b/justfile
@@ -25,3 +25,13 @@ install *OPTIONS: (build OPTIONS)
# Run tests
test *OPTIONS:
meson test -C build --print-errorlogs {{ OPTIONS }}
+
+alias clang-tidy := lint
+
+lint:
+ ninja -C build clang-tidy
+
+alias clang-tidy-fix := lint-fix
+
+lint-fix:
+ ninja -C build clang-tidy-fix
diff --git a/meson.build b/meson.build
index ed50dff78..8577657d9 100644
--- a/meson.build
+++ b/meson.build
@@ -548,3 +548,5 @@ if enable_tests
subdir('tests/unit')
subdir('tests/functional')
endif
+
+subdir('meson/clang-tidy')
diff --git a/meson.options b/meson.options
index fc2050809..679d88347 100644
--- a/meson.options
+++ b/meson.options
@@ -1,7 +1,7 @@
# vim: filetype=meson
option('enable-build', type : 'boolean', value : true,
- description : 'Set to false to not actually build. Only really makes sense with -Dinternal-api-docs=true',
+ description : 'set to false to not actually build. Only really makes sense with -Dinternal-api-docs=true',
)
option('gc', type : 'feature',
@@ -37,7 +37,7 @@ option('tests-brief', type : 'boolean', value : false,
)
option('profile-build', type : 'feature', value: 'disabled',
- description : 'whether to enable -ftime-trace in clang builds, allowing for speeding up the build.'
+ description : 'whether to enable -ftime-trace in clang builds, allowing for diagnosing the cause of build time.'
)
option('store-dir', type : 'string', value : '/nix/store',
@@ -68,3 +68,7 @@ option('profile-dir', type : 'string', value : 'etc/profile.d',
option('enable-pch-std', type : 'boolean', value : true,
description : 'whether to use precompiled headers for C++\'s standard library (breaks clangd if you\'re using GCC)',
)
+
+option('lix-clang-tidy-checks-path', type : 'string', value : '',
+ description: 'path to lix-clang-tidy-checks library file, if providing it externally. Uses an internal one if this is not set',
+)
diff --git a/meson/clang-tidy/build_required_targets.py b/meson/clang-tidy/build_required_targets.py
new file mode 100755
index 000000000..5c0e9641e
--- /dev/null
+++ b/meson/clang-tidy/build_required_targets.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+import subprocess
+
+def get_targets_of_rule(build_root: str, rule_name: str) -> list[str]:
+ return subprocess.check_output(['ninja', '-C', build_root, '-t', 'targets', 'rule', rule_name]).decode().strip().splitlines()
+
+def ninja_build(build_root: str, targets: list[str]):
+ subprocess.check_call(['ninja', '-C', build_root, '--', *targets])
+
+def main():
+ import argparse
+ ap = argparse.ArgumentParser(description='Builds required targets for clang-tidy')
+ ap.add_argument('build_root', help='Ninja build root', type=str)
+
+ args = ap.parse_args()
+
+ targets = [t for t in get_targets_of_rule(args.build_root, 'CUSTOM_COMMAND') if t.endswith('gen.hh')]
+ ninja_build(args.build_root, targets)
+
+if __name__ == '__main__':
+ main()
diff --git a/meson/clang-tidy/clean_compdb.py b/meson/clang-tidy/clean_compdb.py
new file mode 100755
index 000000000..6736fe63a
--- /dev/null
+++ b/meson/clang-tidy/clean_compdb.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+# Deletes the PCH arguments from a compilation database, to workaround nixpkgs
+# stdenv having a cc-wrapper that is impossible to use for anything except cc
+# itself, for example, clang-tidy.
+
+import json
+import shlex
+
+
+def process_compdb(compdb: list[dict]) -> list[dict]:
+
+ def munch_command(args: list[str]) -> list[str]:
+ out = []
+ eat_next = False
+ for i, arg in enumerate(args):
+ if arg == '-fpch-preprocess':
+ # as used with gcc
+ continue
+ elif arg == '-include-pch' or (arg == '-include' and args[i + 1] == 'precompiled-headers.hh'):
+ # -include-pch some-pch (clang), or -include some-pch (gcc)
+ eat_next = True
+ continue
+ if not eat_next:
+ out.append(arg)
+ eat_next = False
+ return out
+
+ def chomp(item: dict) -> dict:
+ item = item.copy()
+ item['command'] = shlex.join(munch_command(shlex.split(item['command'])))
+ return item
+
+ return [chomp(x) for x in compdb if not x['file'].endswith('precompiled-headers.hh')]
+
+
+def main():
+ import argparse
+ ap = argparse.ArgumentParser(
+ description='Delete pch arguments from compilation database')
+ ap.add_argument('input',
+ type=argparse.FileType('r'),
+ help='Input json file')
+ ap.add_argument('output',
+ type=argparse.FileType('w'),
+ help='Output json file')
+ args = ap.parse_args()
+
+ input_json = json.load(args.input)
+ json.dump(process_compdb(input_json), args.output, indent=2)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/meson/clang-tidy/fake.cc b/meson/clang-tidy/fake.cc
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/meson/clang-tidy/fake.cc
diff --git a/meson/clang-tidy/meson.build b/meson/clang-tidy/meson.build
new file mode 100644
index 000000000..9aba5fbdc
--- /dev/null
+++ b/meson/clang-tidy/meson.build
@@ -0,0 +1,98 @@
+# The clang-tidy target for Lix
+
+run_clang_tidy = find_program('run-clang-tidy', required : false)
+# Although this looks like it wants to be pkg-config, pkg-config does not
+# really work for *plugins*, which are executable-like .so files that also
+# cannot be found via find_program. Fun!
+if get_option('lix-clang-tidy-checks-path') != ''
+ lix_clang_tidy_so = get_option('lix-clang-tidy-checks-path')
+ lix_clang_tidy_so_found = true
+else
+ lix_clang_tidy_subproj = subproject(
+ 'lix-clang-tidy',
+ required : false,
+ default_options : {'build-by-default': false}
+ )
+ if lix_clang_tidy_subproj.found()
+ lix_clang_tidy_so = lix_clang_tidy_subproj.get_variable('lix_clang_tidy')
+ lix_clang_tidy_so_found = true
+ else
+ lix_clang_tidy_so_found = false
+ endif
+endif
+
+# Due to numerous problems, such as:
+# - Meson does not expose pch targets, but *fine*, I can just ask Ninja for
+# them with `ninja -t targets rule cpp_PCH` and build them manually:
+# https://github.com/mesonbuild/meson/issues/13499
+# - Nixpkgs stdenv buries the cc-wrapper under a giant pile of assumptions
+# about the cc-wrapper actually being used on the cc of a stdenv, rather than
+# independently for clang-tidy, and we need to use cc-wrapper to get the
+# correct hardening flags so that clang-tidy can actually parse the PCH file
+#
+# I give up. I am going to delete the damn PCH args and then it will work.
+meson.add_postconf_script(
+ python,
+ meson.current_source_dir() / 'clean_compdb.py',
+ meson.global_build_root() / 'compile_commands.json',
+ meson.current_build_dir() / 'compile_commands.json',
+)
+
+# Horrible hack to get around not being able to depend on another target's
+# generated headers in any way in the meson DSL
+# https://github.com/mesonbuild/meson/issues/12817 which was incorrectly
+# closed, if you *actually* need to generate the files once.
+# Also related: https://github.com/mesonbuild/meson/issues/3667
+#
+# Or we could ban meson generators because their design is broken.
+build_all_generated_headers = custom_target(
+ command : [
+ python,
+ meson.current_source_dir() / 'build_required_targets.py',
+ meson.global_build_root(),
+ ],
+ output : 'generated_headers.stamp',
+ build_by_default : false,
+ build_always_stale : true,
+)
+
+if lix_clang_tidy_so_found
+ run_clang_tidy_args = [
+ '-load',
+ lix_clang_tidy_so,
+ '-p',
+ # We have to workaround a run-clang-tidy bug too, so we must give the
+ # directory name rather than the actual compdb file.
+ # https://github.com/llvm/llvm-project/issues/101440
+ meson.current_build_dir(),
+ '-quiet',
+ ]
+ run_target(
+ 'clang-tidy',
+ command : [
+ # XXX: This explicitly invokes it with python because of a nixpkgs bug
+ # where clang-unwrapped does not patch interpreters in run-clang-tidy.
+ # However, making clang-unwrapped depend on python is also silly, so idk.
+ python,
+ run_clang_tidy,
+ run_clang_tidy_args,
+ '-warnings-as-errors',
+ '*',
+ ],
+ depends : [
+ build_all_generated_headers,
+ ],
+ )
+ run_target(
+ 'clang-tidy-fix',
+ command : [
+ python,
+ run_clang_tidy,
+ run_clang_tidy_args,
+ '-fix',
+ ],
+ depends : [
+ build_all_generated_headers,
+ ],
+ )
+endif
diff --git a/clang-tidy/.clang-format b/subprojects/lix-clang-tidy/.clang-format
index cd8995543..cd8995543 100644
--- a/clang-tidy/.clang-format
+++ b/subprojects/lix-clang-tidy/.clang-format
diff --git a/clang-tidy/.editorconfig b/subprojects/lix-clang-tidy/.editorconfig
index 19ee09eec..19ee09eec 100644
--- a/clang-tidy/.editorconfig
+++ b/subprojects/lix-clang-tidy/.editorconfig
diff --git a/clang-tidy/FixIncludes.cc b/subprojects/lix-clang-tidy/FixIncludes.cc
index 602d3d355..602d3d355 100644
--- a/clang-tidy/FixIncludes.cc
+++ b/subprojects/lix-clang-tidy/FixIncludes.cc
diff --git a/clang-tidy/FixIncludes.hh b/subprojects/lix-clang-tidy/FixIncludes.hh
index ea890cd39..ea890cd39 100644
--- a/clang-tidy/FixIncludes.hh
+++ b/subprojects/lix-clang-tidy/FixIncludes.hh
diff --git a/clang-tidy/HasPrefixSuffix.cc b/subprojects/lix-clang-tidy/HasPrefixSuffix.cc
index e29b67e7c..e29b67e7c 100644
--- a/clang-tidy/HasPrefixSuffix.cc
+++ b/subprojects/lix-clang-tidy/HasPrefixSuffix.cc
diff --git a/clang-tidy/HasPrefixSuffix.hh b/subprojects/lix-clang-tidy/HasPrefixSuffix.hh
index 8693b6767..8693b6767 100644
--- a/clang-tidy/HasPrefixSuffix.hh
+++ b/subprojects/lix-clang-tidy/HasPrefixSuffix.hh
diff --git a/clang-tidy/LixClangTidyChecks.cc b/subprojects/lix-clang-tidy/LixClangTidyChecks.cc
index b3503dd3a..b3503dd3a 100644
--- a/clang-tidy/LixClangTidyChecks.cc
+++ b/subprojects/lix-clang-tidy/LixClangTidyChecks.cc
diff --git a/clang-tidy/README.md b/subprojects/lix-clang-tidy/README.md
index c2d1cb258..c2d1cb258 100644
--- a/clang-tidy/README.md
+++ b/subprojects/lix-clang-tidy/README.md
diff --git a/subprojects/lix-clang-tidy/default.nix b/subprojects/lix-clang-tidy/default.nix
new file mode 100644
index 000000000..1bfc2d9a4
--- /dev/null
+++ b/subprojects/lix-clang-tidy/default.nix
@@ -0,0 +1,44 @@
+{
+ lib,
+ stdenv,
+ cmake,
+ meson,
+ ninja,
+ llvmPackages,
+}:
+let
+ inherit (lib) fileset;
+in
+stdenv.mkDerivation {
+ pname = "lix-clang-tidy-checks";
+ # Setting the version to the Lix version is just going to cause pointless
+ # rebuilds due to versionSuffix and similar, and I cannot conceive of a usage
+ # where we actually care about its version since this is internal-only.
+ version = "0.1";
+
+ src = fileset.toSource {
+ root = ./.;
+ fileset = fileset.unions [
+ ./meson.build
+ ./meson.options
+ (fileset.fileFilter (
+ { hasExt, ... }:
+ builtins.any hasExt [
+ "cc"
+ "hh"
+ ]
+ ) ./.)
+ ];
+ };
+
+ nativeBuildInputs = [
+ meson
+ cmake
+ ninja
+ ];
+
+ buildInputs = [
+ llvmPackages.llvm
+ llvmPackages.clang-unwrapped.dev
+ ];
+}
diff --git a/subprojects/lix-clang-tidy/meson.build b/subprojects/lix-clang-tidy/meson.build
new file mode 100644
index 000000000..ef0226420
--- /dev/null
+++ b/subprojects/lix-clang-tidy/meson.build
@@ -0,0 +1,18 @@
+project('lix-clang-tidy', ['cpp', 'c'],
+ version : '0.1',
+ default_options : ['warning_level=3', 'cpp_std=c++20']
+)
+
+llvm = dependency('Clang', version: '>= 17', modules: ['libclang'])
+sources = files(
+ 'HasPrefixSuffix.cc',
+ 'LixClangTidyChecks.cc',
+ 'FixIncludes.cc',
+)
+
+lix_clang_tidy = shared_module('lix-clang-tidy', sources,
+ dependencies: llvm,
+ # overrides build_by_default, see https://github.com/mesonbuild/meson/issues/13498
+ install : get_option('build-by-default'),
+ build_by_default : get_option('build-by-default')
+)
diff --git a/subprojects/lix-clang-tidy/meson.options b/subprojects/lix-clang-tidy/meson.options
new file mode 100644
index 000000000..57f0f713b
--- /dev/null
+++ b/subprojects/lix-clang-tidy/meson.options
@@ -0,0 +1,3 @@
+option('build-by-default', type : 'boolean', value : true,
+ description : 'set to false to not actually build targets by default. This is a hack to deal with meson lacking a build_by_default default option and building subprojects by default'
+)