aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2020-03-29 20:34:08 +0300
committerEgor Tensin <Egor.Tensin@gmail.com>2020-03-29 20:34:08 +0300
commit1106f51640ee3cec107a8ac904a818767c21a9aa (patch)
tree477c6c94b3c5cf2d3cf700eee686377ad2f8ad7c
parentproject: add os.py (diff)
downloadcmake-common-1106f51640ee3cec107a8ac904a818767c21a9aa.tar.gz
cmake-common-1106f51640ee3cec107a8ac904a818767c21a9aa.zip
project.boost: first-class MinGW-w64 support
-rw-r--r--project/boost/build.py32
-rw-r--r--project/boost/toolchain.py108
-rw-r--r--project/os.py5
3 files changed, 130 insertions, 15 deletions
diff --git a/project/boost/build.py b/project/boost/build.py
index a33a538..c9081eb 100644
--- a/project/boost/build.py
+++ b/project/boost/build.py
@@ -22,6 +22,7 @@ import sys
import tempfile
from project.boost.directory import BoostDir
+from project.boost.toolchain import detect_toolchain
from project.configuration import Configuration
from project.linkage import Linkage
from project.platform import Platform
@@ -39,7 +40,7 @@ DEFAULT_B2_ARGS = ['-d0']
class BuildParameters:
def __init__(self, boost_dir, build_dir=None, platforms=None,
configurations=None, link=None, runtime_link=None,
- b2_args=None):
+ b2_args=None, mingw=False):
boost_dir = project.utils.normalize_path(boost_dir)
if build_dir is not None:
@@ -61,6 +62,7 @@ class BuildParameters:
self.link = link
self.runtime_link = runtime_link
self.b2_args = b2_args
+ self.mingw = mingw
@staticmethod
def from_args(args):
@@ -69,11 +71,12 @@ class BuildParameters:
def enum_b2_args(self):
with self._create_build_dir() as build_dir:
for platform in self.platforms:
- for configuration in self.configurations:
- for link, runtime_link in self._linkage_options():
- yield self._build_params(build_dir, platform,
- configuration, link,
- runtime_link)
+ with detect_toolchain(platform, mingw=self.mingw) as toolchain:
+ for configuration in self.configurations:
+ for link, runtime_link in self._linkage_options():
+ yield self._build_params(build_dir, toolchain,
+ configuration, link,
+ runtime_link)
def _linkage_options(self):
for link in self.link:
@@ -102,13 +105,13 @@ class BuildParameters:
logging.info('Removing build directory: %s', build_dir)
return
- def _build_params(self, build_dir, platform, configuration, link, runtime_link):
+ def _build_params(self, build_dir, toolchain, configuration, link, runtime_link):
params = []
params.append(self._build_dir(build_dir))
- params.append(self._stagedir(platform, configuration))
+ params.append(self._stagedir(toolchain, configuration))
+ params += toolchain.get_b2_args()
params.append(self._link(link))
params.append(self._runtime_link(runtime_link))
- params.append(self._address_model(platform))
params.append(self._variant(configuration))
params += self.b2_args
return params
@@ -117,7 +120,7 @@ class BuildParameters:
def _build_dir(build_dir):
return f'--build-dir={build_dir}'
- def _stagedir(self, platform, configuration):
+ def _stagedir(self, toolchain, configuration):
# Having different --stagedir values for every configuration/platform
# combination is unnecessary on Windows. Even for older Boost versions
# (when the binaries weren't tagged with their target platform) only a
@@ -125,7 +128,7 @@ class BuildParameters:
# versions, just a single --stagedir would do, as the binaries are
# tagged with the target platform, as well as their configuration
# (a.k.a. "variant" in Boost's terminology). Still, uniformity helps.
- platform = str(platform)
+ platform = str(toolchain.platform)
configuration = str(configuration)
return f'--stagedir={os.path.join(self.stage_dir, platform, configuration)}'
@@ -138,10 +141,6 @@ class BuildParameters:
return f'runtime-link={runtime_link}'
@staticmethod
- def _address_model(platform):
- return f'address-model={platform.get_address_model()}'
-
- @staticmethod
def _variant(configuration):
return f'variant={configuration.to_boost_variant()}'
@@ -195,6 +194,9 @@ def _parse_args(argv=None):
nargs='*', default=[],
help='additional b2 arguments, to be passed verbatim')
+ parser.add_argument('--mingw', action='store_true',
+ help='build using MinGW-w64')
+
return parser.parse_args(argv)
diff --git a/project/boost/toolchain.py b/project/boost/toolchain.py
new file mode 100644
index 0000000..9cc452b
--- /dev/null
+++ b/project/boost/toolchain.py
@@ -0,0 +1,108 @@
+# 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'''Compiler detection.
+
+It is assumed that Boost.Build is good enough to detect both GCC on Linux and
+MSVC on Windows. From that point on, it's just a matter of setting the correct
+address-model= value.
+
+But I also frequently use MinGW-w64, and the most convinient way to use it that
+I know is making a "user config" and passing it to b2 using the --user-config
+parameter.
+'''
+
+import abc
+from contextlib import contextmanager
+import logging
+import tempfile
+
+import project.os
+
+
+class Toolchain(abc.ABC):
+ def __init__(self, platform):
+ self.platform = platform
+
+ @abc.abstractmethod
+ def get_b2_args(self):
+ pass
+
+
+class NativeToolchain(Toolchain):
+ def get_b2_args(self):
+ return [f'address-model={self.platform.get_address_model()}']
+
+
+class MingwToolchain(Toolchain):
+ TAG = 'custom'
+
+ def __init__(self, platform, config_path):
+ super().__init__(platform)
+ self.config_path = config_path
+
+ def get_b2_args(self):
+ return [f'--user-config={self.config_path}', f'toolset=gcc-{MingwToolchain.TAG}']
+
+
+def _native_toolchain(platform):
+ return NativeToolchain(platform)
+
+
+def _get_mingw_prefix(platform):
+ target_arch = platform.get_address_model()
+ if target_arch == 32:
+ return 'i686'
+ if target_arch == 64:
+ return 'x86_64'
+ raise RuntimeError(f'unexpected address model: {target_arch}')
+
+
+def _get_mingw_path(platform):
+ prefix = _get_mingw_prefix(platform)
+ ext = ''
+ if project.os.on_windows_like():
+ # Boost.Build wants the .exe extension at the end on Cygwin.
+ ext = '.exe'
+ path = f'{prefix}-w64-mingw32-g++{ext}'
+ return path
+
+
+def _format_user_config(tag, compiler, **kwargs):
+ features = (f'<{k}>{v}' for k, v in kwargs.items())
+ features = ' '.join(features)
+ return f'using gcc : {tag} : {compiler} : {features} ;'
+
+
+def _format_mingw_user_config(platform):
+ compiler = _get_mingw_path(platform)
+ features = {
+ 'target-os': 'windows',
+ 'address-model': platform.get_address_model(),
+ }
+ return _format_user_config(MingwToolchain.TAG, compiler, **features)
+
+
+@contextmanager
+def _mingw_toolchain(platform):
+ tmp = tempfile.NamedTemporaryFile(mode='w', prefix='mingw_w64_', suffix='.jam')
+ with tmp as file:
+ config = _format_mingw_user_config(platform)
+ logging.info('Using user config:\n%s', config)
+ file.write(config)
+ file.flush()
+ try:
+ yield MingwToolchain(platform, file.name)
+ finally:
+ logging.info('Removing temporary user config file')
+
+
+@contextmanager
+def detect_toolchain(platform, mingw=False):
+ if mingw:
+ with _mingw_toolchain(platform) as toolchain:
+ yield toolchain
+ else:
+ yield _native_toolchain(platform)
diff --git a/project/os.py b/project/os.py
index 86ccaad..2f5f088 100644
--- a/project/os.py
+++ b/project/os.py
@@ -31,6 +31,11 @@ def on_windows():
return OS.current() is OS.WINDOWS
+def on_windows_like():
+ os = OS.current()
+ return os is OS.WINDOWS or os is OS.CYGWIN
+
+
def on_linux_like():
os = OS.current()
return os is OS.LINUX or os is OS.CYGWIN