xref: /aosp_15_r20/external/perfetto/tools/check_include_violations (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1#!/usr/bin/env python3
2# Copyright (C) 2019 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16# This tool checks for layering violations in the include/ directory.
17# It checks that:
18# - public includes don't end up depending on non-public /ext/ includes.
19# - public includes don't end up depending on private src/ headers.
20# - We use consistently <angle brackets> for other libraries.
21
22from __future__ import absolute_import
23from __future__ import division
24from __future__ import print_function
25
26import os
27import re
28import subprocess
29import sys
30
31ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
32
33
34def main():
35  errors = 0
36  include_root = os.path.join(ROOT_DIR, 'include')
37  for root, _, files in os.walk(include_root):
38    for fname in files:
39      fpath = os.path.join(root, fname).replace('\\', '/')  # For Windows.
40      rel_path = os.path.relpath(fpath, ROOT_DIR).replace('\\', '/')
41      if not os.path.isfile(fpath):
42        continue
43      if fpath.endswith('.cc'):
44        sys.stderr.write('.cc files not allowed in includes/ : ' + rel_path +
45                         '\n')
46        errors += 1
47        continue
48      if fpath.endswith('.h'):
49        with open(fpath) as f:
50          lines = f.readlines()
51        for line in lines:
52          if '// no-include-violation-check' in line:
53            continue
54          m = re.findall(r'^#include "(.*\.h)"', line)
55          if not m:
56            continue
57          incl = m[0]
58
59          # Allow only #include "perfetto/..." or "protos/..." but not "src/".
60          if not (incl.startswith('perfetto/') or incl.startswith('protos/')):
61            sys.stderr.write(
62                ('Public header %s is trying to include %s which is outside ' +
63                 'of include/. If you are trying to include a library use ' +
64                 ' <angle brackets> instead\n') % (rel_path, incl))
65            errors += 1
66            continue
67
68          # Ignore lines marked with nogncheck.
69          if '// nogncheck' in line:
70            continue
71
72          # Public (non-/ext/) headers cannot include /ext/ headers.
73          if (not rel_path.startswith('include/perfetto/ext/') and
74              incl.startswith('perfetto/ext/')):
75            sys.stderr.write(('Public header %s cannot include the non-public' +
76                              '/ext/ header %s.\n') % (rel_path, incl))
77            errors += 1
78            continue
79
80  return 0 if errors == 0 else 1
81
82
83if __name__ == '__main__':
84  sys.exit(main())
85