blob: bdefeac31d13263da69e8169bf9dac43e27179fb [file]
#!/usr/bin/env python3
# Copyright 2012 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Diagnose some common system configuration problems on Linux and macOS, and
suggest fixes."""
from __future__ import print_function
import os
import subprocess
import sys
import socket
from typing import List, Optional, Callable, Tuple, Any
CheckCallable = Callable[[], Optional[str]]
all_checks: List[Tuple[str, CheckCallable]] = []
def Check(
name: str,
platforms: Optional[List[str]] = None
) -> Callable[[CheckCallable], CheckCallable]:
"""Decorator that defines a diagnostic check."""
def wrap(func):
if not platforms or sys.platform in platforms:
all_checks.append((name, func))
return func
return wrap
@Check('/usr/bin/ld is not gold', platforms=['linux'])
def CheckSystemLd() -> Optional[str]:
proc = subprocess.Popen(['/usr/bin/ld', '-v'], stdout=subprocess.PIPE)
stdout = proc.communicate()[0].decode('utf-8')
if 'GNU gold' in stdout:
return ('When /usr/bin/ld is gold, system updates can silently\n'
'corrupt your graphics drivers.\n'
'Try \'sudo apt-get remove binutils-gold\'.\n')
return None
@Check('random lds are not in the $PATH', platforms=['linux'])
def CheckPathLd() -> Optional[str]:
proc = subprocess.Popen(['which', '-a', 'ld'], stdout=subprocess.PIPE)
stdout = proc.communicate()[0].decode('utf-8')
instances = stdout.split()
if len(instances) > 1:
return ('You have multiple \'ld\' binaries in your $PATH:\n' +
'\n'.join(' - ' + i for i in instances) + '\n'
'You should delete all of them but your system one.\n'
'gold is hooked into your build via depot tools.\n')
return None
@Check('/usr/bin/ld doesn\'t point to gold', platforms=['linux'])
def CheckLocalGold() -> Optional[str]:
# Check /usr/bin/ld* symlinks.
for path in ('ld.bfd', 'ld'):
path = '/usr/bin/' + path
try:
target = os.readlink(path)
except OSError as e:
if e.errno == 2:
continue # No such file
if e.errno == 22:
continue # Not a symlink
raise
if '/usr/local/gold' in target:
return ('%s is a symlink into /usr/local/gold.\n'
'It\'s difficult to make a recommendation, because you\n'
'probably set this up yourself. But you should make\n'
'/usr/bin/ld be the standard linker, which you likely\n'
'renamed /usr/bin/ld.bfd or something like that.\n' % path)
return None
@Check('random ninja binaries are not in the $PATH')
def CheckPathNinja() -> Optional[str]:
proc = subprocess.Popen(['which', 'ninja'], stdout=subprocess.PIPE)
stdout = proc.communicate()[0].decode('utf-8')
if not 'depot_tools' in stdout:
return ('The ninja binary in your path isn\'t from depot_tools:\n' +
' ' + stdout +
'Remove custom ninjas from your path so that the one\n'
'in depot_tools is used.\n')
return None
@Check('build dependencies are satisfied', platforms=['linux'])
def CheckBuildDeps() -> Optional[str]:
script_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'build',
'install-build-deps.sh')
proc = subprocess.Popen([script_path, '--quick-check'],
stdout=subprocess.PIPE)
stdout = proc.communicate()[0].decode('utf-8')
if 'WARNING' in stdout:
return ('Your build dependencies are out-of-date.\n'
'Run \'' + script_path + '\' to update.')
return None
@Check('can connect to Google Storage')
def CheckNetwork() -> Optional[str]:
# Try both IPv4 and IPv6 to detect broken dual-stack configs.
results = []
for family in (socket.AF_INET, socket.AF_INET6):
try:
s = socket.socket(family, socket.SOCK_STREAM)
s.settimeout(2)
s.connect(('storage.googleapis.com', 443))
s.close()
results.append(True)
except (OSError, socket.timeout):
results.append(False)
if not any(results):
return ('Total network failure: Cannot connect to storage.googleapis.com '
'via IPv4 or IPv6.\n'
'Check your internet connection, proxy settings, or firewall.')
if False in results:
family_name = 'IPv6' if not results[1] else 'IPv4'
return ('Partial network failure: %s connection to storage.googleapis.com '
'timed out.\n'
'This can cause gclient sync and gsutil to hang while they wait '
'for timeouts.\n'
'Check your network routing, try disabling %s, or set '
'G_PREFER_IPV4=1.' % (family_name, family_name))
return None
def RunChecks() -> None:
for name, check in all_checks:
sys.stdout.write('* Checking %s: ' % name)
sys.stdout.flush()
error = check()
if not error:
print('ok')
else:
print('FAIL')
print(error)
if __name__ == '__main__':
RunChecks()