blob: 268eadfc26e1aa53e7b49c51d053b4b30e23adaf [file] [log] [blame]
mblighfa29d662009-02-05 00:44:26 +00001#!/usr/bin/python
2#
3# Please keep this code python 2.4 compatible and stand alone.
4
5"""
6Fetch, build and install external Python library dependancies.
7
8This fetches external python libraries, builds them using your host's
9python and installs them under our own autotest/site-packages/ directory.
10
11Usage? Just run it.
12 utils/build_externals.py
13"""
14
jamesrenff6e5aa2010-02-12 00:46:40 +000015import compileall, logging, os, sha, shutil, sys, tempfile, time, urllib2
mbligh489e0e02009-06-08 16:46:11 +000016import subprocess, re
mblighe5699e22009-09-18 16:44:12 +000017import common
18from autotest_lib.client.common_lib import logging_config, logging_manager
lmr6f80e7a2010-02-04 03:18:28 +000019from autotest_lib.client.common_lib import utils
jamesrenff6e5aa2010-02-12 00:46:40 +000020from autotest_lib.utils import external_packages
21
22# bring in site packages as well
23utils.import_site_module(__file__, 'autotest_lib.utils.site_external_packages')
mblighfa29d662009-02-05 00:44:26 +000024
25# Where package source be fetched to relative to the top of the autotest tree.
26PACKAGE_DIR = 'ExternalSource'
27
28# Where packages will be installed to relative to the top of the autotest tree.
29INSTALL_DIR = 'site-packages'
30
mbligh489e0e02009-06-08 16:46:11 +000031# Installs all packages, even if the system already has the version required
32INSTALL_ALL = False
33
mblighfa29d662009-02-05 00:44:26 +000034
35# Want to add more packages to fetch, build and install? See the class
jamesrenff6e5aa2010-02-12 00:46:40 +000036# definitions at the end of external_packages.py for examples of how to do it.
mblighfa29d662009-02-05 00:44:26 +000037
38
mblighe5699e22009-09-18 16:44:12 +000039class BuildExternalsLoggingConfig(logging_config.LoggingConfig):
40 def configure_logging(self, results_dir=None, verbose=False):
41 super(BuildExternalsLoggingConfig, self).configure_logging(
42 use_console=True,
43 verbose=verbose)
44
mblighfa29d662009-02-05 00:44:26 +000045
46def main():
47 """
48 Find all ExternalPackage classes defined in this file and ask them to
49 fetch, build and install themselves.
50 """
mblighe5699e22009-09-18 16:44:12 +000051 logging_manager.configure_logging(BuildExternalsLoggingConfig(),
52 verbose=True)
mblighb155d0c2009-07-02 19:04:55 +000053 os.umask(022)
mblighfa29d662009-02-05 00:44:26 +000054
jamesrenff6e5aa2010-02-12 00:46:40 +000055 top_of_tree = external_packages.find_top_of_autotest_tree()
mblighfa29d662009-02-05 00:44:26 +000056 package_dir = os.path.join(top_of_tree, PACKAGE_DIR)
57 install_dir = os.path.join(top_of_tree, INSTALL_DIR)
58
lmr7ebba662009-10-23 12:55:09 +000059 # Make sure the install_dir is in our python module search path
60 # as well as the PYTHONPATH being used by all our setup.py
61 # install subprocesses.
mbligh623c5182009-02-07 01:31:31 +000062 if install_dir not in sys.path:
mbligh623c5182009-02-07 01:31:31 +000063 sys.path.insert(0, install_dir)
lmr7ebba662009-10-23 12:55:09 +000064 env_python_path_varname = 'PYTHONPATH'
65 env_python_path = os.environ.get(env_python_path_varname, '')
66 if install_dir+':' not in env_python_path:
67 os.environ[env_python_path_varname] = ':'.join([
68 install_dir, env_python_path])
mbligh623c5182009-02-07 01:31:31 +000069
mblighb155d0c2009-07-02 19:04:55 +000070 fetched_packages, fetch_errors = fetch_necessary_packages(package_dir,
71 install_dir)
mblighfa29d662009-02-05 00:44:26 +000072 install_errors = build_and_install_packages(fetched_packages, install_dir)
73
74 # Byte compile the code after it has been installed in its final
75 # location as .pyc files contain the path passed to compile_dir().
76 # When printing exception tracebacks, python uses that path first to look
77 # for the source code before checking the directory of the .pyc file.
78 # Don't leave references to our temporary build dir in the files.
mbligh623c5182009-02-07 01:31:31 +000079 logging.info('compiling .py files in %s to .pyc', install_dir)
80 compileall.compile_dir(install_dir, quiet=True)
81
82 # Some things install with whacky permissions, fix that.
jamesrenff6e5aa2010-02-12 00:46:40 +000083 external_packages.system("chmod -R a+rX '%s'" % install_dir)
mblighfa29d662009-02-05 00:44:26 +000084
85 errors = fetch_errors + install_errors
86 for error_msg in errors:
87 print >>sys.stderr, error_msg
88
89 return len(errors)
90
91
mblighb155d0c2009-07-02 19:04:55 +000092def fetch_necessary_packages(dest_dir, install_dir):
mblighfa29d662009-02-05 00:44:26 +000093 """
94 Fetches all ExternalPackages into dest_dir.
95
mblighb155d0c2009-07-02 19:04:55 +000096 @param dest_dir: Directory the packages should be fetched into.
97 @param install_dir: Directory where packages will later installed.
mblighfa29d662009-02-05 00:44:26 +000098
99 @returns A tuple containing two lists:
100 * A list of ExternalPackage instances that were fetched and
101 need to be installed.
102 * A list of error messages for any failed fetches.
103 """
mbligh623c5182009-02-07 01:31:31 +0000104 names_to_check = sys.argv[1:]
mblighfa29d662009-02-05 00:44:26 +0000105 errors = []
106 fetched_packages = []
jamesrenff6e5aa2010-02-12 00:46:40 +0000107 for package_class in external_packages.ExternalPackage.subclasses:
mblighfa29d662009-02-05 00:44:26 +0000108 package = package_class()
mbligh623c5182009-02-07 01:31:31 +0000109 if names_to_check and package.name.lower() not in names_to_check:
110 continue
mblighb155d0c2009-07-02 19:04:55 +0000111 if not package.is_needed(install_dir):
mblighfa29d662009-02-05 00:44:26 +0000112 logging.info('A new %s is not needed on this system.',
113 package.name)
mbligh489e0e02009-06-08 16:46:11 +0000114 if INSTALL_ALL:
115 logging.info('Installing anyways...')
116 else:
117 continue
mblighfa29d662009-02-05 00:44:26 +0000118 if not package.fetch(dest_dir):
119 msg = '!!! Unable to download %s' % package.name
120 print msg
121 errors.append(msg)
122 else:
123 fetched_packages.append(package)
124
125 return fetched_packages, errors
126
127
128def build_and_install_packages(packages, install_dir):
129 """
130 Builds and installs all packages into install_dir.
131
132 @param packages - A list of already fetched ExternalPackage instances.
133 @param install_dir - Directory the packages should be installed into.
134
135 @returns A list of error messages for any installs that failed.
136 """
137 errors = []
138 for package in packages:
139 if not package.build_and_install(install_dir):
140 msg = '!!! Unable to build and install %s' % package.name
141 print msg
142 errors.append(msg)
143 return errors
144
145
mblighfa29d662009-02-05 00:44:26 +0000146if __name__ == '__main__':
147 sys.exit(main())