/ 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")