• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# Copyright 2018 The Amber Authors.
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"""Adds copyright notices to all the files that need them under the
16current directory.
17
18usage: copyright.py [--check]
19
20With --check, prints out all the files missing the copyright notice and exits
21with status 1 if any such files are found, 0 if none.
22"""
23
24from __future__ import print_function
25
26import fileinput
27import fnmatch
28import os
29import re
30import sys
31
32COPYRIGHTRE = re.compile(
33    r'Copyright \d+ The Amber Authors.')
34COPYRIGHT = 'Copyright 2018 The Amber Authors.'
35LICENSED = """
36Licensed under the Apache License, Version 2.0 (the "License");
37you may not use this file except in compliance with the License.
38You may obtain a copy of the License at
39
40    http://www.apache.org/licenses/LICENSE-2.0
41
42Unless required by applicable law or agreed to in writing, software
43distributed under the License is distributed on an "AS IS" BASIS,
44WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
45See the License for the specific language governing permissions and
46limitations under the License."""
47
48
49def find(top, filename_glob, skip_glob_list):
50    """Returns files in the tree rooted at top matching filename_glob but not
51    in directories matching skip_glob_list."""
52
53    file_list = []
54    for path, dirs, files in os.walk(top):
55        for glob in skip_glob_list:
56            for match in fnmatch.filter(dirs, glob):
57                dirs.remove(match)
58        for filename in fnmatch.filter(files, filename_glob):
59            file_list.append(os.path.join(path, filename))
60    return file_list
61
62
63def filtered_descendants(glob):
64    """Returns glob-matching filenames under the current directory, but skips
65    some irrelevant paths."""
66    return find('.', glob, ['third_party', 'external', 'build*', 'out*',
67                            'CompilerIdCXX'])
68
69
70def skip(line):
71    """Returns true if line is all whitespace or shebang."""
72    stripped = line.lstrip()
73    return stripped == '' or stripped.startswith('#!')
74
75
76def comment(text, prefix):
77    """Returns commented-out text.
78
79    Each line of text will be prefixed by prefix and a space character.  Any
80    trailing whitespace will be trimmed.
81    """
82    accum = []
83    for line in text.split('\n'):
84        accum.append((prefix + ' ' + line).rstrip())
85    return '\n'.join(accum)
86
87
88def insert_copyright(glob, comment_prefix):
89    """Finds all glob-matching files under the current directory and inserts the
90    copyright message into them unless they already have it or are empty.
91
92    The copyright message goes into the first non-whitespace, non-shebang line
93    in a file.  It is prefixed on each line by comment_prefix and a space.
94    """
95    copyright = comment(COPYRIGHT, comment_prefix) + '\n'
96    licensed = comment(LICENSED, comment_prefix) + '\n\n'
97    for file in filtered_descendants(glob):
98        has_copyright = False
99        for line in fileinput.input(file, inplace=1):
100            has_copyright = has_copyright or COPYRIGHTRE.search(line)
101            if not has_copyright and not skip(line):
102                sys.stdout.write(copyright)
103                sys.stdout.write(licensed)
104                has_copyright = True
105            sys.stdout.write(line)
106        if not has_copyright:
107            open(file, 'a').write(copyright + licensed)
108
109
110def alert_if_no_copyright(glob, comment_prefix):
111    """Prints names of all files missing a copyright message.
112
113    Finds all glob-matching files under the current directory and checks if they
114    contain the copyright message.  Prints the names of all the files that
115    don't.
116
117    Returns the total number of file names printed.
118    """
119    printed_count = 0
120    for file in filtered_descendants(glob):
121        has_copyright = False
122        with open(file) as contents:
123            for line in contents:
124                if COPYRIGHTRE.search(line):
125                    has_copyright = True
126                    break
127        if not has_copyright:
128            print(file, ' has no copyright message.')
129            printed_count += 1
130    return printed_count
131
132
133def main():
134    glob_comment_pairs = [('*.h', '//'), ('*.hpp', '//'), ('*.cc', '//'),
135                          ('*.py', '#'), ('*.cpp', '//'), ('*.mm', '//')]
136    if '--check' in sys.argv:
137        count = 0
138        for pair in glob_comment_pairs:
139            count += alert_if_no_copyright(*pair)
140        sys.exit(count > 0)
141    else:
142        for pair in glob_comment_pairs:
143            insert_copyright(*pair)
144
145
146if __name__ == '__main__':
147    main()
148