aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/project/platform.py
blob: ed7ff69e91e7930d798a41ece188bbb63579a9d7 (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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# 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 argparse
from enum import Enum
import os.path
import platform

from project.os import on_windows


class Platform(Enum):
    # I only build for x86(-64), so here it goes.
    X86 = 'x86'
    X64 = 'x64'
    # 'auto' means that no additional arguments will be passed to either
    # Boost's b2 nor CMake (except on Windows, see below).
    AUTO = 'auto'

    def __str__(self):
        return str(self.value)

    @staticmethod
    def windows_native():
        # On Windows, no explicit platform would mean x64 for VS 2019 and x86
        # for VS 2017.  To account for this discrepancy, it is assumed that
        # Windows builds can only target either x86 or x64 (which I don't think
        # is true?), and we default to x64 most of the time.
        #
        # Source: https://stackoverflow.com/a/12578715/514684
        if platform.machine().endswith('64'):
            return Platform.X64
        return Platform.X86

    @staticmethod
    def all():
        return Platform.X86, Platform.X64,

    @staticmethod
    def parse(s):
        try:
            if s == 'Win32':
                # Visual Studio/AppVeyor convention:
                return Platform.X86
            return Platform(s)
        except ValueError as e:
            raise argparse.ArgumentTypeError(f'invalid platform: {s}') from e

    def mingw_prefix(self):
        if self is Platform.AUTO:
            if on_windows():
                # On Windows, use the host architecture.
                return Platform.windows_native().mingw_prefix()
            # On Linux, assume that the target is x64.
            return Platform.X64.mingw_prefix()
        if self is Platform.X86:
            return 'i686'
        if self is Platform.X64:
            return 'x86_64'
        raise NotImplementedError(f'unsupported platform: {self}')

    def address_model(self):
        '''Maps to Boost's address-model.'''
        if self is Platform.AUTO:
            if on_windows():
                # On Windows, use the host architecture.
                return Platform.windows_native().address_model()
            # On Linux, assume that the target is x64.
            raise RuntimeError('cannot determine address model unless the target platform is specified explicitly')
        if self is Platform.X86:
            return 32
        if self is Platform.X64:
            return 64
        raise NotImplementedError(f'unsupported platform: {self}')

    def stagedir(self, configuration):
        '''Path to the built libraries inside the Boost build directory.'''
        if self is Platform.AUTO:
            if on_windows():
                # On Windows, use the host architecture.
                return Platform.windows_native().stagedir(configuration)
            # On Linux, the libraries are stored in stage/auto/CONFIGURATION/lib.
        return os.path.join('stage', str(self), str(configuration))

    def boost_librarydir(self, configuration):
        '''Same as above, but for CMake; adds /lib/ at the end.'''
        return os.path.join(self.stagedir(configuration), 'lib')

    def b2_address_model(self):
        if self is Platform.AUTO and not on_windows():
            # On Linux, don't specify the architecture explicitly (it is
            # assumed that the host architecture will be targeted).
            return []
        return [f'address-model={self.address_model()}']

    def b2_stagedir(self, configuration):
        return [f'--stagedir={self.stagedir(configuration)}']

    def b2_args(self, configuration):
        args = []
        args += self.b2_address_model()
        args += self.b2_stagedir(configuration)
        return args

    def cmake_toolset_file(self):
        # For Makefile generators, we make a special toolset file that
        # specifies the -m32/-m64 flags, etc.
        if self is Platform.AUTO:
            # Let the compiler decide.
            return ''
        if self is Platform.X86:
            address_model = 32
        elif self is Platform.X64:
            address_model = 64
        else:
            raise NotImplementedError(f'unsupported platform: {self}')
        return f'''
set(CMAKE_C_FLAGS   -m{address_model})
set(CMAKE_CXX_FLAGS -m{address_model})
'''

    def msvc_arch(self):
        '''Maps to CMake's -A argument for MSVC.'''
        if self is Platform.AUTO:
            if on_windows():
                # On Windows, use the host architecture.
                return Platform.windows_native().msvc_arch()
            # I don't think the -A argument is supported on any generators
            # except the Visual Studio ones.
            raise RuntimeError('-A parameter is only supported for Visual Studio generators')
        if self is Platform.X86:
            return 'Win32'
        if self is Platform.X64:
            return 'x64'
        raise NotImplementedError(f'unsupported platform: {self}')