OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """ | 6 """ |
7 Utilities for checking and processing licensing information in third_party | 7 Utilities for checking and processing licensing information in third_party |
8 directories. | 8 directories. |
9 """ | 9 """ |
10 | 10 |
11 import os | 11 import os |
12 | 12 |
| 13 # Paths from the root of the tree to directories to skip. |
| 14 PRUNE_PATHS = set([ |
| 15 # This is just a tiny vsprops file, presumably written by the googleurl |
| 16 # authors. Not third-party code. |
| 17 "googleurl/third_party/icu", |
| 18 |
| 19 # We don't bundle o3d samples into our resulting binaries. |
| 20 "o3d/samples", |
| 21 |
| 22 # Written as part of Chromium. |
| 23 "tools/fuzzymatch", |
| 24 |
| 25 # Two directories that are the same as those in base/third_party. |
| 26 "v8/src/third_party/dtoa", |
| 27 "v8/src/third_party/valgrind", |
| 28 ]) |
| 29 |
13 | 30 |
14 class LicenseError(Exception): | 31 class LicenseError(Exception): |
15 """We raise this exception when a directory's licensing info isn't | 32 """We raise this exception when a directory's licensing info isn't |
16 fully filled out.""" | 33 fully filled out.""" |
17 pass | 34 pass |
18 | 35 |
19 | 36 |
20 def ParseDir(path): | 37 def ParseDir(path): |
21 """Examine a third_party/foo component and extract its metadata.""" | 38 """Examine a third_party/foo component and extract its metadata.""" |
22 | 39 |
23 # Try to find README.chromium. | 40 # Try to find README.chromium. |
24 readme_path = os.path.join(path, 'README.chromium') | 41 readme_path = os.path.join(path, 'README.chromium') |
25 if not os.path.exists(readme_path): | 42 if not os.path.exists(readme_path): |
26 raise LicenseError("missing README.chromium") | 43 raise LicenseError("missing README.chromium") |
27 | 44 |
28 # Parse metadata fields out of README.chromium. | 45 # Parse metadata fields out of README.chromium. |
29 # We provide a default value of "LICENSE" for the license file. | 46 # We examine "LICENSE" for the license file by default. |
30 metadata = { | 47 metadata = { |
31 "License File": "LICENSE", # Relative path to license text. | 48 "License File": "LICENSE", # Relative path to license text. |
32 "Name": None, # Short name (for header on about:credits). | 49 "Name": None, # Short name (for header on about:credits). |
33 "URL": None, # Project home page. | 50 "URL": None, # Project home page. |
34 } | 51 } |
35 for line in open(readme_path): | 52 for line in open(readme_path): |
36 line = line.strip() | 53 line = line.strip() |
37 for key in metadata.keys(): | 54 for key in metadata.keys(): |
38 field = key + ": " | 55 field = key + ": " |
39 if line.startswith(field): | 56 if line.startswith(field): |
40 metadata[key] = line[len(field):] | 57 metadata[key] = line[len(field):] |
41 | 58 |
42 # Check that all expected metadata is present. | 59 # Check that all expected metadata is present. |
43 for key, value in metadata.iteritems(): | 60 for key, value in metadata.iteritems(): |
44 if not value: | 61 if not value: |
45 raise LicenseError("couldn't find '" + key + "' line " | 62 raise LicenseError("couldn't find '" + key + "' line " |
46 "in README.chromium") | 63 "in README.chromium") |
47 | 64 |
48 # Check that the license file exists. | 65 # Check that the license file exists. |
49 license_file = metadata["License File"] | 66 for filename in (metadata["License File"], "COPYING"): |
50 license_path = os.path.join(path, license_file) | 67 license_path = os.path.join(path, filename) |
51 if not os.path.exists(license_path): | 68 if os.path.exists(license_path): |
52 raise LicenseError("License file '" + license_file + "' doesn't exist. " | 69 metadata["License File"] = filename |
53 "Either add a 'License File:' section to " | 70 break |
54 "README.chromium or add the missing file.") | 71 license_path = None |
| 72 |
| 73 if not license_path: |
| 74 raise LicenseError("License file not found. " |
| 75 "Either add a file named LICENSE, " |
| 76 "import upstream's COPYING if available, " |
| 77 "or add a 'License File:' line to README.chromium " |
| 78 "with the appropriate path.") |
55 | 79 |
56 return metadata | 80 return metadata |
57 | 81 |
58 | 82 |
59 def ScanThirdPartyDirs(third_party_dirs): | 83 def ScanThirdPartyDirs(third_party_dirs): |
60 """Scan a list of directories and report on any problems we find.""" | 84 """Scan a list of directories and report on any problems we find.""" |
61 errors = [] | 85 errors = [] |
62 for path in sorted(third_party_dirs): | 86 for path in sorted(third_party_dirs): |
63 try: | 87 try: |
64 metadata = ParseDir(path) | 88 metadata = ParseDir(path) |
65 except LicenseError, e: | 89 except LicenseError, e: |
66 errors.append((path, e.args[0])) | 90 errors.append((path, e.args[0])) |
67 continue | 91 continue |
68 print path, "OK:", metadata["License File"] | 92 print path, "OK:", metadata["License File"] |
69 | 93 |
70 for path, error in sorted(errors): | 94 for path, error in sorted(errors): |
71 print path + ": " + error | 95 print path + ": " + error |
72 | 96 |
73 | 97 |
74 def FindThirdPartyDirs(): | 98 def FindThirdPartyDirs(): |
75 """Find all third_party directories underneath the current directory.""" | 99 """Find all third_party directories underneath the current directory.""" |
76 skip_dirs = ('.svn', '.git', # VCS metadata | 100 skip_dirs = ('.svn', '.git', # VCS metadata |
77 'out', 'Debug', 'Release', # build files | 101 'out', 'Debug', 'Release', # build files |
78 'layout_tests') # lots of subdirs | 102 'layout_tests') # lots of subdirs |
79 | 103 |
80 third_party_dirs = [] | 104 third_party_dirs = [] |
81 for path, dirs, files in os.walk('.'): | 105 for path, dirs, files in os.walk('.'): |
82 path = path[len('./'):] # Pretty up the path. | 106 path = path[len('./'):] # Pretty up the path. |
83 | 107 |
| 108 if path in PRUNE_PATHS: |
| 109 dirs[:] = [] |
| 110 continue |
| 111 |
84 # Prune out directories we want to skip. | 112 # Prune out directories we want to skip. |
85 for skip in skip_dirs: | 113 for skip in skip_dirs: |
86 if skip in dirs: | 114 if skip in dirs: |
87 dirs.remove(skip) | 115 dirs.remove(skip) |
88 | 116 |
89 if os.path.basename(path) == 'third_party': | 117 if os.path.basename(path) == 'third_party': |
90 third_party_dirs.extend([os.path.join(path, dir) for dir in dirs]) | 118 # Add all subdirectories that are not marked for skipping. |
| 119 for dir in dirs: |
| 120 dirpath = os.path.join(path, dir) |
| 121 if dirpath not in PRUNE_PATHS: |
| 122 third_party_dirs.append(dirpath) |
| 123 |
91 # Don't recurse into any subdirs from here. | 124 # Don't recurse into any subdirs from here. |
92 dirs[:] = [] | 125 dirs[:] = [] |
93 continue | 126 continue |
94 | 127 |
95 return third_party_dirs | 128 return third_party_dirs |
96 | 129 |
97 | 130 |
98 if __name__ == '__main__': | 131 if __name__ == '__main__': |
99 third_party_dirs = FindThirdPartyDirs() | 132 third_party_dirs = FindThirdPartyDirs() |
100 ScanThirdPartyDirs(third_party_dirs) | 133 ScanThirdPartyDirs(third_party_dirs) |
OLD | NEW |