1#!/usr/bin/env python3 2 3# Copyright 2023 The Chromium Authors 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7# Script to install everything needed to build chromium 8# including items requiring sudo privileges. 9# See https://chromium.googlesource.com/chromium/src/+/main/docs/linux/build_instructions.md 10 11import argparse 12import functools 13import os 14import re 15import shutil 16import subprocess 17import sys 18 19 20@functools.lru_cache(maxsize=1) 21def build_apt_package_list(): 22 print("Building apt package list.", file=sys.stderr) 23 output = subprocess.check_output(["apt-cache", "dumpavail"]).decode() 24 arch_map = {"i386": ":i386"} 25 package_regex = re.compile(r"^Package: (.+?)$.+?^Architecture: (.+?)$", 26 re.M | re.S) 27 return set(package + arch_map.get(arch, "") 28 for package, arch in re.findall(package_regex, output)) 29 30 31def package_exists(package_name: str) -> bool: 32 return package_name in build_apt_package_list() 33 34 35def parse_args(argv): 36 parser = argparse.ArgumentParser( 37 description="Install Chromium build dependencies.") 38 parser.add_argument("--syms", 39 action="store_true", 40 help="Enable installation of debugging symbols") 41 parser.add_argument( 42 "--no-syms", 43 action="store_false", 44 dest="syms", 45 help="Disable installation of debugging symbols", 46 ) 47 parser.add_argument( 48 "--lib32", 49 action="store_true", 50 help="Enable installation of 32-bit libraries, e.g. for V8 snapshot", 51 ) 52 parser.add_argument( 53 "--android", 54 action="store_true", 55 help="Enable installation of android dependencies", 56 ) 57 parser.add_argument( 58 "--no-android", 59 action="store_false", 60 dest="android", 61 help="Disable installation of android dependencies", 62 ) 63 parser.add_argument("--arm", 64 action="store_true", 65 help="Enable installation of arm cross toolchain") 66 parser.add_argument( 67 "--no-arm", 68 action="store_false", 69 dest="arm", 70 help="Disable installation of arm cross toolchain", 71 ) 72 parser.add_argument( 73 "--chromeos-fonts", 74 action="store_true", 75 help="Enable installation of Chrome OS fonts", 76 ) 77 parser.add_argument( 78 "--no-chromeos-fonts", 79 action="store_false", 80 dest="chromeos_fonts", 81 help="Disable installation of Chrome OS fonts", 82 ) 83 parser.add_argument( 84 "--nacl", 85 action="store_true", 86 help="Enable installation of prerequisites for building NaCl", 87 ) 88 parser.add_argument( 89 "--no-nacl", 90 action="store_false", 91 dest="nacl", 92 help="Disable installation of prerequisites for building NaCl", 93 ) 94 parser.add_argument( 95 "--backwards-compatible", 96 action="store_true", 97 help= 98 "Enable installation of packages that are no longer currently needed and" 99 + "have been removed from this script. Useful for bisection.", 100 ) 101 parser.add_argument( 102 "--no-backwards-compatible", 103 action="store_false", 104 dest="backwards_compatible", 105 help= 106 "Disable installation of packages that are no longer currently needed and" 107 + "have been removed from this script.", 108 ) 109 parser.add_argument("--no-prompt", 110 action="store_true", 111 help="Automatic yes to prompts") 112 parser.add_argument( 113 "--quick-check", 114 action="store_true", 115 help="Quickly try to determine if dependencies are installed", 116 ) 117 parser.add_argument( 118 "--unsupported", 119 action="store_true", 120 help="Attempt installation even on unsupported systems", 121 ) 122 123 options = parser.parse_args(argv) 124 125 if options.arm or options.android: 126 options.lib32 = True 127 128 return options 129 130 131def check_lsb_release(): 132 if not shutil.which("lsb_release"): 133 print("ERROR: lsb_release not found in $PATH", file=sys.stderr) 134 print("try: sudo apt-get install lsb-release", file=sys.stderr) 135 sys.exit(1) 136 137 138@functools.lru_cache(maxsize=1) 139def distro_codename(): 140 return subprocess.check_output(["lsb_release", "--codename", 141 "--short"]).decode().strip() 142 143 144def check_distro(options): 145 if options.unsupported or options.quick_check: 146 return 147 148 distro_id = subprocess.check_output(["lsb_release", "--id", 149 "--short"]).decode().strip() 150 151 supported_codenames = ["bionic", "focal", "jammy", "noble"] 152 supported_ids = ["Debian"] 153 154 if (distro_codename() not in supported_codenames 155 and distro_id not in supported_ids): 156 print( 157 "WARNING: The following distributions are supported,", 158 "but distributions not in the list below can also try to install", 159 "dependencies by passing the `--unsupported` parameter", 160 "\tUbuntu 18.04 LTS (bionic with EoL April 2028)", 161 "\tUbuntu 20.04 LTS (focal with EoL April 2030)", 162 "\tUbuntu 22.04 LTS (jammy with EoL April 2032)", 163 "\tUbuntu 24.04 LTS (noble with EoL June 2029)", 164 "\tDebian 10 (buster) or later", 165 sep="\n", 166 file=sys.stderr, 167 ) 168 sys.exit(1) 169 170 171def check_architecture(): 172 architecture = subprocess.check_output(["uname", "-m"]).decode().strip() 173 if architecture not in ["i686", "x86_64", 'aarch64']: 174 print("Only x86 and ARM64 architectures are currently supported", 175 file=sys.stderr) 176 sys.exit(1) 177 178 179def check_root(): 180 if os.geteuid() != 0: 181 print("Running as non-root user.", file=sys.stderr) 182 print("You might have to enter your password one or more times for 'sudo'.", 183 file=sys.stderr) 184 print(file=sys.stderr) 185 186 187def apt_update(options): 188 if options.lib32 or options.nacl: 189 subprocess.check_call(["sudo", "dpkg", "--add-architecture", "i386"]) 190 subprocess.check_call(["sudo", "apt-get", "update"]) 191 192 193# Packages needed for development 194def dev_list(): 195 packages = [ 196 "binutils", 197 "bison", 198 "bzip2", 199 "cdbs", 200 "curl", 201 "dbus-x11", 202 "devscripts", 203 "dpkg-dev", 204 "elfutils", 205 "fakeroot", 206 "flex", 207 "git-core", 208 "gperf", 209 "libasound2-dev", 210 "libatspi2.0-dev", 211 "libbrlapi-dev", 212 "libbz2-dev", 213 "libc6-dev", 214 "libcairo2-dev", 215 "libcap-dev", 216 "libcups2-dev", 217 "libcurl4-gnutls-dev", 218 "libdrm-dev", 219 "libelf-dev", 220 "libevdev-dev", 221 "libffi-dev", 222 "libfuse2", 223 "libgbm-dev", 224 "libglib2.0-dev", 225 "libglu1-mesa-dev", 226 "libgtk-3-dev", 227 "libkrb5-dev", 228 "libnspr4-dev", 229 "libnss3-dev", 230 "libpam0g-dev", 231 "libpci-dev", 232 "libpulse-dev", 233 "libsctp-dev", 234 "libspeechd-dev", 235 "libsqlite3-dev", 236 "libssl-dev", 237 "libsystemd-dev", 238 "libudev-dev", 239 "libva-dev", 240 "libwww-perl", 241 "libxshmfence-dev", 242 "libxslt1-dev", 243 "libxss-dev", 244 "libxt-dev", 245 "libxtst-dev", 246 "lighttpd", 247 "locales", 248 "openbox", 249 "p7zip", 250 "patch", 251 "perl", 252 "pkg-config", 253 "rpm", 254 "ruby", 255 "subversion", 256 "uuid-dev", 257 "wdiff", 258 "x11-utils", 259 "xcompmgr", 260 "xz-utils", 261 "zip", 262 ] 263 264 # Packages needed for chromeos only 265 packages += [ 266 "libbluetooth-dev", 267 "libxkbcommon-dev", 268 "mesa-common-dev", 269 "zstd", 270 ] 271 272 if package_exists("realpath"): 273 packages.append("realpath") 274 275 if package_exists("libjpeg-dev"): 276 packages.append("libjpeg-dev") 277 else: 278 packages.append("libjpeg62-dev") 279 280 if package_exists("libudev1"): 281 packages.append("libudev1") 282 else: 283 packages.append("libudev0") 284 285 if package_exists("libbrlapi0.8"): 286 packages.append("libbrlapi0.8") 287 elif package_exists("libbrlapi0.7"): 288 packages.append("libbrlapi0.7") 289 elif package_exists("libbrlapi0.6"): 290 packages.append("libbrlapi0.6") 291 else: 292 packages.append("libbrlapi0.5") 293 294 if package_exists("libav-tools"): 295 packages.append("libav-tools") 296 297 if package_exists("libvulkan-dev"): 298 packages.append("libvulkan-dev") 299 300 if package_exists("libinput-dev"): 301 packages.append("libinput-dev") 302 303 # Cross-toolchain strip is needed for building the sysroots. 304 if package_exists("binutils-arm-linux-gnueabihf"): 305 packages.append("binutils-arm-linux-gnueabihf") 306 if package_exists("binutils-aarch64-linux-gnu"): 307 packages.append("binutils-aarch64-linux-gnu") 308 if package_exists("binutils-mipsel-linux-gnu"): 309 packages.append("binutils-mipsel-linux-gnu") 310 if package_exists("binutils-mips64el-linux-gnuabi64"): 311 packages.append("binutils-mips64el-linux-gnuabi64") 312 313 # 64-bit systems need a minimum set of 32-bit compat packages for the 314 # pre-built NaCl binaries. 315 if "ELF 64-bit" in subprocess.check_output(["file", "-L", 316 "/sbin/init"]).decode(): 317 # ARM64 may not support these. 318 if package_exists("libc6-i386"): 319 packages.append("libc6-i386") 320 if package_exists("lib32stdc++6"): 321 packages.append("lib32stdc++6") 322 323 # lib32gcc-s1 used to be called lib32gcc1 in older distros. 324 if package_exists("lib32gcc-s1"): 325 packages.append("lib32gcc-s1") 326 elif package_exists("lib32gcc1"): 327 packages.append("lib32gcc1") 328 329 return packages 330 331 332# List of required run-time libraries 333def lib_list(): 334 packages = [ 335 "libasound2", 336 "libatk1.0-0", 337 "libatspi2.0-0", 338 "libc6", 339 "libcairo2", 340 "libcap2", 341 "libcgi-session-perl", 342 "libcups2", 343 "libdrm2", 344 "libegl1", 345 "libevdev2", 346 "libexpat1", 347 "libfontconfig1", 348 "libfreetype6", 349 "libgbm1", 350 "libglib2.0-0", 351 "libgl1", 352 "libgtk-3-0", 353 "libpam0g", 354 "libpango-1.0-0", 355 "libpangocairo-1.0-0", 356 "libpci3", 357 "libpcre3", 358 "libpixman-1-0", 359 "libspeechd2", 360 "libstdc++6", 361 "libsqlite3-0", 362 "libuuid1", 363 "libwayland-egl1", 364 "libwayland-egl1-mesa", 365 "libx11-6", 366 "libx11-xcb1", 367 "libxau6", 368 "libxcb1", 369 "libxcomposite1", 370 "libxcursor1", 371 "libxdamage1", 372 "libxdmcp6", 373 "libxext6", 374 "libxfixes3", 375 "libxi6", 376 "libxinerama1", 377 "libxrandr2", 378 "libxrender1", 379 "libxtst6", 380 "x11-utils", 381 "xserver-xorg-core", # TODO(crbug.com/1417069): Experimental. 382 "xserver-xorg-video-dummy", # TODO(crbug.com/1417069): Experimental. 383 "xvfb", 384 "zlib1g", 385 ] 386 387 # Run-time libraries required by chromeos only 388 packages += [ 389 "libpulse0", 390 "libbz2-1.0", 391 ] 392 393 # May not exist (e.g. ARM64) 394 if package_exists("lib32z1"): 395 packages.append("lib32z1") 396 397 if package_exists("libffi8"): 398 packages.append("libffi8") 399 elif package_exists("libffi7"): 400 packages.append("libffi7") 401 elif package_exists("libffi6"): 402 packages.append("libffi6") 403 404 if package_exists("libpng16-16"): 405 packages.append("libpng16-16") 406 else: 407 packages.append("libpng12-0") 408 409 if package_exists("libnspr4"): 410 packages.extend(["libnspr4", "libnss3"]) 411 else: 412 packages.extend(["libnspr4-0d", "libnss3-1d"]) 413 414 if package_exists("appmenu-gtk"): 415 packages.append("appmenu-gtk") 416 if package_exists("libgnome-keyring0"): 417 packages.append("libgnome-keyring0") 418 if package_exists("libgnome-keyring-dev"): 419 packages.append("libgnome-keyring-dev") 420 if package_exists("libvulkan1"): 421 packages.append("libvulkan1") 422 if package_exists("libinput10"): 423 packages.append("libinput10") 424 425 # Work around for dependency On Ubuntu 24.04 LTS (noble) 426 if distro_codename() == "noble": 427 packages.append("libncurses6") 428 else: 429 packages.append("libncurses5") 430 431 return packages 432 433 434def lib32_list(options): 435 if not options.lib32: 436 print("Skipping 32-bit libraries.", file=sys.stderr) 437 return [] 438 print("Including 32-bit libraries.", file=sys.stderr) 439 440 packages = [ 441 # 32-bit libraries needed for a 32-bit build 442 # includes some 32-bit libraries required by the Android SDK 443 # See https://developer.android.com/sdk/installing/index.html?pkg=tools 444 "libasound2:i386", 445 "libatk-bridge2.0-0:i386", 446 "libatk1.0-0:i386", 447 "libatspi2.0-0:i386", 448 "libdbus-1-3:i386", 449 "libegl1:i386", 450 "libgl1:i386", 451 "libglib2.0-0:i386", 452 "libnss3:i386", 453 "libpango-1.0-0:i386", 454 "libpangocairo-1.0-0:i386", 455 "libstdc++6:i386", 456 "libwayland-egl1:i386", 457 "libx11-xcb1:i386", 458 "libxcomposite1:i386", 459 "libxdamage1:i386", 460 "libxkbcommon0:i386", 461 "libxrandr2:i386", 462 "libxtst6:i386", 463 "zlib1g:i386", 464 # 32-bit libraries needed e.g. to compile V8 snapshot for Android or armhf 465 "linux-libc-dev:i386", 466 "libpci3:i386", 467 ] 468 469 # When cross building for arm/Android on 64-bit systems the host binaries 470 # that are part of v8 need to be compiled with -m32 which means 471 # that basic multilib support is needed. 472 if "ELF 64-bit" in subprocess.check_output(["file", "-L", 473 "/sbin/init"]).decode(): 474 # gcc-multilib conflicts with the arm cross compiler but 475 # g++-X.Y-multilib gives us the 32-bit support that we need. Find out the 476 # appropriate value of X and Y by seeing what version the current 477 # distribution's g++-multilib package depends on. 478 lines = subprocess.check_output( 479 ["apt-cache", "depends", "g++-multilib", "--important"]).decode() 480 pattern = re.compile(r"g\+\+-[0-9.]+-multilib") 481 packages += re.findall(pattern, lines) 482 483 # Work around for 32-bit dependency On Ubuntu 24.04 LTS (noble) 484 if distro_codename() == "noble": 485 packages.append("libncurses6:i386") 486 else: 487 packages.append("libncurses5:i386") 488 489 return packages 490 491 492# Packages that have been removed from this script. Regardless of configuration 493# or options passed to this script, whenever a package is removed, it should be 494# added here. 495def backwards_compatible_list(options): 496 if not options.backwards_compatible: 497 print("Skipping backwards compatible packages.", file=sys.stderr) 498 return [] 499 print("Including backwards compatible packages.", file=sys.stderr) 500 501 packages = [ 502 "7za", 503 "fonts-indic", 504 "fonts-ipafont", 505 "fonts-stix", 506 "fonts-thai-tlwg", 507 "fonts-tlwg-garuda", 508 "g++", 509 "g++-4.8-multilib-arm-linux-gnueabihf", 510 "gcc-4.8-multilib-arm-linux-gnueabihf", 511 "g++-9-multilib-arm-linux-gnueabihf", 512 "gcc-9-multilib-arm-linux-gnueabihf", 513 "gcc-arm-linux-gnueabihf", 514 "g++-10-multilib-arm-linux-gnueabihf", 515 "gcc-10-multilib-arm-linux-gnueabihf", 516 "g++-10-arm-linux-gnueabihf", 517 "gcc-10-arm-linux-gnueabihf", 518 "git-svn", 519 "language-pack-da", 520 "language-pack-fr", 521 "language-pack-he", 522 "language-pack-zh-hant", 523 "libappindicator-dev", 524 "libappindicator1", 525 "libappindicator3-1", 526 "libappindicator3-dev", 527 "libdconf-dev", 528 "libdconf1", 529 "libdconf1:i386", 530 "libexif-dev", 531 "libexif12", 532 "libexif12:i386", 533 "libgbm-dev", 534 "libgbm-dev-lts-trusty", 535 "libgbm-dev-lts-xenial", 536 "libgconf-2-4:i386", 537 "libgconf2-dev", 538 "libgl1-mesa-dev", 539 "libgl1-mesa-dev-lts-trusty", 540 "libgl1-mesa-dev-lts-xenial", 541 "libgl1-mesa-glx:i386", 542 "libgl1-mesa-glx-lts-trusty:i386", 543 "libgl1-mesa-glx-lts-xenial:i386", 544 "libgles2-mesa-dev", 545 "libgles2-mesa-dev-lts-trusty", 546 "libgles2-mesa-dev-lts-xenial", 547 "libgtk-3-0:i386", 548 "libgtk2.0-0", 549 "libgtk2.0-0:i386", 550 "libgtk2.0-dev", 551 "mesa-common-dev", 552 "mesa-common-dev-lts-trusty", 553 "mesa-common-dev-lts-xenial", 554 "msttcorefonts", 555 "python-dev", 556 "python-setuptools", 557 "snapcraft", 558 "ttf-dejavu-core", 559 "ttf-indic-fonts", 560 "ttf-kochi-gothic", 561 "ttf-kochi-mincho", 562 "ttf-mscorefonts-installer", 563 "xfonts-mathml", 564 ] 565 566 if package_exists("python-is-python2"): 567 packages.extend(["python-is-python2", "python2-dev"]) 568 else: 569 packages.append("python") 570 571 if package_exists("python-crypto"): 572 packages.append("python-crypto") 573 574 if package_exists("python-numpy"): 575 packages.append("python-numpy") 576 577 if package_exists("python-openssl"): 578 packages.append("python-openssl") 579 580 if package_exists("python-psutil"): 581 packages.append("python-psutil") 582 583 if package_exists("python-yaml"): 584 packages.append("python-yaml") 585 586 if package_exists("apache2.2-bin"): 587 packages.append("apache2.2-bin") 588 else: 589 packages.append("apache2-bin") 590 591 php_versions = [ 592 ("php8.1-cgi", "libapache2-mod-php8.1"), 593 ("php8.0-cgi", "libapache2-mod-php8.0"), 594 ("php7.4-cgi", "libapache2-mod-php7.4"), 595 ("php7.3-cgi", "libapache2-mod-php7.3"), 596 ("php7.2-cgi", "libapache2-mod-php7.2"), 597 ("php7.1-cgi", "libapache2-mod-php7.1"), 598 ("php7.0-cgi", "libapache2-mod-php7.0"), 599 ("php5-cgi", "libapache2-mod-php5"), 600 ] 601 602 for php_cgi, mod_php in php_versions: 603 if package_exists(php_cgi): 604 packages.extend([php_cgi, mod_php]) 605 break 606 607 return [package for package in packages if package_exists(package)] 608 609 610def arm_list(options): 611 if not options.arm: 612 print("Skipping ARM cross toolchain.", file=sys.stderr) 613 return [] 614 print("Including ARM cross toolchain.", file=sys.stderr) 615 616 # arm cross toolchain packages needed to build chrome on armhf 617 packages = [ 618 "libc6-dev-armhf-cross", 619 "linux-libc-dev-armhf-cross", 620 "g++-arm-linux-gnueabihf", 621 ] 622 623 # Work around for dependency issue Ubuntu: http://crbug.com/435056 624 if distro_codename() == "bionic": 625 packages.extend([ 626 "g++-5-multilib-arm-linux-gnueabihf", 627 "gcc-5-multilib-arm-linux-gnueabihf", 628 "gcc-arm-linux-gnueabihf", 629 ]) 630 elif distro_codename() == "focal": 631 packages.extend([ 632 "g++-10-multilib-arm-linux-gnueabihf", 633 "gcc-10-multilib-arm-linux-gnueabihf", 634 "gcc-arm-linux-gnueabihf", 635 ]) 636 elif distro_codename() == "jammy": 637 packages.extend([ 638 "gcc-arm-linux-gnueabihf", 639 "g++-11-arm-linux-gnueabihf", 640 "gcc-11-arm-linux-gnueabihf", 641 ]) 642 elif distro_codename() == "noble": 643 packages.extend([ 644 "gcc-arm-linux-gnueabihf", 645 "g++-13-arm-linux-gnueabihf", 646 "gcc-13-arm-linux-gnueabihf", 647 ]) 648 649 return packages 650 651 652def nacl_list(options): 653 if not options.nacl: 654 print("Skipping NaCl, NaCl toolchain, NaCl ports dependencies.", 655 file=sys.stderr) 656 return [] 657 print("Including NaCl, NaCl toolchain, NaCl ports dependencies.", 658 file=sys.stderr) 659 660 packages = [ 661 "g++-mingw-w64-i686", 662 "lib32z1-dev", 663 "libasound2:i386", 664 "libcap2:i386", 665 "libelf-dev:i386", 666 "libfontconfig1:i386", 667 "libglib2.0-0:i386", 668 "libgpm2:i386", 669 "libnss3:i386", 670 "libpango-1.0-0:i386", 671 "libssl-dev:i386", 672 "libtinfo-dev", 673 "libtinfo-dev:i386", 674 "libtool", 675 "libuuid1:i386", 676 "libxcomposite1:i386", 677 "libxcursor1:i386", 678 "libxdamage1:i386", 679 "libxi6:i386", 680 "libxrandr2:i386", 681 "libxss1:i386", 682 "libxtst6:i386", 683 "texinfo", 684 "xvfb", 685 # Packages to build NaCl, its toolchains, and its ports. 686 "ant", 687 "autoconf", 688 "bison", 689 "cmake", 690 "gawk", 691 "intltool", 692 "xutils-dev", 693 "xsltproc", 694 ] 695 696 # Some package names have changed over time 697 if package_exists("libssl-dev"): 698 packages.append("libssl-dev:i386") 699 elif package_exists("libssl1.1"): 700 packages.append("libssl1.1:i386") 701 elif package_exists("libssl1.0.2"): 702 packages.append("libssl1.0.2:i386") 703 else: 704 packages.append("libssl1.0.0:i386") 705 706 if package_exists("libtinfo5"): 707 packages.append("libtinfo5") 708 709 if package_exists("libudev1"): 710 packages.append("libudev1:i386") 711 else: 712 packages.append("libudev0:i386") 713 714 # Work around for nacl dependency On Ubuntu 24.04 LTS (noble) 715 if distro_codename() == "noble": 716 packages.append("libncurses6:i386") 717 packages.append("lib32ncurses-dev") 718 else: 719 packages.append("libncurses5:i386") 720 packages.append("lib32ncurses5-dev") 721 722 return packages 723 724 725# Debian is in the process of transitioning to automatic debug packages, which 726# have the -dbgsym suffix (https://wiki.debian.org/AutomaticDebugPackages). 727# Untransitioned packages have the -dbg suffix. And on some systems, neither 728# will be available, so exclude the ones that are missing. 729def dbg_package_name(package): 730 if package_exists(package + "-dbgsym"): 731 return [package + "-dbgsym"] 732 if package_exists(package + "-dbg"): 733 return [package + "-dbg"] 734 return [] 735 736 737def dbg_list(options): 738 if not options.syms: 739 print("Skipping debugging symbols.", file=sys.stderr) 740 return [] 741 print("Including debugging symbols.", file=sys.stderr) 742 743 packages = [ 744 dbg_package for package in lib_list() 745 for dbg_package in dbg_package_name(package) 746 ] 747 748 # Debugging symbols packages not following common naming scheme 749 if not dbg_package_name("libstdc++6"): 750 for version in ["8", "7", "6", "5", "4.9", "4.8", "4.7", "4.6"]: 751 if package_exists("libstdc++6-%s-dbg" % version): 752 packages.append("libstdc++6-%s-dbg" % version) 753 break 754 755 if not dbg_package_name("libatk1.0-0"): 756 packages.extend(dbg_package_name("libatk1.0")) 757 758 if not dbg_package_name("libpango-1.0-0"): 759 packages.extend(dbg_package_name("libpango1.0-dev")) 760 761 return packages 762 763 764def package_list(options): 765 packages = (dev_list() + lib_list() + dbg_list(options) + 766 lib32_list(options) + arm_list(options) + nacl_list(options) + 767 backwards_compatible_list(options)) 768 769 # Sort all the :i386 packages to the front, to avoid confusing dpkg-query 770 # (https://crbug.com/446172). 771 return sorted(set(packages), key=lambda x: (not x.endswith(":i386"), x)) 772 773 774def missing_packages(packages): 775 try: 776 subprocess.run( 777 ["dpkg-query", "-W", "-f", " "] + packages, 778 check=True, 779 capture_output=True, 780 ) 781 return [] 782 except subprocess.CalledProcessError as e: 783 return [ 784 line.split(" ")[-1] for line in e.stderr.decode().strip().splitlines() 785 ] 786 787 788def package_is_installable(package): 789 result = subprocess.run(["apt-cache", "show", package], capture_output=True) 790 return result.returncode == 0 791 792 793def quick_check(options): 794 if not options.quick_check: 795 return 796 797 missing = missing_packages(package_list(options)) 798 if not missing: 799 sys.exit(0) 800 801 not_installed = [] 802 unknown = [] 803 for p in missing: 804 if package_is_installable(p): 805 not_installed.append(p) 806 else: 807 unknown.append(p) 808 809 if not_installed: 810 print("WARNING: The following packages are not installed:", file=sys.stderr) 811 print(" ".join(not_installed), file=sys.stderr) 812 813 if unknown: 814 print("WARNING: The following packages are unknown to your system", 815 file=sys.stderr) 816 print("(maybe missing a repo or need to 'sudo apt-get update'):", 817 file=sys.stderr) 818 print(" ".join(unknown), file=sys.stderr) 819 820 sys.exit(1) 821 822 823def find_missing_packages(options): 824 print("Finding missing packages...", file=sys.stderr) 825 826 packages = package_list(options) 827 packages_str = " ".join(packages) 828 print("Packages required: " + packages_str, file=sys.stderr) 829 830 query_cmd = ["apt-get", "--just-print", "install"] + packages 831 env = os.environ.copy() 832 env["LANGUAGE"] = "en" 833 env["LANG"] = "C" 834 cmd_output = subprocess.check_output(query_cmd, env=env).decode() 835 lines = cmd_output.splitlines() 836 837 install = [] 838 for pattern in ( 839 "The following NEW packages will be installed:", 840 "The following packages will be upgraded:", 841 ): 842 if pattern in lines: 843 for line in lines[lines.index(pattern) + 1:]: 844 if not line.startswith(" "): 845 break 846 install += line.strip().split(" ") 847 return install 848 849 850def install_packages(options): 851 try: 852 packages = find_missing_packages(options) 853 if packages: 854 quiet = ["-qq", "--assume-yes"] if options.no_prompt else [] 855 subprocess.check_call(["sudo", "apt-get", "install"] + quiet + packages) 856 print(file=sys.stderr) 857 else: 858 print("No missing packages, and the packages are up to date.", 859 file=sys.stderr) 860 861 except subprocess.CalledProcessError as e: 862 # An apt-get exit status of 100 indicates that a real error has occurred. 863 print("`apt-get --just-print install ...` failed", file=sys.stderr) 864 print("It produced the following output:", file=sys.stderr) 865 print(e.output.decode(), file=sys.stderr) 866 print(file=sys.stderr) 867 print("You will have to install the above packages yourself.", 868 file=sys.stderr) 869 print(file=sys.stderr) 870 sys.exit(100) 871 872 873# Install the Chrome OS default fonts. This must go after running 874# apt-get, since install-chromeos-fonts depends on curl. 875def install_chromeos_fonts(options): 876 if not options.chromeos_fonts: 877 print("Skipping installation of Chrome OS fonts.", file=sys.stderr) 878 return 879 print("Installing Chrome OS fonts.", file=sys.stderr) 880 881 dir = os.path.abspath(os.path.dirname(__file__)) 882 883 try: 884 subprocess.check_call( 885 ["sudo", 886 os.path.join(dir, "linux", "install-chromeos-fonts.py")]) 887 except subprocess.CalledProcessError: 888 print("ERROR: The installation of the Chrome OS default fonts failed.", 889 file=sys.stderr) 890 if (subprocess.check_output( 891 ["stat", "-f", "-c", "%T", dir], ).decode().startswith("nfs")): 892 print( 893 "The reason is that your repo is installed on a remote file system.", 894 file=sys.stderr) 895 else: 896 print( 897 "This is expected if your repo is installed on a remote file system.", 898 file=sys.stderr) 899 900 print("It is recommended to install your repo on a local file system.", 901 file=sys.stderr) 902 print("You can skip the installation of the Chrome OS default fonts with", 903 file=sys.stderr) 904 print("the command line option: --no-chromeos-fonts.", file=sys.stderr) 905 sys.exit(1) 906 907 908def install_locales(): 909 print("Installing locales.", file=sys.stderr) 910 CHROMIUM_LOCALES = [ 911 "da_DK.UTF-8", "en_US.UTF-8", "fr_FR.UTF-8", "he_IL.UTF-8", "zh_TW.UTF-8" 912 ] 913 LOCALE_GEN = "/etc/locale.gen" 914 if os.path.exists(LOCALE_GEN): 915 old_locale_gen = open(LOCALE_GEN).read() 916 for locale in CHROMIUM_LOCALES: 917 subprocess.check_call( 918 ["sudo", "sed", "-i", 919 "s/^# %s/%s/" % (locale, locale), LOCALE_GEN]) 920 921 # Regenerating locales can take a while, so only do it if we need to. 922 locale_gen = open(LOCALE_GEN).read() 923 if locale_gen != old_locale_gen: 924 subprocess.check_call(["sudo", "locale-gen"]) 925 else: 926 print("Locales already up-to-date.", file=sys.stderr) 927 else: 928 for locale in CHROMIUM_LOCALES: 929 subprocess.check_call(["sudo", "locale-gen", locale]) 930 931 932def main(): 933 options = parse_args(sys.argv[1:]) 934 check_lsb_release() 935 check_distro(options) 936 check_architecture() 937 quick_check(options) 938 check_root() 939 apt_update(options) 940 install_packages(options) 941 install_chromeos_fonts(options) 942 install_locales() 943 return 0 944 945 946if __name__ == "__main__": 947 sys.exit(main()) 948