blob: 5a5c9a4d546cb9c01c60cf79daaee59d90494423 [file] [log] [blame]
jamesrenff6e5aa2010-02-12 00:46:40 +00001# Please keep this code python 2.4 compatible and stand alone.
2
3import logging, os, shutil, sys, tempfile, time, urllib2
4import subprocess, re
Scott James Remnantbb1a9672014-03-03 14:03:09 -08005from autotest_lib.client.common_lib import autotemp, revision_control, utils
jamesrenff6e5aa2010-02-12 00:46:40 +00006
7_READ_SIZE = 64*1024
8_MAX_PACKAGE_SIZE = 100*1024*1024
9
10
11class Error(Exception):
12 """Local exception to be raised by code in this file."""
13
14class FetchError(Error):
15 """Failed to fetch a package from any of its listed URLs."""
16
17
18def _checksum_file(full_path):
19 """@returns The hex checksum of a file given its pathname."""
20 inputfile = open(full_path, 'rb')
21 try:
22 hex_sum = utils.hash('sha1', inputfile.read()).hexdigest()
23 finally:
24 inputfile.close()
25 return hex_sum
26
27
28def system(commandline):
Dan Shi57631dc2013-02-22 09:54:14 -080029 """Same as os.system(commandline) but logs the command first.
30
31 @param commandline: commandline to be called.
32 """
jamesrenff6e5aa2010-02-12 00:46:40 +000033 logging.info(commandline)
34 return os.system(commandline)
35
36
37def find_top_of_autotest_tree():
38 """@returns The full path to the top of the autotest directory tree."""
39 dirname = os.path.dirname(__file__)
40 autotest_dir = os.path.abspath(os.path.join(dirname, '..'))
41 return autotest_dir
42
43
44class ExternalPackage(object):
45 """
46 Defines an external package with URLs to fetch its sources from and
47 a build_and_install() method to unpack it, build it and install it
48 beneath our own autotest/site-packages directory.
49
50 Base Class. Subclass this to define packages.
beepsd5335852013-04-09 09:58:52 -070051 Note: Unless your subclass has a specific reason to, it should not
52 re-install the package every time build_externals is invoked, as this
53 happens periodically through the scheduler. To avoid doing so the is_needed
54 method needs to return an appropriate value.
jamesrenff6e5aa2010-02-12 00:46:40 +000055
56 Attributes:
57 @attribute urls - A tuple of URLs to try fetching the package from.
58 @attribute local_filename - A local filename to use when saving the
59 fetched package.
60 @attribute hex_sum - The hex digest (currently SHA1) of this package
61 to be used to verify its contents.
62 @attribute module_name - The installed python module name to be used for
63 for a version check. Defaults to the lower case class name with
64 the word Package stripped off.
65 @attribute version - The desired minimum package version.
Michael Janssena7427612014-11-14 15:44:39 -080066 @attribute os_requirements - A dictionary mapping pathname tuples on the
jamesrenff6e5aa2010-02-12 00:46:40 +000067 the OS distribution to a likely name of a package the user
68 needs to install on their system in order to get this file.
Michael Janssena7427612014-11-14 15:44:39 -080069 One of the files in the tuple must exist.
jamesrenff6e5aa2010-02-12 00:46:40 +000070 @attribute name - Read only, the printable name of the package.
71 @attribute subclasses - This class attribute holds a list of all defined
72 subclasses. It is constructed dynamically using the metaclass.
73 """
Dan Shi16c0a502015-07-14 17:29:48 -070074 # Modules that are meant to be installed in system directory, rather than
75 # autotest/site-packages. These modules should be skipped if the module
76 # is already installed in system directory. This prevents an older version
77 # of the module from being installed in system directory.
78 SYSTEM_MODULES = ['setuptools']
79
jamesrenff6e5aa2010-02-12 00:46:40 +000080 subclasses = []
81 urls = ()
82 local_filename = None
83 hex_sum = None
84 module_name = None
85 version = None
86 os_requirements = None
87
88
89 class __metaclass__(type):
90 """Any time a subclass is defined, add it to our list."""
91 def __init__(mcs, name, bases, dict):
beepsd9153b52013-01-23 20:52:46 -080092 if name != 'ExternalPackage' and not name.startswith('_'):
jamesrenff6e5aa2010-02-12 00:46:40 +000093 mcs.subclasses.append(mcs)
94
95
96 def __init__(self):
97 self.verified_package = ''
98 if not self.module_name:
99 self.module_name = self.name.lower()
100 self.installed_version = ''
101
102
103 @property
104 def name(self):
105 """Return the class name with any trailing 'Package' stripped off."""
106 class_name = self.__class__.__name__
107 if class_name.endswith('Package'):
108 return class_name[:-len('Package')]
109 return class_name
110
111
Dan Shi7b6297b2015-06-23 13:54:09 -0700112 def is_needed(self, install_dir):
beepsd5335852013-04-09 09:58:52 -0700113 """
114 Check to see if we need to reinstall a package. This is contingent on:
115 1. Module name: If the name of the module is different from the package,
116 the class that installs it needs to specify a module_name string,
117 so we can try importing the module.
118
119 2. Installed version: If the module doesn't contain a __version__ the
120 class that installs it needs to override the
121 _get_installed_version_from_module method to return an appropriate
122 version string.
123
124 3. Version/Minimum version: The class that installs the package should
125 contain a version string, and an optional minimum version string.
Dan Shi57631dc2013-02-22 09:54:14 -0800126
Dan Shi7b6297b2015-06-23 13:54:09 -0700127 4. install_dir: If the module exists in a different directory, e.g.,
128 /usr/lib/python2.7/dist-packages/, the module will be forced to be
129 installed in install_dir.
130
131 @param install_dir: install directory.
beepsd5335852013-04-09 09:58:52 -0700132 @returns True if self.module_name needs to be built and installed.
Dan Shi57631dc2013-02-22 09:54:14 -0800133 """
jamesrenff6e5aa2010-02-12 00:46:40 +0000134 if not self.module_name or not self.version:
135 logging.warning('version and module_name required for '
136 'is_needed() check to work.')
137 return True
138 try:
139 module = __import__(self.module_name)
140 except ImportError, e:
jamesrenca2a9002010-04-20 22:33:22 +0000141 logging.info("%s isn't present. Will install.", self.module_name)
jamesrenff6e5aa2010-02-12 00:46:40 +0000142 return True
Dan Shi16c0a502015-07-14 17:29:48 -0700143 if (not module.__file__.startswith(install_dir) and
144 not self.module_name in self.SYSTEM_MODULES):
Dan Shi7b6297b2015-06-23 13:54:09 -0700145 logging.info('Module %s is installed in %s, rather than %s. The '
146 'module will be forced to be installed in %s.',
147 self.module_name, module.__file__, install_dir,
148 install_dir)
149 return True
jamesrenff6e5aa2010-02-12 00:46:40 +0000150 self.installed_version = self._get_installed_version_from_module(module)
151 logging.info('imported %s version %s.', self.module_name,
152 self.installed_version)
Dale Curtis74a314b2011-06-23 14:55:46 -0700153 if hasattr(self, 'minimum_version'):
154 return self.minimum_version > self.installed_version
155 else:
156 return self.version > self.installed_version
jamesrenff6e5aa2010-02-12 00:46:40 +0000157
158
159 def _get_installed_version_from_module(self, module):
160 """Ask our module its version string and return it or '' if unknown."""
161 try:
162 return module.__version__
163 except AttributeError:
164 logging.error('could not get version from %s', module)
165 return ''
166
167
168 def _build_and_install(self, install_dir):
169 """Subclasses MUST provide their own implementation."""
170 raise NotImplementedError
171
172
173 def _build_and_install_current_dir(self, install_dir):
174 """
175 Subclasses that use _build_and_install_from_package() MUST provide
176 their own implementation of this method.
177 """
178 raise NotImplementedError
179
180
181 def build_and_install(self, install_dir):
182 """
183 Builds and installs the package. It must have been fetched already.
184
185 @param install_dir - The package installation directory. If it does
186 not exist it will be created.
187 """
188 if not self.verified_package:
189 raise Error('Must call fetch() first. - %s' % self.name)
190 self._check_os_requirements()
191 return self._build_and_install(install_dir)
192
193
194 def _check_os_requirements(self):
195 if not self.os_requirements:
196 return
197 failed = False
Michael Janssena7427612014-11-14 15:44:39 -0800198 for file_names, package_name in self.os_requirements.iteritems():
199 if not any(os.path.exists(file_name) for file_name in file_names):
jamesrenff6e5aa2010-02-12 00:46:40 +0000200 failed = True
Michael Janssena7427612014-11-14 15:44:39 -0800201 logging.error('Can\'t find %s, %s probably needs it.',
J. Richard Barnette6f7606b2015-06-11 17:26:23 -0700202 ' or '.join(file_names), self.name)
jamesrenff6e5aa2010-02-12 00:46:40 +0000203 logging.error('Perhaps you need to install something similar '
204 'to the %s package for OS first.', package_name)
205 if failed:
206 raise Error('Missing OS requirements for %s. (see above)' %
207 self.name)
208
209
210 def _build_and_install_current_dir_setup_py(self, install_dir):
211 """For use as a _build_and_install_current_dir implementation."""
212 egg_path = self._build_egg_using_setup_py(setup_py='setup.py')
213 if not egg_path:
214 return False
215 return self._install_from_egg(install_dir, egg_path)
216
217
218 def _build_and_install_current_dir_setupegg_py(self, install_dir):
219 """For use as a _build_and_install_current_dir implementation."""
220 egg_path = self._build_egg_using_setup_py(setup_py='setupegg.py')
221 if not egg_path:
222 return False
223 return self._install_from_egg(install_dir, egg_path)
224
225
226 def _build_and_install_current_dir_noegg(self, install_dir):
227 if not self._build_using_setup_py():
228 return False
229 return self._install_using_setup_py_and_rsync(install_dir)
230
231
232 def _build_and_install_from_package(self, install_dir):
233 """
234 This method may be used as a _build_and_install() implementation
235 for subclasses if they implement _build_and_install_current_dir().
236
237 Extracts the .tar.gz file, chdirs into the extracted directory
238 (which is assumed to match the tar filename) and calls
239 _build_and_isntall_current_dir from there.
240
241 Afterwards the build (regardless of failure) extracted .tar.gz
242 directory is cleaned up.
243
244 @returns True on success, False otherwise.
245
246 @raises OSError If the expected extraction directory does not exist.
247 """
248 self._extract_compressed_package()
249 if self.verified_package.endswith('.tar.gz'):
250 extension = '.tar.gz'
251 elif self.verified_package.endswith('.tar.bz2'):
252 extension = '.tar.bz2'
253 elif self.verified_package.endswith('.zip'):
254 extension = '.zip'
255 else:
256 raise Error('Unexpected package file extension on %s' %
257 self.verified_package)
258 os.chdir(os.path.dirname(self.verified_package))
259 os.chdir(self.local_filename[:-len(extension)])
260 extracted_dir = os.getcwd()
261 try:
262 return self._build_and_install_current_dir(install_dir)
263 finally:
264 os.chdir(os.path.join(extracted_dir, '..'))
265 shutil.rmtree(extracted_dir)
266
267
268 def _extract_compressed_package(self):
269 """Extract the fetched compressed .tar or .zip within its directory."""
270 if not self.verified_package:
271 raise Error('Package must have been fetched first.')
272 os.chdir(os.path.dirname(self.verified_package))
273 if self.verified_package.endswith('gz'):
274 status = system("tar -xzf '%s'" % self.verified_package)
275 elif self.verified_package.endswith('bz2'):
276 status = system("tar -xjf '%s'" % self.verified_package)
277 elif self.verified_package.endswith('zip'):
278 status = system("unzip '%s'" % self.verified_package)
279 else:
280 raise Error('Unknown compression suffix on %s.' %
281 self.verified_package)
282 if status:
283 raise Error('tar failed with %s' % (status,))
284
285
286 def _build_using_setup_py(self, setup_py='setup.py'):
287 """
288 Assuming the cwd is the extracted python package, execute a simple
289 python setup.py build.
290
291 @param setup_py - The name of the setup.py file to execute.
292
293 @returns True on success, False otherwise.
294 """
295 if not os.path.exists(setup_py):
296 raise Error('%s does not exist in %s' % (setup_py, os.getcwd()))
297 status = system("'%s' %s build" % (sys.executable, setup_py))
298 if status:
Dan Shi57631dc2013-02-22 09:54:14 -0800299 logging.error('%s build failed.', self.name)
jamesrenff6e5aa2010-02-12 00:46:40 +0000300 return False
301 return True
302
303
304 def _build_egg_using_setup_py(self, setup_py='setup.py'):
305 """
306 Assuming the cwd is the extracted python package, execute a simple
307 python setup.py bdist_egg.
308
309 @param setup_py - The name of the setup.py file to execute.
310
311 @returns The relative path to the resulting egg file or '' on failure.
312 """
313 if not os.path.exists(setup_py):
314 raise Error('%s does not exist in %s' % (setup_py, os.getcwd()))
315 egg_subdir = 'dist'
316 if os.path.isdir(egg_subdir):
317 shutil.rmtree(egg_subdir)
318 status = system("'%s' %s bdist_egg" % (sys.executable, setup_py))
319 if status:
320 logging.error('bdist_egg of setuptools failed.')
321 return ''
322 # I've never seen a bdist_egg lay multiple .egg files.
323 for filename in os.listdir(egg_subdir):
324 if filename.endswith('.egg'):
325 return os.path.join(egg_subdir, filename)
326
327
328 def _install_from_egg(self, install_dir, egg_path):
329 """
330 Install a module from an egg file by unzipping the necessary parts
331 into install_dir.
332
333 @param install_dir - The installation directory.
334 @param egg_path - The pathname of the egg file.
335 """
336 status = system("unzip -q -o -d '%s' '%s'" % (install_dir, egg_path))
337 if status:
338 logging.error('unzip of %s failed', egg_path)
339 return False
340 egg_info = os.path.join(install_dir, 'EGG-INFO')
341 if os.path.isdir(egg_info):
342 shutil.rmtree(egg_info)
343 return True
344
345
346 def _get_temp_dir(self):
347 return tempfile.mkdtemp(dir='/var/tmp')
348
349
350 def _site_packages_path(self, temp_dir):
351 # This makes assumptions about what python setup.py install
352 # does when given a prefix. Is this always correct?
353 python_xy = 'python%s' % sys.version[:3]
354 return os.path.join(temp_dir, 'lib', python_xy, 'site-packages')
355
356
beepsd9153b52013-01-23 20:52:46 -0800357 def _rsync (self, temp_site_dir, install_dir):
358 """Rsync contents. """
359 status = system("rsync -r '%s/' '%s/'" %
360 (os.path.normpath(temp_site_dir),
361 os.path.normpath(install_dir)))
362 if status:
363 logging.error('%s rsync to install_dir failed.', self.name)
364 return False
365 return True
366
367
jamesrenff6e5aa2010-02-12 00:46:40 +0000368 def _install_using_setup_py_and_rsync(self, install_dir,
369 setup_py='setup.py',
370 temp_dir=None):
371 """
372 Assuming the cwd is the extracted python package, execute a simple:
373
374 python setup.py install --prefix=BLA
375
376 BLA will be a temporary directory that everything installed will
377 be picked out of and rsynced to the appropriate place under
378 install_dir afterwards.
379
380 Afterwards, it deconstructs the extra lib/pythonX.Y/site-packages/
381 directory tree that setuptools created and moves all installed
382 site-packages directly up into install_dir itself.
383
384 @param install_dir the directory for the install to happen under.
385 @param setup_py - The name of the setup.py file to execute.
386
387 @returns True on success, False otherwise.
388 """
389 if not os.path.exists(setup_py):
390 raise Error('%s does not exist in %s' % (setup_py, os.getcwd()))
391
392 if temp_dir is None:
393 temp_dir = self._get_temp_dir()
394
395 try:
396 status = system("'%s' %s install --no-compile --prefix='%s'"
397 % (sys.executable, setup_py, temp_dir))
398 if status:
Dan Shi57631dc2013-02-22 09:54:14 -0800399 logging.error('%s install failed.', self.name)
jamesrenff6e5aa2010-02-12 00:46:40 +0000400 return False
401
402 if os.path.isdir(os.path.join(temp_dir, 'lib')):
403 # NOTE: This ignores anything outside of the lib/ dir that
404 # was installed.
405 temp_site_dir = self._site_packages_path(temp_dir)
406 else:
407 temp_site_dir = temp_dir
408
beepsd9153b52013-01-23 20:52:46 -0800409 return self._rsync(temp_site_dir, install_dir)
jamesrenff6e5aa2010-02-12 00:46:40 +0000410 finally:
411 shutil.rmtree(temp_dir)
412
413
414
415 def _build_using_make(self, install_dir):
416 """Build the current package using configure/make.
417
418 @returns True on success, False otherwise.
419 """
420 install_prefix = os.path.join(install_dir, 'usr', 'local')
421 status = system('./configure --prefix=%s' % install_prefix)
422 if status:
423 logging.error('./configure failed for %s', self.name)
424 return False
425 status = system('make')
426 if status:
427 logging.error('make failed for %s', self.name)
428 return False
429 status = system('make check')
430 if status:
431 logging.error('make check failed for %s', self.name)
432 return False
433 return True
434
435
436 def _install_using_make(self):
437 """Install the current package using make install.
438
439 Assumes the install path was set up while running ./configure (in
440 _build_using_make()).
441
442 @returns True on success, False otherwise.
443 """
444 status = system('make install')
445 return status == 0
446
447
448 def fetch(self, dest_dir):
449 """
450 Fetch the package from one its URLs and save it in dest_dir.
451
452 If the the package already exists in dest_dir and the checksum
453 matches this code will not fetch it again.
454
455 Sets the 'verified_package' attribute with the destination pathname.
456
457 @param dest_dir - The destination directory to save the local file.
458 If it does not exist it will be created.
459
460 @returns A boolean indicating if we the package is now in dest_dir.
461 @raises FetchError - When something unexpected happens.
462 """
463 if not os.path.exists(dest_dir):
464 os.makedirs(dest_dir)
465 local_path = os.path.join(dest_dir, self.local_filename)
466
467 # If the package exists, verify its checksum and be happy if it is good.
468 if os.path.exists(local_path):
469 actual_hex_sum = _checksum_file(local_path)
470 if self.hex_sum == actual_hex_sum:
471 logging.info('Good checksum for existing %s package.',
472 self.name)
473 self.verified_package = local_path
474 return True
475 logging.warning('Bad checksum for existing %s package. '
476 'Re-downloading', self.name)
477 os.rename(local_path, local_path + '.wrong-checksum')
478
479 # Download the package from one of its urls, rejecting any if the
480 # checksum does not match.
481 for url in self.urls:
482 logging.info('Fetching %s', url)
483 try:
484 url_file = urllib2.urlopen(url)
485 except (urllib2.URLError, EnvironmentError):
486 logging.warning('Could not fetch %s package from %s.',
487 self.name, url)
488 continue
Dan Shi57631dc2013-02-22 09:54:14 -0800489
jamesrenff6e5aa2010-02-12 00:46:40 +0000490 data_length = int(url_file.info().get('Content-Length',
491 _MAX_PACKAGE_SIZE))
492 if data_length <= 0 or data_length > _MAX_PACKAGE_SIZE:
493 raise FetchError('%s from %s fails Content-Length %d '
494 'sanity check.' % (self.name, url,
495 data_length))
496 checksum = utils.hash('sha1')
497 total_read = 0
498 output = open(local_path, 'wb')
499 try:
500 while total_read < data_length:
501 data = url_file.read(_READ_SIZE)
502 if not data:
503 break
504 output.write(data)
505 checksum.update(data)
506 total_read += len(data)
507 finally:
508 output.close()
509 if self.hex_sum != checksum.hexdigest():
510 logging.warning('Bad checksum for %s fetched from %s.',
511 self.name, url)
512 logging.warning('Got %s', checksum.hexdigest())
513 logging.warning('Expected %s', self.hex_sum)
514 os.unlink(local_path)
515 continue
516 logging.info('Good checksum.')
517 self.verified_package = local_path
518 return True
519 else:
520 return False
521
522
523# NOTE: This class definition must come -before- all other ExternalPackage
524# classes that need to use this version of setuptools so that is is inserted
525# into the ExternalPackage.subclasses list before them.
526class SetuptoolsPackage(ExternalPackage):
Dan Shi57631dc2013-02-22 09:54:14 -0800527 """setuptools package"""
jamesrenff6e5aa2010-02-12 00:46:40 +0000528 # For all known setuptools releases a string compare works for the
529 # version string. Hopefully they never release a 0.10. (Their own
530 # version comparison code would break if they did.)
Dan Shi16c0a502015-07-14 17:29:48 -0700531 # Any system with setuptools > 18.0.1 is fine. If none installed, then
Dale Curtis74a314b2011-06-23 14:55:46 -0700532 # try to install the latest found on the upstream.
Dan Shi16c0a502015-07-14 17:29:48 -0700533 minimum_version = '18.0.1'
534 version = '18.0.1'
jamesrenff6e5aa2010-02-12 00:46:40 +0000535 urls = ('http://pypi.python.org/packages/source/s/setuptools/'
536 'setuptools-%s.tar.gz' % (version,),)
537 local_filename = 'setuptools-%s.tar.gz' % version
Dan Shi16c0a502015-07-14 17:29:48 -0700538 hex_sum = 'ebc4fe81b7f6d61d923d9519f589903824044f52'
jamesrenff6e5aa2010-02-12 00:46:40 +0000539
540 SUDO_SLEEP_DELAY = 15
541
542
543 def _build_and_install(self, install_dir):
544 """Install setuptools on the system."""
545 logging.info('NOTE: setuptools install does not use install_dir.')
546 return self._build_and_install_from_package(install_dir)
547
548
549 def _build_and_install_current_dir(self, install_dir):
550 egg_path = self._build_egg_using_setup_py()
551 if not egg_path:
552 return False
553
554 print '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n'
555 print 'About to run sudo to install setuptools', self.version
556 print 'on your system for use by', sys.executable, '\n'
557 print '!! ^C within', self.SUDO_SLEEP_DELAY, 'seconds to abort.\n'
558 time.sleep(self.SUDO_SLEEP_DELAY)
559
560 # Copy the egg to the local filesystem /var/tmp so that root can
561 # access it properly (avoid NFS squashroot issues).
562 temp_dir = self._get_temp_dir()
563 try:
564 shutil.copy(egg_path, temp_dir)
565 egg_name = os.path.split(egg_path)[1]
566 temp_egg = os.path.join(temp_dir, egg_name)
567 p = subprocess.Popen(['sudo', '/bin/sh', temp_egg],
568 stdout=subprocess.PIPE)
569 regex = re.compile('Copying (.*?) to (.*?)\n')
570 match = regex.search(p.communicate()[0])
571 status = p.wait()
572
573 if match:
574 compiled = os.path.join(match.group(2), match.group(1))
575 os.system("sudo chmod a+r '%s'" % compiled)
576 finally:
577 shutil.rmtree(temp_dir)
578
579 if status:
580 logging.error('install of setuptools from egg failed.')
581 return False
582 return True
583
584
585class MySQLdbPackage(ExternalPackage):
Dan Shi57631dc2013-02-22 09:54:14 -0800586 """mysql package, used in scheduler."""
jamesrenff6e5aa2010-02-12 00:46:40 +0000587 module_name = 'MySQLdb'
Alex Miller47d61282013-04-17 13:53:58 -0700588 version = '1.2.3'
jamesrenff6e5aa2010-02-12 00:46:40 +0000589 urls = ('http://downloads.sourceforge.net/project/mysql-python/'
590 'mysql-python/%(version)s/MySQL-python-%(version)s.tar.gz'
591 % dict(version=version),)
592 local_filename = 'MySQL-python-%s.tar.gz' % version
Alex Miller47d61282013-04-17 13:53:58 -0700593 hex_sum = '3511bb8c57c6016eeafa531d5c3ea4b548915e3c'
jamesrenff6e5aa2010-02-12 00:46:40 +0000594
595 _build_and_install_current_dir = (
596 ExternalPackage._build_and_install_current_dir_setup_py)
597
598
599 def _build_and_install(self, install_dir):
600 if not os.path.exists('/usr/bin/mysql_config'):
Dan Shi16c0a502015-07-14 17:29:48 -0700601 error_msg = ('You need to install /usr/bin/mysql_config.\n'
602 'On Ubuntu or Debian based systems use this: '
603 'sudo apt-get install libmysqlclient15-dev')
604 logging.error(error_msg)
605 return False, error_msg
jamesrenff6e5aa2010-02-12 00:46:40 +0000606 return self._build_and_install_from_package(install_dir)
607
608
609class DjangoPackage(ExternalPackage):
Dan Shi57631dc2013-02-22 09:54:14 -0800610 """django package."""
Alex Miller47d61282013-04-17 13:53:58 -0700611 version = '1.5.1'
jamesrenff6e5aa2010-02-12 00:46:40 +0000612 local_filename = 'Django-%s.tar.gz' % version
613 urls = ('http://www.djangoproject.com/download/%s/tarball/' % version,)
Alex Miller47d61282013-04-17 13:53:58 -0700614 hex_sum = '0ab97b90c4c79636e56337f426f1e875faccbba1'
jamesrenff6e5aa2010-02-12 00:46:40 +0000615
616 _build_and_install = ExternalPackage._build_and_install_from_package
617 _build_and_install_current_dir = (
618 ExternalPackage._build_and_install_current_dir_noegg)
619
620
621 def _get_installed_version_from_module(self, module):
622 try:
623 return module.get_version().split()[0]
624 except AttributeError:
625 return '0.9.6'
626
627
628
629class NumpyPackage(ExternalPackage):
Dan Shi57631dc2013-02-22 09:54:14 -0800630 """numpy package, required by matploglib."""
Scott Zawalskic2c9a012013-03-06 09:13:26 -0500631 version = '1.7.0'
jamesrenff6e5aa2010-02-12 00:46:40 +0000632 local_filename = 'numpy-%s.tar.gz' % version
633 urls = ('http://downloads.sourceforge.net/project/numpy/NumPy/%(version)s/'
634 'numpy-%(version)s.tar.gz' % dict(version=version),)
Scott Zawalskic2c9a012013-03-06 09:13:26 -0500635 hex_sum = 'ba328985f20390b0f969a5be2a6e1141d5752cf9'
jamesrenff6e5aa2010-02-12 00:46:40 +0000636
637 _build_and_install = ExternalPackage._build_and_install_from_package
638 _build_and_install_current_dir = (
639 ExternalPackage._build_and_install_current_dir_setupegg_py)
640
641
jamesrenff6e5aa2010-02-12 00:46:40 +0000642class MatplotlibPackage(ExternalPackage):
Dan Shi57631dc2013-02-22 09:54:14 -0800643 """
644 matplotlib package
645
646 This requires numpy so it must be declared after numpy to guarantee that
647 it is already installed.
648 """
jamesrenff6e5aa2010-02-12 00:46:40 +0000649 version = '0.98.5.3'
650 short_version = '0.98.5'
651 local_filename = 'matplotlib-%s.tar.gz' % version
652 urls = ('http://downloads.sourceforge.net/project/matplotlib/matplotlib/'
653 'matplotlib-%s/matplotlib-%s.tar.gz' % (short_version, version),)
654 hex_sum = '2f6c894cf407192b3b60351bcc6468c0385d47b6'
Michael Janssena7427612014-11-14 15:44:39 -0800655 os_requirements = {('/usr/include/freetype2/ft2build.h',
656 '/usr/include/ft2build.h'): 'libfreetype6-dev',
657 ('/usr/include/png.h'): 'libpng12-dev'}
jamesrenff6e5aa2010-02-12 00:46:40 +0000658
659 _build_and_install = ExternalPackage._build_and_install_from_package
660 _build_and_install_current_dir = (
661 ExternalPackage._build_and_install_current_dir_setupegg_py)
662
663
664class AtForkPackage(ExternalPackage):
Dan Shi57631dc2013-02-22 09:54:14 -0800665 """atfork package"""
jamesrenff6e5aa2010-02-12 00:46:40 +0000666 version = '0.1.2'
667 local_filename = 'atfork-%s.zip' % version
668 urls = ('http://python-atfork.googlecode.com/files/' + local_filename,)
669 hex_sum = '5baa64c73e966b57fa797040585c760c502dc70b'
670
671 _build_and_install = ExternalPackage._build_and_install_from_package
672 _build_and_install_current_dir = (
673 ExternalPackage._build_and_install_current_dir_noegg)
674
675
676class ParamikoPackage(ExternalPackage):
Dan Shi57631dc2013-02-22 09:54:14 -0800677 """paramiko package"""
jamesrenff6e5aa2010-02-12 00:46:40 +0000678 version = '1.7.5'
679 local_filename = 'paramiko-%s.tar.gz' % version
Eric Li861b2d52011-02-04 14:50:35 -0800680 urls = ('http://www.lag.net/paramiko/download/' + local_filename,
681 'ftp://mirrors.kernel.org/gentoo/distfiles/' + local_filename,)
jamesrenff6e5aa2010-02-12 00:46:40 +0000682 hex_sum = '592be7a08290070b71da63a8e6f28a803399e5c5'
683
684
685 _build_and_install = ExternalPackage._build_and_install_from_package
686
687
688 def _check_for_pycrypto(self):
689 # NOTE(gps): Linux distros have better python-crypto packages than we
690 # can easily get today via a wget due to the library's age and staleness
691 # yet many security and behavior bugs are fixed by patches that distros
692 # already apply. PyCrypto has a new active maintainer in 2009. Once a
693 # new release is made (http://pycrypto.org/) we should add an installer.
694 try:
695 import Crypto
696 except ImportError:
697 logging.error('Please run "sudo apt-get install python-crypto" '
698 'or your Linux distro\'s equivalent.')
699 return False
700 return True
701
702
703 def _build_and_install_current_dir(self, install_dir):
704 if not self._check_for_pycrypto():
705 return False
706 # paramiko 1.7.4 doesn't require building, it is just a module directory
707 # that we can rsync into place directly.
708 if not os.path.isdir('paramiko'):
709 raise Error('no paramiko directory in %s.' % os.getcwd())
710 status = system("rsync -r 'paramiko' '%s/'" % install_dir)
711 if status:
Dan Shi57631dc2013-02-22 09:54:14 -0800712 logging.error('%s rsync to install_dir failed.', self.name)
jamesrenff6e5aa2010-02-12 00:46:40 +0000713 return False
714 return True
715
716
Chris Masonebafbbb02012-05-16 13:41:36 -0700717class RequestsPackage(ExternalPackage):
Dan Shi57631dc2013-02-22 09:54:14 -0800718 """requests package"""
Chris Masonebafbbb02012-05-16 13:41:36 -0700719 version = '0.11.2'
720 local_filename = 'requests-%s.tar.gz' % version
721 urls = ('http://pypi.python.org/packages/source/r/requests/' +
722 local_filename,)
723 hex_sum = '00a49e8bd6dd8955acf6f6269d1b85f50c70b712'
724
725 _build_and_install = ExternalPackage._build_and_install_from_package
726 _build_and_install_current_dir = (
727 ExternalPackage._build_and_install_current_dir_setup_py)
728
729
beeps32a63082013-08-22 14:02:29 -0700730class JsonRPCLib(ExternalPackage):
731 """jsonrpclib package"""
732 version = '0.1.3'
733 module_name = 'jsonrpclib'
734 local_filename = '%s-%s.tar.gz' % (module_name, version)
735 urls = ('http://pypi.python.org/packages/source/j/%s/%s' %
736 (module_name, local_filename), )
737 hex_sum = '431714ed19ab677f641ce5d678a6a95016f5c452'
738
739 def _get_installed_version_from_module(self, module):
740 # jsonrpclib doesn't contain a proper version
741 return self.version
742
743 _build_and_install = ExternalPackage._build_and_install_from_package
744 _build_and_install_current_dir = (
745 ExternalPackage._build_and_install_current_dir_noegg)
746
747
jamesrenff6e5aa2010-02-12 00:46:40 +0000748class Httplib2Package(ExternalPackage):
Dan Shi57631dc2013-02-22 09:54:14 -0800749 """httplib2 package"""
jamesrenff6e5aa2010-02-12 00:46:40 +0000750 version = '0.6.0'
751 local_filename = 'httplib2-%s.tar.gz' % version
752 urls = ('http://httplib2.googlecode.com/files/' + local_filename,)
753 hex_sum = '995344b2704826cc0d61a266e995b328d92445a5'
754
755 def _get_installed_version_from_module(self, module):
756 # httplib2 doesn't contain a proper version
757 return self.version
758
759 _build_and_install = ExternalPackage._build_and_install_from_package
760 _build_and_install_current_dir = (
761 ExternalPackage._build_and_install_current_dir_noegg)
762
763
764class GwtPackage(ExternalPackage):
765 """Fetch and extract a local copy of GWT used to build the frontend."""
766
Dale Curtis74a314b2011-06-23 14:55:46 -0700767 version = '2.3.0'
jamesren012d0322010-04-30 20:21:29 +0000768 local_filename = 'gwt-%s.zip' % version
jamesrenff6e5aa2010-02-12 00:46:40 +0000769 urls = ('http://google-web-toolkit.googlecode.com/files/' + local_filename,)
Dale Curtis74a314b2011-06-23 14:55:46 -0700770 hex_sum = 'd51fce9166e6b31349659ffca89baf93e39bc84b'
jamesrenff6e5aa2010-02-12 00:46:40 +0000771 name = 'gwt'
772 about_filename = 'about.txt'
773 module_name = None # Not a Python module.
774
775
776 def is_needed(self, install_dir):
777 gwt_dir = os.path.join(install_dir, self.name)
778 about_file = os.path.join(install_dir, self.name, self.about_filename)
779
780 if not os.path.exists(gwt_dir) or not os.path.exists(about_file):
781 logging.info('gwt not installed for autotest')
782 return True
783
784 f = open(about_file, 'r')
785 version_line = f.readline()
786 f.close()
787
788 match = re.match(r'Google Web Toolkit (.*)', version_line)
789 if not match:
790 logging.info('did not find gwt version')
791 return True
792
793 logging.info('found gwt version %s', match.group(1))
794 return match.group(1) != self.version
795
796
jamesrenca2a9002010-04-20 22:33:22 +0000797 def _build_and_install(self, install_dir):
jamesrenff6e5aa2010-02-12 00:46:40 +0000798 os.chdir(install_dir)
799 self._extract_compressed_package()
jamesren012d0322010-04-30 20:21:29 +0000800 extracted_dir = self.local_filename[:-len('.zip')]
jamesrenff6e5aa2010-02-12 00:46:40 +0000801 target_dir = os.path.join(install_dir, self.name)
802 if os.path.exists(target_dir):
803 shutil.rmtree(target_dir)
804 os.rename(extracted_dir, target_dir)
805 return True
806
807
Mike Trutyddd44b22011-04-14 15:38:56 -0700808class GVizAPIPackage(ExternalPackage):
Dan Shi57631dc2013-02-22 09:54:14 -0800809 """gviz package"""
Alex Miller0e1217c2013-02-22 10:11:08 -0800810 module_name = 'gviz_api'
Mike Trutyddd44b22011-04-14 15:38:56 -0700811 version = '1.7.0'
812 url_filename = 'gviz_api_py-%s.tar.gz' % version
813 local_filename = 'google-visualization-python.tar.gz'
814 urls = ('http://google-visualization-python.googlecode.com/files/%s' % (
815 url_filename),)
816 hex_sum = 'cd9a0fb4ca5c4f86c0d85756f501fd54ccf492d2'
817
818 _build_and_install = ExternalPackage._build_and_install_from_package
819 _build_and_install_current_dir = (
820 ExternalPackage._build_and_install_current_dir_noegg)
beepsd9153b52013-01-23 20:52:46 -0800821
Alex Miller0e1217c2013-02-22 10:11:08 -0800822 def _get_installed_version_from_module(self, module):
823 # gviz doesn't contain a proper version
824 return self.version
825
beepsd9153b52013-01-23 20:52:46 -0800826
Scott Zawalski2a6acf82013-02-11 16:18:27 -0500827class StatsdPackage(ExternalPackage):
Dan Shi57631dc2013-02-22 09:54:14 -0800828 """python-statsd package"""
Fang Dengc0594a62015-07-14 15:14:48 -0700829 version = '1.7.2'
Scott Zawalski2a6acf82013-02-11 16:18:27 -0500830 url_filename = 'python-statsd-%s.tar.gz' % version
831 local_filename = url_filename
832 urls = ('http://pypi.python.org/packages/source/p/python-statsd/%s' % (
833 url_filename),)
Fang Dengc0594a62015-07-14 15:14:48 -0700834 hex_sum = '2cc186ebdb723e2420b432ab71639786d877694b'
Scott Zawalski2a6acf82013-02-11 16:18:27 -0500835
836 _build_and_install = ExternalPackage._build_and_install_from_package
837 _build_and_install_current_dir = (
838 ExternalPackage._build_and_install_current_dir_setup_py)
839
840
beeps93bef482013-02-04 16:24:22 -0800841class GdataPackage(ExternalPackage):
842 """
843 Pulls the GData library, giving us an API to query tracker.
844 """
845
846 version = '2.0.14'
847 url_filename = 'gdata-%s.tar.gz' % version
848 local_filename = url_filename
849 urls = ('http://gdata-python-client.googlecode.com/files/%s' % (
850 url_filename),)
851 hex_sum = '5eed0e01ab931e3f706ec544fc8f06ecac384e91'
852
853 _build_and_install = ExternalPackage._build_and_install_from_package
854 _build_and_install_current_dir = (
855 ExternalPackage._build_and_install_current_dir_noegg)
856
Alex Miller0e1217c2013-02-22 10:11:08 -0800857 def _get_installed_version_from_module(self, module):
858 # gdata doesn't contain a proper version
859 return self.version
860
beeps93bef482013-02-04 16:24:22 -0800861
beepse0db09d2013-02-24 17:13:21 -0800862class GoogleAPIClientPackage(ExternalPackage):
863 """
864 Pulls the Python Google API client library.
865 """
866 version = '1.1'
beepsd5335852013-04-09 09:58:52 -0700867 module_name = 'apiclient'
beepse0db09d2013-02-24 17:13:21 -0800868 url_filename = 'google-api-python-client-%s.tar.gz' % version
869 local_filename = url_filename
870 urls = ('https://google-api-python-client.googlecode.com/files/%s' % (
871 url_filename),)
872 hex_sum = '2294949683e367b3d4ecaeb77502509c5af21e60'
873
874 _build_and_install = ExternalPackage._build_and_install_from_package
875 _build_and_install_current_dir = (
876 ExternalPackage._build_and_install_current_dir_setup_py)
877
878
beepsd5335852013-04-09 09:58:52 -0700879class GFlagsPackage(ExternalPackage):
880 """
881 Gets the Python GFlags client library.
882 """
883 # gflags doesn't contain a proper version
884 version = '2.0'
885 url_filename = 'python-gflags-%s.tar.gz' % version
886 local_filename = url_filename
887 urls = ('https://python-gflags.googlecode.com/files/%s' % (
888 url_filename),)
889 hex_sum = 'db309e6964b102ff36de319ce551db512a78281e'
890
891 _build_and_install = ExternalPackage._build_and_install_from_package
892 _build_and_install_current_dir = (
893 ExternalPackage._build_and_install_current_dir_setup_py)
894
895
896 def _get_installed_version_from_module(self, module):
897 return self.version
898
899
Alex Millere7b6e8b2013-02-13 17:34:04 -0800900class DnsPythonPackage(ExternalPackage):
901 """
902 dns module
903
904 Used in unittests.
905 """
906 module_name = 'dns'
907 version = '1.3.5'
908 url_filename = 'dnspython-%s.tar.gz' % version
909 local_filename = url_filename
910 urls = ('http://www.dnspython.org/kits/%s/%s' % (
Dan Shi57631dc2013-02-22 09:54:14 -0800911 version, url_filename),)
912
Alex Millere7b6e8b2013-02-13 17:34:04 -0800913 hex_sum = '06314dad339549613435470c6add992910e26e5d'
914
915 _build_and_install = ExternalPackage._build_and_install_from_package
916 _build_and_install_current_dir = (
917 ExternalPackage._build_and_install_current_dir_noegg)
918
919 def _get_installed_version_from_module(self, module):
920 """Ask our module its version string and return it or '' if unknown."""
921 try:
922 __import__(self.module_name + '.version')
923 return module.version.version
924 except AttributeError:
925 logging.error('could not get version from %s', module)
926 return ''
927
928
929class PyudevPackage(ExternalPackage):
930 """
931 pyudev module
932
933 Used in unittests.
934 """
935 version = '0.16.1'
936 url_filename = 'pyudev-%s.tar.gz' % version
937 local_filename = url_filename
938 urls = ('http://pypi.python.org/packages/source/p/pyudev/%s' % (
939 url_filename),)
940 hex_sum = 'b36bc5c553ce9b56d32a5e45063a2c88156771c0'
941
942 _build_and_install = ExternalPackage._build_and_install_from_package
943 _build_and_install_current_dir = (
944 ExternalPackage._build_and_install_current_dir_setup_py)
945
946
947class PyMoxPackage(ExternalPackage):
948 """
949 mox module
950
951 Used in unittests.
952 """
953 module_name = 'mox'
954 version = '0.5.3'
955 url_filename = 'mox-%s.tar.gz' % version
956 local_filename = url_filename
957 urls = ('http://pypi.python.org/packages/source/m/mox/%s' % (
958 url_filename),)
959 hex_sum = '1c502d2c0a8aefbba2c7f385a83d33e7d822452a'
960
961 _build_and_install = ExternalPackage._build_and_install_from_package
962 _build_and_install_current_dir = (
963 ExternalPackage._build_and_install_current_dir_noegg)
964
965 def _get_installed_version_from_module(self, module):
966 # mox doesn't contain a proper version
967 return self.version
968
969
Jason Abele2371d1a2013-11-22 12:18:18 -0800970class PySeleniumPackage(ExternalPackage):
971 """
972 selenium module
973
974 Used in wifi_interop suite.
975 """
976 module_name = 'selenium'
977 version = '2.37.2'
978 url_filename = 'selenium-%s.tar.gz' % version
979 local_filename = url_filename
980 urls = ('https://pypi.python.org/packages/source/s/selenium/%s' % (
981 url_filename),)
982 hex_sum = '66946d5349e36d946daaad625c83c30c11609e36'
983
984 _build_and_install = ExternalPackage._build_and_install_from_package
985 _build_and_install_current_dir = (
986 ExternalPackage._build_and_install_current_dir_setup_py)
987
988
Simran Basid6b83772014-01-06 16:31:30 -0800989class FaultHandlerPackage(ExternalPackage):
990 """
991 faulthandler module
992 """
993 module_name = 'faulthandler'
994 version = '2.3'
995 url_filename = '%s-%s.tar.gz' % (module_name, version)
996 local_filename = url_filename
997 urls = ('http://pypi.python.org/packages/source/f/faulthandler/%s' %
998 (url_filename),)
999 hex_sum = 'efb30c068414fba9df892e48fcf86170cbf53589'
1000
1001 _build_and_install = ExternalPackage._build_and_install_from_package
1002 _build_and_install_current_dir = (
1003 ExternalPackage._build_and_install_current_dir_noegg)
1004
1005
Alex Millera87e5482014-06-09 16:04:49 -07001006class PsutilPackage(ExternalPackage):
1007 """
1008 psutil module
1009 """
1010 module_name = 'psutil'
1011 version = '2.1.1'
1012 url_filename = '%s-%s.tar.gz' % (module_name, version)
1013 local_filename = url_filename
1014 urls = ('http://pypi.python.org/packages/source/p/psutil/%s' %
1015 (url_filename),)
1016 hex_sum = '0c20a20ed316e69f2b0881530439213988229916'
1017
1018 _build_and_install = ExternalPackage._build_and_install_from_package
1019 _build_and_install_current_dir = (
1020 ExternalPackage._build_and_install_current_dir_setup_py)
1021
1022
Michael Liangd2d294c2014-06-24 15:24:49 -07001023class ElasticSearchPackage(ExternalPackage):
1024 """elasticsearch-py package."""
1025 version = '1.0.0'
1026 url_filename = 'elasticsearch-%s.tar.gz' % version
1027 local_filename = url_filename
Michael Liang5934b712014-08-12 15:45:14 -07001028 urls = ('https://pypi.python.org/packages/source/e/elasticsearch/%s' %
1029 (url_filename),)
Michael Liangd2d294c2014-06-24 15:24:49 -07001030 hex_sum = 'e53e93eb2729c1dcd1bc3453d22340314027e900'
1031 _build_and_install = ExternalPackage._build_and_install_from_package
1032 _build_and_install_current_dir = (
1033 ExternalPackage._build_and_install_current_dir_setup_py)
1034
1035
Michael Liang5934b712014-08-12 15:45:14 -07001036class Urllib3Package(ExternalPackage):
1037 """elasticsearch-py package."""
1038 version = '1.9'
1039 url_filename = 'urllib3-%s.tar.gz' % version
1040 local_filename = url_filename
1041 urls = ('https://pypi.python.org/packages/source/u/urllib3/%s' %
1042 (url_filename),)
1043 hex_sum = '9522197efb2a2b49ce804de3a515f06d97b6602f'
1044 _build_and_install = ExternalPackage._build_and_install_from_package
1045 _build_and_install_current_dir = (
1046 ExternalPackage._build_and_install_current_dir_setup_py)
1047
1048
J. Richard Barnette428b3442014-07-22 11:38:55 -07001049class ImagingLibraryPackage(ExternalPackage):
1050 """Python Imaging Library (PIL)."""
1051 version = '1.1.7'
1052 url_filename = 'Imaging-%s.tar.gz' % version
1053 local_filename = url_filename
1054 urls = ('http://effbot.org/downloads/%s' % url_filename,)
1055 hex_sum = '76c37504251171fda8da8e63ecb8bc42a69a5c81'
Dan Shi2d9a28c2015-03-13 21:20:32 -07001056 # The path of zlib library might be different from what PIL setup.py is
1057 # expected. Following change does the best attempt to link the library
1058 # to a path PIL setup.py will try.
1059 libz_possible_path = '/usr/lib/x86_64-linux-gnu/libz.so'
1060 libz_expected_path = '/usr/lib/libz.so'
1061 if (os.path.exists(libz_possible_path) and
1062 not os.path.exists(libz_expected_path)):
1063 utils.run('sudo ln -s %s %s' % (libz_possible_path, libz_expected_path))
J. Richard Barnette428b3442014-07-22 11:38:55 -07001064 _build_and_install = ExternalPackage._build_and_install_from_package
1065 _build_and_install_current_dir = (
1066 ExternalPackage._build_and_install_current_dir_noegg)
1067
1068
beepsd9153b52013-01-23 20:52:46 -08001069class _ExternalGitRepo(ExternalPackage):
1070 """
1071 Parent class for any package which needs to pull a git repo.
1072
1073 This class inherits from ExternalPackage only so we can sync git
1074 repos through the build_externals script. We do not reuse any of
1075 ExternalPackage's other methods. Any package that needs a git repo
1076 should subclass this and override build_and_install or fetch as
1077 they see appropriate.
1078 """
1079
Michael Janssena7427612014-11-14 15:44:39 -08001080 os_requirements = {('/usr/bin/git') : 'git-core'}
beepsd9153b52013-01-23 20:52:46 -08001081
Prathmesh Prabhua637db42015-04-06 17:59:27 -07001082 # All the chromiumos projects used on the lab servers should have a 'prod'
1083 # branch used to track the software version deployed in prod.
1084 PROD_BRANCH = 'prod'
1085
beepsd9153b52013-01-23 20:52:46 -08001086 def is_needed(self, unused_install_dir):
1087 """Tell build_externals that we need to fetch."""
1088 # TODO(beeps): check if we're already upto date.
1089 return True
1090
1091
1092 def build_and_install(self, unused_install_dir):
1093 """
1094 Fall through method to install a package.
1095
1096 Overwritten in base classes to pull a git repo.
1097 """
1098 raise NotImplementedError
1099
1100
1101 def fetch(self, unused_dest_dir):
1102 """Fallthrough method to fetch a package."""
1103 return True
1104
1105
1106class HdctoolsRepo(_ExternalGitRepo):
1107 """Clones or updates the hdctools repo."""
1108
Alex Miller0e1217c2013-02-22 10:11:08 -08001109 module_name = 'servo'
beepsd9153b52013-01-23 20:52:46 -08001110 temp_hdctools_dir = tempfile.mktemp(suffix='hdctools')
Alex Miller9fbe67f2013-09-06 15:04:48 -07001111 _GIT_URL = ('https://chromium.googlesource.com/'
beepsd9153b52013-01-23 20:52:46 -08001112 'chromiumos/third_party/hdctools')
1113
1114 def fetch(self, unused_dest_dir):
1115 """
1116 Fetch repo to a temporary location.
1117
1118 We use an intermediate temp directory to stage our
1119 installation because we only care about the servo package.
1120 If we can't get at the top commit hash after fetching
1121 something is wrong. This can happen when we've cloned/pulled
1122 an empty repo. Not something we expect to do.
1123
1124 @parma unused_dest_dir: passed in because we inherit from
1125 ExternalPackage.
1126
1127 @return: True if repo sync was successful.
1128 """
1129 git_repo = revision_control.GitRepo(
1130 self.temp_hdctools_dir,
1131 self._GIT_URL,
1132 None,
beepsaae3f1c2013-03-19 15:49:14 -07001133 abs_work_tree=self.temp_hdctools_dir)
Prathmesh Prabhu5f6b2332015-04-10 16:41:28 -07001134 git_repo.reinit_repo_at(self.PROD_BRANCH)
beepsd9153b52013-01-23 20:52:46 -08001135
1136 if git_repo.get_latest_commit_hash():
1137 return True
1138 return False
1139
1140
1141 def build_and_install(self, install_dir):
1142 """Reach into the hdctools repo and rsync only the servo directory."""
1143
1144 servo_dir = os.path.join(self.temp_hdctools_dir, 'servo')
1145 if not os.path.exists(servo_dir):
1146 return False
1147
1148 rv = self._rsync(servo_dir, os.path.join(install_dir, 'servo'))
1149 shutil.rmtree(self.temp_hdctools_dir)
1150 return rv
1151
1152
1153class ChromiteRepo(_ExternalGitRepo):
1154 """Clones or updates the chromite repo."""
1155
Alex Miller9fbe67f2013-09-06 15:04:48 -07001156 _GIT_URL = ('https://chromium.googlesource.com/chromiumos/chromite')
beepsd9153b52013-01-23 20:52:46 -08001157
1158 def build_and_install(self, install_dir):
1159 """
1160 Clone if the repo isn't initialized, pull clean bits if it is.
1161
1162 Unlike it's hdctools counterpart the chromite repo clones master
1163 directly into site-packages. It doesn't use an intermediate temp
Chris Sosa1bf41c42013-02-27 11:34:23 -08001164 directory because it doesn't need installation.
beepsd9153b52013-01-23 20:52:46 -08001165
1166 @param install_dir: destination directory for chromite installation.
1167 """
1168 local_chromite_dir = os.path.join(install_dir, 'chromite')
Prathmesh Prabhua637db42015-04-06 17:59:27 -07001169 git_repo = revision_control.GitRepo(
1170 local_chromite_dir,
1171 self._GIT_URL,
1172 abs_work_tree=local_chromite_dir)
Prathmesh Prabhu5f6b2332015-04-10 16:41:28 -07001173 git_repo.reinit_repo_at(self.PROD_BRANCH)
Prathmesh Prabhua637db42015-04-06 17:59:27 -07001174
Chris Sosa1bf41c42013-02-27 11:34:23 -08001175
1176 if git_repo.get_latest_commit_hash():
1177 return True
1178 return False
1179
1180
1181class DevServerRepo(_ExternalGitRepo):
1182 """Clones or updates the chromite repo."""
1183
Alex Miller9fbe67f2013-09-06 15:04:48 -07001184 _GIT_URL = ('https://chromium.googlesource.com/'
Chris Sosa1bf41c42013-02-27 11:34:23 -08001185 'chromiumos/platform/dev-util')
1186
1187 def build_and_install(self, install_dir):
1188 """
1189 Clone if the repo isn't initialized, pull clean bits if it is.
1190
1191 Unlike it's hdctools counterpart the dev-util repo clones master
1192 directly into site-packages. It doesn't use an intermediate temp
1193 directory because it doesn't need installation.
1194
1195 @param install_dir: destination directory for chromite installation.
1196 """
1197 local_devserver_dir = os.path.join(install_dir, 'devserver')
1198 git_repo = revision_control.GitRepo(local_devserver_dir, self._GIT_URL,
1199 abs_work_tree=local_devserver_dir)
Prathmesh Prabhu5f6b2332015-04-10 16:41:28 -07001200 git_repo.reinit_repo_at(self.PROD_BRANCH)
beepsd9153b52013-01-23 20:52:46 -08001201
1202 if git_repo.get_latest_commit_hash():
1203 return True
1204 return False
Scott James Remnantbb1a9672014-03-03 14:03:09 -08001205
1206
1207class BtsocketRepo(_ExternalGitRepo):
1208 """Clones or updates the btsocket repo."""
1209
1210 _GIT_URL = ('https://chromium.googlesource.com/'
1211 'chromiumos/platform/btsocket')
1212
1213 def fetch(self, unused_dest_dir):
1214 """
1215 Fetch repo to a temporary location.
1216
1217 We use an intermediate temp directory because we have to build an
1218 egg for installation. If we can't get at the top commit hash after
1219 fetching something is wrong. This can happen when we've cloned/pulled
1220 an empty repo. Not something we expect to do.
1221
1222 @parma unused_dest_dir: passed in because we inherit from
1223 ExternalPackage.
1224
1225 @return: True if repo sync was successful.
1226 """
1227 self.temp_btsocket_dir = autotemp.tempdir(unique_id='btsocket')
1228 try:
1229 git_repo = revision_control.GitRepo(
1230 self.temp_btsocket_dir.name,
1231 self._GIT_URL,
1232 None,
1233 abs_work_tree=self.temp_btsocket_dir.name)
Prathmesh Prabhu5f6b2332015-04-10 16:41:28 -07001234 git_repo.reinit_repo_at(self.PROD_BRANCH)
Scott James Remnantbb1a9672014-03-03 14:03:09 -08001235
1236 if git_repo.get_latest_commit_hash():
1237 return True
1238 except:
1239 self.temp_btsocket_dir.clean()
1240 raise
1241
1242 self.temp_btsocket_dir.clean()
1243 return False
1244
1245
1246 def build_and_install(self, install_dir):
1247 """
1248 Install the btsocket module using setup.py
1249
1250 @param install_dir: Target installation directory.
1251
1252 @return: A boolean indicating success of failure.
1253 """
1254 work_dir = os.getcwd()
1255 try:
1256 os.chdir(self.temp_btsocket_dir.name)
1257 rv = self._build_and_install_current_dir_setup_py(install_dir)
1258 finally:
1259 os.chdir(work_dir)
1260 self.temp_btsocket_dir.clean()
J. Richard Barnette428b3442014-07-22 11:38:55 -07001261 return rv
Dan Shi44e47b82015-03-19 17:07:12 -07001262
1263
1264class NetifacesPackage(ExternalPackage):
1265 """netifaces package."""
1266 version = '0.10.4'
1267 url_filename = 'netifaces-%s.tar.gz' % version
1268 local_filename = url_filename
1269 urls = ('https://pypi.python.org/packages/source/n/netifaces/%s' %
1270 (url_filename),)
1271 hex_sum = 'c3fcd491a89c2994815053e853b005e7fc27c79a'
1272 _build_and_install = ExternalPackage._build_and_install_from_package
1273 _build_and_install_current_dir = (
1274 ExternalPackage._build_and_install_current_dir_setup_py)