meson: wire up static analysis via Coccinelle

Wire up static analysis via Coccinelle via a new test target "coccicheck". This target can be executed via `meson compile coccicheck` and generates the semantic patch for us. Note that we don't hardcode the list of source and header files that shall be analyzed, and instead use git-ls-files(1) to find them for us. This is because we also want to analyze files that may not get built on the current platform, so finding all sources at configure time is easier than introducing a new variable that tracks all sources, including those which aren't being built. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Feb 18, 2025 at 08:45 UTC c1d6628c9433c09ff62f916f2b933ee12995e9d8
3 files changed +92
contrib/coccinelle/meson.build new
+89
@@ -0,0 +1,89 @@
1 +spatch = find_program('spatch', required: get_option('coccinelle'))
2 +if not spatch.found()
3 + subdir_done()
4 +endif
5 +
6 +third_party_sources = [
7 + ':!contrib',
8 + ':!compat/inet_ntop.c',
9 + ':!compat/inet_pton.c',
10 + ':!compat/nedmalloc',
11 + ':!compat/obstack.*',
12 + ':!compat/poll',
13 + ':!compat/regex',
14 + ':!sha1collisiondetection',
15 + ':!sha1dc',
16 + ':!t/unit-tests/clar',
17 + ':!t/unit-tests/clar',
18 + ':!t/t[0-9][0-9][0-9][0-9]*',
19 +]
20 +
21 +rules = [
22 + 'array.cocci',
23 + 'commit.cocci',
24 + 'config_fn_ctx.pending.cocci',
25 + 'equals-null.cocci',
26 + 'flex_alloc.cocci',
27 + 'free.cocci',
28 + 'git_config_number.cocci',
29 + 'hashmap.cocci',
30 + 'index-compatibility.cocci',
31 + 'object_id.cocci',
32 + 'preincr.cocci',
33 + 'qsort.cocci',
34 + 'refs.cocci',
35 + 'strbuf.cocci',
36 + 'swap.cocci',
37 + 'the_repository.cocci',
38 + 'xcalloc.cocci',
39 + 'xopen.cocci',
40 + 'xstrdup_or_null.cocci',
41 + 'xstrncmpz.cocci',
42 +]
43 +
44 +concatenated_rules = custom_target(
45 + command: [
46 + 'cat', '@INPUT@',
47 + ],
48 + input: rules,
49 + output: 'rules.cocci',
50 + capture: true,
51 +)
52 +
53 +sources = [ ]
54 +foreach source : run_command(git, '-C', meson.project_source_root(), 'ls-files', '--deduplicate', '*.c', third_party_sources, check: true).stdout().split()
55 + sources += source
56 +endforeach
57 +
58 +headers = [ ]
59 +foreach header : run_command(git, '-C', meson.project_source_root(), 'ls-files', '--deduplicate', '*.h', third_party_sources, check: true).stdout().split()
60 + headers += meson.project_source_root() / header
61 +endforeach
62 +
63 +patches = [ ]
64 +foreach source : sources
65 + patches += custom_target(
66 + command: [
67 + spatch,
68 + '--all-includes',
69 + '--sp-file', concatenated_rules,
70 + '--patch', meson.project_source_root(),
71 + '@INPUT@',
72 + ],
73 + input: meson.project_source_root() / source,
74 + output: source.underscorify() + '.patch',
75 + capture: true,
76 + depend_files: headers,
77 + )
78 +endforeach
79 +
80 +concatenated_patch = custom_target(
81 + command: [
82 + 'cat', '@INPUT@',
83 + ],
84 + input: patches,
85 + output: 'cocci.patch',
86 + capture: true,
87 +)
88 +
89 +alias_target('coccicheck', concatenated_patch)
contrib/meson.build
+1
@@ -2,4 +2,5 @@ foreach feature : get_option('contrib')
2 subdir(feature)
3 endforeach
4
5 +subdir('coccinelle')
6 subdir('credential')
meson_options.txt
+2
@@ -101,6 +101,8 @@ option('docs_backend', type: 'combo', choices: ['asciidoc', 'asciidoctor', 'auto
101 description: 'Which backend to use to generate documentation.')
102
103 # Testing.
104 +option('coccinelle', type: 'feature', value: 'auto',
105 + description: 'Provide a coccicheck target that generates a Coccinelle patch.')
106 option('tests', type: 'boolean', value: true,
107 description: 'Enable building tests. This requires Perl, but is separate from the "perl" option such that you can build tests without Perl features enabled.')
108 option('test_output_directory', type: 'string',