summaryrefslogtreecommitdiffstats
path: root/waftools/detections/compiler_swift.py
blob: ff118187027d10e051477d7738ba7153d1665c57 (plain)
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
import re
import string
import os.path
from waflib import Utils
from distutils.version import StrictVersion


def __run(cmd):
    try:
        output = Utils.subprocess.check_output(cmd, stderr=Utils.subprocess.STDOUT, universal_newlines=True)
        return output.strip()
    except Exception:
        return ""


def __add_swift_flags(ctx):
    ctx.env.SWIFT_FLAGS = [
        "-frontend", "-c", "-sdk", ctx.env.MACOS_SDK,
        "-enable-objc-interop", "-emit-objc-header", "-parse-as-library",
        "-target", "x86_64-apple-macosx10.10"
    ]

    verRe = re.compile("(?i)version\s?([\d.]+)")
    ctx.env.SWIFT_VERSION = verRe.search(__run([ctx.env.SWIFT, '-version'])).group(1)

    # prevent possible breakages with future swift versions
    if StrictVersion(ctx.env.SWIFT_VERSION) >= StrictVersion("6.0"):
        ctx.env.SWIFT_FLAGS.extend(["-swift-version", "5"])

    if ctx.is_debug_build():
        ctx.env.SWIFT_FLAGS.append("-g")

    if ctx.is_optimization():
        ctx.env.SWIFT_FLAGS.append("-O")


def __add_static_swift_library_linking_flags(ctx, swift_library):
    ctx.env.append_value('LINKFLAGS', [
        '-L%s' % swift_library,
        '-Xlinker', '-force_load_swift_libs', '-lc++',
    ])


def __add_dynamic_swift_library_linking_flags(ctx, swift_library):
    ctx.env.append_value('LINKFLAGS', ['-L%s' % swift_library])

    # ABI compatibility
    if StrictVersion(ctx.env.SWIFT_VERSION) >= StrictVersion("5.0"):
        ctx.env.append_value('LINKFLAGS', [
            '-Xlinker', '-rpath', '-Xlinker', '/usr/lib/swift',
            '-L/usr/lib/swift',
        ])

    ctx.env.append_value('LINKFLAGS', [
        '-Xlinker', '-rpath', '-Xlinker', swift_library,
    ])


def __find_swift_library(ctx):
    swift_libraries = {}
    # look for set lib paths in passed environment variables
    if 'SWIFT_LIB_DYNAMIC' in ctx.environ:
        swift_libraries['SWIFT_LIB_DYNAMIC'] = ctx.environ['SWIFT_LIB_DYNAMIC']
    if 'SWIFT_LIB_STATIC' in ctx.environ:
        swift_libraries['SWIFT_LIB_STATIC'] = ctx.environ['SWIFT_LIB_STATIC']

    # search for swift libs relative to the swift compiler executable
    swift_library_relative_paths = {
        'SWIFT_LIB_DYNAMIC': '../../lib/swift/macosx',
        'SWIFT_LIB_STATIC': '../../lib/swift_static/macosx'
    }

    for lib_type, path in swift_library_relative_paths.items():
        if lib_type not in swift_libraries:
            lib_path = os.path.join(ctx.env.SWIFT, path)
            swift_library = ctx.root.find_dir(lib_path)
            if swift_library is not None:
                swift_libraries[lib_type] = swift_library.abspath()

    # fall back to xcode-select path
    swift_library_paths = {
        'SWIFT_LIB_DYNAMIC': [
            'Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx',
            'usr/lib/swift/macosx'
        ],
        'SWIFT_LIB_STATIC': [
            'Toolchains/XcodeDefault.xctoolchain/usr/lib/swift_static/macosx',
            'usr/lib/swift_static/macosx'
        ]
    }
    dev_path = __run(['xcode-select', '-p'])[1:]

    for lib_type, paths in swift_library_paths.items():
        for path in paths:
            if lib_type not in swift_libraries:
                swift_library = ctx.root.find_dir([dev_path, path])
                if swift_library is not None:
                    swift_libraries[lib_type] = swift_library.abspath()
                    break
            else:
                break

    # check if library paths were found
    ctx.start_msg('Checking for dynamic Swift Library')
    if 'SWIFT_LIB_DYNAMIC' in swift_libraries:
        ctx.end_msg(swift_libraries['SWIFT_LIB_DYNAMIC'])
    else:
        ctx.end_msg(False)

    ctx.start_msg('Checking for static Swift Library')
    if 'SWIFT_LIB_STATIC' in swift_libraries:
        ctx.end_msg(swift_libraries['SWIFT_LIB_STATIC'])
        ctx.env['SWIFT_LIB_STATIC'] = swift_libraries['SWIFT_LIB_STATIC']
    else:
        ctx.end_msg(False)

    enableStatic = getattr(ctx.options, 'enable_swift-static')
    if (enableStatic) and 'SWIFT_LIB_STATIC' in swift_libraries:
        __add_static_swift_library_linking_flags(ctx, swift_libraries['SWIFT_LIB_STATIC'])
    else:
        __add_dynamic_swift_library_linking_flags(ctx, swift_libraries['SWIFT_LIB_DYNAMIC'])


