1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li# Please keep this code python 2.4 compatible and stand alone. 3*9c5db199SXin Li 4*9c5db199SXin Lifrom __future__ import absolute_import 5*9c5db199SXin Lifrom __future__ import division 6*9c5db199SXin Lifrom __future__ import print_function 7*9c5db199SXin Li 8*9c5db199SXin Liimport logging, os, shutil, sys, tempfile, time 9*9c5db199SXin Lifrom six.moves import urllib 10*9c5db199SXin Liimport subprocess, re 11*9c5db199SXin Lifrom distutils.version import LooseVersion 12*9c5db199SXin Li 13*9c5db199SXin Lifrom autotest_lib.client.common_lib import autotemp, revision_control, utils 14*9c5db199SXin Liimport six 15*9c5db199SXin Li 16*9c5db199SXin Li_READ_SIZE = 64*1024 17*9c5db199SXin Li_MAX_PACKAGE_SIZE = 100*1024*1024 18*9c5db199SXin Li_CHROMEOS_MIRROR = ('http://commondatastorage.googleapis.com/' 19*9c5db199SXin Li 'chromeos-mirror/gentoo/distfiles/') 20*9c5db199SXin Li 21*9c5db199SXin Li 22*9c5db199SXin Liclass Error(Exception): 23*9c5db199SXin Li """Local exception to be raised by code in this file.""" 24*9c5db199SXin Li 25*9c5db199SXin Liclass FetchError(Error): 26*9c5db199SXin Li """Failed to fetch a package from any of its listed URLs.""" 27*9c5db199SXin Li 28*9c5db199SXin Li 29*9c5db199SXin Lidef _checksum_file(full_path): 30*9c5db199SXin Li """@returns The hex checksum of a file given its pathname.""" 31*9c5db199SXin Li inputfile = open(full_path, 'rb') 32*9c5db199SXin Li try: 33*9c5db199SXin Li hex_sum = utils.hash('sha1', inputfile.read()).hexdigest() 34*9c5db199SXin Li finally: 35*9c5db199SXin Li inputfile.close() 36*9c5db199SXin Li return hex_sum 37*9c5db199SXin Li 38*9c5db199SXin Li 39*9c5db199SXin Lidef system(commandline): 40*9c5db199SXin Li """Same as os.system(commandline) but logs the command first. 41*9c5db199SXin Li 42*9c5db199SXin Li @param commandline: commandline to be called. 43*9c5db199SXin Li """ 44*9c5db199SXin Li logging.info(commandline) 45*9c5db199SXin Li return os.system(commandline) 46*9c5db199SXin Li 47*9c5db199SXin Li 48*9c5db199SXin Lidef find_top_of_autotest_tree(): 49*9c5db199SXin Li """@returns The full path to the top of the autotest directory tree.""" 50*9c5db199SXin Li dirname = os.path.dirname(__file__) 51*9c5db199SXin Li autotest_dir = os.path.abspath(os.path.join(dirname, '..')) 52*9c5db199SXin Li return autotest_dir 53*9c5db199SXin Li 54*9c5db199SXin Li 55*9c5db199SXin Liclass ExternalPackage(object): 56*9c5db199SXin Li """ 57*9c5db199SXin Li Defines an external package with URLs to fetch its sources from and 58*9c5db199SXin Li a build_and_install() method to unpack it, build it and install it 59*9c5db199SXin Li beneath our own autotest/site-packages directory. 60*9c5db199SXin Li 61*9c5db199SXin Li Base Class. Subclass this to define packages. 62*9c5db199SXin Li Note: Unless your subclass has a specific reason to, it should not 63*9c5db199SXin Li re-install the package every time build_externals is invoked, as this 64*9c5db199SXin Li happens periodically through the scheduler. To avoid doing so the is_needed 65*9c5db199SXin Li method needs to return an appropriate value. 66*9c5db199SXin Li 67*9c5db199SXin Li Attributes: 68*9c5db199SXin Li @attribute urls - A tuple of URLs to try fetching the package from. 69*9c5db199SXin Li @attribute local_filename - A local filename to use when saving the 70*9c5db199SXin Li fetched package. 71*9c5db199SXin Li @attribute dist_name - The name of the Python distribution. For example, 72*9c5db199SXin Li the package MySQLdb is included in the distribution named 73*9c5db199SXin Li MySQL-python. This is generally the PyPI name. Defaults to the 74*9c5db199SXin Li name part of the local_filename. 75*9c5db199SXin Li @attribute hex_sum - The hex digest (currently SHA1) of this package 76*9c5db199SXin Li to be used to verify its contents. 77*9c5db199SXin Li @attribute module_name - The installed python module name to be used for 78*9c5db199SXin Li for a version check. Defaults to the lower case class name with 79*9c5db199SXin Li the word Package stripped off. 80*9c5db199SXin Li @attribute extracted_package_path - The path to package directory after 81*9c5db199SXin Li extracting. 82*9c5db199SXin Li @attribute version - The desired minimum package version. 83*9c5db199SXin Li @attribute os_requirements - A dictionary mapping pathname tuples on the 84*9c5db199SXin Li the OS distribution to a likely name of a package the user 85*9c5db199SXin Li needs to install on their system in order to get this file. 86*9c5db199SXin Li One of the files in the tuple must exist. 87*9c5db199SXin Li @attribute name - Read only, the printable name of the package. 88*9c5db199SXin Li @attribute subclasses - This class attribute holds a list of all defined 89*9c5db199SXin Li subclasses. It is constructed dynamically using the metaclass. 90*9c5db199SXin Li """ 91*9c5db199SXin Li # Modules that are meant to be installed in system directory, rather than 92*9c5db199SXin Li # autotest/site-packages. These modules should be skipped if the module 93*9c5db199SXin Li # is already installed in system directory. This prevents an older version 94*9c5db199SXin Li # of the module from being installed in system directory. 95*9c5db199SXin Li SYSTEM_MODULES = ['setuptools'] 96*9c5db199SXin Li 97*9c5db199SXin Li subclasses = [] 98*9c5db199SXin Li urls = () 99*9c5db199SXin Li local_filename = None 100*9c5db199SXin Li dist_name = None 101*9c5db199SXin Li hex_sum = None 102*9c5db199SXin Li module_name = None 103*9c5db199SXin Li version = None 104*9c5db199SXin Li os_requirements = None 105*9c5db199SXin Li 106*9c5db199SXin Li 107*9c5db199SXin Li class __metaclass__(type): 108*9c5db199SXin Li """Any time a subclass is defined, add it to our list.""" 109*9c5db199SXin Li def __init__(mcs, name, bases, dict): 110*9c5db199SXin Li if name != 'ExternalPackage' and not name.startswith('_'): 111*9c5db199SXin Li mcs.subclasses.append(mcs) 112*9c5db199SXin Li 113*9c5db199SXin Li 114*9c5db199SXin Li def __init__(self): 115*9c5db199SXin Li self.verified_package = '' 116*9c5db199SXin Li if not self.module_name: 117*9c5db199SXin Li self.module_name = self.name.lower() 118*9c5db199SXin Li if not self.dist_name and self.local_filename: 119*9c5db199SXin Li self.dist_name = self.local_filename[:self.local_filename.rindex('-')] 120*9c5db199SXin Li self.installed_version = '' 121*9c5db199SXin Li 122*9c5db199SXin Li 123*9c5db199SXin Li @property 124*9c5db199SXin Li def extracted_package_path(self): 125*9c5db199SXin Li """Return the package path after extracting. 126*9c5db199SXin Li 127*9c5db199SXin Li If the package has assigned its own extracted_package_path, use it. 128*9c5db199SXin Li Or use part of its local_filename as the extracting path. 129*9c5db199SXin Li """ 130*9c5db199SXin Li return self.local_filename[:-len(self._get_extension( 131*9c5db199SXin Li self.local_filename))] 132*9c5db199SXin Li 133*9c5db199SXin Li 134*9c5db199SXin Li @property 135*9c5db199SXin Li def name(self): 136*9c5db199SXin Li """Return the class name with any trailing 'Package' stripped off.""" 137*9c5db199SXin Li class_name = self.__class__.__name__ 138*9c5db199SXin Li if class_name.endswith('Package'): 139*9c5db199SXin Li return class_name[:-len('Package')] 140*9c5db199SXin Li return class_name 141*9c5db199SXin Li 142*9c5db199SXin Li 143*9c5db199SXin Li def is_needed(self, install_dir): 144*9c5db199SXin Li """ 145*9c5db199SXin Li Check to see if we need to reinstall a package. This is contingent on: 146*9c5db199SXin Li 1. Module name: If the name of the module is different from the package, 147*9c5db199SXin Li the class that installs it needs to specify a module_name string, 148*9c5db199SXin Li so we can try importing the module. 149*9c5db199SXin Li 150*9c5db199SXin Li 2. Installed version: If the module doesn't contain a __version__ the 151*9c5db199SXin Li class that installs it needs to override the 152*9c5db199SXin Li _get_installed_version_from_module method to return an appropriate 153*9c5db199SXin Li version string. 154*9c5db199SXin Li 155*9c5db199SXin Li 3. Version/Minimum version: The class that installs the package should 156*9c5db199SXin Li contain a version string, and an optional minimum version string. 157*9c5db199SXin Li 158*9c5db199SXin Li 4. install_dir: If the module exists in a different directory, e.g., 159*9c5db199SXin Li /usr/lib/python2.7/dist-packages/, the module will be forced to be 160*9c5db199SXin Li installed in install_dir. 161*9c5db199SXin Li 162*9c5db199SXin Li @param install_dir: install directory. 163*9c5db199SXin Li @returns True if self.module_name needs to be built and installed. 164*9c5db199SXin Li """ 165*9c5db199SXin Li if not self.module_name or not self.version: 166*9c5db199SXin Li logging.warning('version and module_name required for ' 167*9c5db199SXin Li 'is_needed() check to work.') 168*9c5db199SXin Li return True 169*9c5db199SXin Li try: 170*9c5db199SXin Li module = __import__(self.module_name) 171*9c5db199SXin Li except ImportError as e: 172*9c5db199SXin Li logging.info("%s isn't present. Will install.", self.module_name) 173*9c5db199SXin Li return True 174*9c5db199SXin Li # Check if we're getting a module installed somewhere else, 175*9c5db199SXin Li # e.g. on the system. 176*9c5db199SXin Li if self.module_name not in self.SYSTEM_MODULES: 177*9c5db199SXin Li if (hasattr(module, '__file__') 178*9c5db199SXin Li and not module.__file__.startswith(install_dir)): 179*9c5db199SXin Li path = module.__file__ 180*9c5db199SXin Li elif (hasattr(module, '__path__') 181*9c5db199SXin Li and module.__path__ 182*9c5db199SXin Li and not module.__path__[0].startswith(install_dir)): 183*9c5db199SXin Li path = module.__path__[0] 184*9c5db199SXin Li else: 185*9c5db199SXin Li logging.warning('module %s has no __file__ or __path__', 186*9c5db199SXin Li self.module_name) 187*9c5db199SXin Li return True 188*9c5db199SXin Li logging.info( 189*9c5db199SXin Li 'Found %s installed in %s, installing our version in %s', 190*9c5db199SXin Li self.module_name, path, install_dir) 191*9c5db199SXin Li return True 192*9c5db199SXin Li self.installed_version = self._get_installed_version_from_module(module) 193*9c5db199SXin Li if not self.installed_version: 194*9c5db199SXin Li return True 195*9c5db199SXin Li 196*9c5db199SXin Li logging.info('imported %s version %s.', self.module_name, 197*9c5db199SXin Li self.installed_version) 198*9c5db199SXin Li if hasattr(self, 'minimum_version'): 199*9c5db199SXin Li return LooseVersion(self.minimum_version) > LooseVersion( 200*9c5db199SXin Li self.installed_version) 201*9c5db199SXin Li else: 202*9c5db199SXin Li return LooseVersion(self.version) > LooseVersion( 203*9c5db199SXin Li self.installed_version) 204*9c5db199SXin Li 205*9c5db199SXin Li 206*9c5db199SXin Li def _get_installed_version_from_module(self, module): 207*9c5db199SXin Li """Ask our module its version string and return it or '' if unknown.""" 208*9c5db199SXin Li try: 209*9c5db199SXin Li return module.__version__ 210*9c5db199SXin Li except AttributeError: 211*9c5db199SXin Li logging.error('could not get version from %s', module) 212*9c5db199SXin Li return '' 213*9c5db199SXin Li 214*9c5db199SXin Li 215*9c5db199SXin Li def _build_and_install(self, install_dir): 216*9c5db199SXin Li """Subclasses MUST provide their own implementation.""" 217*9c5db199SXin Li raise NotImplementedError 218*9c5db199SXin Li 219*9c5db199SXin Li 220*9c5db199SXin Li def _build_and_install_current_dir(self, install_dir): 221*9c5db199SXin Li """ 222*9c5db199SXin Li Subclasses that use _build_and_install_from_package() MUST provide 223*9c5db199SXin Li their own implementation of this method. 224*9c5db199SXin Li """ 225*9c5db199SXin Li raise NotImplementedError 226*9c5db199SXin Li 227*9c5db199SXin Li 228*9c5db199SXin Li def build_and_install(self, install_dir): 229*9c5db199SXin Li """ 230*9c5db199SXin Li Builds and installs the package. It must have been fetched already. 231*9c5db199SXin Li 232*9c5db199SXin Li @param install_dir - The package installation directory. If it does 233*9c5db199SXin Li not exist it will be created. 234*9c5db199SXin Li """ 235*9c5db199SXin Li if not self.verified_package: 236*9c5db199SXin Li raise Error('Must call fetch() first. - %s' % self.name) 237*9c5db199SXin Li self._check_os_requirements() 238*9c5db199SXin Li return self._build_and_install(install_dir) 239*9c5db199SXin Li 240*9c5db199SXin Li 241*9c5db199SXin Li def _check_os_requirements(self): 242*9c5db199SXin Li if not self.os_requirements: 243*9c5db199SXin Li return 244*9c5db199SXin Li failed = False 245*9c5db199SXin Li for file_names, package_name in six.iteritems(self.os_requirements): 246*9c5db199SXin Li if not any(os.path.exists(file_name) for file_name in file_names): 247*9c5db199SXin Li failed = True 248*9c5db199SXin Li logging.error('Can\'t find %s, %s probably needs it.', 249*9c5db199SXin Li ' or '.join(file_names), self.name) 250*9c5db199SXin Li logging.error('Perhaps you need to install something similar ' 251*9c5db199SXin Li 'to the %s package for OS first.', package_name) 252*9c5db199SXin Li if failed: 253*9c5db199SXin Li raise Error('Missing OS requirements for %s. (see above)' % 254*9c5db199SXin Li self.name) 255*9c5db199SXin Li 256*9c5db199SXin Li 257*9c5db199SXin Li def _build_and_install_current_dir_setup_py(self, install_dir): 258*9c5db199SXin Li """For use as a _build_and_install_current_dir implementation.""" 259*9c5db199SXin Li egg_path = self._build_egg_using_setup_py(setup_py='setup.py') 260*9c5db199SXin Li if not egg_path: 261*9c5db199SXin Li return False 262*9c5db199SXin Li return self._install_from_egg(install_dir, egg_path) 263*9c5db199SXin Li 264*9c5db199SXin Li 265*9c5db199SXin Li def _build_and_install_current_dir_setupegg_py(self, install_dir): 266*9c5db199SXin Li """For use as a _build_and_install_current_dir implementation.""" 267*9c5db199SXin Li egg_path = self._build_egg_using_setup_py(setup_py='setupegg.py') 268*9c5db199SXin Li if not egg_path: 269*9c5db199SXin Li return False 270*9c5db199SXin Li return self._install_from_egg(install_dir, egg_path) 271*9c5db199SXin Li 272*9c5db199SXin Li 273*9c5db199SXin Li def _build_and_install_current_dir_noegg(self, install_dir): 274*9c5db199SXin Li if not self._build_using_setup_py(): 275*9c5db199SXin Li return False 276*9c5db199SXin Li return self._install_using_setup_py_and_rsync(install_dir) 277*9c5db199SXin Li 278*9c5db199SXin Li 279*9c5db199SXin Li def _get_extension(self, package): 280*9c5db199SXin Li """Get extension of package.""" 281*9c5db199SXin Li valid_package_extensions = ['.tar.gz', '.tar.bz2', '.zip'] 282*9c5db199SXin Li extension = None 283*9c5db199SXin Li 284*9c5db199SXin Li for ext in valid_package_extensions: 285*9c5db199SXin Li if package.endswith(ext): 286*9c5db199SXin Li extension = ext 287*9c5db199SXin Li break 288*9c5db199SXin Li 289*9c5db199SXin Li if not extension: 290*9c5db199SXin Li raise Error('Unexpected package file extension on %s' % package) 291*9c5db199SXin Li 292*9c5db199SXin Li return extension 293*9c5db199SXin Li 294*9c5db199SXin Li 295*9c5db199SXin Li def _build_and_install_from_package(self, install_dir): 296*9c5db199SXin Li """ 297*9c5db199SXin Li This method may be used as a _build_and_install() implementation 298*9c5db199SXin Li for subclasses if they implement _build_and_install_current_dir(). 299*9c5db199SXin Li 300*9c5db199SXin Li Extracts the .tar.gz file, chdirs into the extracted directory 301*9c5db199SXin Li (which is assumed to match the tar filename) and calls 302*9c5db199SXin Li _build_and_isntall_current_dir from there. 303*9c5db199SXin Li 304*9c5db199SXin Li Afterwards the build (regardless of failure) extracted .tar.gz 305*9c5db199SXin Li directory is cleaned up. 306*9c5db199SXin Li 307*9c5db199SXin Li @returns True on success, False otherwise. 308*9c5db199SXin Li 309*9c5db199SXin Li @raises OSError If the expected extraction directory does not exist. 310*9c5db199SXin Li """ 311*9c5db199SXin Li self._extract_compressed_package() 312*9c5db199SXin Li extension = self._get_extension(self.verified_package) 313*9c5db199SXin Li os.chdir(os.path.dirname(self.verified_package)) 314*9c5db199SXin Li os.chdir(self.extracted_package_path) 315*9c5db199SXin Li extracted_dir = os.getcwd() 316*9c5db199SXin Li try: 317*9c5db199SXin Li return self._build_and_install_current_dir(install_dir) 318*9c5db199SXin Li finally: 319*9c5db199SXin Li os.chdir(os.path.join(extracted_dir, '..')) 320*9c5db199SXin Li shutil.rmtree(extracted_dir) 321*9c5db199SXin Li 322*9c5db199SXin Li 323*9c5db199SXin Li def _extract_compressed_package(self): 324*9c5db199SXin Li """Extract the fetched compressed .tar or .zip within its directory.""" 325*9c5db199SXin Li if not self.verified_package: 326*9c5db199SXin Li raise Error('Package must have been fetched first.') 327*9c5db199SXin Li os.chdir(os.path.dirname(self.verified_package)) 328*9c5db199SXin Li if self.verified_package.endswith('gz'): 329*9c5db199SXin Li status = system("tar -xzf '%s'" % self.verified_package) 330*9c5db199SXin Li elif self.verified_package.endswith('bz2'): 331*9c5db199SXin Li status = system("tar -xjf '%s'" % self.verified_package) 332*9c5db199SXin Li elif self.verified_package.endswith('zip'): 333*9c5db199SXin Li status = system("unzip '%s'" % self.verified_package) 334*9c5db199SXin Li else: 335*9c5db199SXin Li raise Error('Unknown compression suffix on %s.' % 336*9c5db199SXin Li self.verified_package) 337*9c5db199SXin Li if status: 338*9c5db199SXin Li raise Error('tar failed with %s' % (status,)) 339*9c5db199SXin Li 340*9c5db199SXin Li 341*9c5db199SXin Li def _build_using_setup_py(self, setup_py='setup.py'): 342*9c5db199SXin Li """ 343*9c5db199SXin Li Assuming the cwd is the extracted python package, execute a simple 344*9c5db199SXin Li python setup.py build. 345*9c5db199SXin Li 346*9c5db199SXin Li @param setup_py - The name of the setup.py file to execute. 347*9c5db199SXin Li 348*9c5db199SXin Li @returns True on success, False otherwise. 349*9c5db199SXin Li """ 350*9c5db199SXin Li if not os.path.exists(setup_py): 351*9c5db199SXin Li raise Error('%s does not exist in %s' % (setup_py, os.getcwd())) 352*9c5db199SXin Li status = system("'%s' %s build" % (sys.executable, setup_py)) 353*9c5db199SXin Li if status: 354*9c5db199SXin Li logging.error('%s build failed.', self.name) 355*9c5db199SXin Li return False 356*9c5db199SXin Li return True 357*9c5db199SXin Li 358*9c5db199SXin Li 359*9c5db199SXin Li def _build_egg_using_setup_py(self, setup_py='setup.py'): 360*9c5db199SXin Li """ 361*9c5db199SXin Li Assuming the cwd is the extracted python package, execute a simple 362*9c5db199SXin Li python setup.py bdist_egg. 363*9c5db199SXin Li 364*9c5db199SXin Li @param setup_py - The name of the setup.py file to execute. 365*9c5db199SXin Li 366*9c5db199SXin Li @returns The relative path to the resulting egg file or '' on failure. 367*9c5db199SXin Li """ 368*9c5db199SXin Li if not os.path.exists(setup_py): 369*9c5db199SXin Li raise Error('%s does not exist in %s' % (setup_py, os.getcwd())) 370*9c5db199SXin Li egg_subdir = 'dist' 371*9c5db199SXin Li if os.path.isdir(egg_subdir): 372*9c5db199SXin Li shutil.rmtree(egg_subdir) 373*9c5db199SXin Li status = system("'%s' %s bdist_egg" % (sys.executable, setup_py)) 374*9c5db199SXin Li if status: 375*9c5db199SXin Li logging.error('bdist_egg of setuptools failed.') 376*9c5db199SXin Li return '' 377*9c5db199SXin Li # I've never seen a bdist_egg lay multiple .egg files. 378*9c5db199SXin Li for filename in os.listdir(egg_subdir): 379*9c5db199SXin Li if filename.endswith('.egg'): 380*9c5db199SXin Li return os.path.join(egg_subdir, filename) 381*9c5db199SXin Li 382*9c5db199SXin Li 383*9c5db199SXin Li def _install_from_egg(self, install_dir, egg_path): 384*9c5db199SXin Li """ 385*9c5db199SXin Li Install a module from an egg file by unzipping the necessary parts 386*9c5db199SXin Li into install_dir. 387*9c5db199SXin Li 388*9c5db199SXin Li @param install_dir - The installation directory. 389*9c5db199SXin Li @param egg_path - The pathname of the egg file. 390*9c5db199SXin Li """ 391*9c5db199SXin Li status = system("unzip -q -o -d '%s' '%s'" % (install_dir, egg_path)) 392*9c5db199SXin Li if status: 393*9c5db199SXin Li logging.error('unzip of %s failed', egg_path) 394*9c5db199SXin Li return False 395*9c5db199SXin Li egg_info_dir = os.path.join(install_dir, 'EGG-INFO') 396*9c5db199SXin Li if os.path.isdir(egg_info_dir): 397*9c5db199SXin Li egg_info_new_path = self._get_egg_info_path(install_dir) 398*9c5db199SXin Li if egg_info_new_path: 399*9c5db199SXin Li if os.path.exists(egg_info_new_path): 400*9c5db199SXin Li shutil.rmtree(egg_info_new_path) 401*9c5db199SXin Li os.rename(egg_info_dir, egg_info_new_path) 402*9c5db199SXin Li else: 403*9c5db199SXin Li shutil.rmtree(egg_info_dir) 404*9c5db199SXin Li return True 405*9c5db199SXin Li 406*9c5db199SXin Li 407*9c5db199SXin Li def _get_egg_info_path(self, install_dir): 408*9c5db199SXin Li """Get egg-info path for this package. 409*9c5db199SXin Li 410*9c5db199SXin Li Example path: install_dir/MySQL_python-1.2.3.egg-info 411*9c5db199SXin Li 412*9c5db199SXin Li """ 413*9c5db199SXin Li if self.dist_name: 414*9c5db199SXin Li egg_info_name_part = self.dist_name.replace('-', '_') 415*9c5db199SXin Li if self.version: 416*9c5db199SXin Li egg_info_filename = '%s-%s.egg-info' % (egg_info_name_part, 417*9c5db199SXin Li self.version) 418*9c5db199SXin Li else: 419*9c5db199SXin Li egg_info_filename = '%s.egg-info' % (egg_info_name_part,) 420*9c5db199SXin Li return os.path.join(install_dir, egg_info_filename) 421*9c5db199SXin Li else: 422*9c5db199SXin Li return None 423*9c5db199SXin Li 424*9c5db199SXin Li 425*9c5db199SXin Li def _get_temp_dir(self): 426*9c5db199SXin Li return tempfile.mkdtemp(dir='/var/tmp') 427*9c5db199SXin Li 428*9c5db199SXin Li 429*9c5db199SXin Li def _site_packages_path(self, temp_dir): 430*9c5db199SXin Li # This makes assumptions about what python setup.py install 431*9c5db199SXin Li # does when given a prefix. Is this always correct? 432*9c5db199SXin Li python_xy = 'python%s' % sys.version[:3] 433*9c5db199SXin Li return os.path.join(temp_dir, 'lib', python_xy, 'site-packages') 434*9c5db199SXin Li 435*9c5db199SXin Li 436*9c5db199SXin Li def _rsync (self, temp_site_dir, install_dir): 437*9c5db199SXin Li """Rsync contents. """ 438*9c5db199SXin Li status = system("rsync -r '%s/' '%s/'" % 439*9c5db199SXin Li (os.path.normpath(temp_site_dir), 440*9c5db199SXin Li os.path.normpath(install_dir))) 441*9c5db199SXin Li if status: 442*9c5db199SXin Li logging.error('%s rsync to install_dir failed.', self.name) 443*9c5db199SXin Li return False 444*9c5db199SXin Li return True 445*9c5db199SXin Li 446*9c5db199SXin Li 447*9c5db199SXin Li def _install_using_setup_py_and_rsync(self, install_dir, 448*9c5db199SXin Li setup_py='setup.py', 449*9c5db199SXin Li temp_dir=None): 450*9c5db199SXin Li """ 451*9c5db199SXin Li Assuming the cwd is the extracted python package, execute a simple: 452*9c5db199SXin Li 453*9c5db199SXin Li python setup.py install --prefix=BLA 454*9c5db199SXin Li 455*9c5db199SXin Li BLA will be a temporary directory that everything installed will 456*9c5db199SXin Li be picked out of and rsynced to the appropriate place under 457*9c5db199SXin Li install_dir afterwards. 458*9c5db199SXin Li 459*9c5db199SXin Li Afterwards, it deconstructs the extra lib/pythonX.Y/site-packages/ 460*9c5db199SXin Li directory tree that setuptools created and moves all installed 461*9c5db199SXin Li site-packages directly up into install_dir itself. 462*9c5db199SXin Li 463*9c5db199SXin Li @param install_dir the directory for the install to happen under. 464*9c5db199SXin Li @param setup_py - The name of the setup.py file to execute. 465*9c5db199SXin Li 466*9c5db199SXin Li @returns True on success, False otherwise. 467*9c5db199SXin Li """ 468*9c5db199SXin Li if not os.path.exists(setup_py): 469*9c5db199SXin Li raise Error('%s does not exist in %s' % (setup_py, os.getcwd())) 470*9c5db199SXin Li 471*9c5db199SXin Li if temp_dir is None: 472*9c5db199SXin Li temp_dir = self._get_temp_dir() 473*9c5db199SXin Li 474*9c5db199SXin Li try: 475*9c5db199SXin Li status = system("'%s' %s install --no-compile --prefix='%s'" 476*9c5db199SXin Li % (sys.executable, setup_py, temp_dir)) 477*9c5db199SXin Li if status: 478*9c5db199SXin Li logging.error('%s install failed.', self.name) 479*9c5db199SXin Li return False 480*9c5db199SXin Li 481*9c5db199SXin Li if os.path.isdir(os.path.join(temp_dir, 'lib')): 482*9c5db199SXin Li # NOTE: This ignores anything outside of the lib/ dir that 483*9c5db199SXin Li # was installed. 484*9c5db199SXin Li temp_site_dir = self._site_packages_path(temp_dir) 485*9c5db199SXin Li else: 486*9c5db199SXin Li temp_site_dir = temp_dir 487*9c5db199SXin Li 488*9c5db199SXin Li return self._rsync(temp_site_dir, install_dir) 489*9c5db199SXin Li finally: 490*9c5db199SXin Li shutil.rmtree(temp_dir) 491*9c5db199SXin Li 492*9c5db199SXin Li 493*9c5db199SXin Li 494*9c5db199SXin Li def _build_using_make(self, install_dir): 495*9c5db199SXin Li """Build the current package using configure/make. 496*9c5db199SXin Li 497*9c5db199SXin Li @returns True on success, False otherwise. 498*9c5db199SXin Li """ 499*9c5db199SXin Li install_prefix = os.path.join(install_dir, 'usr', 'local') 500*9c5db199SXin Li status = system('./configure --prefix=%s' % install_prefix) 501*9c5db199SXin Li if status: 502*9c5db199SXin Li logging.error('./configure failed for %s', self.name) 503*9c5db199SXin Li return False 504*9c5db199SXin Li status = system('make') 505*9c5db199SXin Li if status: 506*9c5db199SXin Li logging.error('make failed for %s', self.name) 507*9c5db199SXin Li return False 508*9c5db199SXin Li status = system('make check') 509*9c5db199SXin Li if status: 510*9c5db199SXin Li logging.error('make check failed for %s', self.name) 511*9c5db199SXin Li return False 512*9c5db199SXin Li return True 513*9c5db199SXin Li 514*9c5db199SXin Li 515*9c5db199SXin Li def _install_using_make(self): 516*9c5db199SXin Li """Install the current package using make install. 517*9c5db199SXin Li 518*9c5db199SXin Li Assumes the install path was set up while running ./configure (in 519*9c5db199SXin Li _build_using_make()). 520*9c5db199SXin Li 521*9c5db199SXin Li @returns True on success, False otherwise. 522*9c5db199SXin Li """ 523*9c5db199SXin Li status = system('make install') 524*9c5db199SXin Li return status == 0 525*9c5db199SXin Li 526*9c5db199SXin Li 527*9c5db199SXin Li def fetch(self, dest_dir): 528*9c5db199SXin Li """ 529*9c5db199SXin Li Fetch the package from one its URLs and save it in dest_dir. 530*9c5db199SXin Li 531*9c5db199SXin Li If the the package already exists in dest_dir and the checksum 532*9c5db199SXin Li matches this code will not fetch it again. 533*9c5db199SXin Li 534*9c5db199SXin Li Sets the 'verified_package' attribute with the destination pathname. 535*9c5db199SXin Li 536*9c5db199SXin Li @param dest_dir - The destination directory to save the local file. 537*9c5db199SXin Li If it does not exist it will be created. 538*9c5db199SXin Li 539*9c5db199SXin Li @returns A boolean indicating if we the package is now in dest_dir. 540*9c5db199SXin Li @raises FetchError - When something unexpected happens. 541*9c5db199SXin Li """ 542*9c5db199SXin Li if not os.path.exists(dest_dir): 543*9c5db199SXin Li os.makedirs(dest_dir) 544*9c5db199SXin Li local_path = os.path.join(dest_dir, self.local_filename) 545*9c5db199SXin Li 546*9c5db199SXin Li # If the package exists, verify its checksum and be happy if it is good. 547*9c5db199SXin Li if os.path.exists(local_path): 548*9c5db199SXin Li actual_hex_sum = _checksum_file(local_path) 549*9c5db199SXin Li if self.hex_sum == actual_hex_sum: 550*9c5db199SXin Li logging.info('Good checksum for existing %s package.', 551*9c5db199SXin Li self.name) 552*9c5db199SXin Li self.verified_package = local_path 553*9c5db199SXin Li return True 554*9c5db199SXin Li logging.warning('Bad checksum for existing %s package. ' 555*9c5db199SXin Li 'Re-downloading', self.name) 556*9c5db199SXin Li os.rename(local_path, local_path + '.wrong-checksum') 557*9c5db199SXin Li 558*9c5db199SXin Li # Download the package from one of its urls, rejecting any if the 559*9c5db199SXin Li # checksum does not match. 560*9c5db199SXin Li for url in self.urls: 561*9c5db199SXin Li logging.info('Fetching %s', url) 562*9c5db199SXin Li try: 563*9c5db199SXin Li url_file = urllib.request.urlopen(url) 564*9c5db199SXin Li except (urllib.error.URLError, EnvironmentError): 565*9c5db199SXin Li logging.warning('Could not fetch %s package from %s.', 566*9c5db199SXin Li self.name, url) 567*9c5db199SXin Li continue 568*9c5db199SXin Li 569*9c5db199SXin Li data_length = int(url_file.info().get('Content-Length', 570*9c5db199SXin Li _MAX_PACKAGE_SIZE)) 571*9c5db199SXin Li if data_length <= 0 or data_length > _MAX_PACKAGE_SIZE: 572*9c5db199SXin Li raise FetchError('%s from %s fails Content-Length %d ' 573*9c5db199SXin Li 'validity check.' % (self.name, url, 574*9c5db199SXin Li data_length)) 575*9c5db199SXin Li checksum = utils.hash('sha1') 576*9c5db199SXin Li total_read = 0 577*9c5db199SXin Li output = open(local_path, 'wb') 578*9c5db199SXin Li try: 579*9c5db199SXin Li while total_read < data_length: 580*9c5db199SXin Li data = url_file.read(_READ_SIZE) 581*9c5db199SXin Li if not data: 582*9c5db199SXin Li break 583*9c5db199SXin Li output.write(data) 584*9c5db199SXin Li checksum.update(data) 585*9c5db199SXin Li total_read += len(data) 586*9c5db199SXin Li finally: 587*9c5db199SXin Li output.close() 588*9c5db199SXin Li if self.hex_sum != checksum.hexdigest(): 589*9c5db199SXin Li logging.warning('Bad checksum for %s fetched from %s.', 590*9c5db199SXin Li self.name, url) 591*9c5db199SXin Li logging.warning('Got %s', checksum.hexdigest()) 592*9c5db199SXin Li logging.warning('Expected %s', self.hex_sum) 593*9c5db199SXin Li os.unlink(local_path) 594*9c5db199SXin Li continue 595*9c5db199SXin Li logging.info('Good checksum.') 596*9c5db199SXin Li self.verified_package = local_path 597*9c5db199SXin Li return True 598*9c5db199SXin Li else: 599*9c5db199SXin Li return False 600*9c5db199SXin Li 601*9c5db199SXin Li 602*9c5db199SXin Li# NOTE: This class definition must come -before- all other ExternalPackage 603*9c5db199SXin Li# classes that need to use this version of setuptools so that is is inserted 604*9c5db199SXin Li# into the ExternalPackage.subclasses list before them. 605*9c5db199SXin Liclass SetuptoolsPackage(ExternalPackage): 606*9c5db199SXin Li """setuptools package""" 607*9c5db199SXin Li # For all known setuptools releases a string compare works for the 608*9c5db199SXin Li # version string. Hopefully they never release a 0.10. (Their own 609*9c5db199SXin Li # version comparison code would break if they did.) 610*9c5db199SXin Li # Any system with setuptools > 18.0.1 is fine. If none installed, then 611*9c5db199SXin Li # try to install the latest found on the upstream. 612*9c5db199SXin Li minimum_version = '18.0.1' 613*9c5db199SXin Li version = '18.0.1' 614*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + 'setuptools-%s.tar.gz' % (version,),) 615*9c5db199SXin Li local_filename = 'setuptools-%s.tar.gz' % version 616*9c5db199SXin Li hex_sum = 'ebc4fe81b7f6d61d923d9519f589903824044f52' 617*9c5db199SXin Li 618*9c5db199SXin Li SUDO_SLEEP_DELAY = 15 619*9c5db199SXin Li 620*9c5db199SXin Li 621*9c5db199SXin Li def _build_and_install(self, install_dir): 622*9c5db199SXin Li """Install setuptools on the system.""" 623*9c5db199SXin Li logging.info('NOTE: setuptools install does not use install_dir.') 624*9c5db199SXin Li return self._build_and_install_from_package(install_dir) 625*9c5db199SXin Li 626*9c5db199SXin Li 627*9c5db199SXin Li def _build_and_install_current_dir(self, install_dir): 628*9c5db199SXin Li egg_path = self._build_egg_using_setup_py() 629*9c5db199SXin Li if not egg_path: 630*9c5db199SXin Li return False 631*9c5db199SXin Li 632*9c5db199SXin Li print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n') 633*9c5db199SXin Li print('About to run sudo to install setuptools', self.version) 634*9c5db199SXin Li print('on your system for use by', sys.executable, '\n') 635*9c5db199SXin Li print('!! ^C within', self.SUDO_SLEEP_DELAY, 'seconds to abort.\n') 636*9c5db199SXin Li time.sleep(self.SUDO_SLEEP_DELAY) 637*9c5db199SXin Li 638*9c5db199SXin Li # Copy the egg to the local filesystem /var/tmp so that root can 639*9c5db199SXin Li # access it properly (avoid NFS squashroot issues). 640*9c5db199SXin Li temp_dir = self._get_temp_dir() 641*9c5db199SXin Li try: 642*9c5db199SXin Li shutil.copy(egg_path, temp_dir) 643*9c5db199SXin Li egg_name = os.path.split(egg_path)[1] 644*9c5db199SXin Li temp_egg = os.path.join(temp_dir, egg_name) 645*9c5db199SXin Li p = subprocess.Popen(['sudo', '/bin/sh', temp_egg], 646*9c5db199SXin Li stdout=subprocess.PIPE) 647*9c5db199SXin Li regex = re.compile('Copying (.*?) to (.*?)\n') 648*9c5db199SXin Li match = regex.search(p.communicate()[0].decode('utf-8')) 649*9c5db199SXin Li status = p.wait() 650*9c5db199SXin Li 651*9c5db199SXin Li if match: 652*9c5db199SXin Li compiled = os.path.join(match.group(2), match.group(1)) 653*9c5db199SXin Li os.system("sudo chmod a+r '%s'" % compiled) 654*9c5db199SXin Li finally: 655*9c5db199SXin Li shutil.rmtree(temp_dir) 656*9c5db199SXin Li 657*9c5db199SXin Li if status: 658*9c5db199SXin Li logging.error('install of setuptools from egg failed.') 659*9c5db199SXin Li return False 660*9c5db199SXin Li return True 661*9c5db199SXin Li 662*9c5db199SXin Li 663*9c5db199SXin Liclass MySQLdbPackage(ExternalPackage): 664*9c5db199SXin Li """mysql package, used in scheduler.""" 665*9c5db199SXin Li module_name = 'MySQLdb' 666*9c5db199SXin Li version = '1.2.3' 667*9c5db199SXin Li local_filename = 'MySQL-python-%s.tar.gz' % version 668*9c5db199SXin Li urls = ('http://commondatastorage.googleapis.com/chromeos-mirror/gentoo/' 669*9c5db199SXin Li 'distfiles/%s' % local_filename,) 670*9c5db199SXin Li hex_sum = '3511bb8c57c6016eeafa531d5c3ea4b548915e3c' 671*9c5db199SXin Li 672*9c5db199SXin Li _build_and_install_current_dir = ( 673*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 674*9c5db199SXin Li 675*9c5db199SXin Li 676*9c5db199SXin Li def _build_and_install(self, install_dir): 677*9c5db199SXin Li if not os.path.exists('/usr/bin/mysql_config'): 678*9c5db199SXin Li error_msg = '''\ 679*9c5db199SXin LiYou need to install /usr/bin/mysql_config. 680*9c5db199SXin LiOn recent Debian based distros, run: \ 681*9c5db199SXin Lisudo apt-get install libmariadbclient-dev-compat 682*9c5db199SXin LiOn older Debian based distros, run: sudo apt-get install libmysqlclient15-dev 683*9c5db199SXin Li''' 684*9c5db199SXin Li logging.error(error_msg) 685*9c5db199SXin Li return False, error_msg 686*9c5db199SXin Li return self._build_and_install_from_package(install_dir) 687*9c5db199SXin Li 688*9c5db199SXin Li 689*9c5db199SXin Liclass DjangoPackage(ExternalPackage): 690*9c5db199SXin Li """django package.""" 691*9c5db199SXin Li version = '1.5.1' 692*9c5db199SXin Li local_filename = 'Django-%s.tar.gz' % version 693*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 694*9c5db199SXin Li hex_sum = '0ab97b90c4c79636e56337f426f1e875faccbba1' 695*9c5db199SXin Li 696*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 697*9c5db199SXin Li _build_and_install_current_dir = ( 698*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_noegg) 699*9c5db199SXin Li 700*9c5db199SXin Li 701*9c5db199SXin Li def _get_installed_version_from_module(self, module): 702*9c5db199SXin Li try: 703*9c5db199SXin Li return module.get_version().split()[0] 704*9c5db199SXin Li except AttributeError: 705*9c5db199SXin Li return '0.9.6' 706*9c5db199SXin Li 707*9c5db199SXin Li 708*9c5db199SXin Li 709*9c5db199SXin Liclass NumpyPackage(ExternalPackage): 710*9c5db199SXin Li """numpy package, required by matploglib.""" 711*9c5db199SXin Li version = '1.7.0' 712*9c5db199SXin Li local_filename = 'numpy-%s.tar.gz' % version 713*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 714*9c5db199SXin Li hex_sum = 'ba328985f20390b0f969a5be2a6e1141d5752cf9' 715*9c5db199SXin Li 716*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 717*9c5db199SXin Li _build_and_install_current_dir = ( 718*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setupegg_py) 719*9c5db199SXin Li 720*9c5db199SXin Li 721*9c5db199SXin Liclass GwtPackage(ExternalPackage): 722*9c5db199SXin Li """Fetch and extract a local copy of GWT used to build the frontend.""" 723*9c5db199SXin Li 724*9c5db199SXin Li version = '2.3.0' 725*9c5db199SXin Li local_filename = 'gwt-%s.zip' % version 726*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 727*9c5db199SXin Li hex_sum = 'd51fce9166e6b31349659ffca89baf93e39bc84b' 728*9c5db199SXin Li name = 'gwt' 729*9c5db199SXin Li about_filename = 'about.txt' 730*9c5db199SXin Li module_name = None # Not a Python module. 731*9c5db199SXin Li 732*9c5db199SXin Li 733*9c5db199SXin Li def is_needed(self, install_dir): 734*9c5db199SXin Li gwt_dir = os.path.join(install_dir, self.name) 735*9c5db199SXin Li about_file = os.path.join(install_dir, self.name, self.about_filename) 736*9c5db199SXin Li 737*9c5db199SXin Li if not os.path.exists(gwt_dir) or not os.path.exists(about_file): 738*9c5db199SXin Li logging.info('gwt not installed for autotest') 739*9c5db199SXin Li return True 740*9c5db199SXin Li 741*9c5db199SXin Li f = open(about_file, 'r') 742*9c5db199SXin Li version_line = f.readline() 743*9c5db199SXin Li f.close() 744*9c5db199SXin Li 745*9c5db199SXin Li match = re.match(r'Google Web Toolkit (.*)', version_line) 746*9c5db199SXin Li if not match: 747*9c5db199SXin Li logging.info('did not find gwt version') 748*9c5db199SXin Li return True 749*9c5db199SXin Li 750*9c5db199SXin Li logging.info('found gwt version %s', match.group(1)) 751*9c5db199SXin Li return match.group(1) != self.version 752*9c5db199SXin Li 753*9c5db199SXin Li 754*9c5db199SXin Li def _build_and_install(self, install_dir): 755*9c5db199SXin Li os.chdir(install_dir) 756*9c5db199SXin Li self._extract_compressed_package() 757*9c5db199SXin Li extracted_dir = self.local_filename[:-len('.zip')] 758*9c5db199SXin Li target_dir = os.path.join(install_dir, self.name) 759*9c5db199SXin Li if os.path.exists(target_dir): 760*9c5db199SXin Li shutil.rmtree(target_dir) 761*9c5db199SXin Li os.rename(extracted_dir, target_dir) 762*9c5db199SXin Li return True 763*9c5db199SXin Li 764*9c5db199SXin Li 765*9c5db199SXin Liclass PyudevPackage(ExternalPackage): 766*9c5db199SXin Li """ 767*9c5db199SXin Li pyudev module 768*9c5db199SXin Li 769*9c5db199SXin Li Used in unittests. 770*9c5db199SXin Li """ 771*9c5db199SXin Li version = '0.16.1' 772*9c5db199SXin Li url_filename = 'pyudev-%s.tar.gz' % version 773*9c5db199SXin Li local_filename = url_filename 774*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 775*9c5db199SXin Li hex_sum = 'b36bc5c553ce9b56d32a5e45063a2c88156771c0' 776*9c5db199SXin Li 777*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 778*9c5db199SXin Li _build_and_install_current_dir = ( 779*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 780*9c5db199SXin Li 781*9c5db199SXin Li 782*9c5db199SXin Liclass PyMoxPackage(ExternalPackage): 783*9c5db199SXin Li """ 784*9c5db199SXin Li mox module 785*9c5db199SXin Li 786*9c5db199SXin Li Used in unittests. 787*9c5db199SXin Li """ 788*9c5db199SXin Li module_name = 'mox' 789*9c5db199SXin Li version = '0.5.3' 790*9c5db199SXin Li # Note: url_filename does not match local_filename, because of 791*9c5db199SXin Li # an uncontrolled fork at some point in time of mox versions. 792*9c5db199SXin Li url_filename = 'mox-%s-autotest.tar.gz' % version 793*9c5db199SXin Li local_filename = 'mox-%s.tar.gz' % version 794*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + url_filename,) 795*9c5db199SXin Li hex_sum = '1c502d2c0a8aefbba2c7f385a83d33e7d822452a' 796*9c5db199SXin Li 797*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 798*9c5db199SXin Li _build_and_install_current_dir = ( 799*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_noegg) 800*9c5db199SXin Li 801*9c5db199SXin Li def _get_installed_version_from_module(self, module): 802*9c5db199SXin Li # mox doesn't contain a proper version 803*9c5db199SXin Li return self.version 804*9c5db199SXin Li 805*9c5db199SXin Li 806*9c5db199SXin Liclass PySeleniumPackage(ExternalPackage): 807*9c5db199SXin Li """ 808*9c5db199SXin Li selenium module 809*9c5db199SXin Li 810*9c5db199SXin Li Used in wifi_interop suite. 811*9c5db199SXin Li """ 812*9c5db199SXin Li module_name = 'selenium' 813*9c5db199SXin Li version = '2.37.2' 814*9c5db199SXin Li url_filename = 'selenium-%s.tar.gz' % version 815*9c5db199SXin Li local_filename = url_filename 816*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 817*9c5db199SXin Li hex_sum = '66946d5349e36d946daaad625c83c30c11609e36' 818*9c5db199SXin Li 819*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 820*9c5db199SXin Li _build_and_install_current_dir = ( 821*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 822*9c5db199SXin Li 823*9c5db199SXin Li 824*9c5db199SXin Liclass FaultHandlerPackage(ExternalPackage): 825*9c5db199SXin Li """ 826*9c5db199SXin Li faulthandler module 827*9c5db199SXin Li """ 828*9c5db199SXin Li module_name = 'faulthandler' 829*9c5db199SXin Li version = '2.3' 830*9c5db199SXin Li url_filename = '%s-%s.tar.gz' % (module_name, version) 831*9c5db199SXin Li local_filename = url_filename 832*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 833*9c5db199SXin Li hex_sum = 'efb30c068414fba9df892e48fcf86170cbf53589' 834*9c5db199SXin Li 835*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 836*9c5db199SXin Li _build_and_install_current_dir = ( 837*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_noegg) 838*9c5db199SXin Li 839*9c5db199SXin Li 840*9c5db199SXin Liclass PsutilPackage(ExternalPackage): 841*9c5db199SXin Li """ 842*9c5db199SXin Li psutil module 843*9c5db199SXin Li """ 844*9c5db199SXin Li module_name = 'psutil' 845*9c5db199SXin Li version = '2.1.1' 846*9c5db199SXin Li url_filename = '%s-%s.tar.gz' % (module_name, version) 847*9c5db199SXin Li local_filename = url_filename 848*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 849*9c5db199SXin Li hex_sum = '0c20a20ed316e69f2b0881530439213988229916' 850*9c5db199SXin Li 851*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 852*9c5db199SXin Li _build_and_install_current_dir = ( 853*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 854*9c5db199SXin Li 855*9c5db199SXin Li 856*9c5db199SXin Liclass Urllib3Package(ExternalPackage): 857*9c5db199SXin Li """elasticsearch-py package.""" 858*9c5db199SXin Li version = '1.23' 859*9c5db199SXin Li url_filename = 'urllib3-%s.tar.gz' % version 860*9c5db199SXin Li local_filename = url_filename 861*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 862*9c5db199SXin Li hex_sum = '0c54209c397958a7cebe13cb453ec8ef5833998d' 863*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 864*9c5db199SXin Li _build_and_install_current_dir = ( 865*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 866*9c5db199SXin Li 867*9c5db199SXin Liclass ImagingLibraryPackage(ExternalPackage): 868*9c5db199SXin Li """Python Imaging Library (PIL).""" 869*9c5db199SXin Li version = '1.1.7' 870*9c5db199SXin Li url_filename = 'Imaging-%s.tar.gz' % version 871*9c5db199SXin Li local_filename = url_filename 872*9c5db199SXin Li urls = ('http://commondatastorage.googleapis.com/chromeos-mirror/gentoo/' 873*9c5db199SXin Li 'distfiles/%s' % url_filename,) 874*9c5db199SXin Li hex_sum = '76c37504251171fda8da8e63ecb8bc42a69a5c81' 875*9c5db199SXin Li 876*9c5db199SXin Li def _build_and_install(self, install_dir): 877*9c5db199SXin Li #The path of zlib library might be different from what PIL setup.py is 878*9c5db199SXin Li #expected. Following change does the best attempt to link the library 879*9c5db199SXin Li #to a path PIL setup.py will try. 880*9c5db199SXin Li libz_possible_path = '/usr/lib/x86_64-linux-gnu/libz.so' 881*9c5db199SXin Li libz_expected_path = '/usr/lib/libz.so' 882*9c5db199SXin Li # TODO(crbug.com/957186): this sudo command fails if build_externals 883*9c5db199SXin Li # is running in non-interactive mode, and requires a workaround when 884*9c5db199SXin Li # running within a docker build process. Remove this operation, or 885*9c5db199SXin Li # remove this entire package. 886*9c5db199SXin Li if (os.path.exists(libz_possible_path) and 887*9c5db199SXin Li not os.path.exists(libz_expected_path)): 888*9c5db199SXin Li utils.run('sudo ln -s %s %s' % 889*9c5db199SXin Li (libz_possible_path, libz_expected_path)) 890*9c5db199SXin Li return self._build_and_install_from_package(install_dir) 891*9c5db199SXin Li 892*9c5db199SXin Li _build_and_install_current_dir = ( 893*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_noegg) 894*9c5db199SXin Li 895*9c5db199SXin Li 896*9c5db199SXin Liclass AstroidPackage(ExternalPackage): 897*9c5db199SXin Li """astroid package.""" 898*9c5db199SXin Li version = '1.5.3' 899*9c5db199SXin Li url_filename = 'astroid-%s.tar.gz' % version 900*9c5db199SXin Li local_filename = url_filename 901*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 902*9c5db199SXin Li hex_sum = 'e654225ab5bd2788e5e246b156910990bf33cde6' 903*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 904*9c5db199SXin Li _build_and_install_current_dir = ( 905*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 906*9c5db199SXin Li 907*9c5db199SXin Li 908*9c5db199SXin Liclass LazyObjectProxyPackage(ExternalPackage): 909*9c5db199SXin Li """lazy-object-proxy package (dependency for astroid).""" 910*9c5db199SXin Li version = '1.3.1' 911*9c5db199SXin Li url_filename = 'lazy-object-proxy-%s.tar.gz' % version 912*9c5db199SXin Li local_filename = url_filename 913*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 914*9c5db199SXin Li hex_sum = '984828d8f672986ca926373986214d7057b772fb' 915*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 916*9c5db199SXin Li _build_and_install_current_dir = ( 917*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 918*9c5db199SXin Li 919*9c5db199SXin Li 920*9c5db199SXin Liclass SingleDispatchPackage(ExternalPackage): 921*9c5db199SXin Li """singledispatch package (dependency for astroid).""" 922*9c5db199SXin Li version = '3.4.0.3' 923*9c5db199SXin Li url_filename = 'singledispatch-%s.tar.gz' % version 924*9c5db199SXin Li local_filename = url_filename 925*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 926*9c5db199SXin Li hex_sum = 'f93241b06754a612af8bb7aa208c4d1805637022' 927*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 928*9c5db199SXin Li _build_and_install_current_dir = ( 929*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 930*9c5db199SXin Li 931*9c5db199SXin Li 932*9c5db199SXin Liclass Enum34Package(ExternalPackage): 933*9c5db199SXin Li """enum34 package (dependency for astroid).""" 934*9c5db199SXin Li version = '1.1.6' 935*9c5db199SXin Li url_filename = 'enum34-%s.tar.gz' % version 936*9c5db199SXin Li local_filename = url_filename 937*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 938*9c5db199SXin Li hex_sum = '014ef5878333ff91099893d615192c8cd0b1525a' 939*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 940*9c5db199SXin Li _build_and_install_current_dir = ( 941*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 942*9c5db199SXin Li 943*9c5db199SXin Li 944*9c5db199SXin Liclass WraptPackage(ExternalPackage): 945*9c5db199SXin Li """wrapt package (dependency for astroid).""" 946*9c5db199SXin Li version = '1.10.10' 947*9c5db199SXin Li url_filename = 'wrapt-%s.tar.gz' % version 948*9c5db199SXin Li local_filename = url_filename 949*9c5db199SXin Li #md5=97365e906afa8b431f266866ec4e2e18 950*9c5db199SXin Li urls = ('https://pypi.python.org/packages/a3/bb/' 951*9c5db199SXin Li '525e9de0a220060394f4aa34fdf6200853581803d92714ae41fc3556e7d7/%s' % 952*9c5db199SXin Li (url_filename),) 953*9c5db199SXin Li hex_sum = '6be4f1bb50db879863f4247692360eb830a3eb33' 954*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 955*9c5db199SXin Li _build_and_install_current_dir = ( 956*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_noegg) 957*9c5db199SXin Li 958*9c5db199SXin Li 959*9c5db199SXin Liclass SixPackage(ExternalPackage): 960*9c5db199SXin Li """six package (dependency for astroid).""" 961*9c5db199SXin Li version = '1.10.0' 962*9c5db199SXin Li url_filename = 'six-%s.tar.gz' % version 963*9c5db199SXin Li local_filename = url_filename 964*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 965*9c5db199SXin Li hex_sum = '30d480d2e352e8e4c2aae042cf1bf33368ff0920' 966*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 967*9c5db199SXin Li _build_and_install_current_dir = ( 968*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 969*9c5db199SXin Li 970*9c5db199SXin Li 971*9c5db199SXin Liclass SetuptoolsScmPackage(ExternalPackage): 972*9c5db199SXin Li """setuptools_scm package.""" 973*9c5db199SXin Li version = '5.0.2' 974*9c5db199SXin Li url_filename = 'setuptools_scm-%s.tar.gz' % version 975*9c5db199SXin Li local_filename = url_filename 976*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename, ) 977*9c5db199SXin Li hex_sum = '28ec9ce4a5270f82f07e919398c74221da67a8bb' 978*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 979*9c5db199SXin Li _build_and_install_current_dir = ( 980*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 981*9c5db199SXin Li 982*9c5db199SXin Li 983*9c5db199SXin Liclass LruCachePackage(ExternalPackage): 984*9c5db199SXin Li """backports.functools_lru_cache package (dependency for astroid).""" 985*9c5db199SXin Li version = '1.4' 986*9c5db199SXin Li url_filename = 'backports.functools_lru_cache-%s.tar.gz' % version 987*9c5db199SXin Li local_filename = url_filename 988*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 989*9c5db199SXin Li hex_sum = '8a546e7887e961c2873c9b053f4e2cd2a96bd71d' 990*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 991*9c5db199SXin Li _build_and_install_current_dir = ( 992*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 993*9c5db199SXin Li 994*9c5db199SXin Li 995*9c5db199SXin Liclass LogilabCommonPackage(ExternalPackage): 996*9c5db199SXin Li """logilab-common package.""" 997*9c5db199SXin Li version = '1.2.2' 998*9c5db199SXin Li module_name = 'logilab' 999*9c5db199SXin Li url_filename = 'logilab-common-%s.tar.gz' % version 1000*9c5db199SXin Li local_filename = url_filename 1001*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 1002*9c5db199SXin Li hex_sum = 'ecad2d10c31dcf183c8bed87b6ec35e7ed397d27' 1003*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 1004*9c5db199SXin Li _build_and_install_current_dir = ( 1005*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 1006*9c5db199SXin Li 1007*9c5db199SXin Li 1008*9c5db199SXin Liclass PytestRunnerPackage(ExternalPackage): 1009*9c5db199SXin Li """pytest-runner package.""" 1010*9c5db199SXin Li version = '5.2' 1011*9c5db199SXin Li url_filename = 'pytest-runner-%s.tar.gz' % version 1012*9c5db199SXin Li local_filename = url_filename 1013*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 1014*9c5db199SXin Li hex_sum = '3427663b575c5d885ea3869a1be09aca36517f74' 1015*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 1016*9c5db199SXin Li _build_and_install_current_dir = ( 1017*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 1018*9c5db199SXin Li 1019*9c5db199SXin Li 1020*9c5db199SXin Liclass PyLintPackage(ExternalPackage): 1021*9c5db199SXin Li """pylint package.""" 1022*9c5db199SXin Li version = '1.7.2' 1023*9c5db199SXin Li url_filename = 'pylint-%s.tar.gz' % version 1024*9c5db199SXin Li local_filename = url_filename 1025*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 1026*9c5db199SXin Li hex_sum = '42d8b9394e5a485377ae128b01350f25d8b131e0' 1027*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 1028*9c5db199SXin Li _build_and_install_current_dir = ( 1029*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 1030*9c5db199SXin Li 1031*9c5db199SXin Li 1032*9c5db199SXin Liclass ConfigParserPackage(ExternalPackage): 1033*9c5db199SXin Li """configparser package (dependency for pylint).""" 1034*9c5db199SXin Li version = '3.5.0' 1035*9c5db199SXin Li url_filename = 'configparser-%s.tar.gz' % version 1036*9c5db199SXin Li local_filename = url_filename 1037*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 1038*9c5db199SXin Li hex_sum = '8ee6b29c6a11977c0e094da1d4f5f71e7e7ac78b' 1039*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 1040*9c5db199SXin Li _build_and_install_current_dir = ( 1041*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 1042*9c5db199SXin Li 1043*9c5db199SXin Li 1044*9c5db199SXin Liclass IsortPackage(ExternalPackage): 1045*9c5db199SXin Li """isort package (dependency for pylint).""" 1046*9c5db199SXin Li version = '4.2.15' 1047*9c5db199SXin Li url_filename = 'isort-%s.tar.gz' % version 1048*9c5db199SXin Li local_filename = url_filename 1049*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 1050*9c5db199SXin Li hex_sum = 'acacc36e476b70e13e6fda812c193f4c3c187781' 1051*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 1052*9c5db199SXin Li _build_and_install_current_dir = ( 1053*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 1054*9c5db199SXin Li 1055*9c5db199SXin Li 1056*9c5db199SXin Liclass DateutilPackage(ExternalPackage): 1057*9c5db199SXin Li """python-dateutil package.""" 1058*9c5db199SXin Li version = '2.6.1' 1059*9c5db199SXin Li local_filename = 'python-dateutil-%s.tar.gz' % version 1060*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 1061*9c5db199SXin Li hex_sum = 'db2ace298dee7e47fd720ed03eb790885347bf4e' 1062*9c5db199SXin Li 1063*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 1064*9c5db199SXin Li _build_and_install_current_dir = ( 1065*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 1066*9c5db199SXin Li 1067*9c5db199SXin Li 1068*9c5db199SXin Liclass PyYAMLPackage(ExternalPackage): 1069*9c5db199SXin Li """pyyaml package.""" 1070*9c5db199SXin Li version = '3.12' 1071*9c5db199SXin Li local_filename = 'PyYAML-%s.tar.gz' % version 1072*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 1073*9c5db199SXin Li hex_sum = 'cb7fd3e58c129494ee86e41baedfec69eb7dafbe' 1074*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 1075*9c5db199SXin Li _build_and_install_current_dir = ( 1076*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_noegg) 1077*9c5db199SXin Li 1078*9c5db199SXin Li 1079*9c5db199SXin Liclass GoogleAuthPackage(ExternalPackage): 1080*9c5db199SXin Li """Google Auth Client.""" 1081*9c5db199SXin Li version = '1.6.3' 1082*9c5db199SXin Li local_filename = 'google-auth-%s.tar.gz' % version 1083*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 1084*9c5db199SXin Li hex_sum = 'a76f97686ebe42097d91e0996a72b26b54118f3b' 1085*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 1086*9c5db199SXin Li _build_and_install_current_dir = ( 1087*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 1088*9c5db199SXin Li 1089*9c5db199SXin Li 1090*9c5db199SXin Liclass GrpcioPackage(ExternalPackage): 1091*9c5db199SXin Li """GrpcioPackage package.""" 1092*9c5db199SXin Li version = '1.26.0' 1093*9c5db199SXin Li hex_sum = "b9a61f855bf3656d9b8ac305bd1e52442e120c48" 1094*9c5db199SXin Li local_filename = 'grpcio-%s.tar.gz' % version 1095*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 1096*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 1097*9c5db199SXin Li _build_and_install_current_dir = ( 1098*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 1099*9c5db199SXin Li 1100*9c5db199SXin Li 1101*9c5db199SXin Liclass GrpcioToolsPackage(ExternalPackage): 1102*9c5db199SXin Li """GrpcioPackage package.""" 1103*9c5db199SXin Li version = '1.26.0' 1104*9c5db199SXin Li hex_sum = "298724d8704523c6ff443303e0c26fc1d54f9acb" 1105*9c5db199SXin Li local_filename = 'grpcio-tools-%s.tar.gz' % version 1106*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 1107*9c5db199SXin Li _build_and_install = ExternalPackage._build_and_install_from_package 1108*9c5db199SXin Li _build_and_install_current_dir = ( 1109*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 1110*9c5db199SXin Li 1111*9c5db199SXin Li 1112*9c5db199SXin Liclass Protobuf(ExternalPackage): 1113*9c5db199SXin Li """GrpcioPackage package.""" 1114*9c5db199SXin Li version = '3.11.2' 1115*9c5db199SXin Li hex_sum = "e1f3ffa028ece5a529149dd56a3d64aea4ae1b1a" 1116*9c5db199SXin Li local_filename = 'protobuf-%s.tar.gz' % version 1117*9c5db199SXin Li urls = (_CHROMEOS_MIRROR + local_filename,) 1118*9c5db199SXin Li _build_and_install_current_dir = ( 1119*9c5db199SXin Li ExternalPackage._build_and_install_current_dir_setup_py) 1120*9c5db199SXin Li 1121*9c5db199SXin Li def _build_and_install(self, install_dir): 1122*9c5db199SXin Li """ 1123*9c5db199SXin Li This method may be used as a _build_and_install() implementation 1124*9c5db199SXin Li for subclasses if they implement _build_and_install_current_dir(). 1125*9c5db199SXin Li 1126*9c5db199SXin Li Extracts the .tar.gz file, chdirs into the extracted directory 1127*9c5db199SXin Li (which is assumed to match the tar filename) and calls 1128*9c5db199SXin Li _build_and_isntall_current_dir from there. 1129*9c5db199SXin Li 1130*9c5db199SXin Li Afterwards the build (regardless of failure) extracted .tar.gz 1131*9c5db199SXin Li directory is cleaned up. 1132*9c5db199SXin Li 1133*9c5db199SXin Li @returns True on success, False otherwise. 1134*9c5db199SXin Li 1135*9c5db199SXin Li @raises OSError If the expected extraction directory does not exist. 1136*9c5db199SXin Li """ 1137*9c5db199SXin Li self._extract_compressed_package() 1138*9c5db199SXin Li extension = self._get_extension(self.verified_package) 1139*9c5db199SXin Li os.chdir(os.path.dirname(self.verified_package)) 1140*9c5db199SXin Li os.chdir(os.path.join(self.extracted_package_path, "python")) 1141*9c5db199SXin Li extracted_dir = os.getcwd() 1142*9c5db199SXin Li try: 1143*9c5db199SXin Li return self._build_and_install_current_dir(install_dir) 1144*9c5db199SXin Li finally: 1145*9c5db199SXin Li os.chdir(os.path.join(extracted_dir, '..')) 1146*9c5db199SXin Li shutil.rmtree(extracted_dir) 1147*9c5db199SXin Li 1148*9c5db199SXin Li 1149*9c5db199SXin Liclass _ExternalGitRepo(ExternalPackage): 1150*9c5db199SXin Li """ 1151*9c5db199SXin Li Parent class for any package which needs to pull a git repo. 1152*9c5db199SXin Li 1153*9c5db199SXin Li This class inherits from ExternalPackage only so we can sync git 1154*9c5db199SXin Li repos through the build_externals script. We do not reuse any of 1155*9c5db199SXin Li ExternalPackage's other methods. Any package that needs a git repo 1156*9c5db199SXin Li should subclass this and override build_and_install or fetch as 1157*9c5db199SXin Li they see appropriate. 1158*9c5db199SXin Li """ 1159*9c5db199SXin Li 1160*9c5db199SXin Li os_requirements = {('/usr/bin/git') : 'git-core'} 1161*9c5db199SXin Li 1162*9c5db199SXin Li # All the chromiumos projects used on the lab servers should have a 'prod' 1163*9c5db199SXin Li # branch used to track the software version deployed in prod. 1164*9c5db199SXin Li PROD_BRANCH = 'prod' 1165*9c5db199SXin Li 1166*9c5db199SXin Li def is_needed(self, unused_install_dir): 1167*9c5db199SXin Li """Tell build_externals that we need to fetch.""" 1168*9c5db199SXin Li # TODO(beeps): check if we're already upto date. 1169*9c5db199SXin Li return True 1170*9c5db199SXin Li 1171*9c5db199SXin Li 1172*9c5db199SXin Li def build_and_install(self, unused_install_dir): 1173*9c5db199SXin Li """ 1174*9c5db199SXin Li Fall through method to install a package. 1175*9c5db199SXin Li 1176*9c5db199SXin Li Overwritten in base classes to pull a git repo. 1177*9c5db199SXin Li """ 1178*9c5db199SXin Li raise NotImplementedError 1179*9c5db199SXin Li 1180*9c5db199SXin Li 1181*9c5db199SXin Li def fetch(self, unused_dest_dir): 1182*9c5db199SXin Li """Fallthrough method to fetch a package.""" 1183*9c5db199SXin Li return True 1184*9c5db199SXin Li 1185*9c5db199SXin Li 1186*9c5db199SXin Liclass HdctoolsRepo(_ExternalGitRepo): 1187*9c5db199SXin Li """Clones or updates the hdctools repo.""" 1188*9c5db199SXin Li 1189*9c5db199SXin Li module_name = 'servo' 1190*9c5db199SXin Li temp_hdctools_dir = tempfile.mktemp(suffix='hdctools') 1191*9c5db199SXin Li _GIT_URL = ('https://chromium.googlesource.com/' 1192*9c5db199SXin Li 'chromiumos/third_party/hdctools') 1193*9c5db199SXin Li MAIN_BRANCH = 'main' 1194*9c5db199SXin Li 1195*9c5db199SXin Li def fetch(self, unused_dest_dir): 1196*9c5db199SXin Li """ 1197*9c5db199SXin Li Fetch repo to a temporary location. 1198*9c5db199SXin Li 1199*9c5db199SXin Li We use an intermediate temp directory to stage our 1200*9c5db199SXin Li installation because we only care about the servo package. 1201*9c5db199SXin Li If we can't get at the top commit hash after fetching 1202*9c5db199SXin Li something is wrong. This can happen when we've cloned/pulled 1203*9c5db199SXin Li an empty repo. Not something we expect to do. 1204*9c5db199SXin Li 1205*9c5db199SXin Li @parma unused_dest_dir: passed in because we inherit from 1206*9c5db199SXin Li ExternalPackage. 1207*9c5db199SXin Li 1208*9c5db199SXin Li @return: True if repo sync was successful. 1209*9c5db199SXin Li """ 1210*9c5db199SXin Li git_repo = revision_control.GitRepo( 1211*9c5db199SXin Li self.temp_hdctools_dir, 1212*9c5db199SXin Li self._GIT_URL, 1213*9c5db199SXin Li None, 1214*9c5db199SXin Li abs_work_tree=self.temp_hdctools_dir) 1215*9c5db199SXin Li git_repo.reinit_repo_at(self.PROD_BRANCH) 1216*9c5db199SXin Li 1217*9c5db199SXin Li if git_repo.get_latest_commit_hash(): 1218*9c5db199SXin Li return True 1219*9c5db199SXin Li return False 1220*9c5db199SXin Li 1221*9c5db199SXin Li 1222*9c5db199SXin Li def build_and_install(self, install_dir): 1223*9c5db199SXin Li """Reach into the hdctools repo and rsync only the servo directory.""" 1224*9c5db199SXin Li 1225*9c5db199SXin Li servo_dir = os.path.join(self.temp_hdctools_dir, 'servo') 1226*9c5db199SXin Li if not os.path.exists(servo_dir): 1227*9c5db199SXin Li return False 1228*9c5db199SXin Li 1229*9c5db199SXin Li rv = self._rsync(servo_dir, os.path.join(install_dir, 'servo')) 1230*9c5db199SXin Li shutil.rmtree(self.temp_hdctools_dir) 1231*9c5db199SXin Li return rv 1232*9c5db199SXin Li 1233*9c5db199SXin Li 1234*9c5db199SXin Liclass ChromiteRepo(_ExternalGitRepo): 1235*9c5db199SXin Li """Clones or updates the chromite repo.""" 1236*9c5db199SXin Li 1237*9c5db199SXin Li _GIT_URL = ('https://chromium.googlesource.com/chromiumos/chromite') 1238*9c5db199SXin Li MAIN_BRANCH = 'main' 1239*9c5db199SXin Li 1240*9c5db199SXin Li def build_and_install(self, install_dir, main_branch=False): 1241*9c5db199SXin Li """ 1242*9c5db199SXin Li Clone if the repo isn't initialized, pull clean bits if it is. 1243*9c5db199SXin Li 1244*9c5db199SXin Li Unlike it's hdctools counterpart the chromite repo clones main 1245*9c5db199SXin Li directly into site-packages. It doesn't use an intermediate temp 1246*9c5db199SXin Li directory because it doesn't need installation. 1247*9c5db199SXin Li 1248*9c5db199SXin Li @param install_dir: destination directory for chromite installation. 1249*9c5db199SXin Li @param main_branch: if True, install main branch. Otherwise, 1250*9c5db199SXin Li install prod branch. 1251*9c5db199SXin Li """ 1252*9c5db199SXin Li init_branch = (self.MAIN_BRANCH if main_branch 1253*9c5db199SXin Li else self.PROD_BRANCH) 1254*9c5db199SXin Li local_chromite_dir = os.path.join(install_dir, 'chromite') 1255*9c5db199SXin Li git_repo = revision_control.GitRepo( 1256*9c5db199SXin Li local_chromite_dir, 1257*9c5db199SXin Li self._GIT_URL, 1258*9c5db199SXin Li abs_work_tree=local_chromite_dir) 1259*9c5db199SXin Li git_repo.reinit_repo_at(init_branch) 1260*9c5db199SXin Li 1261*9c5db199SXin Li 1262*9c5db199SXin Li if git_repo.get_latest_commit_hash(): 1263*9c5db199SXin Li return True 1264*9c5db199SXin Li return False 1265*9c5db199SXin Li 1266*9c5db199SXin Li 1267*9c5db199SXin Liclass BtsocketRepo(_ExternalGitRepo): 1268*9c5db199SXin Li """Clones or updates the btsocket repo.""" 1269*9c5db199SXin Li 1270*9c5db199SXin Li _GIT_URL = ('https://chromium.googlesource.com/' 1271*9c5db199SXin Li 'chromiumos/platform/btsocket') 1272*9c5db199SXin Li # TODO b:169251326 terms below are set outside of this codebase and should 1273*9c5db199SXin Li # be updated when possible ("master" -> "main"). 1274*9c5db199SXin Li MAIN_BRANCH = 'master' 1275*9c5db199SXin Li 1276*9c5db199SXin Li def fetch(self, unused_dest_dir): 1277*9c5db199SXin Li """ 1278*9c5db199SXin Li Fetch repo to a temporary location. 1279*9c5db199SXin Li 1280*9c5db199SXin Li We use an intermediate temp directory because we have to build an 1281*9c5db199SXin Li egg for installation. If we can't get at the top commit hash after 1282*9c5db199SXin Li fetching something is wrong. This can happen when we've cloned/pulled 1283*9c5db199SXin Li an empty repo. Not something we expect to do. 1284*9c5db199SXin Li 1285*9c5db199SXin Li @parma unused_dest_dir: passed in because we inherit from 1286*9c5db199SXin Li ExternalPackage. 1287*9c5db199SXin Li 1288*9c5db199SXin Li @return: True if repo sync was successful. 1289*9c5db199SXin Li """ 1290*9c5db199SXin Li self.temp_btsocket_dir = autotemp.tempdir(unique_id='btsocket') 1291*9c5db199SXin Li try: 1292*9c5db199SXin Li git_repo = revision_control.GitRepo( 1293*9c5db199SXin Li self.temp_btsocket_dir.name, 1294*9c5db199SXin Li self._GIT_URL, 1295*9c5db199SXin Li None, 1296*9c5db199SXin Li abs_work_tree=self.temp_btsocket_dir.name) 1297*9c5db199SXin Li git_repo.reinit_repo_at(self.PROD_BRANCH) 1298*9c5db199SXin Li 1299*9c5db199SXin Li if git_repo.get_latest_commit_hash(): 1300*9c5db199SXin Li return True 1301*9c5db199SXin Li except: 1302*9c5db199SXin Li self.temp_btsocket_dir.clean() 1303*9c5db199SXin Li raise 1304*9c5db199SXin Li 1305*9c5db199SXin Li self.temp_btsocket_dir.clean() 1306*9c5db199SXin Li return False 1307*9c5db199SXin Li 1308*9c5db199SXin Li 1309*9c5db199SXin Li def build_and_install(self, install_dir): 1310*9c5db199SXin Li """ 1311*9c5db199SXin Li Install the btsocket module using setup.py 1312*9c5db199SXin Li 1313*9c5db199SXin Li @param install_dir: Target installation directory. 1314*9c5db199SXin Li 1315*9c5db199SXin Li @return: A boolean indicating success of failure. 1316*9c5db199SXin Li """ 1317*9c5db199SXin Li work_dir = os.getcwd() 1318*9c5db199SXin Li try: 1319*9c5db199SXin Li os.chdir(self.temp_btsocket_dir.name) 1320*9c5db199SXin Li rv = self._build_and_install_current_dir_setup_py(install_dir) 1321*9c5db199SXin Li finally: 1322*9c5db199SXin Li os.chdir(work_dir) 1323*9c5db199SXin Li self.temp_btsocket_dir.clean() 1324*9c5db199SXin Li return rv 1325*9c5db199SXin Li 1326*9c5db199SXin Li 1327*9c5db199SXin Liclass SkylabInventoryRepo(_ExternalGitRepo): 1328*9c5db199SXin Li """Clones or updates the skylab_inventory repo.""" 1329*9c5db199SXin Li 1330*9c5db199SXin Li _GIT_URL = ('https://chromium.googlesource.com/chromiumos/infra/' 1331*9c5db199SXin Li 'skylab_inventory') 1332*9c5db199SXin Li # TODO b:169251326 terms below are set outside of this codebase and should 1333*9c5db199SXin Li # be updated when possible ("master" -> "main"). 1334*9c5db199SXin Li MAIN_BRANCH = 'master' 1335*9c5db199SXin Li 1336*9c5db199SXin Li # TODO(nxia): create a prod branch for skylab_inventory. 1337*9c5db199SXin Li def build_and_install(self, install_dir): 1338*9c5db199SXin Li """ 1339*9c5db199SXin Li @param install_dir: destination directory for skylab_inventory 1340*9c5db199SXin Li installation. 1341*9c5db199SXin Li """ 1342*9c5db199SXin Li local_skylab_dir = os.path.join(install_dir, 'infra_skylab_inventory') 1343*9c5db199SXin Li git_repo = revision_control.GitRepo( 1344*9c5db199SXin Li local_skylab_dir, 1345*9c5db199SXin Li self._GIT_URL, 1346*9c5db199SXin Li abs_work_tree=local_skylab_dir) 1347*9c5db199SXin Li git_repo.reinit_repo_at(self.MAIN_BRANCH) 1348*9c5db199SXin Li 1349*9c5db199SXin Li # The top-level __init__.py for skylab is at venv/skylab_inventory. 1350*9c5db199SXin Li source = os.path.join(local_skylab_dir, 'venv', 'skylab_inventory') 1351*9c5db199SXin Li link_name = os.path.join(install_dir, 'skylab_inventory') 1352*9c5db199SXin Li 1353*9c5db199SXin Li if (os.path.exists(link_name) and 1354*9c5db199SXin Li os.path.realpath(link_name) != os.path.realpath(source)): 1355*9c5db199SXin Li os.remove(link_name) 1356*9c5db199SXin Li 1357*9c5db199SXin Li if not os.path.exists(link_name): 1358*9c5db199SXin Li os.symlink(source, link_name) 1359*9c5db199SXin Li 1360*9c5db199SXin Li if git_repo.get_latest_commit_hash(): 1361*9c5db199SXin Li return True 1362*9c5db199SXin Li return False 1363