/ checkdeps.py
checkdeps.py
  1  #!/usr/bin/env python
  2  """
  3  Check dependencies and give recommendations about how to satisfy them
  4  
  5  Limitations:
  6  
  7      * Does not detect whether packages are already installed. Solving this requires writing more of a configuration
  8      management system. Or we could switch to an existing one.
  9      * Not fully PEP508 compliant. Not slightly. It makes bold assumptions about the simplicity of the contents of
 10      EXTRAS_REQUIRE. This is fine because most developers do, too.
 11  """
 12  
 13  import os
 14  import sys
 15  from distutils.errors import CompileError
 16  try:
 17      from setuptools.dist import Distribution
 18      from setuptools.extension import Extension
 19      from setuptools.command.build_ext import build_ext
 20      HAVE_SETUPTOOLS = True
 21      # another import from setuptools is in setup.py
 22      from setup import EXTRAS_REQUIRE
 23  except ImportError:
 24      HAVE_SETUPTOOLS = False
 25      EXTRAS_REQUIRE = {}
 26  
 27  from importlib import import_module
 28  
 29  from src.depends import detectOS, PACKAGES, PACKAGE_MANAGER
 30  
 31  
 32  COMPILING = {
 33      "Debian": "build-essential libssl-dev",
 34      "Ubuntu": "build-essential libssl-dev",
 35      "Fedora": "gcc-c++ redhat-rpm-config python-devel openssl-devel",
 36      "openSUSE": "gcc-c++ libopenssl-devel python-devel",
 37      "optional": False,
 38  }
 39  
 40  # OS-specific dependencies for optional components listed in EXTRAS_REQUIRE
 41  EXTRAS_REQUIRE_DEPS = {
 42      # The values from setup.EXTRAS_REQUIRE
 43      'python_prctl': {
 44          # The packages needed for this requirement, by OS
 45          "OpenBSD": [""],
 46          "FreeBSD": [""],
 47          "Debian": ["libcap-dev python-prctl"],
 48          "Ubuntu": ["libcap-dev python-prctl"],
 49          "Ubuntu 12": ["libcap-dev python-prctl"],
 50          "Ubuntu 20": [""],
 51          "openSUSE": [""],
 52          "Fedora": ["prctl"],
 53          "Guix": [""],
 54          "Gentoo": ["dev-python/python-prctl"],
 55      },
 56  }
 57  
 58  
 59  def detectPrereqs(missing=True):
 60      available = []
 61      for module in PACKAGES:
 62          try:
 63              import_module(module)
 64              if not missing:
 65                  available.append(module)
 66          except ImportError:
 67              if missing:
 68                  available.append(module)
 69      return available
 70  
 71  
 72  def prereqToPackages():
 73      if not detectPrereqs():
 74          return
 75      print(("%s %s" % (
 76          PACKAGE_MANAGER[detectOS()], " ".join(
 77              PACKAGES[x][detectOS()] for x in detectPrereqs()))))
 78  
 79  
 80  def compilerToPackages():
 81      if not detectOS() in COMPILING:
 82          return
 83      print(("%s %s" % (
 84          PACKAGE_MANAGER[detectOS.result], COMPILING[detectOS.result])))
 85  
 86  
 87  def testCompiler():
 88      if not HAVE_SETUPTOOLS:
 89          # silent, we can't test without setuptools
 90          return True
 91  
 92      bitmsghash = Extension(
 93          'bitmsghash',
 94          sources=['src/bitmsghash/bitmsghash.cpp'],
 95          libraries=['pthread', 'crypto'],
 96      )
 97  
 98      dist = Distribution()
 99      dist.ext_modules = [bitmsghash]
100      cmd = build_ext(dist)
101      cmd.initialize_options()
102      cmd.finalize_options()
103      cmd.force = True
104      try:
105          cmd.run()
106      except CompileError:
107          return False
108      else:
109          fullPath = os.path.join(cmd.build_lib, cmd.get_ext_filename("bitmsghash"))
110          return os.path.isfile(fullPath)
111  
112  
113  prereqs = detectPrereqs()
114  compiler = testCompiler()
115  
116  if (not compiler or prereqs) and detectOS() in PACKAGE_MANAGER:
117      print((
118          "It looks like you're using %s. "
119          "It is highly recommended to use the package manager\n"
120          "to install the missing dependencies." % detectOS.result))
121  
122  if not compiler:
123      print(
124          "Building the bitmsghash module failed.\n"
125          "You may be missing a C++ compiler and/or the OpenSSL headers.")
126  
127  if prereqs:
128      mandatory = [x for x in prereqs if not PACKAGES[x].get("optional")]
129      optional = [x for x in prereqs if PACKAGES[x].get("optional")]
130      if mandatory:
131          print(("Missing mandatory dependencies: %s" % " ".join(mandatory)))
132      if optional:
133          print(("Missing optional dependencies: %s" % " ".join(optional)))
134          for package in optional:
135              print((PACKAGES[package].get('description')))
136  
137  # Install the system dependencies of optional extras_require components
138  OPSYS = detectOS()
139  CMD = PACKAGE_MANAGER[OPSYS] if OPSYS in PACKAGE_MANAGER else 'UNKNOWN_INSTALLER'
140  for lhs, rhs in list(EXTRAS_REQUIRE.items()):
141      if OPSYS is None:
142          break
143      if rhs and any([
144          EXTRAS_REQUIRE_DEPS[x][OPSYS]
145          for x in rhs
146          if x in EXTRAS_REQUIRE_DEPS
147      ]):
148          try:
149              import_module(lhs)
150          except Exception as e:
151              rhs_cmd = ''.join([
152                  CMD,
153                  ' ',
154                  ' '.join([
155                      ''. join([
156                          xx for xx in EXTRAS_REQUIRE_DEPS[x][OPSYS]
157                      ])
158                      for x in rhs
159                      if x in EXTRAS_REQUIRE_DEPS
160                  ]),
161              ])
162              print((
163                  "Optional dependency `pip install .[{}]` would require `{}`"
164                  " to be run as root".format(lhs, rhs_cmd)))
165  
166  if detectOS.result == "Ubuntu 20":
167      print((
168          "Qt interface isn't supported in %s" % detectOS.result))
169  
170  if (not compiler or prereqs) and OPSYS in PACKAGE_MANAGER:
171      print("You can install the missing dependencies by running, as root:")
172      if not compiler:
173          compilerToPackages()
174      prereqToPackages()
175      if prereqs and mandatory:
176          sys.exit(1)
177  else:
178      print("All the dependencies satisfied, you can install PyBitmessage")