1 #!/usr/bin/env python 2 # Copyright (C) 2017 The Android Open Source Project 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 16 import argparse 17 import hashlib 18 import logging 19 import os 20 import shutil 21 import subprocess 22 import sys 23 import urllib 24 import zipfile 25 26 from collections import namedtuple 27 28 # When adding a new git dependency here please also add a corresponding entry in 29 # .travis.yml under the "cache:" section. 30 31 # The format for the deps below is the following: 32 # (target_folder, source_url, sha1, target_platform) 33 # |source_url| can be either a git repo or a http url. 34 # If a git repo, |sha1| is the committish that will be checked out. 35 # If a http url, |sha1| is the shasum of the original file. 36 # If the url is a .zip or .tgz file it will be automatically deflated under 37 # |target_folder|, taking care of stripping the root folder if it's a single 38 # root (to avoid ending up with buildtools/protobuf/protobuf-1.2.3/... and have 39 # instead just buildtools/protobuf). 40 # |target_platform| is either 'darwin', 'linux2' or 'all' and applies the dep 41 # only on the given platform (ask python why linux2 and not just linux). 42 43 # Dependencies required to build code on the host or when targeting desktop OS. 44 BUILD_DEPS_HOST = [ 45 # GN 46 ('buildtools/mac/gn', 47 'https://storage.googleapis.com/chromium-gn/9be792dd9010ce303a9c3a497a67bcc5ac8c7666', 48 '9be792dd9010ce303a9c3a497a67bcc5ac8c7666', 49 'darwin' 50 ), 51 ('buildtools/linux64/gn', 52 'https://storage.googleapis.com/chromium-gn/2f27ff0b6118e5886df976da5effa6003d19d1ce', 53 '2f27ff0b6118e5886df976da5effa6003d19d1ce', 54 'linux2' 55 ), 56 57 # clang-format 58 ('buildtools/mac/clang-format', 59 'https://storage.googleapis.com/chromium-clang-format/0679b295e2ce2fce7919d1e8d003e497475f24a3', 60 '0679b295e2ce2fce7919d1e8d003e497475f24a3', 61 'darwin' 62 ), 63 ('buildtools/linux64/clang-format', 64 'https://storage.googleapis.com/chromium-clang-format/5349d1954e17f6ccafb6e6663b0f13cdb2bb33c8', 65 '5349d1954e17f6ccafb6e6663b0f13cdb2bb33c8', 66 'linux2' 67 ), 68 # Keep the SHA1 in sync with |clang_format_rev| in chromium //buildtools/DEPS. 69 ('buildtools/clang_format/script', 70 'https://chromium.googlesource.com/chromium/llvm-project/cfe/tools/clang-format.git', 71 '0653eee0c81ea04715c635dd0885e8096ff6ba6d', 72 'all' 73 ), 74 75 # Ninja 76 ('buildtools/mac/ninja', 77 'https://storage.googleapis.com/fuchsia-build/fuchsia/ninja/mac/a1db595e824c50cf565fbf0af2437fd91b7babf4', 78 'a1db595e824c50cf565fbf0af2437fd91b7babf4', 79 'darwin' 80 ), 81 ('buildtools/linux64/ninja', 82 'https://storage.googleapis.com/fuchsia-build/fuchsia/ninja/linux64/d35b36c84a09f7e38b25947cafada10e8bf835bc', 83 'd35b36c84a09f7e38b25947cafada10e8bf835bc', 84 'linux2' 85 ), 86 87 # Keep in sync with Android's //external/googletest/README.version. 88 ('buildtools/googletest.zip', 89 'https://github.com/google/googletest/archive/ff07a5de0e81580547f1685e101194ed1a4fcd56.zip', 90 'c7edec7d7e6db1fc37a20710de9c4d89e3a3893b', 91 'all' 92 ), 93 94 # Keep in sync with Android's //external/protobuf/README.version. 95 ('buildtools/protobuf.zip', 96 'https://github.com/google/protobuf/releases/download/v3.0.0-beta-3/protobuf-cpp-3.0.0-beta-3.zip', 97 '3caec60aa9d8eefc8c3c3201b6b8ca19935edb89', 98 'all' 99 ), 100 101 # libc++, libc++abi and libunwind for Linux where we need to rebuild the C++ 102 # lib from sources. Keep the SHA1s in sync with Chrome's src/buildtools/DEPS. 103 ('buildtools/libcxx', 104 'https://chromium.googlesource.com/chromium/llvm-project/libcxx.git', 105 '2199647acb904b91eea0a5e045f5b227c87d6e85', 106 'all' 107 ), 108 ('buildtools/libcxxabi', 109 'https://chromium.googlesource.com/chromium/llvm-project/libcxxabi.git', 110 'c3f4753f7139c73063304235781e4f7788a94c06', 111 'all' 112 ), 113 ('buildtools/libunwind', 114 'https://chromium.googlesource.com/external/llvm.org/libunwind.git', 115 '317087cfd8e608bd24e53934d59b5b85e0a9ded6', 116 'all' 117 ), 118 119 # Keep the revision in sync with Chrome's CLANG_REVISION in 120 # tools/clang/scripts/update.py. 121 ('buildtools/clang.tgz', 122 'https://commondatastorage.googleapis.com/chromium-browser-clang/Linux_x64/clang-346388-1.tgz', 123 'c2998d67a9c623fe12e01a33e8b7cf437b396099', 124 'linux2' 125 ), 126 127 # Keep in sync with chromium DEPS. 128 ('buildtools/libfuzzer', 129 'https://chromium.googlesource.com/chromium/llvm-project/compiler-rt/lib/fuzzer.git', 130 '2a53098584c48af50aec3fb51febe5e651489774', 131 'linux2' 132 ), 133 134 # Benchmarking tool. 135 ('buildtools/benchmark.zip', 136 'https://github.com/google/benchmark/archive/v1.3.0.zip', 137 'f387e0df37d54bfd5be239e8d0d3ea2e2c3e34f4', 138 'all' 139 ), 140 141 # Libbacktrace, for stacktraces in Linux/Android debug builds. 142 ('buildtools/libbacktrace.zip', 143 'https://github.com/ianlancetaylor/libbacktrace/archive/177940370e4a6b2509e92a0aaa9749184e64af43.zip', 144 'b723fe9d671d1ab54df1297f6afbf2893a41c3ea', 145 'all' 146 ), 147 148 # Sqlite for the trace processing library. 149 # This is the amalgamated source whose compiled output is meant to be faster. 150 # We still pull the full source for the extensions (not amalgamated). 151 ('buildtools/sqlite.zip', 152 'https://storage.googleapis.com/perfetto/sqlite-amalgamation-3250300.zip', 153 'b78c2cb0d2c9182686c582312479f96a82bf5380', 154 'all' 155 ), 156 ('buildtools/sqlite_src.zip', 157 'https://storage.googleapis.com/perfetto/sqlite-src-3250300.zip', 158 'd1af2883bb800852946f9bf8ab6055e7698e18ee', 159 'all' 160 ), 161 162 # JsonCpp for legacy json import. Used only by the trace processor in 163 # standalone builds. 164 ('buildtools/jsoncpp.zip', 165 'https://github.com/open-source-parsers/jsoncpp/archive/1.0.0.zip', 166 '3219e26f2e249bb46b7d688478208c7ec138fea4', 167 'all' 168 ), 169 170 # These dependencies are for libunwindstack, which is used by src/profiling. 171 ('buildtools/android-core', 172 'https://android.googlesource.com/platform/system/core.git', 173 '9d3310c019839ec342b5c0712f3ba59cfd5ca4a0', 174 'all' 175 ), 176 177 ('buildtools/lzma', 178 'https://android.googlesource.com/platform/external/lzma.git', 179 '7851dce6f4ca17f5caa1c93a4e0a45686b1d56c3', 180 'all' 181 ), 182 183 ('buildtools/zlib', 184 'https://android.googlesource.com/platform/external/zlib.git', 185 'dfa0646a03b4e1707469e04dc931b09774968fe6', 186 'all' 187 ), 188 189 ('buildtools/bionic', 190 'https://android.googlesource.com/platform/bionic.git', 191 'a60488109cda997dfd83832731c8527feaa2825e', 192 'all' 193 ), 194 195 # Example traces for regression tests. 196 ('buildtools/test_data.zip', 197 'https://storage.googleapis.com/perfetto/test-data-20190423-131328.zip', 198 '263db97612203fd0dd047edd54eaa7007e32bf91', 199 'all', 200 ), 201 202 # Linenoise, used only by trace_processor in standalone builds. 203 ('buildtools/linenoise', 204 'https://fuchsia.googlesource.com/third_party/linenoise.git', 205 'c894b9e59f02203dbe4e2be657572cf88c4230c3', 206 'all' 207 ), 208 ] 209 210 # Dependencies required to build Android code. 211 # URLs and SHA1s taken from: 212 # - https://dl.google.com/android/repository/repository-11.xml 213 # - https://dl.google.com/android/repository/sys-img/android/sys-img.xml 214 BUILD_DEPS_ANDROID = [ 215 # Android NDK 216 ('buildtools/ndk.zip', 217 'https://dl.google.com/android/repository/android-ndk-r17b-darwin-x86_64.zip', 218 'f990aafaffec0b583d2c5420bfa622e52ac14248', 219 'darwin' 220 ), 221 ('buildtools/ndk.zip', 222 'https://dl.google.com/android/repository/android-ndk-r17b-linux-x86_64.zip', 223 'dd5762ee7ef4995ad04fe0c45a608c344d99ca9f', 224 'linux2' 225 ), 226 ] 227 228 # Dependencies required to run Android tests. 229 TEST_DEPS_ANDROID = [ 230 # Android emulator images. 231 ('buildtools/aosp-arm.zip', 232 'https://storage.googleapis.com/perfetto/aosp-02022018-arm.zip', 233 'a480d5e7d3ca888b0a58fe15ce76b1791537429a', 234 'all' 235 ), 236 237 # platform-tools.zip contains adb binaries. 238 ('buildtools/android_sdk/platform-tools.zip', 239 'https://dl.google.com/android/repository/platform-tools_r26.0.0-darwin.zip', 240 'e75b6137dc444f777eb02f44a6d9819b3aabff82', 241 'darwin' 242 ), 243 ('buildtools/android_sdk/platform-tools.zip', 244 'https://dl.google.com/android/repository/platform-tools_r26.0.0-linux.zip', 245 '00de8a6631405b617c10f68cd11ff2e1cd528e23', 246 'linux2' 247 ), 248 249 # Android emulator binaries. 250 ('buildtools/emulator', 251 'https://android.googlesource.com/platform/prebuilts/android-emulator.git', 252 '4b260028dc27bc92c39bee9129cb2ba839970956', 253 'all' 254 ), 255 ] 256 257 # This variable is updated by tools/roll-catapult-trace-viewer. 258 CATAPULT_SHA1 = 'ff5d8fd7244680b4d4456c25d5fdc04c76f9ef66' 259 260 TYPEFACES_SHA1 = '756b0a015b8f99f5718f7fdf967d052c1ec55ab3' 261 262 UI_DEPS = [ 263 ('buildtools/nodejs.tgz', 264 'https://storage.googleapis.com/perfetto/node-v10.3.0-darwin-x64.tar.gz', 265 '6d9a122785f38c256add3b25f74adf125497861a', 266 'darwin' 267 ), 268 ('buildtools/nodejs.tgz', 269 'https://storage.googleapis.com/perfetto/node-v10.3.0-linux-x64.tar.xz', 270 '118f6ea19f75089b3f12ac2ddfce357bff872b5e', 271 'linux2' 272 ), 273 ('buildtools/emsdk/emscripten.tgz', 274 'https://storage.googleapis.com/perfetto/emscripten-1.37.40.tar.gz', 275 '588c28221321ebbdfc8e3a6f47ea6106f589669b', 276 'all' 277 ), 278 ('buildtools/emsdk/llvm.tgz', 279 'https://storage.googleapis.com/perfetto/emscripten-llvm-e1.37.40-darwin.tar.gz', 280 '7a894ef0a52821c62f6abaac552dc4ce5d424607', 281 'darwin' 282 ), 283 ('buildtools/emsdk/llvm.tgz', 284 'https://storage.googleapis.com/perfetto/emscripten-llvm-e1.37.40-static-linux.tar.gz', 285 '478501b9b7a14884e546c84efe209a90052cbb07', 286 'linux2' 287 ), 288 ('buildtools/d8.tgz', 289 'https://storage.googleapis.com/perfetto/d8-linux2-5.7.492.65.tar.gz', 290 '95e82ad7faf0a6f74d950c2aa65e3858b7bdb6c6', 291 'linux2' 292 ), 293 ('buildtools/d8.tgz', 294 'https://storage.googleapis.com/perfetto/d8-darwin-6.6.346.32.tar.gz', 295 '1abd630619bb1977ab62095570a113d782a1545d', 296 'darwin' 297 ), 298 ('buildtools/catapult_trace_viewer.tgz', 299 'https://storage.googleapis.com/perfetto/catapult_trace_viewer-%s.tar.gz' % CATAPULT_SHA1, 300 CATAPULT_SHA1, 301 'all' 302 ), 303 ('buildtools/typefaces.tgz', 304 'https://storage.googleapis.com/perfetto/typefaces-%s.tar.gz' % TYPEFACES_SHA1, 305 TYPEFACES_SHA1, 306 'all' 307 ) 308 ] 309 310 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 311 312 313 def ReadFile(path): 314 if not os.path.exists(path): 315 return None 316 with open(path) as f: 317 return f.read().strip() 318 319 320 def MkdirRecursive(path): 321 # Works with both relative and absolute paths 322 cwd = '/' if path.startswith('/') else ROOT_DIR 323 for part in path.split('/'): 324 cwd = os.path.join(cwd, part) 325 if not os.path.exists(cwd): 326 os.makedirs(cwd) 327 else: 328 assert(os.path.isdir(cwd)) 329 330 331 def HashLocalFile(path): 332 if not os.path.exists(path): 333 return None 334 with open(path, 'rb') as f: 335 return hashlib.sha1(f.read()).hexdigest() 336 337 338 def ExtractZipfilePreservePermissions(zf, info, path): 339 zf.extract(info.filename, path=path) 340 target_path = os.path.join(path, info.filename) 341 min_acls = 0o755 if info.filename.endswith('/') else 0o644 342 os.chmod(target_path, (info.external_attr >> 16L) | min_acls) 343 344 345 def IsGitRepoCheckoutOutAtRevision(path, revision): 346 return ReadFile(os.path.join(path, '.git', 'HEAD')) == revision 347 348 349 def CheckoutGitRepo(path, git_url, revision): 350 if IsGitRepoCheckoutOutAtRevision(path, revision): 351 return False 352 if os.path.exists(path): 353 shutil.rmtree(path) 354 MkdirRecursive(path) 355 logging.info('Fetching %s @ %s into %s', git_url, revision, path) 356 subprocess.check_call(['git', 'init', path], cwd=path) 357 subprocess.check_call( 358 ['git', 'fetch', '--quiet', '--depth', '1', git_url, revision], cwd=path) 359 subprocess.check_call(['git', 'checkout', revision, '--quiet'], cwd=path) 360 assert(IsGitRepoCheckoutOutAtRevision(path, revision)) 361 return True 362 363 def InstallNodeModules(): 364 ui_dir = os.path.join(ROOT_DIR, 'ui') 365 logging.info("Running npm install in {0}".format(ui_dir)) 366 subprocess.check_call( 367 [os.path.join(ui_dir, 'npm'), 'install', '--no-save'], 368 cwd=os.path.join(ROOT_DIR, 'ui')) 369 370 def Main(): 371 parser = argparse.ArgumentParser() 372 parser.add_argument('--no-android', action='store_true') 373 parser.add_argument('--ui', action='store_true') 374 args = parser.parse_args() 375 deps = BUILD_DEPS_HOST 376 if not args.no_android: 377 deps += BUILD_DEPS_ANDROID + TEST_DEPS_ANDROID 378 if args.ui: 379 deps += UI_DEPS 380 deps_updated = False 381 for rel_path, url, expected_sha1, platform in deps: 382 if (platform != 'all' and platform != sys.platform): 383 continue 384 local_path = os.path.join(ROOT_DIR, rel_path) 385 if url.endswith('.git'): 386 deps_updated |= CheckoutGitRepo(local_path, url, expected_sha1) 387 continue 388 is_zip = local_path.endswith('.zip') or local_path.endswith('.tgz') 389 zip_target_dir = local_path[:-4] if is_zip else None 390 zip_dir_stamp = os.path.join(zip_target_dir, '.stamp') if is_zip else None 391 392 if ((not is_zip and HashLocalFile(local_path) == expected_sha1) or 393 (is_zip and ReadFile(zip_dir_stamp) == expected_sha1)): 394 continue 395 deps_updated = True 396 MkdirRecursive(os.path.dirname(rel_path)) 397 if HashLocalFile(local_path) != expected_sha1: 398 download_path = local_path + '.tmp' 399 logging.info('Downloading %s from %s', local_path, url) 400 urllib.urlretrieve(url, download_path) 401 os.chmod(download_path, 0o755) 402 actual_sha1 = HashLocalFile(download_path) 403 if (actual_sha1 != expected_sha1): 404 os.remove(download_path) 405 logging.fatal('SHA1 mismatch for {} expected {} was {}'.format( 406 download_path, expected_sha1, actual_sha1)) 407 return 1 408 os.rename(download_path, local_path) 409 assert(HashLocalFile(local_path) == expected_sha1) 410 411 if is_zip: 412 logging.info('Extracting %s into %s' % (local_path, zip_target_dir)) 413 assert(os.path.commonprefix((ROOT_DIR, zip_target_dir)) == ROOT_DIR) 414 if os.path.exists(zip_target_dir): 415 logging.info('Deleting stale dir %s' % zip_target_dir) 416 shutil.rmtree(zip_target_dir) 417 418 # Decompress the archive. 419 if local_path.endswith('.tgz'): 420 MkdirRecursive(zip_target_dir) 421 subprocess.check_call(['tar', '-xf', local_path], cwd=zip_target_dir) 422 elif local_path.endswith('.zip'): 423 with zipfile.ZipFile(local_path, 'r') as zf: 424 for info in zf.infolist(): 425 ExtractZipfilePreservePermissions(zf, info, zip_target_dir) 426 427 # If the zip contains one root folder, rebase one level up moving all 428 # its sub files and folders inside |target_dir|. 429 subdir = os.listdir(zip_target_dir) 430 if len(subdir) == 1: 431 subdir = os.path.join(zip_target_dir, subdir[0]) 432 if os.path.isdir(subdir): 433 for subf in os.listdir(subdir): 434 shutil.move(os.path.join(subdir,subf), zip_target_dir) 435 os.rmdir(subdir) 436 437 # Create stamp and remove the archive. 438 with open(zip_dir_stamp, 'w') as stamp_file: 439 stamp_file.write(expected_sha1) 440 os.remove(local_path) 441 442 if args.ui: 443 # Needs to happen after nodejs is installed above. 444 InstallNodeModules() 445 446 if deps_updated: 447 # Stale binary files may be compiled against old sysroot headers that aren't 448 # tracked by gn. 449 logging.warn('Remember to run "gn clean <output_directory>" ' + 450 'to avoid stale binary files.') 451 452 if __name__ == '__main__': 453 logging.basicConfig(level=logging.INFO) 454 sys.exit(Main()) 455