aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2020-03-28 23:01:09 +0000
committerEgor Tensin <Egor.Tensin@gmail.com>2020-03-28 23:54:46 +0000
commitc58a3787eca9c0c4a7f376ba841cd7e39ab95ece (patch)
tree709b5ab8385bbb952912206ae561fbe5e1e752d8
parentproject.boost: factor out BoostVersion (diff)
downloadcmake-common-c58a3787eca9c0c4a7f376ba841cd7e39ab95ece.tar.gz
cmake-common-c58a3787eca9c0c4a7f376ba841cd7e39ab95ece.zip
project.boost: factor out everything else
I finally snapped. This starts to resemble sensible structure though.
-rw-r--r--.ci/Makefile8
-rw-r--r--.pylintrc2
-rw-r--r--project/boost/archive.py87
-rw-r--r--project/boost/build.py346
-rw-r--r--project/boost/directory.py60
-rw-r--r--project/boost/download.py112
-rw-r--r--project/ci/appveyor/boost.py66
-rw-r--r--project/ci/travis/boost.py62
-rw-r--r--project/utils.py50
9 files changed, 420 insertions, 373 deletions
diff --git a/.ci/Makefile b/.ci/Makefile
index 13fa74f..bfc2917 100644
--- a/.ci/Makefile
+++ b/.ci/Makefile
@@ -186,11 +186,11 @@ echo/boost/%/finished: FORCE
.PHONY: boost/58/download
boost/58/download: echo/boost/58/build
- "$(python)" -m project.boost.build download 1.58.0
+ "$(python)" -m project.boost.download 1.58.0
.PHONY: boost/58/build
boost/58/build:
- "$(python)" -m project.boost.build build --configuration Debug --platform x86 --link static -- ./boost_1_58_0 --with-filesystem --with-program_options $(b2_flags)
+ "$(python)" -m project.boost.build --configuration Debug --platform x86 --link static -- ./boost_1_58_0 --with-filesystem --with-program_options $(b2_flags)
.PHONY: boost/58/ls
boost/58/ls: echo/boost/58/ls
@@ -223,11 +223,11 @@ boost/58: boost/58/download boost/58/build boost/58/ls boost/58/exe echo/boost/5
.PHONY: boost/72/download
boost/72/download: echo/boost/72/build
- "$(python)" -m project.boost.build download --cache . 1.72.0
+ "$(python)" -m project.boost.download --cache . 1.72.0
.PHONY: boost/72/build
boost/72/build:
- "$(python)" -m project.boost.build build --platform x86 x64 --link shared -- ./boost_1_72_0 --with-filesystem --with-program_options $(b2_flags)
+ "$(python)" -m project.boost.build --platform x86 x64 --link shared -- ./boost_1_72_0 --with-filesystem --with-program_options $(b2_flags)
.PHONY: boost/72/ls
boost/72/ls: echo/boost/72/ls
diff --git a/.pylintrc b/.pylintrc
index 81d8b4e..bccfc70 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -1,5 +1,3 @@
[MESSAGES CONTROL]
disable=invalid-name,missing-docstring
-
-ignored-modules=build
diff --git a/project/boost/archive.py b/project/boost/archive.py
new file mode 100644
index 0000000..55d05f2
--- /dev/null
+++ b/project/boost/archive.py
@@ -0,0 +1,87 @@
+# Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+# This file is part of the "cmake-common" project.
+# For details, see https://github.com/egor-tensin/cmake-common.
+# Distributed under the MIT License.
+
+import abc
+from contextlib import contextmanager
+import logging
+import os.path
+import shutil
+import tempfile
+
+from project.boost.directory import BoostDir
+
+
+class Archive:
+ def __init__(self, version, path):
+ self.version = version
+ self.path = path
+
+ @property
+ def dir_name(self):
+ return self.version.dir_name
+
+ 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):
+ @abc.abstractmethod
+ def get_archive(self, version):
+ pass
+
+ @contextmanager
+ @abc.abstractmethod
+ def write_archive(self, version, contents):
+ pass
+
+
+class PermanentStorage(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 TemporaryStorage(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)
diff --git a/project/boost/build.py b/project/boost/build.py
index e9f2ffc..541e2ba 100644
--- a/project/boost/build.py
+++ b/project/boost/build.py
@@ -1,240 +1,59 @@
-#!/usr/bin/env python3
-
# Copyright (c) 2019 Egor Tensin <Egor.Tensin@gmail.com>
# This file is part of the "cmake-common" project.
# For details, see https://github.com/egor-tensin/cmake-common.
# Distributed under the MIT License.
-'''Download & build Boost.
-
-This script downloads and builds the Boost libraries. It's main purpose is to:
-1) provide a cross-platform way to download & unpack the Boost distribution
-archive,
-2) set the correct --stagedir parameter value to avoid name clashes.
+R'''Build Boost.
-Please pick a command below. You can execute `%(prog)s COMMAND --help` to view
-its usage message.
+This script builds the Boost libraries. It's main utility is setting the
+correct --stagedir parameter value to avoid name clashes.
-A simple usage example:
+Usage examples:
- $ %(prog)s download 1.71.0
- ...
- $ %(prog)s build -- boost_1_71_0/ --with-filesystem --with-program_options
+ $ %(prog)s -- boost_1_71_0/ --with-filesystem --with-program_options
...
'''
-import abc
import argparse
-from collections import namedtuple
from contextlib import contextmanager
-from enum import Enum
-from functools import total_ordering
import logging
import os.path
-import platform
-import re
-import shutil
-import subprocess
import sys
import tempfile
-import urllib.request
-from project.boost.version import Version
+from project.boost.directory import BoostDir
from project.configuration import Configuration
from project.linkage import Linkage
from project.platform import Platform
-
-
-@contextmanager
-def _chdir(path):
- cwd = os.getcwd()
- os.chdir(path)
- try:
- yield
- finally:
- os.chdir(cwd)
-
-
-def _setup_logging():
- logging.basicConfig(
- format='%(asctime)s | %(levelname)s | %(message)s',
- level=logging.INFO)
-
-
-def _on_windows():
- return platform.system() == 'Windows'
-
-
-def _on_linux():
- return not _on_windows()
-
-
-def _run_executable(cmd_line):
- logging.info('Running executable: %s', cmd_line)
- return subprocess.run(cmd_line, check=True)
-
-
-class BoostArchive:
- def __init__(self, version, path):
- self.version = version
- self.path = path
-
- @property
- def dir_name(self):
- return self.version.dir_name
-
- 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
-
- urls = version.get_download_urls()
-
- for url in urls:
- logging.info('Trying URL: %s', url)
- try:
- with urllib.request.urlopen(url, timeout=20) as request:
- with self.write_archive(version, request.read()) as path:
- yield BoostArchive(version, path)
- return
- except urllib.request.URLError as e:
- logging.error("Couldn't download from this mirror, an error occured:")
- logging.exception(e)
-
- raise RuntimeError("Couldn't download Boost from any of the mirrors")
-
- @abc.abstractmethod
- def get_archive(self, version):
- pass
-
- @contextmanager
- @abc.abstractmethod
- def write_archive(self, version, contents):
- pass
-
-
-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:
- def __init__(self, path):
- if not os.path.isdir(path):
- raise RuntimeError(f"Boost directory doesn't exist: {path}")
- self.path = path
-
- def _go(self):
- return _chdir(self.path)
-
- def build(self, params):
- with self._go():
- self._bootstrap_if_required()
- self._b2(params)
-
- def _bootstrap_if_required(self):
- if os.path.isfile(self._b2_path()):
- logging.info('Not going to bootstrap, b2 is already there')
- return
- self.bootstrap()
-
- def bootstrap(self):
- with self._go():
- _run_executable(self._bootstrap_path())
-
- def _b2(self, params):
- for b2_params in params.enum_b2_args():
- _run_executable([self._b2_path()] + b2_params)
-
- @staticmethod
- def _bootstrap_path():
- return os.path.join('.', BoostDir._bootstrap_name())
-
- @staticmethod
- def _bootstrap_name():
- ext = '.sh'
- if _on_windows():
- ext = '.bat'
- return f'bootstrap{ext}'
-
- @staticmethod
- def _b2_path():
- return os.path.join('.', BoostDir._b2_name())
-
- @staticmethod
- def _b2_name():
- ext = ''
- if _on_windows():
- ext = '.exe'
- return f'b2{ext}'
+import project.utils
class BuildParameters:
- def __init__(self, args):
- self.platforms = args.platforms or Platform.all()
- self.configurations = args.configurations or Configuration.all()
- self.link = args.link or Linkage.all()
- self.runtime_link = args.runtime_link
-
+ def __init__(self, boost_dir, build_dir=None, platforms=None, configurations=None, link=None,
+ runtime_link=None, b2_args=None):
+
+ boost_dir = project.utils.normalize_path(boost_dir)
+ if build_dir is not None:
+ build_dir = project.utils.normalize_path(build_dir)
+ platforms = platforms or Platform.all()
+ configurations = configurations or Configuration.all()
+ link = link or Linkage.all()
+ runtime_link = runtime_link or Linkage.STATIC
+ b2_args = b2_args or []
+
+ self.boost_dir = boost_dir
self.stage_dir = 'stage'
+ self.build_dir = build_dir
+ self.platforms = platforms
+ self.configurations = configurations
+ self.link = link
+ self.runtime_link = runtime_link
+ self.b2_args = b2_args
- self.build_dir = args.build_dir
- self.boost_dir = args.boost_dir
-
- self.b2_args = args.b2_args
+ @staticmethod
+ def from_args(args):
+ return BuildParameters(**vars(args))
def enum_b2_args(self):
with self._create_build_dir() as build_dir:
@@ -250,7 +69,7 @@ class BuildParameters:
if link is Linkage.SHARED:
logging.warning("Cannot link the runtime statically to a dynamic library, going to link dynamically")
runtime_link = Linkage.SHARED
- elif _on_linux():
+ elif project.utils.on_linux():
logging.warning("Cannot link to the GNU C Library (which is assumed) statically, going to link dynamically")
runtime_link = Linkage.SHARED
yield link, runtime_link
@@ -316,8 +135,9 @@ class BuildParameters:
return f'variant={configuration.to_boost_variant()}'
-def _parse_dir(s):
- return os.path.abspath(os.path.normpath(s))
+def build(params):
+ boost_dir = BoostDir(params.boost_dir)
+ boost_dir.build(params)
def _parse_args(argv=None):
@@ -329,93 +149,45 @@ def _parse_args(argv=None):
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
- subparsers = parser.add_subparsers(dest='command')
-
- download = subparsers.add_parser('download', help='download & bootstrap Boost')
-
- download.add_argument('--cache', metavar='DIR', dest='cache_dir',
- type=_parse_dir,
- help='download directory (temporary file unless specified)')
- download.add_argument('--unpack', metavar='DIR', dest='unpack_dir',
- type=_parse_dir, default='.',
- help='directory to unpack the archive to')
- download.add_argument('boost_version', metavar='VERSION',
- type=Version.from_string,
- help='Boost version (in the MAJOR.MINOR.PATCH format)')
-
- build = subparsers.add_parser('build', help='build the libraries')
-
# These are used to put the built libraries into proper stage/
# subdirectories (to avoid name clashes).
- build.add_argument('--platform', metavar='PLATFORM',
- nargs='*', dest='platforms', default=[],
- type=Platform.parse,
- help=f'target platform ({"/".join(map(str, Platform))})')
- build.add_argument('--configuration', metavar='CONFIGURATION',
- nargs='*', dest='configurations', default=[],
- type=Configuration.parse,
- help=f'target configuration ({"/".join(map(str, Configuration))})')
+ parser.add_argument('--platform', metavar='PLATFORM',
+ nargs='*', dest='platforms', default=[],
+ type=Platform.parse,
+ help=f'target platform ({"/".join(map(str, Platform))})')
+ parser.add_argument('--configuration', metavar='CONFIGURATION',
+ nargs='*', dest='configurations', default=[],
+ type=Configuration.parse,
+ help=f'target configuration ({"/".join(map(str, Configuration))})')
# This is needed because the default behaviour on Linux and Windows is
# different: static & dynamic libs are built on Linux, but only static libs
# are built on Windows by default.
- build.add_argument('--link', metavar='LINKAGE',
- nargs='*', default=[],
- type=Linkage.parse,
- help=f'how the libraries are linked ({"/".join(map(str, Linkage))})')
+ parser.add_argument('--link', metavar='LINKAGE',
+ nargs='*', default=[],
+ type=Linkage.parse,
+ help=f'how the libraries are linked ({"/".join(map(str, Linkage))})')
# This is used to omit runtime-link=static I'd have to otherwise use a lot,
# plus the script validates the link= and runtime-link= combinations.
- build.add_argument('--runtime-link', metavar='LINKAGE',
- type=Linkage.parse, default=Linkage.STATIC,
- help=f'how the libraries link to the runtime ({"/".join(map(str, Linkage))})')
-
- build.add_argument('--build', metavar='DIR', dest='build_dir',
- type=_parse_dir,
- help='Boost build directory (temporary directory unless specified)')
- build.add_argument('boost_dir', metavar='DIR',
- type=_parse_dir,
- help='root Boost directory')
-
- build.add_argument('b2_args', nargs='*', metavar='B2_ARG', default=[],
- help='additional b2 arguments, to be passed verbatim')
-
- args = parser.parse_args(argv)
- if args.command is None:
- parser.error("please specify a command")
- return args
-
-
-def build(args):
- build_params = BuildParameters(args)
- boost_dir = BoostDir(args.boost_dir)
- boost_dir.build(build_params)
-
+ parser.add_argument('--runtime-link', metavar='LINKAGE',
+ type=Linkage.parse, default=Linkage.STATIC,
+ help=f'how the libraries link to the runtime ({"/".join(map(str, Linkage))})')
-def download(args):
- 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()
+ parser.add_argument('--build', metavar='DIR', dest='build_dir',
+ type=project.utils.normalize_path,
+ help='Boost build directory (temporary directory unless specified)')
+ parser.add_argument('boost_dir', metavar='DIR',
+ type=project.utils.normalize_path,
+ help='root Boost directory')
+ parser.add_argument('b2_args', nargs='*', metavar='B2_ARG', default=[],
+ help='additional b2 arguments, to be passed verbatim')
-def main(argv=None):
- args = _parse_args(argv)
- if args.command == 'download':
- download(args)
- elif args.command == 'build':
- build(args)
- else:
- raise NotImplementedError(f'unsupported command: {args.command}')
+ return parser.parse_args(argv)
def _main(argv=None):
- _setup_logging()
- try:
- main(argv)
- except Exception as e:
- logging.exception(e)
- raise
+ with project.utils.setup_logging():
+ build(BuildParameters.from_args(_parse_args(argv)))
if __name__ == '__main__':
diff --git a/project/boost/directory.py b/project/boost/directory.py
new file mode 100644
index 0000000..e50041f
--- /dev/null
+++ b/project/boost/directory.py
@@ -0,0 +1,60 @@
+# Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+# This file is part of the "cmake-common" project.
+# For details, see https://github.com/egor-tensin/cmake-common.
+# Distributed under the MIT License.
+
+import logging
+import os.path
+
+from project.utils import cd, run, on_windows
+
+
+class BoostDir:
+ def __init__(self, path):
+ if not os.path.isdir(path):
+ raise RuntimeError(f"Boost directory doesn't exist: {path}")
+ self.path = path
+
+ def _go(self):
+ return cd(self.path)
+
+ def build(self, params):
+ with self._go():
+ self._bootstrap_if_required()
+ self._b2(params)
+
+ def _bootstrap_if_required(self):
+ if os.path.isfile(self._b2_path()):
+ logging.info('Not going to bootstrap, b2 is already there')
+ return
+ self.bootstrap()
+
+ def bootstrap(self):
+ with self._go():
+ run(self._bootstrap_path())
+
+ def _b2(self, params):
+ for b2_params in params.enum_b2_args():
+ run([self._b2_path()] + b2_params)
+
+ @staticmethod
+ def _bootstrap_path():
+ return os.path.join('.', BoostDir._bootstrap_name())
+
+ @staticmethod
+ def _bootstrap_name():
+ ext = '.sh'
+ if on_windows():
+ ext = '.bat'
+ return f'bootstrap{ext}'
+
+ @staticmethod
+ def _b2_path():
+ return os.path.join('.', BoostDir._b2_name())
+
+ @staticmethod
+ def _b2_name():
+ ext = ''
+ if on_windows():
+ ext = '.exe'
+ return f'b2{ext}'
diff --git a/project/boost/download.py b/project/boost/download.py
new file mode 100644
index 0000000..954ec1f
--- /dev/null
+++ b/project/boost/download.py
@@ -0,0 +1,112 @@
+# Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+# This file is part of the "cmake-common" project.
+# For details, see https://github.com/egor-tensin/cmake-common.
+# Distributed under the MIT License.
+
+R'''Download & bootstrap Boost.
+
+This script downloads and bootstraps a Boost distribution. It's main utility
+is that it's supposed to be cross-platform.
+
+Usage examples:
+
+ $ %(prog)s 1.71.0
+ ...
+
+ $ %(prog)s --unpack ~/workspace/third-party/ 1.65.0
+ ...
+'''
+
+import argparse
+from contextlib import contextmanager
+import logging
+import sys
+import urllib.request
+
+from project.boost.archive import Archive, PermanentStorage, TemporaryStorage
+from project.boost.version import Version
+import project.utils
+
+
+def _download_try_url(url):
+ logging.info('Trying URL: %s', url)
+ try:
+ with urllib.request.urlopen(url, timeout=20) as request:
+ return request.read()
+ except urllib.request.URLError as e:
+ logging.error("Couldn't download from this mirror, an error occured:")
+ logging.exception(e)
+
+
+@contextmanager
+def _download_try_all_urls(version, storage):
+ urls = version.get_download_urls()
+ for url in urls:
+ reply = _download_try_url(url)
+ if reply is None:
+ continue
+ with storage.write_archive(version, reply) as path:
+ yield path
+ return
+ raise RuntimeError("Couldn't download Boost from any of the mirrors")
+
+
+@contextmanager
+def _download_if_necessary(version, storage):
+ path = storage.get_archive(version)
+ if path is not None:
+ logging.info('Using existing Boost archive: %s', path)
+ yield path
+ return
+ with _download_try_all_urls(version, storage) as path:
+ yield path
+
+
+class DownloadParameters:
+ def __init__(self, version, unpack_dir='.', cache_dir=None):
+ self.version = version
+ self.unpack_dir = project.utils.normalize_path(unpack_dir)
+ self.storage = TemporaryStorage(unpack_dir)
+ if cache_dir is not None:
+ cache_dir = project.utils.normalize_path(cache_dir)
+ self.storage = PermanentStorage(cache_dir)
+
+ @staticmethod
+ def from_args(args):
+ return DownloadParameters(**vars(args))
+
+
+def download(params):
+ with _download_if_necessary(params.version, params.storage) as path:
+ archive = Archive(params.version, path)
+ boost_dir = archive.unpack(params.unpack_dir)
+ boost_dir.bootstrap()
+
+
+def _parse_args(argv=None):
+ if argv is None:
+ argv = sys.argv[1:]
+ logging.info('Command line arguments: %s', argv)
+
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+
+ parser.add_argument('--unpack', metavar='DIR', dest='unpack_dir',
+ type=project.utils.normalize_path, default='.',
+ help='directory to unpack the archive to')
+ parser.add_argument('--cache', metavar='DIR', dest='cache_dir',
+ type=project.utils.normalize_path,
+ help='download directory (temporary file unless specified)')
+ parser.add_argument('version', metavar='VERSION', type=Version.from_string,
+ help='Boost version (in the MAJOR.MINOR.PATCH format)')
+ return parser.parse_args(argv)
+
+
+def _main(argv=None):
+ with project.utils.setup_logging():
+ download(DownloadParameters.from_args(_parse_args(argv)))
+
+
+if __name__ == '__main__':
+ _main()
diff --git a/project/ci/appveyor/boost.py b/project/ci/appveyor/boost.py
index aa2d77a..0a15b7b 100644
--- a/project/ci/appveyor/boost.py
+++ b/project/ci/appveyor/boost.py
@@ -1,11 +1,9 @@
-#!/usr/bin/env python3
-
# Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
# This file is part of the "cmake-common" project.
# For details, see https://github.com/egor-tensin/cmake-common.
# Distributed under the MIT License.
-'''Download & build Boost on AppVeyor.
+R'''Download & build Boost on AppVeyor.
This is similar to build.py, but auto-fills some parameters for build.py from
the AppVeyor-defined environment variables. This script is rarely usefull,
@@ -22,7 +20,12 @@ import os.path
import sys
from project.boost.version import Version
-from project.boost.build import main as build_main
+from project.boost.download import DownloadParameters, download
+from project.boost.build import BuildParameters, build
+from project.configuration import Configuration
+from project.linkage import Linkage
+from project.platform import Platform
+import project.utils
def _env(name):
@@ -37,7 +40,7 @@ def _check_appveyor():
def _get_build_dir():
- return 'C:\\projects'
+ return R'C:\projects'
def _get_boost_dir():
@@ -45,21 +48,15 @@ def _get_boost_dir():
def _get_boost_version():
- return _env('appveyor_boost_version')
+ return Version.from_string(_env('appveyor_boost_version'))
def _get_configuration():
- return _env('CONFIGURATION')
+ return Configuration.parse(_env('CONFIGURATION'))
def _get_platform():
- return _env('PLATFORM')
-
-
-def _setup_logging():
- logging.basicConfig(
- format='%(asctime)s | %(levelname)s | %(message)s',
- level=logging.INFO)
+ return Platform.parse(_env('PLATFORM'))
def _parse_args(argv=None):
@@ -70,9 +67,9 @@ def _parse_args(argv=None):
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
- parser.add_argument('--link', metavar='LINKAGE', nargs='*',
+ parser.add_argument('--link', metavar='LINKAGE', nargs='*', type=Linkage.parse,
help='how the libraries are linked (i.e. static/shared)')
- parser.add_argument('--runtime-link', metavar='LINKAGE',
+ parser.add_argument('--runtime-link', metavar='LINKAGE', type=Linkage.parse,
help='how the libraries link to the runtime')
parser.add_argument('b2_args', nargs='*', metavar='B2_ARG', default=[],
help='additional b2 arguments, to be passed verbatim')
@@ -83,39 +80,26 @@ def build_appveyor(argv=None):
args = _parse_args(argv)
_check_appveyor()
- version = Version.from_string(_get_boost_version())
- appveyor_argv = [
- 'download',
- '--unpack', _get_build_dir(),
- '--', str(version)
- ]
- build_main(appveyor_argv)
+ version = _get_boost_version()
+ build_dir = _get_build_dir()
+ download(DownloadParameters(version, unpack_dir=build_dir))
- unpacked_boost_dir = version.dir_path(_get_build_dir())
+ unpacked_boost_dir = version.dir_path(build_dir)
boost_dir = _get_boost_dir()
os.rename(unpacked_boost_dir, boost_dir)
- appveyor_argv = [
- 'build',
- '--configuration', _get_configuration(),
- '--platform', _get_platform(),
- ]
- if args.link is not None:
- appveyor_argv.append('--link')
- appveyor_argv += args.link
- if args.runtime_link is not None:
- appveyor_argv += ['--runtime-link', args.runtime_link]
- appveyor_argv += ['--', boost_dir]
- build_main(appveyor_argv + args.b2_args)
+ params = BuildParameters(boost_dir,
+ platforms=(_get_platform(),),
+ configurations=(_get_configuration(),),
+ link=args.link,
+ runtime_link=args.runtime_link,
+ b2_args=args.b2_args)
+ build(params)
def main(argv=None):
- _setup_logging()
- try:
+ with project.utils.setup_logging():
build_appveyor(argv)
- except Exception as e:
- logging.exception(e)
- raise
if __name__ == '__main__':
diff --git a/project/ci/travis/boost.py b/project/ci/travis/boost.py
index 6d3e911..b8e843f 100644
--- a/project/ci/travis/boost.py
+++ b/project/ci/travis/boost.py
@@ -1,11 +1,9 @@
-#!/usr/bin/env python3
-
# Copyright (c) 2019 Egor Tensin <Egor.Tensin@gmail.com>
# This file is part of the "cmake-common" project.
# For details, see https://github.com/egor-tensin/cmake-common.
# Distributed under the MIT License.
-'''Download & build Boost on Travis.
+R'''Download & build Boost on Travis.
This is similar to build.py, but auto-fills some parameters for build.py from
the Travis-defined environment variables.
@@ -20,7 +18,12 @@ import os.path
import sys
from project.boost.version import Version
-from project.boost.build import main as build_main
+from project.boost.download import DownloadParameters, download
+from project.boost.build import BuildParameters, build
+from project.configuration import Configuration
+from project.linkage import Linkage
+from project.platform import Platform
+import project.utils
def _env(name):
@@ -43,21 +46,15 @@ def _get_boost_dir():
def _get_boost_version():
- return _env('travis_boost_version')
+ return Version.from_string(_env('travis_boost_version'))
def _get_configuration():
- return _env('configuration')
+ return Configuration.parse(_env('configuration'))
def _get_platform():
- return _env('platform')
-
-
-def _setup_logging():
- logging.basicConfig(
- format='%(asctime)s | %(levelname)s | %(message)s',
- level=logging.INFO)
+ return Platform.parse(_env('platform'))
def _parse_args(argv=None):
@@ -68,9 +65,9 @@ def _parse_args(argv=None):
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
- parser.add_argument('--link', metavar='LINKAGE', nargs='*',
+ parser.add_argument('--link', metavar='LINKAGE', nargs='*', type=Linkage.parse,
help='how the libraries are linked')
- parser.add_argument('--runtime-link', metavar='LINKAGE',
+ parser.add_argument('--runtime-link', metavar='LINKAGE', type=Linkage.parse,
help='how the libraries link to the runtime')
parser.add_argument('b2_args', nargs='*', metavar='B2_ARG', default=[],
help='additional b2 arguments, to be passed verbatim')
@@ -81,39 +78,26 @@ def build_travis(argv=None):
args = _parse_args(argv)
_check_travis()
- version = Version.from_string(_get_boost_version())
- travis_argv = [
- 'download',
- '--unpack', _get_build_dir(),
- '--', str(version)
- ]
- build_main(travis_argv)
+ version = _get_boost_version()
+ build_dir = _get_build_dir()
+ download(DownloadParameters(version, unpack_dir=build_dir))
unpacked_boost_dir = version.dir_path(_get_build_dir())
boost_dir = _get_boost_dir()
os.rename(unpacked_boost_dir, boost_dir)
- travis_argv = [
- 'build',
- '--configuration', _get_configuration(),
- '--platform', _get_platform(),
- ]
- if args.link is not None:
- travis_argv.append('--link')
- travis_argv += args.link
- if args.runtime_link is not None:
- travis_argv += ['--runtime-link', args.runtime_link]
- travis_argv += ['--', boost_dir]
- build_main(travis_argv + args.b2_args)
+ params = BuildParameters(boost_dir,
+ platforms=(_get_platform(),),
+ configurations=(_get_configuration(),),
+ link=args.link,
+ runtime_link=args.runtime_link,
+ b2_args=args.b2_args)
+ build(params)
def main(argv=None):
- _setup_logging()
- try:
+ with project.utils.setup_logging():
build_travis(argv)
- except Exception as e:
- logging.exception(e)
- raise
if __name__ == '__main__':
diff --git a/project/utils.py b/project/utils.py
new file mode 100644
index 0000000..a766f90
--- /dev/null
+++ b/project/utils.py
@@ -0,0 +1,50 @@
+# Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+# This file is part of the "cmake-common" project.
+# For details, see https://github.com/egor-tensin/cmake-common.
+# Distributed under the MIT License.
+
+from contextlib import contextmanager
+import logging
+import os.path
+import platform
+import subprocess
+
+
+def normalize_path(s):
+ return os.path.abspath(os.path.normpath(s))
+
+
+@contextmanager
+def setup_logging():
+ logging.basicConfig(
+ format='%(asctime)s | %(levelname)s | %(message)s',
+ datefmt='%Y-%m-%d %H:%M:%S',
+ level=logging.INFO)
+ try:
+ yield
+ except Exception as e:
+ logging.exception(e)
+ raise
+
+
+@contextmanager
+def cd(path):
+ cwd = os.getcwd()
+ os.chdir(path)
+ try:
+ yield
+ finally:
+ os.chdir(cwd)
+
+
+def run(cmd_line):
+ logging.info('Running executable: %s', cmd_line)
+ return subprocess.run(cmd_line, check=True)
+
+
+def on_windows():
+ return platform.system() == 'Windows'
+
+
+def on_linux():
+ return not on_windows()