aboutsummaryrefslogtreecommitdiff
path: root/scripts/stripdecls.py
blob: b4ac34dcfe4734c914d700a180f17566522b6050 (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
#!/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):
    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)