11cb0ef41Sopenharmony_ci#!/usr/bin/env python
21cb0ef41Sopenharmony_ci# vim:fenc=utf-8:shiftwidth=2
31cb0ef41Sopenharmony_ci
41cb0ef41Sopenharmony_ci# Copyright 2018 the V8 project authors. All rights reserved.
51cb0ef41Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be
61cb0ef41Sopenharmony_ci# found in the LICENSE file.
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ci"""Check that each header can be included in isolation.
91cb0ef41Sopenharmony_ci
101cb0ef41Sopenharmony_ciFor each header we generate one .cc file which only includes this one header.
111cb0ef41Sopenharmony_ciAll these .cc files are then added to a sources.gni file which is included in
121cb0ef41Sopenharmony_ciBUILD.gn. Just compile to check whether there are any violations to the rule
131cb0ef41Sopenharmony_cithat each header must be includable in isolation.
141cb0ef41Sopenharmony_ci"""
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_ci# for py2/py3 compatibility
171cb0ef41Sopenharmony_cifrom __future__ import print_function
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ciimport argparse
201cb0ef41Sopenharmony_ciimport os
211cb0ef41Sopenharmony_ciimport os.path
221cb0ef41Sopenharmony_ciimport re
231cb0ef41Sopenharmony_ciimport sys
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ci# TODO(clemensb): Extend to tests.
261cb0ef41Sopenharmony_ciDEFAULT_INPUT = ['base', 'include', 'src']
271cb0ef41Sopenharmony_ciDEFAULT_GN_FILE = 'BUILD.gn'
281cb0ef41Sopenharmony_ciMY_DIR = os.path.dirname(os.path.realpath(__file__))
291cb0ef41Sopenharmony_ciV8_DIR = os.path.dirname(MY_DIR)
301cb0ef41Sopenharmony_ciOUT_DIR = os.path.join(V8_DIR, 'check-header-includes')
311cb0ef41Sopenharmony_ciAUTO_EXCLUDE = [
321cb0ef41Sopenharmony_ci  # flag-definitions.h needs a mode set for being included.
331cb0ef41Sopenharmony_ci  'src/flags/flag-definitions.h',
341cb0ef41Sopenharmony_ci  # recorder.h should only be included conditionally.
351cb0ef41Sopenharmony_ci  'src/libplatform/tracing/recorder.h',
361cb0ef41Sopenharmony_ci  # trap-handler-simulator.h can only be included in simulator builds.
371cb0ef41Sopenharmony_ci  'src/trap-handler/trap-handler-simulator.h',
381cb0ef41Sopenharmony_ci]
391cb0ef41Sopenharmony_ciAUTO_EXCLUDE_PATTERNS = [
401cb0ef41Sopenharmony_ci    'src/base/atomicops_internals_.*',
411cb0ef41Sopenharmony_ci    # TODO(petermarshall): Enable once Perfetto is built by default.
421cb0ef41Sopenharmony_ci    'src/libplatform/tracing/perfetto*',
431cb0ef41Sopenharmony_ci    # TODO(v8:7700): Enable once Maglev is built by default.
441cb0ef41Sopenharmony_ci    'src/maglev/.*',
451cb0ef41Sopenharmony_ci] + [
461cb0ef41Sopenharmony_ci    # platform-specific headers
471cb0ef41Sopenharmony_ci    '\\b{}\\b'.format(p)
481cb0ef41Sopenharmony_ci    for p in ('win', 'win32', 'ia32', 'x64', 'arm', 'arm64', 'mips', 'mips64',
491cb0ef41Sopenharmony_ci              's390', 'ppc', 'riscv64', 'loong64')
501cb0ef41Sopenharmony_ci]
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ciargs = None
531cb0ef41Sopenharmony_cidef parse_args():
541cb0ef41Sopenharmony_ci  global args
551cb0ef41Sopenharmony_ci  parser = argparse.ArgumentParser()
561cb0ef41Sopenharmony_ci  parser.add_argument('-i', '--input', type=str, action='append',
571cb0ef41Sopenharmony_ci                      help='Headers or directories to check (directories '
581cb0ef41Sopenharmony_ci                           'are scanned for headers recursively); default: ' +
591cb0ef41Sopenharmony_ci                           ','.join(DEFAULT_INPUT))
601cb0ef41Sopenharmony_ci  parser.add_argument('-x', '--exclude', type=str, action='append',
611cb0ef41Sopenharmony_ci                      help='Add an exclude pattern (regex)')
621cb0ef41Sopenharmony_ci  parser.add_argument('-v', '--verbose', action='store_true',
631cb0ef41Sopenharmony_ci                      help='Be verbose')
641cb0ef41Sopenharmony_ci  args = parser.parse_args()
651cb0ef41Sopenharmony_ci  args.exclude = (args.exclude or []) + AUTO_EXCLUDE_PATTERNS
661cb0ef41Sopenharmony_ci  args.exclude += ['^' + re.escape(x) + '$' for x in AUTO_EXCLUDE]
671cb0ef41Sopenharmony_ci  if not args.input:
681cb0ef41Sopenharmony_ci    args.input=DEFAULT_INPUT
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci
711cb0ef41Sopenharmony_cidef printv(line):
721cb0ef41Sopenharmony_ci  if args.verbose:
731cb0ef41Sopenharmony_ci    print(line)
741cb0ef41Sopenharmony_ci
751cb0ef41Sopenharmony_ci
761cb0ef41Sopenharmony_cidef find_all_headers():
771cb0ef41Sopenharmony_ci  printv('Searching for headers...')
781cb0ef41Sopenharmony_ci  header_files = []
791cb0ef41Sopenharmony_ci  exclude_patterns = [re.compile(x) for x in args.exclude]
801cb0ef41Sopenharmony_ci  def add_recursively(filename):
811cb0ef41Sopenharmony_ci    full_name = os.path.join(V8_DIR, filename)
821cb0ef41Sopenharmony_ci    if not os.path.exists(full_name):
831cb0ef41Sopenharmony_ci      sys.exit('File does not exist: {}'.format(full_name))
841cb0ef41Sopenharmony_ci    if os.path.isdir(full_name):
851cb0ef41Sopenharmony_ci      for subfile in os.listdir(full_name):
861cb0ef41Sopenharmony_ci        full_name = os.path.join(filename, subfile)
871cb0ef41Sopenharmony_ci        printv('Scanning {}'.format(full_name))
881cb0ef41Sopenharmony_ci        add_recursively(full_name)
891cb0ef41Sopenharmony_ci    elif filename.endswith('.h'):
901cb0ef41Sopenharmony_ci      printv('--> Found header file {}'.format(filename))
911cb0ef41Sopenharmony_ci      for p in exclude_patterns:
921cb0ef41Sopenharmony_ci        if p.search(filename):
931cb0ef41Sopenharmony_ci          printv('--> EXCLUDED (matches {})'.format(p.pattern))
941cb0ef41Sopenharmony_ci          return
951cb0ef41Sopenharmony_ci      header_files.append(filename)
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci  for filename in args.input:
981cb0ef41Sopenharmony_ci    add_recursively(filename)
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_ci  return header_files
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci
1031cb0ef41Sopenharmony_cidef get_cc_file_name(header):
1041cb0ef41Sopenharmony_ci  split = os.path.split(header)
1051cb0ef41Sopenharmony_ci  header_dir = os.path.relpath(split[0], V8_DIR)
1061cb0ef41Sopenharmony_ci  # Prefix with the directory name, to avoid collisions in the object files.
1071cb0ef41Sopenharmony_ci  prefix = header_dir.replace(os.path.sep, '-')
1081cb0ef41Sopenharmony_ci  cc_file_name = 'test-include-' + prefix + '-' + split[1][:-1] + 'cc'
1091cb0ef41Sopenharmony_ci  return os.path.join(OUT_DIR, cc_file_name)
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_ci
1121cb0ef41Sopenharmony_cidef create_including_cc_files(header_files):
1131cb0ef41Sopenharmony_ci  comment = 'check including this header in isolation'
1141cb0ef41Sopenharmony_ci  for header in header_files:
1151cb0ef41Sopenharmony_ci    cc_file_name = get_cc_file_name(header)
1161cb0ef41Sopenharmony_ci    rel_cc_file_name = os.path.relpath(cc_file_name, V8_DIR)
1171cb0ef41Sopenharmony_ci    content = '#include "{}"  // {}\n'.format(header, comment)
1181cb0ef41Sopenharmony_ci    if os.path.exists(cc_file_name):
1191cb0ef41Sopenharmony_ci      with open(cc_file_name) as cc_file:
1201cb0ef41Sopenharmony_ci        if cc_file.read() == content:
1211cb0ef41Sopenharmony_ci          printv('File {} is up to date'.format(rel_cc_file_name))
1221cb0ef41Sopenharmony_ci          continue
1231cb0ef41Sopenharmony_ci    printv('Creating file {}'.format(rel_cc_file_name))
1241cb0ef41Sopenharmony_ci    with open(cc_file_name, 'w') as cc_file:
1251cb0ef41Sopenharmony_ci      cc_file.write(content)
1261cb0ef41Sopenharmony_ci
1271cb0ef41Sopenharmony_ci
1281cb0ef41Sopenharmony_cidef generate_gni(header_files):
1291cb0ef41Sopenharmony_ci  gni_file = os.path.join(OUT_DIR, 'sources.gni')
1301cb0ef41Sopenharmony_ci  printv('Generating file "{}"'.format(os.path.relpath(gni_file, V8_DIR)))
1311cb0ef41Sopenharmony_ci  with open(gni_file, 'w') as gn:
1321cb0ef41Sopenharmony_ci    gn.write("""\
1331cb0ef41Sopenharmony_ci# Copyright 2018 The Chromium Authors. All rights reserved.
1341cb0ef41Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be
1351cb0ef41Sopenharmony_ci# found in the LICENSE file.
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_ci# This list is filled automatically by tools/check_header_includes.py.
1381cb0ef41Sopenharmony_cicheck_header_includes_sources = [
1391cb0ef41Sopenharmony_ci""");
1401cb0ef41Sopenharmony_ci    for header in header_files:
1411cb0ef41Sopenharmony_ci      cc_file_name = get_cc_file_name(header)
1421cb0ef41Sopenharmony_ci      gn.write('    "{}",\n'.format(os.path.relpath(cc_file_name, V8_DIR)))
1431cb0ef41Sopenharmony_ci    gn.write(']\n')
1441cb0ef41Sopenharmony_ci
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_cidef main():
1471cb0ef41Sopenharmony_ci  parse_args()
1481cb0ef41Sopenharmony_ci  header_files = find_all_headers()
1491cb0ef41Sopenharmony_ci  if not os.path.exists(OUT_DIR):
1501cb0ef41Sopenharmony_ci    os.mkdir(OUT_DIR)
1511cb0ef41Sopenharmony_ci  create_including_cc_files(header_files)
1521cb0ef41Sopenharmony_ci  generate_gni(header_files)
1531cb0ef41Sopenharmony_ci
1541cb0ef41Sopenharmony_ciif __name__ == '__main__':
1551cb0ef41Sopenharmony_ci  main()
156