aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/project/boost/toolchain.py
blob: cacc2c2e0a75e3c90b2df27cc53ee12dd19a1c8b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# 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 project.os
from project.utils import temp_file


class Toolchain(abc.ABC):
    def __init__(self, platform):
        self.platform = platform

    @abc.abstractmethod
    def get_b2_args(self):
        pass

    @staticmethod
    @contextmanager
    def detect(platform, mingw=False):
        if mingw:
            with MinGW.setup(platform) as toolchain:
                yield toolchain
        else:
            yield Native(platform)

    @staticmethod
    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} ;'


class Native(Toolchain):
    def get_b2_args(self):
        return [f'address-model={self.platform.get_address_model()}']


class MinGW(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-{MinGW.TAG}']

    @staticmethod
    def _get_compiler_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}')

    @staticmethod
    def _get_compiler_path(platform):
        prefix = MinGW._get_compiler_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

    @staticmethod
    def _format_mingw_user_config(platform):
        compiler = MinGW._get_compiler_path(platform)
        features = {
            'target-os': 'windows',
            'address-model': platform.get_address_model(),
        }
        return Toolchain._format_user_config(MinGW.TAG, compiler, **features)

    @staticmethod
    @contextmanager
    def setup(platform):
        config = MinGW._format_mingw_user_config(platform)
        logging.info('Using user config:\n%s', config)
        tmp = temp_file(config, mode='w', prefix='mingw_w64_', suffix='.jam')
        with tmp as path:
            yield MinGW(platform, path)