aboutsummaryrefslogtreecommitdiff
path: root/scripts/stripdecls.py
blob: 34be2a1578747b0e28118820eeeaa37f12184da8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#!/usr/bin/python
# vim: set fileencoding=utf-8:

from __future__ import print_function, unicode_literals, division

from clang.cindex import Index, CursorKind
from collections import namedtuple, OrderedDict, defaultdict
import sys
import os


DECL_KINDS = {
  CursorKind.FUNCTION_DECL,
}


Strip = namedtuple('Strip', 'start_line start_column end_line end_column')


def main(progname, cfname, only_static, move_all):
  only_static = False

  cfname = os.path.abspath(os.path.normpath(cfname))

  hfname1 = os.path.splitext(cfname)[0] + os.extsep + 'h'
  hfname2 = os.path.splitext(cfname)[0] + '_defs' + os.extsep + 'h'

  files_to_modify = (cfname, hfname1, hfname2)

  index = Index.create()
  src_dirname = os.path.join(os.path.dirname(__file__), '..', 'src')
  src_dirname = os.path.abspath(os.path.normpath(src_dirname))
  relname = os.path.join(src_dirname, 'nvim')
  unit = index.parse(cfname, args=('-I' + src_dirname,
                                   '-DUNIX',
                                   '-DEXITFREE',
                                   '-DFEAT_USR_CMDS',
                                   '-DFEAT_CMDL_COMPL',
                                   '-DFEAT_COMPL_FUNC',
                                   '-DPROTO',
                                   '-DUSE_MCH_ERRMSG'))
  cursor = unit.cursor

  tostrip = defaultdict(OrderedDict)
  definitions = set()

  for child in cursor.get_children():
    if not (child.location and child.location.file):
      continue
    fname = os.path.abspath(os.path.normpath(child.location.file.name))
    if fname not in files_to_modify:
      continue
    if child.kind not in DECL_KINDS:
      continue
    if only_static and next(child.get_tokens()).spelling == 'static':
      continue

    if child.is_definition() and fname == cfname:
      definitions.add(child.spelling)
    else:
      stripdict = tostrip[fname]
      assert(child.spelling not in stripdict)
      stripdict[child.spelling] = Strip(
        child.extent.start.line,
        child.extent.start.column,
        child.extent.end.line,
        child.extent.end.column,
      )

  for (fname, stripdict) in tostrip.items():
    if not move_all:
      for name in set(stripdict) - definitions:
        stripdict.pop(name)

    if not stripdict:
      continue

    if fname.endswith('.h'):
      is_h_file = True
      include_line = next(reversed(stripdict.values())).start_line + 1
    else:
      is_h_file = False
      include_line = next(iter(stripdict.values())).start_line

    lines = None
    generated_existed = os.path.exists(fname + '.generated.h')
    with open(fname, 'rb') as F:
      lines = list(F)

    stripped = []

    for name, position in reversed(stripdict.items()):
      sl = slice(position.start_line - 1, position.end_line)
      if is_h_file:
        include_line -= sl.stop - sl.start
      stripped += lines[sl]
      lines[sl] = ()

    if not generated_existed:
      lines[include_line:include_line] = [
        '#ifdef INCLUDE_GENERATED_DECLARATIONS\n',
        '# include "{0}.generated.h"\n'.format(os.path.relpath(fname, relname)),
        '#endif\n',
      ]

    with open(fname, 'wb') as F:
      F.writelines(lines)


if __name__ == '__main__':
  progname = sys.argv[0]
  args = sys.argv[1:]
  if not args or '--help' in args:
    print('Usage:')
    print('')
    print('  {0} [--static [--all]] file.c...'.format(progname))
    print('')
    print('Stripts all declarations from file.c, file.h and file_defs.h.')
    print('If --static argument is given then only static declarations are')
    print('stripped. Declarations are stripped only if corresponding')
    print('definition is found unless --all argument was given.')
    print('')
    print('Note: it is assumed that static declarations starts with "static"')
    print('      keyword.')
    sys.exit(0 if args else 1)

  if args[0] == '--static':
    only_static = True
    args = args[1:]
  else:
    only_static = False

  if args[0] == '--all':
    move_all = True
    args = args[1:]
  else:
    move_all = False

  for cfname in args:
    print('Processing {0}'.format(cfname))
    main(progname, cfname, only_static, move_all)