Mike Frysinger | d03e6b5 | 2019-08-03 12:49:01 -0400 | [diff] [blame] | 1 | #!/usr/bin/python2 |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 2 | # |
Craig Bergstrom | 0bee598 | 2018-06-13 13:56:52 -0600 | [diff] [blame] | 3 | # Please keep this code python 2.4 compatible and standalone. |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 4 | |
| 5 | """ |
| 6 | Fetch, build and install external Python library dependancies. |
| 7 | |
| 8 | This fetches external python libraries, builds them using your host's |
| 9 | python and installs them under our own autotest/site-packages/ directory. |
| 10 | |
| 11 | Usage? Just run it. |
| 12 | utils/build_externals.py |
| 13 | """ |
| 14 | |
Shuqian Zhao | 697f7ed | 2016-10-21 14:58:28 -0700 | [diff] [blame] | 15 | import argparse |
| 16 | import compileall |
| 17 | import logging |
| 18 | import os |
| 19 | import sys |
| 20 | |
mbligh | e5699e2 | 2009-09-18 16:44:12 +0000 | [diff] [blame] | 21 | import common |
| 22 | from autotest_lib.client.common_lib import logging_config, logging_manager |
lmr | 6f80e7a | 2010-02-04 03:18:28 +0000 | [diff] [blame] | 23 | from autotest_lib.client.common_lib import utils |
jamesren | ff6e5aa | 2010-02-12 00:46:40 +0000 | [diff] [blame] | 24 | from autotest_lib.utils import external_packages |
| 25 | |
| 26 | # bring in site packages as well |
| 27 | utils.import_site_module(__file__, 'autotest_lib.utils.site_external_packages') |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 28 | |
| 29 | # Where package source be fetched to relative to the top of the autotest tree. |
| 30 | PACKAGE_DIR = 'ExternalSource' |
| 31 | |
| 32 | # Where packages will be installed to relative to the top of the autotest tree. |
| 33 | INSTALL_DIR = 'site-packages' |
| 34 | |
mbligh | 489e0e0 | 2009-06-08 16:46:11 +0000 | [diff] [blame] | 35 | # Installs all packages, even if the system already has the version required |
| 36 | INSTALL_ALL = False |
| 37 | |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 38 | |
| 39 | # Want to add more packages to fetch, build and install? See the class |
jamesren | ff6e5aa | 2010-02-12 00:46:40 +0000 | [diff] [blame] | 40 | # definitions at the end of external_packages.py for examples of how to do it. |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 41 | |
| 42 | |
mbligh | e5699e2 | 2009-09-18 16:44:12 +0000 | [diff] [blame] | 43 | class BuildExternalsLoggingConfig(logging_config.LoggingConfig): |
Craig Bergstrom | 0bee598 | 2018-06-13 13:56:52 -0600 | [diff] [blame] | 44 | """Logging manager config.""" |
| 45 | |
mbligh | e5699e2 | 2009-09-18 16:44:12 +0000 | [diff] [blame] | 46 | def configure_logging(self, results_dir=None, verbose=False): |
Craig Bergstrom | 0bee598 | 2018-06-13 13:56:52 -0600 | [diff] [blame] | 47 | """Configure logging.""" |
mbligh | e5699e2 | 2009-09-18 16:44:12 +0000 | [diff] [blame] | 48 | super(BuildExternalsLoggingConfig, self).configure_logging( |
| 49 | use_console=True, |
| 50 | verbose=verbose) |
| 51 | |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 52 | |
| 53 | def main(): |
| 54 | """ |
| 55 | Find all ExternalPackage classes defined in this file and ask them to |
| 56 | fetch, build and install themselves. |
| 57 | """ |
Shuqian Zhao | 697f7ed | 2016-10-21 14:58:28 -0700 | [diff] [blame] | 58 | options = parse_arguments(sys.argv[1:]) |
mbligh | e5699e2 | 2009-09-18 16:44:12 +0000 | [diff] [blame] | 59 | logging_manager.configure_logging(BuildExternalsLoggingConfig(), |
| 60 | verbose=True) |
Derek Beckett | db73511 | 2020-08-27 10:25:15 -0700 | [diff] [blame] | 61 | os.umask(0o22) |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 62 | |
jamesren | ff6e5aa | 2010-02-12 00:46:40 +0000 | [diff] [blame] | 63 | top_of_tree = external_packages.find_top_of_autotest_tree() |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 64 | package_dir = os.path.join(top_of_tree, PACKAGE_DIR) |
| 65 | install_dir = os.path.join(top_of_tree, INSTALL_DIR) |
| 66 | |
lmr | 7ebba66 | 2009-10-23 12:55:09 +0000 | [diff] [blame] | 67 | # Make sure the install_dir is in our python module search path |
| 68 | # as well as the PYTHONPATH being used by all our setup.py |
| 69 | # install subprocesses. |
mbligh | 623c518 | 2009-02-07 01:31:31 +0000 | [diff] [blame] | 70 | if install_dir not in sys.path: |
mbligh | 623c518 | 2009-02-07 01:31:31 +0000 | [diff] [blame] | 71 | sys.path.insert(0, install_dir) |
lmr | 7ebba66 | 2009-10-23 12:55:09 +0000 | [diff] [blame] | 72 | env_python_path_varname = 'PYTHONPATH' |
| 73 | env_python_path = os.environ.get(env_python_path_varname, '') |
| 74 | if install_dir+':' not in env_python_path: |
| 75 | os.environ[env_python_path_varname] = ':'.join([ |
| 76 | install_dir, env_python_path]) |
mbligh | 623c518 | 2009-02-07 01:31:31 +0000 | [diff] [blame] | 77 | |
Shuqian Zhao | 697f7ed | 2016-10-21 14:58:28 -0700 | [diff] [blame] | 78 | fetched_packages, fetch_errors = fetch_necessary_packages( |
| 79 | package_dir, install_dir, set(options.names_to_check)) |
Sanika Kulkarni | 0135f9f | 2020-01-02 09:51:54 -0800 | [diff] [blame] | 80 | install_errors = build_and_install_packages(fetched_packages, install_dir, |
Derek Beckett | 83e1651 | 2020-12-23 10:48:28 -0800 | [diff] [blame] | 81 | options.use_chromite_main) |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 82 | |
| 83 | # Byte compile the code after it has been installed in its final |
| 84 | # location as .pyc files contain the path passed to compile_dir(). |
| 85 | # When printing exception tracebacks, python uses that path first to look |
| 86 | # for the source code before checking the directory of the .pyc file. |
| 87 | # Don't leave references to our temporary build dir in the files. |
mbligh | 623c518 | 2009-02-07 01:31:31 +0000 | [diff] [blame] | 88 | logging.info('compiling .py files in %s to .pyc', install_dir) |
| 89 | compileall.compile_dir(install_dir, quiet=True) |
| 90 | |
| 91 | # Some things install with whacky permissions, fix that. |
jamesren | ff6e5aa | 2010-02-12 00:46:40 +0000 | [diff] [blame] | 92 | external_packages.system("chmod -R a+rX '%s'" % install_dir) |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 93 | |
| 94 | errors = fetch_errors + install_errors |
| 95 | for error_msg in errors: |
lmr | 65bec4f | 2010-04-08 02:30:35 +0000 | [diff] [blame] | 96 | logging.error(error_msg) |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 97 | |
Craig Bergstrom | 0bee598 | 2018-06-13 13:56:52 -0600 | [diff] [blame] | 98 | if not errors: |
Jacob Kopczynski | 2dc409a | 2018-06-13 11:24:46 -0700 | [diff] [blame] | 99 | logging.info("Syntax errors from pylint above are expected, not " |
| 100 | "problematic. SUCCESS.") |
Craig Bergstrom | 0bee598 | 2018-06-13 13:56:52 -0600 | [diff] [blame] | 101 | else: |
Jacob Kopczynski | 2dc409a | 2018-06-13 11:24:46 -0700 | [diff] [blame] | 102 | logging.info("Problematic errors encountered. FAILURE.") |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 103 | return len(errors) |
| 104 | |
| 105 | |
Shuqian Zhao | 697f7ed | 2016-10-21 14:58:28 -0700 | [diff] [blame] | 106 | def parse_arguments(args): |
| 107 | """Parse command line arguments. |
| 108 | |
| 109 | @param args: The command line arguments to parse. (ususally sys.argsv[1:]) |
| 110 | |
| 111 | @returns An argparse.Namespace populated with argument values. |
| 112 | """ |
| 113 | parser = argparse.ArgumentParser( |
| 114 | description='Command to build third party dependencies required ' |
| 115 | 'for autotest.') |
Derek Beckett | 83e1651 | 2020-12-23 10:48:28 -0800 | [diff] [blame] | 116 | parser.add_argument('--use_chromite_main', action='store_true', |
| 117 | help='Update chromite to main branch, rather than ' |
Shuqian Zhao | 697f7ed | 2016-10-21 14:58:28 -0700 | [diff] [blame] | 118 | 'prod.') |
| 119 | parser.add_argument('--names_to_check', nargs='*', type=str, default=set(), |
| 120 | help='Package names to check whether they are needed ' |
| 121 | 'in current system.') |
| 122 | return parser.parse_args(args) |
| 123 | |
| 124 | |
| 125 | def fetch_necessary_packages(dest_dir, install_dir, names_to_check=set()): |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 126 | """ |
| 127 | Fetches all ExternalPackages into dest_dir. |
| 128 | |
mbligh | b155d0c | 2009-07-02 19:04:55 +0000 | [diff] [blame] | 129 | @param dest_dir: Directory the packages should be fetched into. |
| 130 | @param install_dir: Directory where packages will later installed. |
Shuqian Zhao | 697f7ed | 2016-10-21 14:58:28 -0700 | [diff] [blame] | 131 | @param names_to_check: A set of package names to check whether they are |
| 132 | needed on current system. Default is empty. |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 133 | |
| 134 | @returns A tuple containing two lists: |
| 135 | * A list of ExternalPackage instances that were fetched and |
| 136 | need to be installed. |
| 137 | * A list of error messages for any failed fetches. |
| 138 | """ |
| 139 | errors = [] |
| 140 | fetched_packages = [] |
jamesren | ff6e5aa | 2010-02-12 00:46:40 +0000 | [diff] [blame] | 141 | for package_class in external_packages.ExternalPackage.subclasses: |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 142 | package = package_class() |
mbligh | 623c518 | 2009-02-07 01:31:31 +0000 | [diff] [blame] | 143 | if names_to_check and package.name.lower() not in names_to_check: |
| 144 | continue |
mbligh | b155d0c | 2009-07-02 19:04:55 +0000 | [diff] [blame] | 145 | if not package.is_needed(install_dir): |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 146 | logging.info('A new %s is not needed on this system.', |
| 147 | package.name) |
mbligh | 489e0e0 | 2009-06-08 16:46:11 +0000 | [diff] [blame] | 148 | if INSTALL_ALL: |
| 149 | logging.info('Installing anyways...') |
| 150 | else: |
| 151 | continue |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 152 | if not package.fetch(dest_dir): |
lmr | 65bec4f | 2010-04-08 02:30:35 +0000 | [diff] [blame] | 153 | msg = 'Unable to download %s' % package.name |
| 154 | logging.error(msg) |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 155 | errors.append(msg) |
| 156 | else: |
| 157 | fetched_packages.append(package) |
| 158 | |
| 159 | return fetched_packages, errors |
| 160 | |
| 161 | |
Shuqian Zhao | 697f7ed | 2016-10-21 14:58:28 -0700 | [diff] [blame] | 162 | def build_and_install_packages(packages, install_dir, |
Derek Beckett | 83e1651 | 2020-12-23 10:48:28 -0800 | [diff] [blame] | 163 | use_chromite_main=True): |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 164 | """ |
| 165 | Builds and installs all packages into install_dir. |
| 166 | |
| 167 | @param packages - A list of already fetched ExternalPackage instances. |
| 168 | @param install_dir - Directory the packages should be installed into. |
Derek Beckett | 83e1651 | 2020-12-23 10:48:28 -0800 | [diff] [blame] | 169 | @param use_chromite_main: True if updating chromite to main branch. Due |
Sanika Kulkarni | 0135f9f | 2020-01-02 09:51:54 -0800 | [diff] [blame] | 170 | to the non-usage of origin/prod tag, the default |
| 171 | value for this argument has been set to True. |
| 172 | This argument has not been removed for backward |
| 173 | compatibility. |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 174 | |
| 175 | @returns A list of error messages for any installs that failed. |
| 176 | """ |
| 177 | errors = [] |
| 178 | for package in packages: |
Sanika Kulkarni | 0135f9f | 2020-01-02 09:51:54 -0800 | [diff] [blame] | 179 | if package.name.lower() == 'chromiterepo': |
Derek Beckett | 83e1651 | 2020-12-23 10:48:28 -0800 | [diff] [blame] | 180 | if not use_chromite_main: |
Sanika Kulkarni | 0135f9f | 2020-01-02 09:51:54 -0800 | [diff] [blame] | 181 | logging.warning( |
Derek Beckett | 83e1651 | 2020-12-23 10:48:28 -0800 | [diff] [blame] | 182 | 'Even though use_chromite_main has been set to %s, it ' |
| 183 | 'will be checked out to main as the origin/prod tag ' |
| 184 | 'carries little value now.', use_chromite_main) |
| 185 | logging.info('Checking out autotest-chromite to main branch.') |
Sanika Kulkarni | 0135f9f | 2020-01-02 09:51:54 -0800 | [diff] [blame] | 186 | result = package.build_and_install( |
Derek Beckett | 83e1651 | 2020-12-23 10:48:28 -0800 | [diff] [blame] | 187 | install_dir, main_branch=True) |
Shuqian Zhao | 697f7ed | 2016-10-21 14:58:28 -0700 | [diff] [blame] | 188 | else: |
| 189 | result = package.build_and_install(install_dir) |
Dan Shi | 16c0a50 | 2015-07-14 17:29:48 -0700 | [diff] [blame] | 190 | if isinstance(result, bool): |
| 191 | success = result |
| 192 | message = None |
| 193 | else: |
| 194 | success = result[0] |
| 195 | message = result[1] |
| 196 | if not success: |
| 197 | msg = ('Unable to build and install %s.\nError: %s' % |
| 198 | (package.name, message)) |
lmr | 65bec4f | 2010-04-08 02:30:35 +0000 | [diff] [blame] | 199 | logging.error(msg) |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 200 | errors.append(msg) |
| 201 | return errors |
| 202 | |
| 203 | |
mbligh | fa29d66 | 2009-02-05 00:44:26 +0000 | [diff] [blame] | 204 | if __name__ == '__main__': |
| 205 | sys.exit(main()) |