blob: 1687b94b10336941d0a8db0cb8774f11aecd2806 [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import operator
import os
import platform
import re
import subprocess
import sys
SUPPORTED_UBUNTU_VERSIONS = (
{'number': '12.04', 'codename': 'precise'},
{'number': '14.04', 'codename': 'trusty'},
{'number': '14.10', 'codename': 'utopic'},
{'number': '15.04', 'codename': 'vivid'},
{'number': '15.10', 'codename': 'wily'},
)
# Packages needed for chromeos only.
_packages_chromeos_dev = (
'libbluetooth-dev',
'libxkbcommon-dev',
'realpath',
)
# Packages needed for development.
_packages_dev = (
'bison',
'cdbs',
'curl',
'devscripts',
'dpkg-dev',
'elfutils',
'fakeroot',
'flex',
'fonts-ipafont',
'fonts-thai-tlwg',
'g++',
'git-core',
'git-svn',
'gperf',
'language-pack-da',
'language-pack-fr',
'language-pack-he',
'language-pack-zh-hant',
'libapache2-mod-php5',
'libasound2-dev',
'libav-tools',
'libbrlapi-dev',
'libbz2-dev',
'libcairo2-dev',
'libcap-dev',
'libcups2-dev',
'libcurl4-gnutls-dev',
'libdrm-dev',
'libelf-dev',
'libgconf2-dev',
'libglib2.0-dev',
'libglu1-mesa-dev',
'libgnome-keyring-dev',
'libgtk2.0-dev',
'libkrb5-dev',
'libnspr4-dev',
'libnss3-dev',
'libpam0g-dev',
'libpci-dev',
'libpulse-dev',
'libsctp-dev',
'libspeechd-dev',
'libsqlite3-dev',
'libssl-dev',
'libudev-dev',
'libwww-perl',
'libxslt1-dev',
'libxss-dev',
'libxt-dev',
'libxtst-dev',
'openbox',
'patch',
'perl',
'php5-cgi',
'pkg-config',
'python',
'python-cherrypy3',
'python-crypto',
'python-dev',
'python-numpy',
'python-opencv',
'python-openssl',
'python-psutil',
'python-yaml',
'rpm',
'ruby',
'subversion',
'ttf-dejavu-core',
'ttf-indic-fonts',
'ttf-kochi-gothic',
'ttf-kochi-mincho',
'wdiff',
'zip',
)
# Run-time libraries required by chromeos only.
_packages_chromeos_lib = (
'libbz2-1.0',
'libpulse0',
)
# Full list of required run-time libraries.
_packages_lib = (
'libasound2',
'libatk1.0-0',
'libc6',
'libcairo2',
'libcap2',
'libcups2',
'libexpat1',
'libfontconfig1',
'libfreetype6',
'libglib2.0-0',
'libgnome-keyring0',
'libgtk2.0-0',
'libpam0g',
'libpango1.0-0',
'libpci3',
'libpcre3',
'libpixman-1-0',
'libpng12-0',
'libspeechd2',
'libsqlite3-0',
'libstdc++6',
'libx11-6',
'libx11-xcb1',
'libxau6',
'libxcb1',
'libxcomposite1',
'libxcursor1',
'libxdamage1',
'libxdmcp6',
'libxext6',
'libxfixes3',
'libxi6',
'libxinerama1',
'libxrandr2',
'libxrender1',
'libxtst6',
'zlib1g',
)
# Debugging symbols for all of the run-time libraries.
_packages_dbg = (
'libatk1.0-dbg',
'libc6-dbg',
'libcairo2-dbg',
'libfontconfig1-dbg',
'libglib2.0-0-dbg',
'libgtk2.0-0-dbg',
'libpango1.0-0-dbg',
'libpcre3-dbg',
'libpixman-1-0-dbg',
'libsqlite3-0-dbg',
'libx11-6-dbg',
'libx11-xcb1-dbg',
'libxau6-dbg',
'libxcb1-dbg',
'libxcomposite1-dbg',
'libxcursor1-dbg',
'libxdamage1-dbg',
'libxdmcp6-dbg',
'libxext6-dbg',
'libxfixes3-dbg',
'libxi6-dbg',
'libxinerama1-dbg',
'libxrandr2-dbg',
'libxrender1-dbg',
'libxtst6-dbg',
'zlib1g-dbg',
)
# 32-bit libraries needed e.g. to compile V8 snapshot for Android or armhf.
_packages_lib32 = (
'linux-libc-dev:i386',
)
# arm cross toolchain packages needed to build chrome on armhf.
_packages_arm = (
'g++-arm-linux-gnueabihf',
'libc6-dev-armhf-cross',
'linux-libc-dev-armhf-cross',
)
# Packages to build NaCl, its toolchains, and its ports.
_packages_naclports = (
'ant',
'autoconf',
'bison',
'cmake',
'gawk',
'intltool',
'xsltproc',
'xutils-dev',
)
_packages_nacl = (
'g++-mingw-w64-i686',
'lib32ncurses5-dev',
'lib32z1-dev',
'libasound2:i386',
'libcap2:i386',
'libelf-dev:i386',
'libfontconfig1:i386',
'libgconf-2-4:i386',
'libglib2.0-0:i386',
'libgpm2:i386',
'libgtk2.0-0:i386',
'libncurses5:i386',
'libnss3:i386',
'libpango1.0-0:i386',
'libssl1.0.0:i386',
'libtinfo-dev',
'libtinfo-dev:i386',
'libtool',
'libxcomposite1:i386',
'libxcursor1:i386',
'libxdamage1:i386',
'libxi6:i386',
'libxrandr2:i386',
'libxss1:i386',
'libxtst6:i386',
'texinfo',
'xvfb',
)
def is_userland_64_bit():
return platform.architecture()[0] == '64bit'
def package_exists(pkg):
return pkg in subprocess.check_output(['apt-cache', 'pkgnames']).splitlines()
def lsb_release_short_codename():
return subprocess.check_output(
['lsb_release', '--codename', '--short']).strip()
def write_error(message):
sys.stderr.write('ERROR: %s\n' % message)
sys.stderr.flush()
def nonfatal_get_output(*popenargs, **kwargs):
process = subprocess.Popen(
stdout=subprocess.PIPE, stderr=subprocess.PIPE, *popenargs, **kwargs)
stdout, stderr = process.communicate()
retcode = process.poll()
return retcode, stdout, stderr
def compute_dynamic_package_lists():
global _packages_arm
global _packages_dbg
global _packages_dev
global _packages_lib
global _packages_lib32
global _packages_nacl
if is_userland_64_bit():
# 64-bit systems need a minimum set of 32-bit compat packages
# for the pre-built NaCl binaries.
_packages_dev += (
'lib32gcc1',
'lib32stdc++6',
'libc6-i386',
)
# When cross building for arm/Android on 64-bit systems the host binaries
# that are part of v8 need to be compiled with -m32 which means
# that basic multilib support is needed.
# gcc-multilib conflicts with the arm cross compiler (at least in trusty)
# but g++-X.Y-multilib gives us the 32-bit support that we need. Find out
# the appropriate value of X and Y by seeing what version the current
# distribution's g++-multilib package depends on.
output = subprocess.check_output(['apt-cache', 'depends', 'g++-multilib'])
multilib_package = re.search(r'g\+\+-[0-9.]+-multilib', output).group()
_packages_lib32 += (multilib_package,)
lsb_codename = lsb_release_short_codename()
# Find the proper version of libstdc++6-4.x-dbg.
if lsb_codename == 'precise':
_packages_dbg += ('libstdc++6-4.6-dbg',)
elif lsb_codename == 'trusty':
_packages_dbg += ('libstdc++6-4.8-dbg',)
else:
_packages_dbg += ('libstdc++6-4.9-dbg',)
# Work around for dependency issue Ubuntu/Trusty: http://crbug.com/435056 .
if lsb_codename == 'trusty':
_packages_arm += (
'g++-4.8-multilib-arm-linux-gnueabihf',
'gcc-4.8-multilib-arm-linux-gnueabihf',
)
# Find the proper version of libgbm-dev. We can't just install libgbm-dev as
# it depends on mesa, and only one version of mesa can exists on the system.
# Hence we must match the same version or this entire script will fail.
mesa_variant = ''
for variant in ('-lts-trusty', '-lts-utopic'):
rc, stdout, stderr = nonfatal_get_output(
['dpkg-query', '-Wf\'{Status}\'', 'libgl1-mesa-glx' + variant])
if 'ok installed' in output:
mesa_variant = variant
_packages_dev += (
'libgbm-dev' + mesa_variant,
'libgl1-mesa-dev' + mesa_variant,
'libgles2-mesa-dev' + mesa_variant,
'mesa-common-dev' + mesa_variant,
)
if package_exists('ttf-mscorefonts-installer'):
_packages_dev += ('ttf-mscorefonts-installer',)
else:
_packages_dev += ('msttcorefonts',)
if package_exists('libnspr4-dbg'):
_packages_dbg += ('libnspr4-dbg', 'libnss3-dbg')
_packages_lib += ('libnspr4', 'libnss3')
else:
_packages_dbg += ('libnspr4-0d-dbg', 'libnss3-1d-dbg')
_packages_lib += ('libnspr4-0d', 'libnss3-1d')
if package_exists('libjpeg-dev'):
_packages_dev += ('libjpeg-dev',)
else:
_packages_dev += ('libjpeg62-dev',)
if package_exists('libudev1'):
_packages_dev += ('libudev1',)
_packages_nacl += ('libudev1:i386',)
else:
_packages_dev += ('libudev0',)
_packages_nacl += ('libudev0:i386',)
if package_exists('libbrlapi0.6'):
_packages_dev += ('libbrlapi0.6',)
else:
_packages_dev += ('libbrlapi0.5',)
if package_exists('apache2-bin'):
_packages_dev += ('apache2-bin',)
else:
_packages_dev += ('apache2.2-bin',)
if package_exists('xfonts-mathml'):
_packages_dev += ('xfonts-mathml',)
# Some packages are only needed if the distribution actually supports
# installing them.
if package_exists('appmenu-gtk'):
_packages_lib += ('appmenu-gtk',)
_packages_dev += _packages_chromeos_dev
_packages_lib += _packages_chromeos_lib
_packages_nacl += _packages_naclports
def quick_check(packages):
rc, stdout, stderr = nonfatal_get_output([
'dpkg-query', '-W', '-f', '${PackageSpec}:${Status}\n'] + list(packages))
if rc == 0 and not stderr:
return 0
print stderr
return 1
def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument('--quick-check', action='store_true',
help='quickly try to determine if dependencies are '
'installed (this avoids interactive prompts and '
'sudo commands so might not be 100% accurate)')
parser.add_argument('--unsupported', action='store_true',
help='attempt installation even on unsupported systems')
args = parser.parse_args(argv)
lsb_codename = lsb_release_short_codename()
if not args.unsupported and not args.quick_check:
if lsb_codename not in map(
operator.itemgetter('codename'), SUPPORTED_UBUNTU_VERSIONS):
supported_ubuntus = ['%(number)s (%(codename)s)' % v
for v in SUPPORTED_UBUNTU_VERSIONS]
write_error('Only Ubuntu %s are currently supported.' %
', '.join(supported_ubuntus))
return 1
if platform.machine() not in ('i686', 'x86_64'):
write_error('Only x86 architectures are currently supported.')
return 1
if os.geteuid() != 0 and not args.quick_check:
print 'Running as non-root user.'
print 'You might have to enter your password one or more times'
print 'for \'sudo\'.'
print
compute_dynamic_package_lists()
packages = (_packages_dev + _packages_lib + _packages_dbg + _packages_lib32 +
_packages_arm + _packages_nacl)
def packages_key(pkg):
s = pkg.rsplit(':', 1)
if len(s) == 1:
return (s, '')
return s
packages = sorted(set(packages), key=packages_key)
if args.quick_check:
return quick_check(packages)
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))