From fd34d988ef84c20c6d4f26ed88112168d54d0f2b Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Sat, 4 Jan 2020 00:34:52 +0300 Subject: boost/build: overhaul `build.py download` For instance, it can now cache archives. --- boost/build/build.py | 120 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 88 insertions(+), 32 deletions(-) (limited to 'boost/build') diff --git a/boost/build/build.py b/boost/build/build.py index d535338..7668330 100755 --- a/boost/build/build.py +++ b/boost/build/build.py @@ -10,6 +10,7 @@ # archive in a cross-platform way + setting the correct --stagedir parameter # value to avoid name clashes. +import abc import argparse from contextlib import contextmanager from enum import Enum @@ -137,29 +138,87 @@ class BoostArchive: self.version = version self.path = path - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - logging.info('Removing temporary file: %s', self.path) - os.remove(self.path) - @property def dir_name(self): return self.version.dir_name - @staticmethod - def download(version): - path = None - with tempfile.NamedTemporaryFile(prefix='boost_', suffix=version.archive_ext, delete=False) as dest: - path = dest.name - logging.info('Downloading Boost to: %s', path) - url = version.get_download_url() - logging.info('Download URL: %s', url) + def unpack(self, dest_dir): + path = os.path.join(dest_dir, self.dir_name) + if os.path.exists(path): + raise RuntimeError(f'Boost directory already exists: {path}') + logging.info('Unpacking Boost to: %s', path) + shutil.unpack_archive(self.path, dest_dir) + return BoostDir(path) + + +class ArchiveStorage(abc.ABC): + @contextmanager + def download(self, version): + path = self.get_archive(version) + if path is not None: + logging.info('Using existing Boost archive: %s', path) + yield BoostArchive(version, path) + return + + url = version.get_download_url() + logging.info('Download URL: %s', url) + + with urllib.request.urlopen(url) as request: + with self.write_archive(version, request.read()) as path: + yield BoostArchive(version, path) + + @abc.abstractmethod + def get_archive(self, version): + pass + + @contextmanager + @abc.abstractmethod + def write_archive(self, version, contents): + pass + - with urllib.request.urlopen(url) as request: - dest.write(request.read()) - return BoostArchive(version, path) +class CacheStorage(ArchiveStorage): + def __init__(self, cache_dir): + self._dir = cache_dir + + def _archive_path(self, version): + return os.path.join(self._dir, version.archive_name) + + def get_archive(self, version): + path = self._archive_path(version) + if os.path.exists(path): + return path + return None + + @contextmanager + def write_archive(self, version, contents): + path = self._archive_path(version) + logging.info('Writing Boost archive: %s', path) + if os.path.exists(path): + raise RuntimeError(f'cannot download Boost, file already exists: {path}') + with open(path, mode='w+b') as dest: + dest.write(contents) + yield path + + +class TempStorage(ArchiveStorage): + def __init__(self, temp_dir): + self._dir = temp_dir + + def get_archive(self, version): + return None + + @contextmanager + def write_archive(self, version, contents): + with tempfile.NamedTemporaryFile(prefix=f'boost_{version}_', suffix=version.archive_ext, dir=self._dir, delete=False) as dest: + path = dest.name + logging.info('Writing Boost archive: %s', path) + dest.write(contents) + try: + yield path + finally: + logging.info('Removing temporary Boost archive: %s', path) + os.remove(path) class BoostDir: @@ -168,15 +227,6 @@ class BoostDir: raise RuntimeError(f"Boost directory doesn't exist: {path}") self.path = path - @staticmethod - def unpack(archive, dest): - path = os.path.join(dest, archive.dir_name) - if os.path.exists(path): - raise RuntimeError(f'Boost directory already exists: {path}') - logging.info('Unpacking Boost to: %s', path) - shutil.unpack_archive(archive.path, dest) - return BoostDir(path) - def _go(self): return _chdir(self.path) @@ -274,13 +324,16 @@ def _parse_args(argv=None): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='command') - download = subparsers.add_parser('download', help='download Boost') + download = subparsers.add_parser('download', help='download & bootstrap Boost') download.add_argument('boost_version', metavar='VERSION', type=BoostVersion.from_string, help='Boost version (in the MAJOR.MINOR.PATCH format)') - download.add_argument('--build', metavar='DIR', dest='build_dir', + download.add_argument('--cache', metavar='DIR', dest='cache_dir', + type=os.path.abspath, + help='download directory (will download Boost to a temporary file and delete it unless specified)') + download.add_argument('--unpack', metavar='DIR', dest='unpack_dir', type=os.path.abspath, default='.', - help='destination directory') + help='directory to unpack Boost to') build = subparsers.add_parser('build', help='build Boost libraries') build.add_argument('--platform', metavar='PLATFORM', @@ -306,8 +359,11 @@ def build(args): def download(args): - with BoostArchive.download(args.boost_version) as archive: - boost_dir = BoostDir.unpack(archive, args.build_dir) + storage = TempStorage(args.unpack_dir) + if args.cache_dir is not None: + storage = CacheStorage(args.cache_dir) + with storage.download(args.boost_version) as archive: + boost_dir = archive.unpack(args.unpack_dir) boost_dir.bootstrap() -- cgit v1.2.3