[email protected] | cb155a8 | 2011-11-29 17:25:34 | [diff] [blame] | 1 | #!/usr/bin/env python |
[email protected] | 9aaa0a5 | 2012-01-31 18:42:41 | [diff] [blame] | 2 | # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
[email protected] | 54ce726 | 2010-04-17 00:02:55 | [diff] [blame] | 6 | """Utility for checking and processing licensing information in third_party |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 7 | directories. |
[email protected] | 54ce726 | 2010-04-17 00:02:55 | [diff] [blame] | 8 | |
| 9 | Usage: licenses.py <command> |
| 10 | |
| 11 | Commands: |
| 12 | scan scan third_party directories, verifying that we have licensing info |
| 13 | credits generate about:credits on stdout |
| 14 | |
| 15 | (You can also import this as a module.) |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 16 | """ |
Eli Ribble | 63a757b | 2019-07-15 18:28:42 | [diff] [blame] | 17 | from __future__ import print_function |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 18 | |
jamiewalch | 6275de5 | 2015-06-17 23:25:34 | [diff] [blame] | 19 | import argparse |
Eli Ribble | 63a757b | 2019-07-15 18:28:42 | [diff] [blame] | 20 | import codecs |
Nicolas Dossou-gbete | b1b678fa | 2018-01-31 14:38:05 | [diff] [blame] | 21 | import json |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 22 | import os |
ichikawa | cc4ae12d | 2017-04-11 05:03:16 | [diff] [blame] | 23 | import shutil |
ichikawa | baea082 | 2017-04-19 05:24:02 | [diff] [blame] | 24 | import re |
lambroslambrou | 04a2ae170 | 2017-02-28 19:07:55 | [diff] [blame] | 25 | import subprocess |
[email protected] | 54ce726 | 2010-04-17 00:02:55 | [diff] [blame] | 26 | import sys |
ichikawa | cc4ae12d | 2017-04-11 05:03:16 | [diff] [blame] | 27 | import tempfile |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 28 | |
Raul Tambre | 3bc9933 | 2019-10-15 06:19:39 | [diff] [blame] | 29 | if sys.version_info.major == 2: |
| 30 | import cgi as html |
| 31 | else: |
| 32 | import html |
| 33 | |
agrieve | be96180 | 2017-02-15 18:48:20 | [diff] [blame] | 34 | # TODO(agrieve): Move build_utils.WriteDepFile into a non-android directory. |
Hiroshi Ichikawa | 3c5e1dfb | 2018-01-19 08:40:11 | [diff] [blame] | 35 | _REPOSITORY_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) |
Raul Tambre | 33ead332 | 2019-04-09 15:42:53 | [diff] [blame] | 36 | sys.path.insert(0, os.path.join(_REPOSITORY_ROOT, 'build/android/gyp')) |
| 37 | from util import build_utils |
agrieve | be96180 | 2017-02-15 18:48:20 | [diff] [blame] | 38 | |
| 39 | |
[email protected] | e64b3d8 | 2010-03-19 00:46:18 | [diff] [blame] | 40 | # Paths from the root of the tree to directories to skip. |
| 41 | PRUNE_PATHS = set([ |
[email protected] | 1657543 | 2012-08-07 11:21:06 | [diff] [blame] | 42 | # Placeholder directory only, not third-party code. |
[email protected] | 946cbf75 | 2011-01-16 06:07:29 | [diff] [blame] | 43 | os.path.join('third_party','adobe'), |
[email protected] | 2539549 | 2010-04-15 15:29:44 | [diff] [blame] | 44 | |
Yun Liu | f483471c | 2019-07-02 02:44:55 | [diff] [blame] | 45 | # Will remove it once converted private sdk using cipd. |
| 46 | os.path.join('third_party','android_tools_internal'), |
| 47 | |
[email protected] | 2f5dc57 | 2012-10-06 18:44:49 | [diff] [blame] | 48 | # Build files only, not third-party code. |
| 49 | os.path.join('third_party','widevine'), |
| 50 | |
[email protected] | f5283229 | 2010-12-15 00:43:16 | [diff] [blame] | 51 | # Only binaries, used during development. |
[email protected] | 946cbf75 | 2011-01-16 06:07:29 | [diff] [blame] | 52 | os.path.join('third_party','valgrind'), |
[email protected] | f5283229 | 2010-12-15 00:43:16 | [diff] [blame] | 53 | |
[email protected] | 946cbf75 | 2011-01-16 06:07:29 | [diff] [blame] | 54 | # Used for development and test, not in the shipping product. |
[email protected] | 26688b86e | 2014-06-19 13:14:26 | [diff] [blame] | 55 | os.path.join('build','secondary'), |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 56 | os.path.join('third_party','bison'), |
[email protected] | c48b9d7 | 2014-06-10 21:06:59 | [diff] [blame] | 57 | os.path.join('third_party','blanketjs'), |
agrieve | c11f4b9a | 2016-05-03 17:38:49 | [diff] [blame] | 58 | os.path.join('third_party','chromite'), |
[email protected] | 946cbf75 | 2011-01-16 06:07:29 | [diff] [blame] | 59 | os.path.join('third_party','cygwin'), |
mnaganov | 8754c6f9 | 2015-05-27 15:44:58 | [diff] [blame] | 60 | os.path.join('third_party','gles2_conform'), |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 61 | os.path.join('third_party','gnu_binutils'), |
[email protected] | b07806c1 | 2012-02-03 22:44:59 | [diff] [blame] | 62 | os.path.join('third_party','gold'), |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 63 | os.path.join('third_party','gperf'), |
[email protected] | 946cbf75 | 2011-01-16 06:07:29 | [diff] [blame] | 64 | os.path.join('third_party','lighttpd'), |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 65 | os.path.join('third_party','llvm'), |
| 66 | os.path.join('third_party','llvm-build'), |
[email protected] | 946cbf75 | 2011-01-16 06:07:29 | [diff] [blame] | 67 | os.path.join('third_party','mingw-w64'), |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 68 | os.path.join('third_party','nacl_sdk_binaries'), |
[email protected] | 946cbf75 | 2011-01-16 06:07:29 | [diff] [blame] | 69 | os.path.join('third_party','pefile'), |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 70 | os.path.join('third_party','perl'), |
| 71 | os.path.join('third_party','psyco_win32'), |
agrieve | c11f4b9a | 2016-05-03 17:38:49 | [diff] [blame] | 72 | os.path.join('third_party','pyelftools'), |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 73 | os.path.join('third_party','pylib'), |
[email protected] | 51e3e3d | 2012-08-21 02:27:44 | [diff] [blame] | 74 | os.path.join('third_party','pywebsocket'), |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 75 | os.path.join('third_party','syzygy'), |
| 76 | |
raphael.kubo.da.costa | 982d27b | 2016-11-01 07:39:52 | [diff] [blame] | 77 | # Chromium code. |
[email protected] | 10d8ac3 | 2013-10-31 13:34:31 | [diff] [blame] | 78 | os.path.join('tools', 'swarming_client'), |
[email protected] | 946cbf75 | 2011-01-16 06:07:29 | [diff] [blame] | 79 | |
[email protected] | fe33b98 | 2011-02-05 04:10:05 | [diff] [blame] | 80 | # Stuff pulled in from chrome-internal for official builds/tools. |
| 81 | os.path.join('third_party', 'clear_cache'), |
| 82 | os.path.join('third_party', 'gnu'), |
| 83 | os.path.join('third_party', 'googlemac'), |
| 84 | os.path.join('third_party', 'pcre'), |
| 85 | os.path.join('third_party', 'psutils'), |
| 86 | os.path.join('third_party', 'sawbuck'), |
mnaganov | 8754c6f9 | 2015-05-27 15:44:58 | [diff] [blame] | 87 | # See crbug.com/350472 |
| 88 | os.path.join('chrome', 'browser', 'resources', 'chromeos', 'quickoffice'), |
| 89 | # Chrome for Android proprietary code. |
| 90 | os.path.join('clank'), |
[email protected] | fe33b98 | 2011-02-05 04:10:05 | [diff] [blame] | 91 | |
[email protected] | 946cbf75 | 2011-01-16 06:07:29 | [diff] [blame] | 92 | # Redistribution does not require attribution in documentation. |
| 93 | os.path.join('third_party','directxsdk'), |
mnaganov | 8754c6f9 | 2015-05-27 15:44:58 | [diff] [blame] | 94 | |
| 95 | # For testing only, presents on some bots. |
| 96 | os.path.join('isolate_deps_dir'), |
hichris123 | 6175da8 | 2016-01-13 12:37:30 | [diff] [blame] | 97 | |
Raphael Kubo da Costa | 14835ad | 2018-01-29 19:48:29 | [diff] [blame] | 98 | # Mock test data. |
| 99 | os.path.join('tools', 'binary_size', 'libsupersize', 'testdata'), |
| 100 | |
hichris123 | 6175da8 | 2016-01-13 12:37:30 | [diff] [blame] | 101 | # Overrides some WebRTC files, same license. Skip this one. |
| 102 | os.path.join('third_party', 'webrtc_overrides'), |
[email protected] | e64b3d8 | 2010-03-19 00:46:18 | [diff] [blame] | 103 | ]) |
| 104 | |
| 105 | # Directories we don't scan through. |
[email protected] | d065fb7 | 2013-05-14 23:17:05 | [diff] [blame] | 106 | VCS_METADATA_DIRS = ('.svn', '.git') |
Andrew Grieve | 914ce0a7 | 2020-06-26 16:02:45 | [diff] [blame] | 107 | PRUNE_DIRS = VCS_METADATA_DIRS + ('layout_tests', ) # lots of subdirs |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 108 | |
Nicolas Dossou-gbete | b1b678fa | 2018-01-31 14:38:05 | [diff] [blame] | 109 | # A third_party directory can define this file, containing a list of |
Raphael Kubo da Costa | a984c00 | 2019-06-12 14:09:17 | [diff] [blame] | 110 | # subdirectories to process in addition to itself. Intended for directories |
| 111 | # that contain multiple others as transitive dependencies. |
Nicolas Dossou-gbete | b1b678fa | 2018-01-31 14:38:05 | [diff] [blame] | 112 | ADDITIONAL_PATHS_FILENAME = 'additional_readme_paths.json' |
| 113 | |
Justin DeWitt | e8d6580 | 2020-06-26 00:43:45 | [diff] [blame] | 114 | # A list of paths that contain license information but that would otherwise |
| 115 | # not be included. Possible reasons include: |
| 116 | # - Third party directories in //clank which are considered to be Google-owned |
| 117 | # - Directories that are directly checked out from upstream, and thus |
| 118 | # don't have a README.chromium |
| 119 | # - Directories that contain example code, or build tooling. |
| 120 | # - Nested third_party code inside other third_party libraries. |
[email protected] | e32bfad | 2010-10-22 01:34:32 | [diff] [blame] | 121 | ADDITIONAL_PATHS = ( |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 122 | os.path.join('chrome', 'common', 'extensions', 'docs', 'examples'), |
| 123 | os.path.join('chrome', 'test', 'chromeos', 'autotest'), |
| 124 | os.path.join('chrome', 'test', 'data'), |
Justin DeWitt | e8d6580 | 2020-06-26 00:43:45 | [diff] [blame] | 125 | os.path.join('clank', 'third_party', 'elements'), |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 126 | os.path.join('native_client'), |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 127 | os.path.join('testing', 'gmock'), |
piman | 5623da4 | 2016-07-21 03:03:15 | [diff] [blame] | 128 | os.path.join('testing', 'gtest'), |
David Benjamin | 1f13273c | 2018-01-11 15:14:08 | [diff] [blame] | 129 | os.path.join('third_party', 'boringssl', 'src', 'third_party', 'fiat'), |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 130 | os.path.join('tools', 'gyp'), |
| 131 | os.path.join('tools', 'page_cycler', 'acid3'), |
[email protected] | f968211 | 2013-08-12 05:01:49 | [diff] [blame] | 132 | os.path.join('url', 'third_party', 'mozilla'), |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 133 | os.path.join('v8'), |
yangguo | 6459a75 | 2015-05-07 14:47:54 | [diff] [blame] | 134 | # Fake directories to include the strongtalk and fdlibm licenses. |
[email protected] | 9aaa0a5 | 2012-01-31 18:42:41 | [diff] [blame] | 135 | os.path.join('v8', 'strongtalk'), |
yangguo | 6459a75 | 2015-05-07 14:47:54 | [diff] [blame] | 136 | os.path.join('v8', 'fdlibm'), |
[email protected] | e32bfad | 2010-10-22 01:34:32 | [diff] [blame] | 137 | ) |
| 138 | |
| 139 | |
[email protected] | e657bfc | 2010-03-22 23:56:19 | [diff] [blame] | 140 | # Directories where we check out directly from upstream, and therefore |
| 141 | # can't provide a README.chromium. Please prefer a README.chromium |
| 142 | # wherever possible. |
| 143 | SPECIAL_CASES = { |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 144 | os.path.join('native_client'): { |
| 145 | "Name": "native client", |
| 146 | "URL": "http://code.google.com/p/nativeclient", |
| 147 | "License": "BSD", |
| 148 | }, |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 149 | os.path.join('testing', 'gmock'): { |
| 150 | "Name": "gmock", |
| 151 | "URL": "http://code.google.com/p/googlemock", |
| 152 | "License": "BSD", |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 153 | "License File": "NOT_SHIPPED", |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 154 | }, |
piman | 5623da4 | 2016-07-21 03:03:15 | [diff] [blame] | 155 | os.path.join('testing', 'gtest'): { |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 156 | "Name": "gtest", |
| 157 | "URL": "http://code.google.com/p/googletest", |
| 158 | "License": "BSD", |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 159 | "License File": "NOT_SHIPPED", |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 160 | }, |
[email protected] | 878cbc7 | 2010-05-03 14:22:28 | [diff] [blame] | 161 | os.path.join('third_party', 'angle'): { |
[email protected] | dd4f060 | 2010-04-29 21:50:58 | [diff] [blame] | 162 | "Name": "Almost Native Graphics Layer Engine", |
| 163 | "URL": "http://code.google.com/p/angleproject/", |
[email protected] | 31eac5b | 2012-08-01 15:50:29 | [diff] [blame] | 164 | "License": "BSD", |
[email protected] | dd4f060 | 2010-04-29 21:50:58 | [diff] [blame] | 165 | }, |
[email protected] | abb1b86 | 2012-07-27 01:52:11 | [diff] [blame] | 166 | os.path.join('third_party', 'cros_system_api'): { |
| 167 | "Name": "Chromium OS system API", |
| 168 | "URL": "http://www.chromium.org/chromium-os", |
| 169 | "License": "BSD", |
| 170 | # Absolute path here is resolved as relative to the source root. |
| 171 | "License File": "/LICENSE.chromium_os", |
| 172 | }, |
Stephen Roe | 108ce9d | 2020-11-20 22:59:06 | [diff] [blame] | 173 | os.path.join('third_party', 'devtools-frontend'): { |
| 174 | # TODO(crbug.com/1151057): Remove this special case when issue is fixed. |
| 175 | "Name": "Devtools-Frontend", |
| 176 | "URL": "https://chromium.googlesource.com/devtools/devtools-frontend", |
| 177 | "License": "BSD", |
| 178 | "License File": "src/LICENSE", |
| 179 | }, |
[email protected] | 07c75d5 | 2010-10-05 19:02:35 | [diff] [blame] | 180 | os.path.join('third_party', 'lss'): { |
| 181 | "Name": "linux-syscall-support", |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 182 | "URL": "http://code.google.com/p/linux-syscall-support/", |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 183 | "License": "BSD", |
| 184 | "License File": "/LICENSE", |
[email protected] | 07c75d5 | 2010-10-05 19:02:35 | [diff] [blame] | 185 | }, |
Stephen Roe | 108ce9d | 2020-11-20 22:59:06 | [diff] [blame] | 186 | os.path.join('third_party', 'openscreen', 'src', 'third_party', 'abseil'): { |
| 187 | "Name": "abseil", |
| 188 | "URL": "https://github.com/abseil/abseil-cpp/", |
| 189 | "License": "Apache 2.0", |
| 190 | "License File": "/third_party/abseil-cpp/LICENSE", |
| 191 | }, |
| 192 | os.path.join('third_party', 'openscreen', 'src', 'third_party', |
| 193 | 'boringssl'): |
| 194 | { |
| 195 | "Name": "BoringSSL", |
| 196 | "URL": "https://boringssl.googlesource.com/boringssl/", |
| 197 | "License": "BSDish", |
| 198 | "License File": "/third_party/boringssl/src/LICENSE", |
| 199 | }, |
| 200 | os.path.join('third_party', 'openscreen', 'src', 'third_party', 'jsoncpp'): |
| 201 | { |
| 202 | "Name": "jsoncpp", |
| 203 | "URL": "https://github.com/open-source-parsers/jsoncpp", |
| 204 | "License": "MIT", |
| 205 | "License File": "/third_party/jsoncpp/LICENSE", |
| 206 | }, |
| 207 | os.path.join('third_party', 'openscreen', 'src', 'third_party', 'mozilla'): |
| 208 | { |
| 209 | "Name": "mozilla", |
| 210 | "URL": "https://github.com/mozilla", |
| 211 | "License": "MPL 1.1/GPL 2.0/LGPL 2.1", |
| 212 | "License File": "LICENSE.txt", |
| 213 | }, |
[email protected] | 21aa3368 | 2014-05-19 19:47:36 | [diff] [blame] | 214 | os.path.join('third_party', 'pdfium'): { |
[email protected] | d6a2e88f1 | 2014-05-19 19:27:03 | [diff] [blame] | 215 | "Name": "PDFium", |
| 216 | "URL": "http://code.google.com/p/pdfium/", |
| 217 | "License": "BSD", |
| 218 | }, |
[email protected] | c482685 | 2010-09-10 21:32:40 | [diff] [blame] | 219 | os.path.join('third_party', 'ppapi'): { |
| 220 | "Name": "ppapi", |
| 221 | "URL": "http://code.google.com/p/ppapi/", |
| 222 | }, |
[email protected] | abb1b86 | 2012-07-27 01:52:11 | [diff] [blame] | 223 | os.path.join('third_party', 'scons-2.0.1'): { |
| 224 | "Name": "scons-2.0.1", |
| 225 | "URL": "http://www.scons.org", |
| 226 | "License": "MIT", |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 227 | "License File": "NOT_SHIPPED", |
[email protected] | 2539549 | 2010-04-15 15:29:44 | [diff] [blame] | 228 | }, |
sullivan | 0cf8767 | 2015-08-04 17:10:32 | [diff] [blame] | 229 | os.path.join('third_party', 'catapult'): { |
| 230 | "Name": "catapult", |
| 231 | "URL": "https://github.com/catapult-project/catapult", |
[email protected] | abb1b86 | 2012-07-27 01:52:11 | [diff] [blame] | 232 | "License": "BSD", |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 233 | "License File": "NOT_SHIPPED", |
[email protected] | 946cbf75 | 2011-01-16 06:07:29 | [diff] [blame] | 234 | }, |
Stephen Roe | 108ce9d | 2020-11-20 22:59:06 | [diff] [blame] | 235 | os.path.join('third_party', 'crashpad', 'crashpad', 'third_party', 'lss'): { |
Joshua Peraza | 8ca9deb | 2019-04-10 01:28:07 | [diff] [blame] | 236 | "Name": "linux-syscall-support", |
| 237 | "URL": "https://chromium.googlesource.com/linux-syscall-support/", |
| 238 | "License": "BSD", |
| 239 | "License File": "NOT_SHIPPED", |
| 240 | }, |
| 241 | os.path.join('third_party', 'crashpad', 'crashpad', 'third_party', |
Stephen Roe | 108ce9d | 2020-11-20 22:59:06 | [diff] [blame] | 242 | 'mini_chromium'): |
| 243 | { |
Joshua Peraza | f076c74 | 2018-12-03 03:41:54 | [diff] [blame] | 244 | "Name": "mini_chromium", |
| 245 | "URL": "https://chromium.googlesource.com/chromium/mini_chromium/", |
| 246 | "License": "BSD", |
| 247 | "License File": "NOT_SHIPPED", |
| 248 | }, |
Scott Graham | d16fde5 | 2019-03-15 01:08:55 | [diff] [blame] | 249 | os.path.join('third_party', 'crashpad', 'crashpad', 'third_party', 'xnu'): { |
| 250 | "Name": "xnu", |
| 251 | "URL": "https://opensource.apple.com/source/xnu/", |
| 252 | "License": "Apple Public Source License 2.0", |
| 253 | "License File": "APPLE_LICENSE", |
| 254 | }, |
Stephen Roe | 108ce9d | 2020-11-20 22:59:06 | [diff] [blame] | 255 | os.path.join('third_party', 'crashpad', 'crashpad', 'third_party', 'zlib'): |
| 256 | { |
Joshua Peraza | f076c74 | 2018-12-03 03:41:54 | [diff] [blame] | 257 | "Name": "zlib", |
| 258 | "URL": "https://zlib.net/", |
| 259 | "License": "zlib", |
| 260 | "License File": "NOT_SHIPPED", |
| 261 | }, |
[email protected] | 38acf47 | 2012-03-19 13:15:36 | [diff] [blame] | 262 | os.path.join('third_party', 'v8-i18n'): { |
| 263 | "Name": "Internationalization Library for v8", |
| 264 | "URL": "http://code.google.com/p/v8-i18n/", |
[email protected] | 484de3a | 2012-11-12 13:43:06 | [diff] [blame] | 265 | "License": "Apache 2.0", |
[email protected] | 38acf47 | 2012-03-19 13:15:36 | [diff] [blame] | 266 | }, |
Kevin Marshall | 8f688b5f | 2018-08-10 16:56:59 | [diff] [blame] | 267 | os.path.join('third_party', 'blink'): { |
Kent Tamura | 65f56d3 | 2018-11-27 05:20:49 | [diff] [blame] | 268 | # about:credits doesn't show "Blink" but "WebKit". |
| 269 | # Blink is a fork of WebKit, and Chromium project has maintained it |
| 270 | # since the fork. about:credits needs to mention the code before |
| 271 | # the fork. |
[email protected] | abb1b86 | 2012-07-27 01:52:11 | [diff] [blame] | 272 | "Name": "WebKit", |
| 273 | "URL": "http://webkit.org/", |
kai.koehne | 70fdcea | 2017-01-17 12:22:47 | [diff] [blame] | 274 | "License": "BSD and LGPL v2 and LGPL v2.1", |
[email protected] | abb1b86 | 2012-07-27 01:52:11 | [diff] [blame] | 275 | # Absolute path here is resolved as relative to the source root. |
Kent Tamura | 65f56d3 | 2018-11-27 05:20:49 | [diff] [blame] | 276 | "License File": "/third_party/blink/LICENSE_FOR_ABOUT_CREDITS", |
[email protected] | abb1b86 | 2012-07-27 01:52:11 | [diff] [blame] | 277 | }, |
[email protected] | 506a85f | 2014-05-02 19:21:28 | [diff] [blame] | 278 | os.path.join('third_party', 'webpagereplay'): { |
[email protected] | abb1b86 | 2012-07-27 01:52:11 | [diff] [blame] | 279 | "Name": "webpagereplay", |
| 280 | "URL": "http://code.google.com/p/web-page-replay", |
| 281 | "License": "Apache 2.0", |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 282 | "License File": "NOT_SHIPPED", |
[email protected] | abb1b86 | 2012-07-27 01:52:11 | [diff] [blame] | 283 | }, |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 284 | os.path.join('tools', 'gyp'): { |
| 285 | "Name": "gyp", |
| 286 | "URL": "http://code.google.com/p/gyp", |
| 287 | "License": "BSD", |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 288 | "License File": "NOT_SHIPPED", |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 289 | }, |
| 290 | os.path.join('v8'): { |
[email protected] | 2d765fc2 | 2012-08-16 13:58:28 | [diff] [blame] | 291 | "Name": "V8 JavaScript Engine", |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 292 | "URL": "http://code.google.com/p/v8", |
| 293 | "License": "BSD", |
| 294 | }, |
[email protected] | 9aaa0a5 | 2012-01-31 18:42:41 | [diff] [blame] | 295 | os.path.join('v8', 'strongtalk'): { |
| 296 | "Name": "Strongtalk", |
| 297 | "URL": "http://www.strongtalk.org/", |
[email protected] | 31eac5b | 2012-08-01 15:50:29 | [diff] [blame] | 298 | "License": "BSD", |
[email protected] | abb1b86 | 2012-07-27 01:52:11 | [diff] [blame] | 299 | # Absolute path here is resolved as relative to the source root. |
[email protected] | 9aaa0a5 | 2012-01-31 18:42:41 | [diff] [blame] | 300 | "License File": "/v8/LICENSE.strongtalk", |
| 301 | }, |
yangguo | 6459a75 | 2015-05-07 14:47:54 | [diff] [blame] | 302 | os.path.join('v8', 'fdlibm'): { |
[email protected] | 0bdf803 | 2014-08-05 13:07:07 | [diff] [blame] | 303 | "Name": "fdlibm", |
| 304 | "URL": "http://www.netlib.org/fdlibm/", |
| 305 | "License": "Freely Distributable", |
| 306 | # Absolute path here is resolved as relative to the source root. |
Stephen Roe | 108ce9d | 2020-11-20 22:59:06 | [diff] [blame] | 307 | "License File": "/v8/LICENSE.fdlibm", |
| 308 | "License Android Compatible": "yes", |
[email protected] | 0bdf803 | 2014-08-05 13:07:07 | [diff] [blame] | 309 | }, |
kbr | a348ef0b | 2014-11-13 01:50:17 | [diff] [blame] | 310 | os.path.join('third_party', 'khronos_glcts'): { |
| 311 | # These sources are not shipped, are not public, and it isn't |
| 312 | # clear why they're tripping the license check. |
| 313 | "Name": "khronos_glcts", |
| 314 | "URL": "http://no-public-url", |
| 315 | "License": "Khronos", |
| 316 | "License File": "NOT_SHIPPED", |
| 317 | }, |
sergiyb | 9bf92547 | 2015-02-06 17:58:17 | [diff] [blame] | 318 | os.path.join('tools', 'telemetry', 'third_party', 'gsutil'): { |
| 319 | "Name": "gsutil", |
| 320 | "URL": "https://cloud.google.com/storage/docs/gsutil", |
| 321 | "License": "Apache 2.0", |
| 322 | "License File": "NOT_SHIPPED", |
| 323 | }, |
capn | d37d668 | 2016-08-15 21:57:02 | [diff] [blame] | 324 | os.path.join('third_party', 'swiftshader'): { |
| 325 | "Name": "SwiftShader", |
| 326 | "URL": "https://swiftshader.googlesource.com/SwiftShader", |
| 327 | "License": "Apache 2.0 and compatible licenses", |
| 328 | "License Android Compatible": "yes", |
| 329 | "License File": "/third_party/swiftshader/LICENSE.txt", |
| 330 | }, |
[email protected] | e657bfc | 2010-03-22 23:56:19 | [diff] [blame] | 331 | } |
| 332 | |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 333 | # Special value for 'License File' field used to indicate that the license file |
| 334 | # should not be used in about:credits. |
| 335 | NOT_SHIPPED = "NOT_SHIPPED" |
| 336 | |
sdefresne | d13d15b | 2016-02-17 18:06:54 | [diff] [blame] | 337 | # Paths for libraries that we have checked are not shipped on iOS. These are |
| 338 | # left out of the licenses file primarily because we don't want to cause a |
| 339 | # firedrill due to someone thinking that Chrome for iOS is using LGPL code |
| 340 | # when it isn't. |
| 341 | # This is a temporary hack; the real solution is crbug.com/178215 |
| 342 | KNOWN_NON_IOS_LIBRARIES = set([ |
| 343 | os.path.join('base', 'third_party', 'symbolize'), |
| 344 | os.path.join('base', 'third_party', 'xdg_mime'), |
| 345 | os.path.join('base', 'third_party', 'xdg_user_dirs'), |
Misha Efimov | 45ca19d | 2019-03-21 15:53:15 | [diff] [blame] | 346 | os.path.join('buildtools', 'third_party', 'libc++'), |
| 347 | os.path.join('buildtools', 'third_party', 'libc++abi'), |
sdefresne | d13d15b | 2016-02-17 18:06:54 | [diff] [blame] | 348 | os.path.join('chrome', 'installer', 'mac', 'third_party', 'bsdiff'), |
| 349 | os.path.join('chrome', 'installer', 'mac', 'third_party', 'xz'), |
| 350 | os.path.join('chrome', 'test', 'data', 'third_party', 'kraken'), |
| 351 | os.path.join('chrome', 'test', 'data', 'third_party', 'spaceport'), |
sdefresne | d13d15b | 2016-02-17 18:06:54 | [diff] [blame] | 352 | os.path.join('chrome', 'third_party', 'mozilla_security_manager'), |
sdefresne | d13d15b | 2016-02-17 18:06:54 | [diff] [blame] | 353 | os.path.join('third_party', 'angle'), |
| 354 | os.path.join('third_party', 'apple_apsl'), |
| 355 | os.path.join('third_party', 'apple_sample_code'), |
| 356 | os.path.join('third_party', 'ashmem'), |
Kent Tamura | 1adafee | 2018-03-07 10:12:08 | [diff] [blame] | 357 | os.path.join('third_party', 'blink'), |
sdefresne | d13d15b | 2016-02-17 18:06:54 | [diff] [blame] | 358 | os.path.join('third_party', 'bspatch'), |
sdefresne | d13d15b | 2016-02-17 18:06:54 | [diff] [blame] | 359 | os.path.join('third_party', 'cld'), |
sdefresne | d13d15b | 2016-02-17 18:06:54 | [diff] [blame] | 360 | os.path.join('third_party', 'flot'), |
| 361 | os.path.join('third_party', 'gtk+'), |
| 362 | os.path.join('third_party', 'iaccessible2'), |
| 363 | os.path.join('third_party', 'iccjpeg'), |
| 364 | os.path.join('third_party', 'isimpledom'), |
| 365 | os.path.join('third_party', 'jsoncpp'), |
| 366 | os.path.join('third_party', 'khronos'), |
| 367 | os.path.join('third_party', 'libXNVCtrl'), |
| 368 | os.path.join('third_party', 'libevent'), |
| 369 | os.path.join('third_party', 'libjpeg'), |
sdefresne | d13d15b | 2016-02-17 18:06:54 | [diff] [blame] | 370 | os.path.join('third_party', 'libusb'), |
sdefresne | d13d15b | 2016-02-17 18:06:54 | [diff] [blame] | 371 | os.path.join('third_party', 'libxslt'), |
| 372 | os.path.join('third_party', 'lss'), |
| 373 | os.path.join('third_party', 'lzma_sdk'), |
| 374 | os.path.join('third_party', 'mesa'), |
sdefresne | d13d15b | 2016-02-17 18:06:54 | [diff] [blame] | 375 | os.path.join('third_party', 'motemplate'), |
| 376 | os.path.join('third_party', 'mozc'), |
| 377 | os.path.join('third_party', 'mozilla'), |
| 378 | os.path.join('third_party', 'npapi'), |
| 379 | os.path.join('third_party', 'ots'), |
David Jean | 78ac18c | 2020-03-27 10:39:38 | [diff] [blame] | 380 | os.path.join('third_party', 'perfetto'), |
sdefresne | d13d15b | 2016-02-17 18:06:54 | [diff] [blame] | 381 | os.path.join('third_party', 'ppapi'), |
| 382 | os.path.join('third_party', 'qcms'), |
| 383 | os.path.join('third_party', 're2'), |
| 384 | os.path.join('third_party', 'safe_browsing'), |
sdefresne | d13d15b | 2016-02-17 18:06:54 | [diff] [blame] | 385 | os.path.join('third_party', 'smhasher'), |
| 386 | os.path.join('third_party', 'sudden_motion_sensor'), |
| 387 | os.path.join('third_party', 'swiftshader'), |
| 388 | os.path.join('third_party', 'swig'), |
| 389 | os.path.join('third_party', 'talloc'), |
| 390 | os.path.join('third_party', 'tcmalloc'), |
| 391 | os.path.join('third_party', 'usb_ids'), |
| 392 | os.path.join('third_party', 'v8-i18n'), |
| 393 | os.path.join('third_party', 'wtl'), |
| 394 | os.path.join('third_party', 'yasm'), |
| 395 | os.path.join('v8', 'strongtalk'), |
| 396 | ]) |
| 397 | |
[email protected] | e459859 | 2012-08-09 15:47:21 | [diff] [blame] | 398 | |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 399 | class LicenseError(Exception): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 400 | """We raise this exception when a directory's licensing info isn't |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 401 | fully filled out.""" |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 402 | pass |
| 403 | |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 404 | |
[email protected] | a8d5a8e78 | 2012-10-05 17:38:23 | [diff] [blame] | 405 | def AbsolutePath(path, filename, root): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 406 | """Convert a path in README.chromium to be absolute based on the source |
[email protected] | 689d81c | 2012-02-03 20:03:38 | [diff] [blame] | 407 | root.""" |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 408 | if filename.startswith('/'): |
| 409 | # Absolute-looking paths are relative to the source root |
| 410 | # (which is the directory we're run from). |
| 411 | absolute_path = os.path.join(root, filename[1:]) |
| 412 | else: |
| 413 | absolute_path = os.path.join(root, path, filename) |
| 414 | if os.path.exists(absolute_path): |
| 415 | return absolute_path |
| 416 | return None |
| 417 | |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 418 | |
mckev | 95468c6e | 2014-10-31 02:30:10 | [diff] [blame] | 419 | def ParseDir(path, root, require_license_file=True, optional_keys=None): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 420 | """Examine a third_party/foo component and extract its metadata.""" |
| 421 | # Parse metadata fields out of README.chromium. |
| 422 | # We examine "LICENSE" for the license file by default. |
| 423 | metadata = { |
| 424 | "License File": "LICENSE", # Relative path to license text. |
| 425 | "Name": None, # Short name (for header on about:credits). |
| 426 | "URL": None, # Project home page. |
| 427 | "License": None, # Software license. |
| 428 | } |
[email protected] | e657bfc | 2010-03-22 23:56:19 | [diff] [blame] | 429 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 430 | if optional_keys is None: |
| 431 | optional_keys = [] |
[email protected] | 689d81c | 2012-02-03 20:03:38 | [diff] [blame] | 432 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 433 | if path in SPECIAL_CASES: |
| 434 | metadata.update(SPECIAL_CASES[path]) |
| 435 | else: |
| 436 | # Try to find README.chromium. |
| 437 | readme_path = os.path.join(root, path, 'README.chromium') |
| 438 | if not os.path.exists(readme_path): |
| 439 | raise LicenseError("missing README.chromium or licenses.py " |
| 440 | "SPECIAL_CASES entry in %s\n" % path) |
[email protected] | e657bfc | 2010-03-22 23:56:19 | [diff] [blame] | 441 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 442 | for line in open(readme_path): |
| 443 | line = line.strip() |
| 444 | if not line: |
| 445 | break |
| 446 | for key in list(metadata.keys()) + optional_keys: |
| 447 | field = key + ": " |
| 448 | if line.startswith(field): |
| 449 | metadata[key] = line[len(field):] |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 450 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 451 | # Check that all expected metadata is present. |
| 452 | errors = [] |
| 453 | for key, value in metadata.items(): |
| 454 | if not value: |
| 455 | errors.append("couldn't find '" + key + "' line " |
| 456 | "in README.chromium or licences.py " |
| 457 | "SPECIAL_CASES") |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 458 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 459 | # Special-case modules that aren't in the shipping product, so don't need |
| 460 | # their license in about:credits. |
| 461 | if metadata["License File"] != NOT_SHIPPED: |
| 462 | # Check that the license file exists. |
| 463 | for filename in (metadata["License File"], "COPYING"): |
| 464 | license_path = AbsolutePath(path, filename, root) |
| 465 | if license_path is not None: |
| 466 | break |
[email protected] | e64b3d8 | 2010-03-19 00:46:18 | [diff] [blame] | 467 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 468 | if require_license_file and not license_path: |
| 469 | errors.append("License file not found. " |
| 470 | "Either add a file named LICENSE, " |
| 471 | "import upstream's COPYING if available, " |
| 472 | "or add a 'License File:' line to " |
| 473 | "README.chromium with the appropriate path.") |
| 474 | metadata["License File"] = license_path |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 475 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 476 | if errors: |
| 477 | raise LicenseError("Errors in %s:\n %s\n" % (path, ";\n ".join(errors))) |
| 478 | return metadata |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 479 | |
| 480 | |
[email protected] | a8d5a8e78 | 2012-10-05 17:38:23 | [diff] [blame] | 481 | def ContainsFiles(path, root): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 482 | """Determines whether any files exist in a directory or in any of its |
[email protected] | abb1b86 | 2012-07-27 01:52:11 | [diff] [blame] | 483 | subdirectories.""" |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 484 | for _, dirs, files in os.walk(os.path.join(root, path)): |
| 485 | if files: |
| 486 | return True |
| 487 | for vcs_metadata in VCS_METADATA_DIRS: |
| 488 | if vcs_metadata in dirs: |
| 489 | dirs.remove(vcs_metadata) |
| 490 | return False |
[email protected] | abb1b86 | 2012-07-27 01:52:11 | [diff] [blame] | 491 | |
| 492 | |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 493 | def FilterDirsWithFiles(dirs_list, root): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 494 | # If a directory contains no files, assume it's a DEPS directory for a |
| 495 | # project not used by our current configuration and skip it. |
| 496 | return [x for x in dirs_list if ContainsFiles(x, root)] |
[email protected] | 7472d1e0 | 2012-11-29 23:05:37 | [diff] [blame] | 497 | |
| 498 | |
David Landell | e71c95b | 2020-08-28 16:18:27 | [diff] [blame] | 499 | def ProcessAdditionalReadmePathsJson(root, dirname, third_party_dirs): |
Justin DeWitt | e8d6580 | 2020-06-26 00:43:45 | [diff] [blame] | 500 | """For a given directory, process the additional readme paths, and add to |
| 501 | third_party_dirs.""" |
David Landell | e71c95b | 2020-08-28 16:18:27 | [diff] [blame] | 502 | additional_paths_file = os.path.join(root, dirname, ADDITIONAL_PATHS_FILENAME) |
Justin DeWitt | e8d6580 | 2020-06-26 00:43:45 | [diff] [blame] | 503 | if os.path.exists(additional_paths_file): |
| 504 | with open(additional_paths_file) as paths_file: |
| 505 | extra_paths = json.load(paths_file) |
| 506 | third_party_dirs.update([os.path.join(dirname, p) for p in extra_paths]) |
| 507 | |
| 508 | |
[email protected] | a8d5a8e78 | 2012-10-05 17:38:23 | [diff] [blame] | 509 | def FindThirdPartyDirs(prune_paths, root): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 510 | """Find all third_party directories underneath the source root.""" |
| 511 | third_party_dirs = set() |
| 512 | for path, dirs, files in os.walk(root): |
| 513 | path = path[len(root) + 1:] # Pretty up the path. |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 514 | |
Andrew Grieve | 914ce0a7 | 2020-06-26 16:02:45 | [diff] [blame] | 515 | # .gitignore ignores /out*/, so do the same here. |
| 516 | if path in prune_paths or path.startswith('out'): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 517 | dirs[:] = [] |
| 518 | continue |
[email protected] | e64b3d8 | 2010-03-19 00:46:18 | [diff] [blame] | 519 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 520 | # Prune out directories we want to skip. |
| 521 | # (Note that we loop over PRUNE_DIRS so we're not iterating over a |
| 522 | # list that we're simultaneously mutating.) |
| 523 | for skip in PRUNE_DIRS: |
| 524 | if skip in dirs: |
| 525 | dirs.remove(skip) |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 526 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 527 | if os.path.basename(path) == 'third_party': |
| 528 | # Add all subdirectories that are not marked for skipping. |
| 529 | for dir in dirs: |
| 530 | dirpath = os.path.join(path, dir) |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 531 | if dirpath not in prune_paths: |
| 532 | third_party_dirs.add(dirpath) |
Justin DeWitt | e8d6580 | 2020-06-26 00:43:45 | [diff] [blame] | 533 | |
David Landell | e71c95b | 2020-08-28 16:18:27 | [diff] [blame] | 534 | ProcessAdditionalReadmePathsJson(root, dirpath, third_party_dirs) |
[email protected] | e64b3d8 | 2010-03-19 00:46:18 | [diff] [blame] | 535 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 536 | # Don't recurse into any subdirs from here. |
| 537 | dirs[:] = [] |
| 538 | continue |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 539 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 540 | # Don't recurse into paths in ADDITIONAL_PATHS, like we do with regular |
| 541 | # third_party/foo paths. |
| 542 | if path in ADDITIONAL_PATHS: |
| 543 | dirs[:] = [] |
[email protected] | abb1b86 | 2012-07-27 01:52:11 | [diff] [blame] | 544 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 545 | for dir in ADDITIONAL_PATHS: |
| 546 | if dir not in prune_paths: |
| 547 | third_party_dirs.add(dir) |
David Landell | e71c95b | 2020-08-28 16:18:27 | [diff] [blame] | 548 | ProcessAdditionalReadmePathsJson(root, dir, third_party_dirs) |
[email protected] | e32bfad | 2010-10-22 01:34:32 | [diff] [blame] | 549 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 550 | return third_party_dirs |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 551 | |
[email protected] | cb155a8 | 2011-11-29 17:25:34 | [diff] [blame] | 552 | |
mnaganov | 4b533e8 | 2015-05-28 16:08:44 | [diff] [blame] | 553 | def FindThirdPartyDirsWithFiles(root): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 554 | third_party_dirs = FindThirdPartyDirs(PRUNE_PATHS, root) |
| 555 | return FilterDirsWithFiles(third_party_dirs, root) |
mnaganov | 4b533e8 | 2015-05-28 16:08:44 | [diff] [blame] | 556 | |
| 557 | |
lambroslambrou | 7703f4b | 2017-03-08 23:05:56 | [diff] [blame] | 558 | # Many builders do not contain 'gn' in their PATH, so use the GN binary from |
Xiaoqian Dai | 8962649 | 2018-06-28 17:07:46 | [diff] [blame] | 559 | # //buildtools. |
lambroslambrou | 7703f4b | 2017-03-08 23:05:56 | [diff] [blame] | 560 | def _GnBinary(): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 561 | exe = 'gn' |
| 562 | if sys.platform.startswith('linux'): |
| 563 | subdir = 'linux64' |
| 564 | elif sys.platform == 'darwin': |
| 565 | subdir = 'mac' |
| 566 | elif sys.platform == 'win32': |
| 567 | subdir, exe = 'win', 'gn.exe' |
| 568 | else: |
| 569 | raise RuntimeError("Unsupported platform '%s'." % sys.platform) |
Xiaoqian Dai | 8962649 | 2018-06-28 17:07:46 | [diff] [blame] | 570 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 571 | return os.path.join(_REPOSITORY_ROOT, 'buildtools', subdir, exe) |
lambroslambrou | 7703f4b | 2017-03-08 23:05:56 | [diff] [blame] | 572 | |
| 573 | |
Misha Efimov | 45ca19d | 2019-03-21 15:53:15 | [diff] [blame] | 574 | def GetThirdPartyDepsFromGNDepsOutput(gn_deps, target_os): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 575 | """Returns third_party/foo directories given the output of "gn desc deps". |
ichikawa | baea082 | 2017-04-19 05:24:02 | [diff] [blame] | 576 | |
| 577 | Note that it always returns the direct sub-directory of third_party |
| 578 | where README.chromium and LICENSE files are, so that it can be passed to |
| 579 | ParseDir(). e.g.: |
Hiroshi Ichikawa | 3c5e1dfb | 2018-01-19 08:40:11 | [diff] [blame] | 580 | third_party/cld_3/src/src/BUILD.gn -> third_party/cld_3 |
| 581 | |
| 582 | It returns relative paths from _REPOSITORY_ROOT, not absolute paths. |
ichikawa | baea082 | 2017-04-19 05:24:02 | [diff] [blame] | 583 | """ |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 584 | third_party_deps = set() |
| 585 | for absolute_build_dep in gn_deps.split(): |
| 586 | relative_build_dep = os.path.relpath(absolute_build_dep, _REPOSITORY_ROOT) |
| 587 | m = re.search( |
| 588 | r'^((.+[/\\])?third_party[/\\][^/\\]+[/\\])(.+[/\\])?BUILD\.gn$', |
| 589 | relative_build_dep) |
| 590 | if not m: |
| 591 | continue |
| 592 | third_party_path = m.group(1) |
| 593 | if any(third_party_path.startswith(p + os.sep) for p in PRUNE_PATHS): |
| 594 | continue |
| 595 | if (target_os == 'ios' and any( |
| 596 | third_party_path.startswith(p + os.sep) |
| 597 | for p in KNOWN_NON_IOS_LIBRARIES)): |
| 598 | # Skip over files that are known not to be used on iOS. |
| 599 | continue |
| 600 | third_party_deps.add(third_party_path[:-1]) |
| 601 | return third_party_deps |
ichikawa | baea082 | 2017-04-19 05:24:02 | [diff] [blame] | 602 | |
| 603 | |
Misha Efimov | 45ca19d | 2019-03-21 15:53:15 | [diff] [blame] | 604 | def FindThirdPartyDeps(gn_out_dir, gn_target, target_os): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 605 | if not gn_out_dir: |
| 606 | raise RuntimeError("--gn-out-dir is required if --gn-target is used.") |
lambroslambrou | 04a2ae170 | 2017-02-28 19:07:55 | [diff] [blame] | 607 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 608 | # Generate gn project in temp directory and use it to find dependencies. |
| 609 | # Current gn directory cannot be used when we run this script in a gn action |
| 610 | # rule, because gn doesn't allow recursive invocations due to potential side |
| 611 | # effects. |
| 612 | tmp_dir = None |
| 613 | try: |
| 614 | tmp_dir = tempfile.mkdtemp(dir=gn_out_dir) |
| 615 | shutil.copy(os.path.join(gn_out_dir, "args.gn"), tmp_dir) |
| 616 | subprocess.check_output([_GnBinary(), "gen", tmp_dir]) |
| 617 | gn_deps = subprocess.check_output([ |
| 618 | _GnBinary(), "desc", tmp_dir, gn_target, "deps", "--as=buildfile", |
| 619 | "--all" |
| 620 | ]) |
| 621 | if isinstance(gn_deps, bytes): |
| 622 | gn_deps = gn_deps.decode("utf-8") |
| 623 | finally: |
| 624 | if tmp_dir and os.path.exists(tmp_dir): |
| 625 | shutil.rmtree(tmp_dir) |
ichikawa | cc4ae12d | 2017-04-11 05:03:16 | [diff] [blame] | 626 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 627 | return GetThirdPartyDepsFromGNDepsOutput(gn_deps, target_os) |
lambroslambrou | 04a2ae170 | 2017-02-28 19:07:55 | [diff] [blame] | 628 | |
| 629 | |
[email protected] | a8d5a8e78 | 2012-10-05 17:38:23 | [diff] [blame] | 630 | def ScanThirdPartyDirs(root=None): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 631 | """Scan a list of directories and report on any problems we find.""" |
| 632 | if root is None: |
| 633 | root = os.getcwd() |
| 634 | third_party_dirs = FindThirdPartyDirsWithFiles(root) |
[email protected] | 54ce726 | 2010-04-17 00:02:55 | [diff] [blame] | 635 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 636 | errors = [] |
| 637 | for path in sorted(third_party_dirs): |
| 638 | try: |
| 639 | metadata = ParseDir(path, root) |
| 640 | except LicenseError as e: |
| 641 | errors.append((path, e.args[0])) |
| 642 | continue |
[email protected] | 54ce726 | 2010-04-17 00:02:55 | [diff] [blame] | 643 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 644 | for path, error in sorted(errors): |
| 645 | print(path + ": " + error) |
[email protected] | 54ce726 | 2010-04-17 00:02:55 | [diff] [blame] | 646 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 647 | return len(errors) == 0 |
[email protected] | 54ce726 | 2010-04-17 00:02:55 | [diff] [blame] | 648 | |
[email protected] | cb155a8 | 2011-11-29 17:25:34 | [diff] [blame] | 649 | |
sdefresne | d13d15b | 2016-02-17 18:06:54 | [diff] [blame] | 650 | def GenerateCredits( |
agrieve | be96180 | 2017-02-15 18:48:20 | [diff] [blame] | 651 | file_template_file, entry_template_file, output_file, target_os, |
lambroslambrou | 04a2ae170 | 2017-02-28 19:07:55 | [diff] [blame] | 652 | gn_out_dir, gn_target, depfile=None): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 653 | """Generate about:credits.""" |
[email protected] | 284f26f | 2013-02-07 13:32:13 | [diff] [blame] | 654 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 655 | def EvaluateTemplate(template, env, escape=True): |
| 656 | """Expand a template with variables like {{foo}} using a |
[email protected] | 54ce726 | 2010-04-17 00:02:55 | [diff] [blame] | 657 | dictionary of expansions.""" |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 658 | for key, val in env.items(): |
| 659 | if escape: |
| 660 | val = html.escape(val) |
| 661 | template = template.replace('{{%s}}' % key, val) |
| 662 | return template |
[email protected] | 54ce726 | 2010-04-17 00:02:55 | [diff] [blame] | 663 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 664 | def MetadataToTemplateEntry(metadata, entry_template): |
| 665 | env = { |
| 666 | 'name': metadata['Name'], |
| 667 | 'url': metadata['URL'], |
| 668 | 'license': open(metadata['License File']).read(), |
| 669 | } |
| 670 | return { |
| 671 | 'name': metadata['Name'], |
| 672 | 'content': EvaluateTemplate(entry_template, env), |
| 673 | 'license_file': metadata['License File'], |
| 674 | } |
Yipeng Wang | 5671d4fe | 2017-06-08 17:59:44 | [diff] [blame] | 675 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 676 | if gn_target: |
| 677 | third_party_dirs = FindThirdPartyDeps(gn_out_dir, gn_target, target_os) |
lambroslambrou | 04a2ae170 | 2017-02-28 19:07:55 | [diff] [blame] | 678 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 679 | # Sanity-check to raise a build error if invalid gn_... settings are |
| 680 | # somehow passed to this script. |
| 681 | if not third_party_dirs: |
| 682 | raise RuntimeError("No deps found.") |
| 683 | else: |
| 684 | third_party_dirs = FindThirdPartyDirs(PRUNE_PATHS, _REPOSITORY_ROOT) |
[email protected] | 54ce726 | 2010-04-17 00:02:55 | [diff] [blame] | 685 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 686 | if not file_template_file: |
| 687 | file_template_file = os.path.join(_REPOSITORY_ROOT, 'components', |
| 688 | 'about_ui', 'resources', |
| 689 | 'about_credits.tmpl') |
| 690 | if not entry_template_file: |
| 691 | entry_template_file = os.path.join(_REPOSITORY_ROOT, 'components', |
| 692 | 'about_ui', 'resources', |
| 693 | 'about_credits_entry.tmpl') |
jamiewalch | 6275de5 | 2015-06-17 23:25:34 | [diff] [blame] | 694 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 695 | entry_template = open(entry_template_file).read() |
| 696 | entries = [] |
| 697 | # Start from Chromium's LICENSE file |
| 698 | chromium_license_metadata = { |
| 699 | 'Name': 'The Chromium Project', |
| 700 | 'URL': 'http://www.chromium.org', |
| 701 | 'License File': os.path.join(_REPOSITORY_ROOT, 'LICENSE') |
| 702 | } |
| 703 | entries.append( |
| 704 | MetadataToTemplateEntry(chromium_license_metadata, entry_template)) |
Yipeng Wang | 5671d4fe | 2017-06-08 17:59:44 | [diff] [blame] | 705 | |
Andrew Grieve | 9efa3811 | 2020-06-10 17:42:34 | [diff] [blame] | 706 | entries_by_name = {} |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 707 | for path in third_party_dirs: |
| 708 | try: |
| 709 | metadata = ParseDir(path, _REPOSITORY_ROOT) |
| 710 | except LicenseError: |
| 711 | # TODO(phajdan.jr): Convert to fatal error (http://crbug.com/39240). |
| 712 | continue |
| 713 | if metadata['License File'] == NOT_SHIPPED: |
| 714 | continue |
| 715 | if target_os == 'ios' and not gn_target: |
| 716 | # Skip over files that are known not to be used on iOS. But |
| 717 | # skipping is unnecessary if GN was used to query the actual |
| 718 | # dependencies. |
| 719 | # TODO(lambroslambrou): Remove this step once the iOS build is |
| 720 | # updated to provide --gn-target to this script. |
| 721 | if path in KNOWN_NON_IOS_LIBRARIES: |
| 722 | continue |
Andrew Grieve | 9efa3811 | 2020-06-10 17:42:34 | [diff] [blame] | 723 | |
| 724 | new_entry = MetadataToTemplateEntry(metadata, entry_template) |
| 725 | # Skip entries that we've already seen (it exists in multiple directories). |
| 726 | prev_entry = entries_by_name.setdefault(new_entry['name'], new_entry) |
| 727 | if prev_entry is not new_entry and ( |
| 728 | prev_entry['content'] == new_entry['content']): |
| 729 | continue |
| 730 | |
| 731 | entries.append(new_entry) |
Yipeng Wang | 5671d4fe | 2017-06-08 17:59:44 | [diff] [blame] | 732 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 733 | entries.sort(key=lambda entry: (entry['name'].lower(), entry['content'])) |
| 734 | for entry_id, entry in enumerate(entries): |
| 735 | entry['content'] = entry['content'].replace('{{id}}', str(entry_id)) |
[email protected] | 54ce726 | 2010-04-17 00:02:55 | [diff] [blame] | 736 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 737 | entries_contents = '\n'.join([entry['content'] for entry in entries]) |
| 738 | file_template = open(file_template_file).read() |
| 739 | template_contents = "<!-- Generated by licenses.py; do not edit. -->" |
| 740 | template_contents += EvaluateTemplate( |
| 741 | file_template, {'entries': entries_contents}, escape=False) |
[email protected] | 284f26f | 2013-02-07 13:32:13 | [diff] [blame] | 742 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 743 | if output_file: |
| 744 | changed = True |
| 745 | try: |
| 746 | old_output = open(output_file, 'r').read() |
| 747 | if old_output == template_contents: |
| 748 | changed = False |
| 749 | except: |
| 750 | pass |
| 751 | if changed: |
| 752 | with open(output_file, 'w') as output: |
| 753 | output.write(template_contents) |
| 754 | else: |
| 755 | print(template_contents) |
[email protected] | 284f26f | 2013-02-07 13:32:13 | [diff] [blame] | 756 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 757 | if depfile: |
| 758 | assert output_file |
| 759 | # Add in build.ninja so that the target will be considered dirty whenever |
| 760 | # gn gen is run. Otherwise, it will fail to notice new files being added. |
| 761 | # This is still no perfect, as it will fail if no build files are changed, |
| 762 | # but a new README.chromium / LICENSE is added. This shouldn't happen in |
| 763 | # practice however. |
| 764 | license_file_list = (entry['license_file'] for entry in entries) |
| 765 | license_file_list = (os.path.relpath(p) for p in license_file_list) |
| 766 | license_file_list = sorted(set(license_file_list)) |
| 767 | build_utils.WriteDepfile(depfile, output_file, |
| 768 | license_file_list + ['build.ninja']) |
agrieve | be96180 | 2017-02-15 18:48:20 | [diff] [blame] | 769 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 770 | return True |
[email protected] | 957082a0 | 2010-03-18 21:55:24 | [diff] [blame] | 771 | |
[email protected] | cb155a8 | 2011-11-29 17:25:34 | [diff] [blame] | 772 | |
ichikawa | cc4ae12d | 2017-04-11 05:03:16 | [diff] [blame] | 773 | def _ReadFile(path): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 774 | """Reads a file from disk. |
ichikawa | cc4ae12d | 2017-04-11 05:03:16 | [diff] [blame] | 775 | Args: |
| 776 | path: The path of the file to read, relative to the root of the |
| 777 | repository. |
| 778 | Returns: |
| 779 | The contents of the file as a string. |
| 780 | """ |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 781 | with codecs.open(os.path.join(_REPOSITORY_ROOT, path), 'r', 'utf-8') as f: |
| 782 | return f.read() |
ichikawa | cc4ae12d | 2017-04-11 05:03:16 | [diff] [blame] | 783 | |
| 784 | |
Misha Efimov | 45ca19d | 2019-03-21 15:53:15 | [diff] [blame] | 785 | def GenerateLicenseFile(output_file, gn_out_dir, gn_target, target_os): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 786 | """Generate a plain-text LICENSE file which can be used when you ship a part |
ichikawa | cc4ae12d | 2017-04-11 05:03:16 | [diff] [blame] | 787 | of Chromium code (specified by gn_target) as a stand-alone library |
| 788 | (e.g., //ios/web_view). |
| 789 | |
| 790 | The LICENSE file contains licenses of both Chromium and third-party |
| 791 | libraries which gn_target depends on. """ |
| 792 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 793 | third_party_dirs = FindThirdPartyDeps(gn_out_dir, gn_target, target_os) |
ichikawa | cc4ae12d | 2017-04-11 05:03:16 | [diff] [blame] | 794 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 795 | # Start with Chromium's LICENSE file. |
| 796 | content = [_ReadFile('LICENSE')] |
ichikawa | cc4ae12d | 2017-04-11 05:03:16 | [diff] [blame] | 797 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 798 | # Add necessary third_party. |
| 799 | for directory in sorted(third_party_dirs): |
| 800 | metadata = ParseDir(directory, _REPOSITORY_ROOT, require_license_file=True) |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 801 | license_file = metadata['License File'] |
| 802 | if license_file and license_file != NOT_SHIPPED: |
Matt Mueller | 5f06c70 | 2020-06-04 21:34:04 | [diff] [blame] | 803 | content.append('-' * 20) |
| 804 | content.append(directory.split(os.sep)[-1]) |
| 805 | content.append('-' * 20) |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 806 | content.append(_ReadFile(license_file)) |
ichikawa | cc4ae12d | 2017-04-11 05:03:16 | [diff] [blame] | 807 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 808 | content_text = '\n'.join(content) |
ichikawa | cc4ae12d | 2017-04-11 05:03:16 | [diff] [blame] | 809 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 810 | if output_file: |
| 811 | with codecs.open(output_file, 'w', 'utf-8') as output: |
| 812 | output.write(content_text) |
| 813 | else: |
| 814 | print(content_text) |
ichikawa | cc4ae12d | 2017-04-11 05:03:16 | [diff] [blame] | 815 | |
ichikawa | cc4ae12d | 2017-04-11 05:03:16 | [diff] [blame] | 816 | |
| 817 | |
[email protected] | cb155a8 | 2011-11-29 17:25:34 | [diff] [blame] | 818 | def main(): |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 819 | parser = argparse.ArgumentParser() |
| 820 | parser.add_argument( |
| 821 | '--file-template', help='Template HTML to use for the license page.') |
| 822 | parser.add_argument( |
| 823 | '--entry-template', help='Template HTML to use for each license.') |
| 824 | parser.add_argument('--target-os', help='OS that this build is targeting.') |
| 825 | parser.add_argument( |
| 826 | '--gn-out-dir', help='GN output directory for scanning dependencies.') |
| 827 | parser.add_argument('--gn-target', help='GN target to scan for dependencies.') |
| 828 | parser.add_argument( |
| 829 | 'command', choices=['help', 'scan', 'credits', 'license_file']) |
| 830 | parser.add_argument('output_file', nargs='?') |
| 831 | build_utils.AddDepfileOption(parser) |
| 832 | args = parser.parse_args() |
[email protected] | 54ce726 | 2010-04-17 00:02:55 | [diff] [blame] | 833 | |
Hiroshi Ichikawa | f4fa98d | 2020-04-03 10:01:55 | [diff] [blame] | 834 | if args.command == 'scan': |
| 835 | if not ScanThirdPartyDirs(): |
| 836 | return 1 |
| 837 | elif args.command == 'credits': |
| 838 | if not GenerateCredits(args.file_template, args.entry_template, |
| 839 | args.output_file, args.target_os, args.gn_out_dir, |
| 840 | args.gn_target, args.depfile): |
| 841 | return 1 |
| 842 | elif args.command == 'license_file': |
| 843 | try: |
| 844 | GenerateLicenseFile(args.output_file, args.gn_out_dir, args.gn_target, |
| 845 | args.target_os) |
| 846 | except LicenseError as e: |
| 847 | print("Failed to parse README.chromium: {}".format(e)) |
| 848 | return 1 |
| 849 | else: |
| 850 | print(__doc__) |
| 851 | return 1 |
[email protected] | cb155a8 | 2011-11-29 17:25:34 | [diff] [blame] | 852 | |
| 853 | |
| 854 | if __name__ == '__main__': |
| 855 | sys.exit(main()) |