Paweł Hajdan, Jr | ef4c48b | 2015-07-13 18:51:42 +0900 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright 2015 The Chromium Authors. All rights reserved. |
| 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
| 6 | import argparse |
| 7 | import operator |
| 8 | import os |
| 9 | import platform |
| 10 | import re |
| 11 | import subprocess |
| 12 | import sys |
| 13 | |
| 14 | |
| 15 | SUPPORTED_UBUNTU_VERSIONS = ( |
| 16 | {'number': '12.04', 'codename': 'precise'}, |
| 17 | {'number': '14.04', 'codename': 'trusty'}, |
| 18 | {'number': '14.10', 'codename': 'utopic'}, |
| 19 | {'number': '15.04', 'codename': 'vivid'}, |
| 20 | ) |
| 21 | |
| 22 | |
| 23 | # Packages needed for chromeos only. |
| 24 | _packages_chromeos_dev = ( |
| 25 | 'libbluetooth-dev', |
| 26 | 'libxkbcommon-dev', |
| 27 | 'realpath', |
| 28 | ) |
| 29 | |
| 30 | |
| 31 | # Packages needed for development. |
| 32 | _packages_dev = ( |
| 33 | 'apache2.2-bin', |
| 34 | 'bison', |
| 35 | 'cdbs', |
| 36 | 'curl', |
| 37 | 'devscripts', |
| 38 | 'dpkg-dev', |
| 39 | 'elfutils', |
| 40 | 'fakeroot', |
| 41 | 'flex', |
| 42 | 'fonts-thai-tlwg', |
| 43 | 'g++', |
| 44 | 'git-core', |
| 45 | 'git-svn', |
| 46 | 'gperf', |
| 47 | 'language-pack-da', |
| 48 | 'language-pack-fr', |
| 49 | 'language-pack-he', |
| 50 | 'language-pack-zh-hant', |
| 51 | 'libapache2-mod-php5', |
| 52 | 'libasound2-dev', |
| 53 | 'libav-tools', |
| 54 | 'libbrlapi-dev', |
| 55 | 'libbz2-dev', |
| 56 | 'libcairo2-dev', |
| 57 | 'libcap-dev', |
| 58 | 'libcups2-dev', |
| 59 | 'libcurl4-gnutls-dev', |
| 60 | 'libdrm-dev', |
| 61 | 'libelf-dev', |
| 62 | 'libexif-dev', |
| 63 | 'libgconf2-dev', |
| 64 | 'libglib2.0-dev', |
| 65 | 'libglu1-mesa-dev', |
| 66 | 'libgnome-keyring-dev', |
| 67 | 'libgtk2.0-dev', |
| 68 | 'libkrb5-dev', |
| 69 | 'libnspr4-dev', |
| 70 | 'libnss3-dev', |
| 71 | 'libpam0g-dev', |
| 72 | 'libpci-dev', |
| 73 | 'libpulse-dev', |
| 74 | 'libsctp-dev', |
| 75 | 'libspeechd-dev', |
| 76 | 'libsqlite3-dev', |
| 77 | 'libssl-dev', |
| 78 | 'libudev-dev', |
| 79 | 'libwww-perl', |
| 80 | 'libxslt1-dev', |
| 81 | 'libxss-dev', |
| 82 | 'libxt-dev', |
| 83 | 'libxtst-dev', |
| 84 | 'openbox', |
| 85 | 'patch', |
| 86 | 'perl', |
| 87 | 'php5-cgi', |
| 88 | 'pkg-config', |
| 89 | 'python', |
| 90 | 'python-cherrypy3', |
| 91 | 'python-crypto', |
| 92 | 'python-dev', |
| 93 | 'python-numpy', |
| 94 | 'python-opencv', |
| 95 | 'python-openssl', |
| 96 | 'python-psutil', |
| 97 | 'python-yaml', |
| 98 | 'rpm', |
| 99 | 'ruby', |
| 100 | 'subversion', |
| 101 | 'ttf-dejavu-core', |
| 102 | 'ttf-indic-fonts', |
| 103 | 'ttf-kochi-gothic', |
| 104 | 'ttf-kochi-mincho', |
| 105 | 'wdiff', |
| 106 | 'xfonts-mathml', |
| 107 | 'zip', |
| 108 | ) |
| 109 | |
| 110 | |
| 111 | # Run-time libraries required by chromeos only. |
| 112 | _packages_chromeos_lib = ( |
| 113 | 'libbz2-1.0', |
| 114 | 'libpulse0', |
| 115 | ) |
| 116 | |
| 117 | |
| 118 | # Full list of required run-time libraries. |
| 119 | _packages_lib = ( |
| 120 | 'libasound2', |
| 121 | 'libatk1.0-0', |
| 122 | 'libc6', |
| 123 | 'libcairo2', |
| 124 | 'libcap2', |
| 125 | 'libcups2', |
| 126 | 'libexif12', |
| 127 | 'libexpat1', |
| 128 | 'libfontconfig1', |
| 129 | 'libfreetype6', |
| 130 | 'libglib2.0-0', |
| 131 | 'libgnome-keyring0', |
| 132 | 'libgtk2.0-0', |
| 133 | 'libpam0g', |
| 134 | 'libpango1.0-0', |
| 135 | 'libpci3', |
| 136 | 'libpcre3', |
| 137 | 'libpixman-1-0', |
| 138 | 'libpng12-0', |
| 139 | 'libspeechd2', |
| 140 | 'libsqlite3-0', |
| 141 | 'libstdc++6', |
| 142 | 'libx11-6', |
| 143 | 'libxau6', |
| 144 | 'libxcb1', |
| 145 | 'libxcomposite1', |
| 146 | 'libxcursor1', |
| 147 | 'libxdamage1', |
| 148 | 'libxdmcp6', |
| 149 | 'libxext6', |
| 150 | 'libxfixes3', |
| 151 | 'libxi6', |
| 152 | 'libxinerama1', |
| 153 | 'libxrandr2', |
| 154 | 'libxrender1', |
| 155 | 'libxtst6', |
| 156 | 'zlib1g', |
| 157 | ) |
| 158 | |
| 159 | |
| 160 | # Debugging symbols for all of the run-time libraries. |
| 161 | _packages_dbg = ( |
| 162 | 'libatk1.0-dbg', |
| 163 | 'libc6-dbg', |
| 164 | 'libcairo2-dbg', |
| 165 | 'libfontconfig1-dbg', |
| 166 | 'libglib2.0-0-dbg', |
| 167 | 'libgtk2.0-0-dbg', |
| 168 | 'libpango1.0-0-dbg', |
| 169 | 'libpcre3-dbg', |
| 170 | 'libpixman-1-0-dbg', |
| 171 | 'libsqlite3-0-dbg', |
| 172 | 'libx11-6-dbg', |
| 173 | 'libxau6-dbg', |
| 174 | 'libxcb1-dbg', |
| 175 | 'libxcomposite1-dbg', |
| 176 | 'libxcursor1-dbg', |
| 177 | 'libxdamage1-dbg', |
| 178 | 'libxdmcp6-dbg', |
| 179 | 'libxext6-dbg', |
| 180 | 'libxfixes3-dbg', |
| 181 | 'libxi6-dbg', |
| 182 | 'libxinerama1-dbg', |
| 183 | 'libxrandr2-dbg', |
| 184 | 'libxrender1-dbg', |
| 185 | 'libxtst6-dbg', |
| 186 | 'zlib1g-dbg', |
| 187 | ) |
| 188 | |
| 189 | |
| 190 | # 32-bit libraries needed e.g. to compile V8 snapshot for Android or armhf. |
| 191 | _packages_lib32 = ( |
| 192 | 'linux-libc-dev:i386', |
| 193 | ) |
| 194 | |
| 195 | |
| 196 | # arm cross toolchain packages needed to build chrome on armhf. |
| 197 | _packages_arm = ( |
| 198 | 'g++-arm-linux-gnueabihf', |
| 199 | 'libc6-dev-armhf-cross', |
| 200 | 'linux-libc-dev-armhf-cross', |
| 201 | ) |
| 202 | |
| 203 | |
| 204 | # Packages to build NaCl, its toolchains, and its ports. |
| 205 | _packages_naclports = ( |
| 206 | 'ant', |
| 207 | 'autoconf', |
| 208 | 'bison', |
| 209 | 'cmake', |
| 210 | 'gawk', |
| 211 | 'intltool', |
| 212 | 'xsltproc', |
| 213 | 'xutils-dev', |
| 214 | ) |
| 215 | _packages_nacl = ( |
| 216 | 'g++-mingw-w64-i686', |
| 217 | 'lib32ncurses5-dev', |
| 218 | 'lib32z1-dev', |
| 219 | 'libasound2:i386', |
| 220 | 'libcap2:i386', |
| 221 | 'libelf-dev:i386', |
| 222 | 'libexif12:i386', |
| 223 | 'libfontconfig1:i386', |
| 224 | 'libgconf-2-4:i386', |
| 225 | 'libglib2.0-0:i386', |
| 226 | 'libgpm2:i386', |
| 227 | 'libgtk2.0-0:i386', |
| 228 | 'libncurses5:i386', |
| 229 | 'libnss3:i386', |
| 230 | 'libpango1.0-0:i386', |
| 231 | 'libssl1.0.0:i386', |
| 232 | 'libtinfo-dev', |
| 233 | 'libtinfo-dev:i386', |
| 234 | 'libtool', |
| 235 | 'libxcomposite1:i386', |
| 236 | 'libxcursor1:i386', |
| 237 | 'libxdamage1:i386', |
| 238 | 'libxi6:i386', |
| 239 | 'libxrandr2:i386', |
| 240 | 'libxss1:i386', |
| 241 | 'libxtst6:i386', |
| 242 | 'texinfo', |
| 243 | 'xvfb', |
| 244 | ) |
| 245 | |
| 246 | |
| 247 | def is_userland_64_bit(): |
| 248 | return platform.architecture()[0] == '64bit' |
| 249 | |
| 250 | |
| 251 | def package_exists(pkg): |
| 252 | return pkg in subprocess.check_output(['apt-cache', 'pkgnames']).splitlines() |
| 253 | |
| 254 | |
| 255 | def lsb_release_short_codename(): |
| 256 | return subprocess.check_output( |
| 257 | ['lsb_release', '--codename', '--short']).strip() |
| 258 | |
| 259 | |
| 260 | def write_error(message): |
| 261 | sys.stderr.write('ERROR: %s\n' % message) |
| 262 | sys.stderr.flush() |
| 263 | |
| 264 | |
| 265 | def nonfatal_get_output(*popenargs, **kwargs): |
| 266 | process = subprocess.Popen( |
| 267 | stdout=subprocess.PIPE, stderr=subprocess.PIPE, *popenargs, **kwargs) |
| 268 | stdout, stderr = process.communicate() |
| 269 | retcode = process.poll() |
| 270 | return retcode, stdout, stderr |
| 271 | |
| 272 | |
| 273 | def compute_dynamic_package_lists(): |
| 274 | global _packages_arm |
| 275 | global _packages_dbg |
| 276 | global _packages_dev |
| 277 | global _packages_lib |
| 278 | global _packages_lib32 |
| 279 | global _packages_nacl |
| 280 | |
| 281 | if is_userland_64_bit(): |
| 282 | # 64-bit systems need a minimum set of 32-bit compat packages |
| 283 | # for the pre-built NaCl binaries. |
| 284 | _packages_dev += ( |
| 285 | 'lib32gcc1', |
| 286 | 'lib32stdc++6', |
| 287 | 'libc6-i386', |
| 288 | ) |
| 289 | |
| 290 | # When cross building for arm/Android on 64-bit systems the host binaries |
| 291 | # that are part of v8 need to be compiled with -m32 which means |
| 292 | # that basic multilib support is needed. |
| 293 | # gcc-multilib conflicts with the arm cross compiler (at least in trusty) |
| 294 | # but g++-X.Y-multilib gives us the 32-bit support that we need. Find out |
| 295 | # the appropriate value of X and Y by seeing what version the current |
| 296 | # distribution's g++-multilib package depends on. |
| 297 | output = subprocess.check_output(['apt-cache', 'depends', 'g++-multilib']) |
| 298 | multilib_package = re.search(r'g\+\+-[0-9.]+-multilib', output).group() |
| 299 | _packages_lib32 += (multilib_package,) |
| 300 | |
| 301 | lsb_codename = lsb_release_short_codename() |
| 302 | |
| 303 | # Find the proper version of libstdc++6-4.x-dbg. |
| 304 | if lsb_codename == 'precise': |
| 305 | _packages_dbg += ('libstdc++6-4.6-dbg',) |
| 306 | elif lsb_codename == 'trusty': |
| 307 | _packages_dbg += ('libstdc++6-4.8-dbg',) |
| 308 | else: |
| 309 | _packages_dbg += ('libstdc++6-4.9-dbg',) |
| 310 | |
| 311 | # Work around for dependency issue Ubuntu/Trusty: http://crbug.com/435056 . |
| 312 | if lsb_codename == 'trusty': |
| 313 | _packages_arm += ( |
| 314 | 'g++-4.8-multilib-arm-linux-gnueabihf', |
| 315 | 'gcc-4.8-multilib-arm-linux-gnueabihf', |
| 316 | ) |
| 317 | |
| 318 | # Find the proper version of libgbm-dev. We can't just install libgbm-dev as |
| 319 | # it depends on mesa, and only one version of mesa can exists on the system. |
| 320 | # Hence we must match the same version or this entire script will fail. |
| 321 | mesa_variant = '' |
| 322 | for variant in ('-lts-trusty', '-lts-utopic'): |
| 323 | rc, stdout, stderr = nonfatal_get_output( |
| 324 | ['dpkg-query', '-Wf\'{Status}\'', 'libgl1-mesa-glx' + variant]) |
| 325 | if 'ok installed' in output: |
| 326 | mesa_variant = variant |
| 327 | _packages_dev += ( |
| 328 | 'libgbm-dev' + mesa_variant, |
| 329 | 'libgl1-mesa-dev' + mesa_variant, |
| 330 | 'libgles2-mesa-dev' + mesa_variant, |
| 331 | 'mesa-common-dev' + mesa_variant, |
| 332 | ) |
| 333 | |
| 334 | if package_exists('ttf-mscorefonts-installer'): |
| 335 | _packages_dev += ('ttf-mscorefonts-installer',) |
| 336 | else: |
| 337 | _packages_dev += ('msttcorefonts',) |
| 338 | |
| 339 | if package_exists('libnspr4-dbg'): |
| 340 | _packages_dbg += ('libnspr4-dbg', 'libnss3-dbg') |
| 341 | _packages_lib += ('libnspr4', 'libnss3') |
| 342 | else: |
| 343 | _packages_dbg += ('libnspr4-0d-dbg', 'libnss3-1d-dbg') |
| 344 | _packages_lib += ('libnspr4-0d', 'libnss3-1d') |
| 345 | |
| 346 | if package_exists('libjpeg-dev'): |
| 347 | _packages_dev += ('libjpeg-dev',) |
| 348 | else: |
| 349 | _packages_dev += ('libjpeg62-dev',) |
| 350 | |
| 351 | if package_exists('libudev1'): |
| 352 | _packages_dev += ('libudev1',) |
| 353 | _packages_nacl += ('libudev1:i386',) |
| 354 | else: |
| 355 | _packages_dev += ('libudev0',) |
| 356 | _packages_nacl += ('libudev0:i386',) |
| 357 | |
| 358 | if package_exists('libbrlapi0.6'): |
| 359 | _packages_dev += ('libbrlapi0.6',) |
| 360 | else: |
| 361 | _packages_dev += ('libbrlapi0.5',) |
| 362 | |
| 363 | # Some packages are only needed if the distribution actually supports |
| 364 | # installing them. |
| 365 | if package_exists('appmenu-gtk'): |
| 366 | _packages_lib += ('appmenu-gtk',) |
| 367 | |
| 368 | _packages_dev += _packages_chromeos_dev |
| 369 | _packages_lib += _packages_chromeos_lib |
| 370 | _packages_nacl += _packages_naclports |
| 371 | |
| 372 | |
| 373 | def quick_check(packages): |
| 374 | rc, stdout, stderr = nonfatal_get_output([ |
| 375 | 'dpkg-query', '-W', '-f', '${PackageSpec}:${Status}\n'] + list(packages)) |
| 376 | if rc == 0 and not stderr: |
| 377 | return 0 |
| 378 | print stderr |
| 379 | return 1 |
| 380 | |
| 381 | |
| 382 | def main(argv): |
| 383 | parser = argparse.ArgumentParser() |
| 384 | parser.add_argument('--quick-check', action='store_true', |
| 385 | help='quickly try to determine if dependencies are ' |
| 386 | 'installed (this avoids interactive prompts and ' |
| 387 | 'sudo commands so might not be 100% accurate)') |
| 388 | parser.add_argument('--unsupported', action='store_true', |
| 389 | help='attempt installation even on unsupported systems') |
| 390 | args = parser.parse_args(argv) |
| 391 | |
| 392 | lsb_codename = lsb_release_short_codename() |
| 393 | if not args.unsupported and not args.quick_check: |
| 394 | if lsb_codename not in map( |
| 395 | operator.itemgetter('codename'), SUPPORTED_UBUNTU_VERSIONS): |
| 396 | supported_ubuntus = ['%(number)s (%(codename)s)' % v |
| 397 | for v in SUPPORTED_UBUNTU_VERSIONS] |
| 398 | write_error('Only Ubuntu %s are currently supported.' % |
| 399 | ', '.join(supported_ubuntus)) |
| 400 | return 1 |
| 401 | |
| 402 | if platform.machine() not in ('i686', 'x86_64'): |
| 403 | write_error('Only x86 architectures are currently supported.') |
| 404 | return 1 |
| 405 | |
| 406 | if os.geteuid() != 0 and not args.quick_check: |
| 407 | print 'Running as non-root user.' |
| 408 | print 'You might have to enter your password one or more times' |
| 409 | print 'for \'sudo\'.' |
| 410 | print |
| 411 | |
| 412 | compute_dynamic_package_lists() |
| 413 | |
| 414 | packages = (_packages_dev + _packages_lib + _packages_dbg + _packages_lib32 + |
| 415 | _packages_arm + _packages_nacl) |
| 416 | def packages_key(pkg): |
| 417 | s = pkg.rsplit(':', 1) |
| 418 | if len(s) == 1: |
| 419 | return (s, '') |
| 420 | return s |
| 421 | packages = sorted(set(packages), key=packages_key) |
| 422 | |
| 423 | if args.quick_check: |
| 424 | return quick_check(packages) |
| 425 | |
| 426 | return 0 |
| 427 | |
| 428 | |
| 429 | if __name__ == '__main__': |
| 430 | sys.exit(main(sys.argv[1:])) |