def __find_macos_sdk(ctx):
    ctx.start_msg('Checking for macOS SDK')
    sdk = None
    sdk_build_version = None
    sdk_version = None

    # look for set macOS SDK paths and version in passed environment variables
    if 'MACOS_SDK' in ctx.environ:
        sdk = ctx.environ['MACOS_SDK']
    if 'MACOS_SDK_VERSION' in ctx.environ:
        ctx.env.MACOS_SDK_VERSION = ctx.environ['MACOS_SDK_VERSION']

    # find macOS SDK paths and version
    if not sdk:
        sdk = __run(['xcrun', '--sdk', 'macosx', '--show-sdk-path'])
    if not ctx.env.MACOS_SDK_VERSION:
        # show-sdk-build-version: is not available on older command line tools, but return a build version (eg 17A360)
        # show-sdk-version: is always available, but on older dev tools it's only the major version
        sdk_build_version = __run(['xcrun', '--sdk', 'macosx', '--show-sdk-build-version'])
        sdk_version = __run(['xcrun', '--sdk', 'macosx', '--show-sdk-version'])

    if sdk:
        ctx.env.MACOS_SDK = sdk
        build_version = '10.10.0'

        if not ctx.env.MACOS_SDK_VERSION:
            # convert build version to a version string
            # first 2 two digits are the major version, starting with 15 which is 10.11 (offset of 4)
            # 1 char is the minor version, A => 0, B => 1 and ongoing
            # las digits are bugfix version, which are nor relevant for us
            # eg 16E185 => 10.12.4, 17A360 => 10.13, 18B71 => 10.14.1
            if sdk_build_version and isinstance(sdk_build_version, str):
                verRe = re.compile("(\d+)(\D+)(\d+)")
                version_parts = verRe.search(sdk_build_version)
                major = int(version_parts.group(1)) - 4
                minor = string.ascii_lowercase.index(version_parts.group(2).lower())
                build_version = '10.' + str(major) + '.' + str(minor)

            if not isinstance(sdk_version, str):
                sdk_version = '10.10.0'

            # pick the higher version, always pick sdk over build if newer
            if StrictVersion(build_version) > StrictVersion(sdk_version):
                ctx.env.MACOS_SDK_VERSION = build_version
            else:
                ctx.env.MACOS_SDK_VERSION = sdk_version

        ctx.end_msg(sdk + ' (version found: ' + ctx.env.MACOS_SDK_VERSION + ')')
    else:
        ctx.end_msg(False)


def __find_swift_compiler(ctx):
    ctx.start_msg('Checking for swift (Swift compiler)')
    swift = ''

    # look for set swift paths in passed environment variables
    if 'SWIFT' in ctx.environ:
        swift = ctx.environ['SWIFT']

    # find swift executable
    if not swift:
        swift = __run(['xcrun', '-find', 'swift'])

    if swift:
        ctx.end_msg(swift)
        ctx.env.SWIFT = swift
        __add_swift_flags(ctx)
        __find_swift_library(ctx)
    else:
        ctx.end_msg(False)


def configure(ctx):
    if ctx.env.DEST_OS == "darwin":
        __find_macos_sdk(ctx)
        if ctx.options.enable_swift is not False:
            __find_swift_compiler(ctx)