Reused depends in checkdeps script and formatted it also,

made checkdeps executable instead of depends
This commit is contained in:
Dmitri Bogomolov 2018-05-22 14:16:13 +03:00
parent 701521c55a
commit 9f8955ccdd
Signed by untrusted user: g1itch
GPG Key ID: 720A756F18DEED13
2 changed files with 232 additions and 276 deletions

203
checkdeps.py Normal file → Executable file
View File

@ -1,3 +1,4 @@
#!/usr/bin/env python2
""" """
Check dependendies and give recommendations about how to satisfy them Check dependendies and give recommendations about how to satisfy them
@ -9,112 +10,24 @@ Limitations:
EXTRAS_REQUIRE. This is fine because most developers do, too. EXTRAS_REQUIRE. This is fine because most developers do, too.
""" """
import os
from distutils.errors import CompileError from distutils.errors import CompileError
try: try:
from setuptools.dist import Distribution from setuptools.dist import Distribution
from setuptools.extension import Extension from setuptools.extension import Extension
from setuptools.command.build_ext import build_ext from setuptools.command.build_ext import build_ext
HAVE_SETUPTOOLS = True HAVE_SETUPTOOLS = True
# another import from setuptools is in setup.py
from setup import EXTRAS_REQUIRE
except ImportError: except ImportError:
HAVE_SETUPTOOLS = False HAVE_SETUPTOOLS = False
EXTRAS_REQUIRE = []
from importlib import import_module from importlib import import_module
import os
import sys
from setup import EXTRAS_REQUIRE from src.depends import detectOS, PACKAGES, PACKAGE_MANAGER
PROJECT_ROOT = os.path.abspath('..')
sys.path.insert(0, PROJECT_ROOT)
# OS-specific dependencies for optional components listed in EXTRAS_REQUIRE
EXTRAS_REQUIRE_DEPS = {
# The values from setup.EXTRAS_REQUIRE
'python_prctl': {
# The packages needed for this requirement, by OS
"OpenBSD": [""],
"FreeBSD": [""],
"Debian": ["libcap-dev"],
"Ubuntu": [""],
"Ubuntu 12": [""],
"openSUSE": [""],
"Fedora": [""],
"Guix": [""],
"Gentoo": [""],
},
}
PACKAGE_MANAGER = {
"OpenBSD": "pkg_add",
"FreeBSD": "pkg install",
"Debian": "apt-get install",
"Ubuntu": "apt-get install",
"Ubuntu 12": "apt-get install",
"openSUSE": "zypper install",
"Fedora": "dnf install",
"Guix": "guix package -i",
"Gentoo": "emerge"
}
PACKAGES = {
"PyQt4": {
"OpenBSD": "py-qt4",
"FreeBSD": "py27-qt4",
"Debian": "python-qt4",
"Ubuntu": "python-qt4",
"Ubuntu 12": "python-qt4",
"openSUSE": "python-qt",
"Fedora": "PyQt4",
"Guix": "python2-pyqt@4.11.4",
"Gentoo": "dev-python/PyQt4",
'optional': True,
'description': "You only need PyQt if you want to use the GUI. " \
"When only running as a daemon, this can be skipped.\n" \
"However, you would have to install it manually " \
"because setuptools does not support PyQt."
},
"msgpack": {
"OpenBSD": "py-msgpack",
"FreeBSD": "py27-msgpack-python",
"Debian": "python-msgpack",
"Ubuntu": "python-msgpack",
"Ubuntu 12": "msgpack-python",
"openSUSE": "python-msgpack-python",
"Fedora": "python2-msgpack",
"Guix": "python2-msgpack",
"Gentoo": "dev-python/msgpack",
"optional": True,
"description": "python-msgpack is recommended for improved performance of message encoding/decoding"
},
"pyopencl": {
"FreeBSD": "py27-pyopencl",
"Debian": "python-pyopencl",
"Ubuntu": "python-pyopencl",
"Ubuntu 12": "python-pyopencl",
"Fedora": "python2-pyopencl",
"openSUSE": "",
"OpenBSD": "",
"Guix": "",
"Gentoo": "dev-python/pyopencl",
"optional": True,
'description': "If you install pyopencl, you will be able to use " \
"GPU acceleration for proof of work. \n" \
"You also need a compatible GPU and drivers."
},
"setuptools": {
"OpenBSD": "py-setuptools",
"FreeBSD": "py27-setuptools",
"Debian": "python-setuptools",
"Ubuntu": "python-setuptools",
"Ubuntu 12": "python-setuptools",
"Fedora": "python2-setuptools",
"openSUSE": "python-setuptools",
"Guix": "python2-setuptools",
"Gentoo": "",
"optional": False,
}
}
COMPILING = { COMPILING = {
"Debian": "build-essential libssl-dev", "Debian": "build-essential libssl-dev",
"Ubuntu": "build-essential libssl-dev", "Ubuntu": "build-essential libssl-dev",
@ -123,46 +36,23 @@ COMPILING = {
"optional": False, "optional": False,
} }
def detectOSRelease(): # OS-specific dependencies for optional components listed in EXTRAS_REQUIRE
with open("/etc/os-release", 'r') as osRelease: EXTRAS_REQUIRE_DEPS = {
version = None # The values from setup.EXTRAS_REQUIRE
for line in osRelease: 'python_prctl': {
if line.startswith("NAME="): # The packages needed for this requirement, by OS
line = line.lower() "OpenBSD": [""],
if "fedora" in line: "FreeBSD": [""],
detectOS.result = "Fedora" "Debian": ["libcap-dev python-prctl"],
elif "opensuse" in line: "Ubuntu": ["libcap-dev python-prctl"],
detectOS.result = "openSUSE" "Ubuntu 12": ["libcap-dev python-prctl"],
elif "ubuntu" in line: "openSUSE": [""],
detectOS.result = "Ubuntu" "Fedora": ["prctl"],
elif "debian" in line: "Guix": [""],
detectOS.result = "Debian" "Gentoo": ["dev-python/python-prctl"],
elif "gentoo" in line or "calculate" in line: },
detectOS.result = "Gentoo" }
else:
detectOS.result = None
if line.startswith("VERSION_ID="):
try:
version = float(line.split("=")[1].replace("\"", ""))
except ValueError:
pass
if detectOS.result == "Ubuntu" and version < 14:
detectOS.result = "Ubuntu 12"
def detectOS():
if detectOS.result is not None:
return detectOS.result
if sys.platform.startswith('openbsd'):
detectOS.result = "OpenBSD"
elif sys.platform.startswith('freebsd'):
detectOS.result = "FreeBSD"
elif sys.platform.startswith('win'):
detectOS.result = "Windows"
elif os.path.isfile("/etc/os-release"):
detectOSRelease()
elif os.path.isfile("/etc/config.scm"):
detectOS.result = "Guix"
return detectOS.result
def detectPrereqs(missing=True): def detectPrereqs(missing=True):
available = [] available = []
@ -176,18 +66,21 @@ def detectPrereqs(missing=True):
available.append(module) available.append(module)
return available return available
def prereqToPackages(): def prereqToPackages():
if not detectPrereqs(): if not detectPrereqs():
return return
print "%s %s" % ( print("%s %s" % (
PACKAGE_MANAGER[detectOS()], " ".join( PACKAGE_MANAGER[detectOS()], " ".join(
PACKAGES[x][detectOS()] for x in detectPrereqs())) PACKAGES[x][detectOS()] for x in detectPrereqs())))
def compilerToPackages(): def compilerToPackages():
if not detectOS() in COMPILING: if not detectOS() in COMPILING:
return return
print "%s %s" % ( print("%s %s" % (
PACKAGE_MANAGER[detectOS.result], COMPILING[detectOS.result]) PACKAGE_MANAGER[detectOS.result], COMPILING[detectOS.result]))
def testCompiler(): def testCompiler():
if not HAVE_SETUPTOOLS: if not HAVE_SETUPTOOLS:
@ -214,30 +107,30 @@ def testCompiler():
fullPath = os.path.join(cmd.build_lib, cmd.get_ext_filename("bitmsghash")) fullPath = os.path.join(cmd.build_lib, cmd.get_ext_filename("bitmsghash"))
return os.path.isfile(fullPath) return os.path.isfile(fullPath)
detectOS.result = None
prereqs = detectPrereqs()
prereqs = detectPrereqs()
compiler = testCompiler() compiler = testCompiler()
if (not compiler or prereqs) and detectOS() in PACKAGE_MANAGER: if (not compiler or prereqs) and detectOS() in PACKAGE_MANAGER:
print "It looks like you're using %s. " \ print(
"It is highly recommended to use the package manager\n" \ "It looks like you're using %s. "
"to install the missing dependencies." % (detectOS.result) "It is highly recommended to use the package manager\n"
"to install the missing dependencies." % detectOS.result)
if not compiler: if not compiler:
print "Building the bitmsghash module failed.\n" \ print(
"You may be missing a C++ compiler and/or the OpenSSL headers." "Building the bitmsghash module failed.\n"
"You may be missing a C++ compiler and/or the OpenSSL headers.")
if prereqs: if prereqs:
mandatory = list(x for x in prereqs if "optional" not in PACKAGES[x] or not PACKAGES[x]["optional"]) mandatory = [x for x in prereqs if not PACKAGES[x].get("optional")]
optional = list(x for x in prereqs if "optional" in PACKAGES[x] and PACKAGES[x]["optional"]) optional = [x for x in prereqs if PACKAGES[x].get("optional")]
if mandatory: if mandatory:
print "Missing mandatory dependencies: %s" % (" ".join(mandatory)) print("Missing mandatory dependencies: %s" % " ".join(mandatory))
if optional: if optional:
print "Missing optional dependencies: %s" % (" ".join(optional)) print("Missing optional dependencies: %s" % " ".join(optional))
for package in optional: for package in optional:
print PACKAGES[package].get('description') print(PACKAGES[package].get('description'))
# Install the system dependencies of optional extras_require components # Install the system dependencies of optional extras_require components
OPSYS = detectOS() OPSYS = detectOS()
@ -259,12 +152,14 @@ for lhs, rhs in EXTRAS_REQUIRE.items():
if x in EXTRAS_REQUIRE_DEPS if x in EXTRAS_REQUIRE_DEPS
]), ]),
]) ])
print "Optional dependency `pip install .[{}]` would require `{}` to be run as root".format(lhs, rhs_cmd) print(
"Optional dependency `pip install .[{}]` would require `{}`"
" to be run as root".format(lhs, rhs_cmd))
if (not compiler or prereqs) and detectOS() in PACKAGE_MANAGER: if (not compiler or prereqs) and OPSYS in PACKAGE_MANAGER:
print "You can install the missing dependencies by running, as root:" print("You can install the missing dependencies by running, as root:")
if not compiler: if not compiler:
compilerToPackages() compilerToPackages()
prereqToPackages() prereqToPackages()
else: else:
print "All the dependencies satisfied, you can install PyBitmessage" print("All the dependencies satisfied, you can install PyBitmessage")

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python2 """
Utility functions to check the availability of dependencies
and suggest how it may be installed
"""
import os
import pyelliptic.openssl
import subprocess
import sys import sys
# Only really old versions of Python don't have sys.hexversion. We don't # Only really old versions of Python don't have sys.hexversion. We don't
@ -14,22 +14,160 @@ if not hasattr(sys, 'hexversion') or sys.hexversion < 0x20300F0:
% sys.version % sys.version
) )
# We can now use logging so set up a simple configuration
import logging import logging
import os
from importlib import import_module
# We can now use logging so set up a simple configuration
formatter = logging.Formatter( formatter = logging.Formatter(
'%(levelname)s: %(message)s' '%(levelname)s: %(message)s'
) )
handler = logging.StreamHandler(sys.stdout) handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(formatter) handler.setFormatter(formatter)
logger = logging.getLogger(__name__) logger = logging.getLogger('both')
logger.addHandler(handler) logger.addHandler(handler)
logger.setLevel(logging.ERROR) logger.setLevel(logging.ERROR)
PACKAGE_MANAGER = {
"OpenBSD": "pkg_add",
"FreeBSD": "pkg install",
"Debian": "apt-get install",
"Ubuntu": "apt-get install",
"Ubuntu 12": "apt-get install",
"openSUSE": "zypper install",
"Fedora": "dnf install",
"Guix": "guix package -i",
"Gentoo": "emerge"
}
PACKAGES = {
"PyQt4": {
"OpenBSD": "py-qt4",
"FreeBSD": "py27-qt4",
"Debian": "python-qt4",
"Ubuntu": "python-qt4",
"Ubuntu 12": "python-qt4",
"openSUSE": "python-qt",
"Fedora": "PyQt4",
"Guix": "python2-pyqt@4.11.4",
"Gentoo": "dev-python/PyQt4",
"optional": True,
"description":
"You only need PyQt if you want to use the GUI."
" When only running as a daemon, this can be skipped.\n"
"However, you would have to install it manually"
" because setuptools does not support PyQt."
},
"msgpack": {
"OpenBSD": "py-msgpack",
"FreeBSD": "py27-msgpack-python",
"Debian": "python-msgpack",
"Ubuntu": "python-msgpack",
"Ubuntu 12": "msgpack-python",
"openSUSE": "python-msgpack-python",
"Fedora": "python2-msgpack",
"Guix": "python2-msgpack",
"Gentoo": "dev-python/msgpack",
"optional": True,
"description":
"python-msgpack is recommended for improved performance of"
" message encoding/decoding"
},
"pyopencl": {
"FreeBSD": "py27-pyopencl",
"Debian": "python-pyopencl",
"Ubuntu": "python-pyopencl",
"Ubuntu 12": "python-pyopencl",
"Fedora": "python2-pyopencl",
"openSUSE": "",
"OpenBSD": "",
"Guix": "",
"Gentoo": "dev-python/pyopencl",
"optional": True,
"description":
"If you install pyopencl, you will be able to use"
" GPU acceleration for proof of work.\n"
"You also need a compatible GPU and drivers."
},
"setuptools": {
"OpenBSD": "py-setuptools",
"FreeBSD": "py27-setuptools",
"Debian": "python-setuptools",
"Ubuntu": "python-setuptools",
"Ubuntu 12": "python-setuptools",
"Fedora": "python2-setuptools",
"openSUSE": "python-setuptools",
"Guix": "python2-setuptools",
"Gentoo": "dev-python/setuptools",
"optional": False,
}
}
def detectOS():
if detectOS.result is not None:
return detectOS.result
if sys.platform.startswith('openbsd'):
detectOS.result = "OpenBSD"
elif sys.platform.startswith('freebsd'):
detectOS.result = "FreeBSD"
elif sys.platform.startswith('win'):
detectOS.result = "Windows"
elif os.path.isfile("/etc/os-release"):
detectOSRelease()
elif os.path.isfile("/etc/config.scm"):
detectOS.result = "Guix"
return detectOS.result
detectOS.result = None
def detectOSRelease():
with open("/etc/os-release", 'r') as osRelease:
version = None
for line in osRelease:
if line.startswith("NAME="):
line = line.lower()
if "fedora" in line:
detectOS.result = "Fedora"
elif "opensuse" in line:
detectOS.result = "openSUSE"
elif "ubuntu" in line:
detectOS.result = "Ubuntu"
elif "debian" in line:
detectOS.result = "Debian"
elif "gentoo" in line or "calculate" in line:
detectOS.result = "Gentoo"
else:
detectOS.result = None
if line.startswith("VERSION_ID="):
try:
version = float(line.split("=")[1].replace("\"", ""))
except ValueError:
pass
if detectOS.result == "Ubuntu" and version < 14:
detectOS.result = "Ubuntu 12"
def try_import(module, log_extra=False):
try:
return import_module(module)
except ImportError:
module = module.split('.')[0]
logger.error('The %s module is not available.', module)
if log_extra:
logger.error(log_extra)
dist = detectOS()
logger.error(
'On %s, try running "%s %s" as root.',
dist, PACKAGE_MANAGER[dist], PACKAGES[module][dist])
return False
# We need to check hashlib for RIPEMD-160, as it won't be available # We need to check hashlib for RIPEMD-160, as it won't be available
# if OpenSSL is not linked against or the linked OpenSSL has RIPEMD # if OpenSSL is not linked against or the linked OpenSSL has RIPEMD
# disabled. # disabled.
def check_hashlib(): def check_hashlib():
"""Do hashlib check. """Do hashlib check.
@ -73,19 +211,18 @@ def check_sqlite():
logger.error( logger.error(
'On FreeBSD, try running "pkg install py27-sqlite3" as root.') 'On FreeBSD, try running "pkg install py27-sqlite3" as root.')
return False return False
try:
import sqlite3 sqlite3 = try_import('sqlite3')
except ImportError: if not sqlite3:
logger.error('The sqlite3 module is not available')
return False return False
logger.info('sqlite3 Module Version: %s', sqlite3.version) logger.info('sqlite3 Module Version: %s', sqlite3.version)
logger.info('SQLite Library Version: %s', sqlite3.sqlite_version) logger.info('SQLite Library Version: %s', sqlite3.sqlite_version)
# sqlite_version_number formula: https://sqlite.org/c3ref/c_source_id.html # sqlite_version_number formula: https://sqlite.org/c3ref/c_source_id.html
sqlite_version_number = ( sqlite_version_number = (
sqlite3.sqlite_version_info[0] * 1000000 sqlite3.sqlite_version_info[0] * 1000000 +
+ sqlite3.sqlite_version_info[1] * 1000 sqlite3.sqlite_version_info[1] * 1000 +
+ sqlite3.sqlite_version_info[2] sqlite3.sqlite_version_info[2]
) )
conn = None conn = None
@ -126,11 +263,10 @@ def check_openssl():
Here we are checking for openssl with its all dependent libraries Here we are checking for openssl with its all dependent libraries
and version checking. and version checking.
""" """
try:
import ctypes ctypes = try_import('ctypes')
except ImportError: if not ctypes:
logger.error( logger.error('Unable to check OpenSSL.')
'Unable to check OpenSSL. The ctypes module is not available.')
return False return False
# We need to emulate the way PyElliptic searches for OpenSSL. # We need to emulate the way PyElliptic searches for OpenSSL.
@ -163,6 +299,8 @@ def check_openssl():
cflags_regex = re.compile(r'(?:OPENSSL_NO_)(AES|EC|ECDH|ECDSA)(?!\w)') cflags_regex = re.compile(r'(?:OPENSSL_NO_)(AES|EC|ECDH|ECDSA)(?!\w)')
import pyelliptic.openssl
for path in paths: for path in paths:
logger.info('Checking OpenSSL at %s', path) logger.info('Checking OpenSSL at %s', path)
try: try:
@ -213,21 +351,20 @@ def check_curses():
'The curses interface requires the pythondialog package and' 'The curses interface requires the pythondialog package and'
' the dialog utility.') ' the dialog utility.')
return False return False
try: curses = try_import('curses')
import curses if not curses:
except ImportError: logger.error('The curses interface can not be used.')
logger.error(
'The curses interface can not be used. The curses module'
' is not available.')
return False return False
logger.info('curses Module Version: %s', curses.version) logger.info('curses Module Version: %s', curses.version)
try:
import dialog dialog = try_import('dialog')
except ImportError: if not dialog:
logger.error( logger.error('The curses interface can not be used.')
'The curses interface can not be used. The pythondialog'
' package is not available.')
return False return False
import subprocess
try: try:
subprocess.check_call('which dialog') subprocess.check_call('which dialog')
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
@ -249,54 +386,23 @@ def check_pyqt():
"""Do pyqt dependency check. """Do pyqt dependency check.
Here we are checking for PyQt4 with its version, as for it require Here we are checking for PyQt4 with its version, as for it require
PyQt 4.7 or later. PyQt 4.8 or later.
""" """
try: QtCore = try_import(
import PyQt4.QtCore 'PyQt4.QtCore', 'PyBitmessage requires PyQt 4.8 or later and Qt 4.7 or later.')
except ImportError:
logger.error( if not QtCore:
'The PyQt4 package is not available. PyBitmessage requires'
' PyQt 4.8 or later and Qt 4.7 or later.')
if sys.platform.startswith('openbsd'):
logger.error('On OpenBSD, try running "pkg_add py-qt4" as root.')
elif sys.platform.startswith('freebsd'):
logger.error(
'On FreeBSD, try running "pkg install py27-qt4" as root.')
elif os.path.isfile("/etc/os-release"):
with open("/etc/os-release", 'rt') as osRelease:
for line in osRelease:
if line.startswith("NAME="):
if "fedora" in line.lower():
logger.error(
'On Fedora, try running'
' "dnf install PyQt4" as root.')
elif "opensuse" in line.lower():
logger.error(
'On openSUSE, try running'
' "zypper install python-qt" as root.')
elif "ubuntu" in line.lower():
logger.error(
'On Ubuntu, try running'
' "apt-get install python-qt4" as root.')
elif "debian" in line.lower():
logger.error(
'On Debian, try running'
' "apt-get install python-qt4" as root.')
else:
logger.error(
'If your package manager does not have'
' this package, try running'
' "pip install PyQt4".')
return False return False
logger.info('PyQt Version: %s', PyQt4.QtCore.PYQT_VERSION_STR)
logger.info('Qt Version: %s', PyQt4.QtCore.QT_VERSION_STR) logger.info('PyQt Version: %s', QtCore.PYQT_VERSION_STR)
logger.info('Qt Version: %s', QtCore.QT_VERSION_STR)
passed = True passed = True
if PyQt4.QtCore.PYQT_VERSION < 0x40800: if QtCore.PYQT_VERSION < 0x40800:
logger.error( logger.error(
'This version of PyQt is too old. PyBitmessage requries' 'This version of PyQt is too old. PyBitmessage requries'
' PyQt 4.8 or later.') ' PyQt 4.8 or later.')
passed = False passed = False
if PyQt4.QtCore.QT_VERSION < 0x40700: if QtCore.QT_VERSION < 0x40700:
logger.error( logger.error(
'This version of Qt is too old. PyBitmessage requries' 'This version of Qt is too old. PyBitmessage requries'
' Qt 4.7 or later.') ' Qt 4.7 or later.')
@ -310,47 +416,8 @@ def check_msgpack():
simply checking if msgpack package with all its dependency simply checking if msgpack package with all its dependency
is available or not as recommended for messages coding. is available or not as recommended for messages coding.
""" """
try: return try_import(
import msgpack 'msgpack', 'It is highly recommended for messages coding.') is not False
except ImportError:
logger.error(
'The msgpack package is not available.'
'It is highly recommended for messages coding.')
if sys.platform.startswith('openbsd'):
logger.error(
'On OpenBSD, try running "pkg_add py-msgpack" as root.')
elif sys.platform.startswith('freebsd'):
logger.error(
'On FreeBSD, try running "pkg install py27-msgpack-python"'
' as root.')
elif os.path.isfile("/etc/os-release"):
with open("/etc/os-release", 'rt') as osRelease:
for line in osRelease:
if line.startswith("NAME="):
if "fedora" in line.lower():
logger.error(
'On Fedora, try running'
' "dnf install python2-msgpack" as root.')
elif "opensuse" in line.lower():
logger.error(
'On openSUSE, try running'
' "zypper install python-msgpack-python"'
' as root.')
elif "ubuntu" in line.lower():
logger.error(
'On Ubuntu, try running'
' "apt-get install python-msgpack" as root.')
elif "debian" in line.lower():
logger.error(
'On Debian, try running'
' "apt-get install python-msgpack" as root.')
else:
logger.error(
'If your package manager does not have'
' this package, try running'
' "pip install msgpack-python".')
return True
def check_dependencies(verbose=False, optional=False): def check_dependencies(verbose=False, optional=False):
@ -381,10 +448,9 @@ def check_dependencies(verbose=False, optional=False):
' or greater is required.') ' or greater is required.')
has_all_dependencies = False has_all_dependencies = False
check_functions = [ check_functions = [check_hashlib, check_sqlite, check_openssl]
check_hashlib, check_sqlite, check_openssl, check_msgpack]
if optional: if optional:
check_functions.extend([check_pyqt, check_curses]) check_functions.extend([check_msgpack, check_pyqt, check_curses])
# Unexpected exceptions are handled here # Unexpected exceptions are handled here
for check in check_functions: for check in check_functions:
@ -399,8 +465,3 @@ def check_dependencies(verbose=False, optional=False):
'PyBitmessage cannot start. One or more dependencies are' 'PyBitmessage cannot start. One or more dependencies are'
' unavailable.' ' unavailable.'
) )
if __name__ == '__main__':
"""Check Dependencies"""
check_dependencies(True, True)