aboutsummaryrefslogtreecommitdiff
path: root/tools/copyright.py
blob: ebf0a19d3c63fc549994cd56e73ebd6b3e435247 (plain)
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
142
143
144
145
146
147
#!/usr/bin/env python
# Copyright 2018 The Amber Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Adds copyright notices to all the files that need them under the
current directory.

usage: copyright.py [--check]

With --check, prints out all the files missing the copyright notice and exits
with status 1 if any such files are found, 0 if none.
"""

from __future__ import print_function

import fileinput
import fnmatch
import os
import re
import sys

COPYRIGHTRE = re.compile(
    r'Copyright \d+ The Amber Authors.')
COPYRIGHT = 'Copyright 2018 The Amber Authors.'
LICENSED = """
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License."""


def find(top, filename_glob, skip_glob_list):
    """Returns files in the tree rooted at top matching filename_glob but not
    in directories matching skip_glob_list."""

    file_list = []
    for path, dirs, files in os.walk(top):
        for glob in skip_glob_list:
            for match in fnmatch.filter(dirs, glob):
                dirs.remove(match)
        for filename in fnmatch.filter(files, filename_glob):
            file_list.append(os.path.join(path, filename))
    return file_list


def filtered_descendants(glob):
    """Returns glob-matching filenames under the current directory, but skips
    some irrelevant paths."""
    return find('.', glob, ['third_party', 'external', 'build*', 'out*',
                            'CompilerIdCXX'])


def skip(line):
    """Returns true if line is all whitespace or shebang."""
    stripped = line.lstrip()
    return stripped == '' or stripped.startswith('#!')


def comment(text, prefix):
    """Returns commented-out text.

    Each line of text will be prefixed by prefix and a space character.  Any
    trailing whitespace will be trimmed.
    """
    accum = []
    for line in text.split('\n'):
        accum.append((prefix + ' ' + line).rstrip())
    return '\n'.join(accum)


def insert_copyright(glob, comment_prefix):
    """Finds all glob-matching files under the current directory and inserts the
    copyright message into them unless they already have it or are empty.

    The copyright message goes into the first non-whitespace, non-shebang line
    in a file.  It is prefixed on each line by comment_prefix and a space.
    """
    copyright = comment(COPYRIGHT, comment_prefix) + '\n'
    licensed = comment(LICENSED, comment_prefix) + '\n\n'
    for file in filtered_descendants(glob):
        has_copyright = False
        for line in fileinput.input(file, inplace=1):
            has_copyright = has_copyright or COPYRIGHTRE.search(line)
            if not has_copyright and not skip(line):
                sys.stdout.write(copyright)
                sys.stdout.write(licensed)
                has_copyright = True
            sys.stdout.write(line)
        if not has_copyright:
            open(file, 'a').write(copyright + licensed)


def alert_if_no_copyright(glob, comment_prefix):
    """Prints names of all files missing a copyright message.

    Finds all glob-matching files under the current directory and checks if they
    contain the copyright message.  Prints the names of all the files that
    don't.

    Returns the total number of file names printed.
    """
    printed_count = 0
    for file in filtered_descendants(glob):
        has_copyright = False
        with open(file) as contents:
            for line in contents:
                if COPYRIGHTRE.search(line):
                    has_copyright = True
                    break
        if not has_copyright:
            print(file, ' has no copyright message.')
            printed_count += 1
    return printed_count


def main():
    glob_comment_pairs = [('*.h', '//'), ('*.hpp', '//'), ('*.cc', '//'),
                          ('*.py', '#'), ('*.cpp', '//'), ('*.mm', '//')]
    if '--check' in sys.argv:
        count = 0
        for pair in glob_comment_pairs:
            count += alert_if_no_copyright(*pair)
        sys.exit(count > 0)
    else:
        for pair in glob_comment_pairs:
            insert_copyright(*pair)


if __name__ == '__main__':
    main()