3 ###########################################################################
5 # Copyright 2013 BMW Car IT GmbH
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
11 # http://www.apache.org/licenses/LICENSE-2.0
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
19 ###########################################################################
21 import sys, re, string
22 from common_modules.common import *
23 from common_modules.config import G_LICENSE_TEMPLATES
26 def clean_comment_chars(s):
28 Removes comment characters from a string
31 s = string.replace(s, "/", "")
32 s = string.replace(s, "*", "")
33 s = string.replace(s, "#", "")
36 def make_license_re(license_text):
38 Makes a regular expression for every line in the license, this would match the license
39 text tolerating extra spaces
42 license_lines = license_text.split("\n")
44 for i in range(len(license_lines)):
45 license_line = license_lines[i]
46 re_text = clean_comment_chars(license_line)
47 #remove white space paddings
48 re_text = re_text.strip(" \n\t\r\f")
49 #replace special characters
50 re_text = string.replace(re_text, "(", "\(")
51 re_text = string.replace(re_text, ")", "\)")
52 re_text = string.replace(re_text, " ", "(\s+)")
53 re_text = string.replace(re_text, "\n", "(\s*)")
54 re_text = string.replace(re_text, "\t", "(\s+)")
55 re_text = string.replace(re_text, ".", "\.")
57 #this replaces the text [YYYY] with a regex that mathces years in one of the following forms:
58 #2013 or 2000-2013 or 2000 or 2000, 2001, 2002, 2013
59 re_text = string.replace(re_text, "[YYYY]", r"(\d{4})(((-(\d{4}))|(((\s*),(\s*)\d{4})+))?)")
62 re_text = "(\s*)" + re_text + "(\s*)"
64 #remove unneeded space matches
65 while current_text != re_text:
66 current_text = re_text
67 re_text = string.replace(re_text, "(\s*)(\s*)", "(\s*)")
68 re_text = string.replace(re_text, "(\s+)(\s+)", "(\s+)")
69 re_text = string.replace(re_text, "(\s*)(\s+)", "(\s+)")
71 license_lines_re[i] = re_text
73 return license_lines_re
75 def check_specific_license_in_file(filename, clean_file_contents, license_text):
77 Checks if the file contains a valid license according to the license template provided
80 license_lines = license_text.split("\n")
82 license_re = make_license_re(license_text)
84 #search for the first line of the license in the target file
85 line_re = re.compile(license_re.values()[0])
86 found_match = line_re.search(clean_file_contents)
89 clean_file_contents = clean_file_contents[found_match.start():]
91 #check that license exists without any added or removed words
92 for (line_num, line_re_text) in license_re.items():
93 line_re = re.compile(line_re_text)
94 found_match = line_re.match(clean_file_contents)
97 clean_file_contents = clean_file_contents[found_match.end():]
99 #log_warning(filename, 1, "license does not match at", license_lines[line_num])
100 return (line_num, license_lines[line_num])
102 return None # success
104 def check_license_in_file(filename, file_contents):
106 Checks if the file contains a valid license.
107 It tries to find a match inside the file with any of the licenses configured
110 clean_file_contents = clean_comment_chars(file_contents)
112 #license that had the best match with the file
113 best_match = (-1, None)
115 #try to match with every license
116 for license in G_LICENSE_TEMPLATES:
117 call_result = check_specific_license_in_file(filename, clean_file_contents, license)
119 #if match is found just return
120 if call_result == None:
123 #if no match found check if this license was a good candidate for the match
125 best_match = call_result if call_result[0] > best_match[0] else best_match
127 #(this else clause is executed if the for loop exists naturally)
128 #if loop ended without return, this means no license matched
130 #if no license matched at all
131 if best_match[1] == None:
132 log_warning(filename, 1, "no license found")
134 #get the license with the best match
136 log_warning(filename, 1, "license does not match at", best_match[1])
138 if __name__ == "__main__":
139 targets = sys.argv[1:]
140 targets = get_all_files(targets)
142 if len(targets) == 0:
144 \t**** No input provided ****
145 \tTakes a list of files/directories as input and performs specific style checking on all files/directories.
147 \tGives warnings if the file does not contain a valid license text. It does not check if Copyright statements are included.
152 file_contents, _, _, _ = read_file(t)
153 check_license_in_file(t, file_contents)