1import re 2import string 3import os.path 4from waflib import Utils 5from distutils.version import StrictVersion 6 7 8def __run(cmd): 9 try: 10 output = Utils.subprocess.check_output(cmd, stderr=Utils.subprocess.STDOUT, universal_newlines=True) 11 return output.strip() 12 except Exception: 13 return "" 14 15 16def __add_swift_flags(ctx): 17 ctx.env.SWIFT_FLAGS = [ 18 "-frontend", "-c", "-sdk", ctx.env.MACOS_SDK, 19 "-enable-objc-interop", "-emit-objc-header", "-parse-as-library", 20 ] 21 22 verRe = re.compile("(?i)version\s?([\d.]+)") 23 ctx.env.SWIFT_VERSION = verRe.search(__run([ctx.env.SWIFT, '-version'])).group(1) 24 25 # prevent possible breakages with future swift versions 26 if StrictVersion(ctx.env.SWIFT_VERSION) >= StrictVersion("6.0"): 27 ctx.env.SWIFT_FLAGS.extend(["-swift-version", "5"]) 28 29 if ctx.is_debug_build(): 30 ctx.env.SWIFT_FLAGS.append("-g") 31 32 if ctx.is_optimization(): 33 ctx.env.SWIFT_FLAGS.append("-O") 34 35 36def __add_static_swift_library_linking_flags(ctx, swift_library): 37 ctx.env.append_value('LINKFLAGS', [ 38 '-L%s' % swift_library, 39 '-Xlinker', '-force_load_swift_libs', '-lc++', 40 ]) 41 42 43def __add_dynamic_swift_library_linking_flags(ctx, swift_library): 44 ctx.env.append_value('LINKFLAGS', ['-L%s' % swift_library]) 45 46 # ABI compatibility 47 if StrictVersion(ctx.env.SWIFT_VERSION) >= StrictVersion("5.0"): 48 ctx.env.append_value('LINKFLAGS', [ 49 '-Xlinker', '-rpath', '-Xlinker', '/usr/lib/swift', 50 '-L/usr/lib/swift', 51 ]) 52 53 ctx.env.append_value('LINKFLAGS', [ 54 '-Xlinker', '-rpath', '-Xlinker', swift_library, 55 ]) 56 57 58def __find_swift_library(ctx): 59 swift_libraries = {} 60 # look for set lib paths in passed environment variables 61 if 'SWIFT_LIB_DYNAMIC' in ctx.environ: 62 swift_libraries['SWIFT_LIB_DYNAMIC'] = ctx.environ['SWIFT_LIB_DYNAMIC'] 63 if 'SWIFT_LIB_STATIC' in ctx.environ: 64 swift_libraries['SWIFT_LIB_STATIC'] = ctx.environ['SWIFT_LIB_STATIC'] 65 66 # search for swift libs relative to the swift compiler executable 67 swift_library_relative_paths = { 68 'SWIFT_LIB_DYNAMIC': '../../lib/swift/macosx', 69 'SWIFT_LIB_STATIC': '../../lib/swift_static/macosx' 70 } 71 72 for lib_type, path in swift_library_relative_paths.items(): 73 if lib_type not in swift_libraries: 74 lib_path = os.path.join(ctx.env.SWIFT, path) 75 swift_library = ctx.root.find_dir(lib_path) 76 if swift_library is not None: 77 swift_libraries[lib_type] = swift_library.abspath() 78 79 # fall back to xcode-select path 80 swift_library_paths = { 81 'SWIFT_LIB_DYNAMIC': [ 82 'Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx', 83 'usr/lib/swift/macosx' 84 ], 85 'SWIFT_LIB_STATIC': [ 86 'Toolchains/XcodeDefault.xctoolchain/usr/lib/swift_static/macosx', 87 'usr/lib/swift_static/macosx' 88 ] 89 } 90 dev_path = __run(['xcode-select', '-p'])[1:] 91 92 for lib_type, paths in swift_library_paths.items(): 93 for path in paths: 94 if lib_type not in swift_libraries: 95 swift_library = ctx.root.find_dir([dev_path, path]) 96 if swift_library is not None: 97 swift_libraries[lib_type] = swift_library.abspath() 98 break 99 else: 100 break 101 102 # check if library paths were found 103 ctx.start_msg('Checking for dynamic Swift Library') 104 if 'SWIFT_LIB_DYNAMIC' in swift_libraries: 105 ctx.end_msg(swift_libraries['SWIFT_LIB_DYNAMIC']) 106 else: 107 ctx.end_msg(False) 108 109 ctx.start_msg('Checking for static Swift Library') 110 if 'SWIFT_LIB_STATIC' in swift_libraries: 111 ctx.end_msg(swift_libraries['SWIFT_LIB_STATIC']) 112 ctx.env['SWIFT_LIB_STATIC'] = swift_libraries['SWIFT_LIB_STATIC'] 113 else: 114 ctx.end_msg(False) 115 116 enableStatic = getattr(ctx.options, 'enable_swift-static') 117 if (enableStatic) and 'SWIFT_LIB_STATIC' in swift_libraries: 118 __add_static_swift_library_linking_flags(ctx, swift_libraries['SWIFT_LIB_STATIC']) 119 else: 120 __add_dynamic_swift_library_linking_flags(ctx, swift_libraries['SWIFT_LIB_DYNAMIC']) 121 122 123def __find_macos_sdk(ctx): 124 ctx.start_msg('Checking for macOS SDK') 125 sdk = None 126 sdk_build_version = None 127 sdk_version = None 128 129 # look for set macOS SDK paths and version in passed environment variables 130 if 'MACOS_SDK' in ctx.environ: 131 sdk = ctx.environ['MACOS_SDK'] 132 if 'MACOS_SDK_VERSION' in ctx.environ: 133 ctx.env.MACOS_SDK_VERSION = ctx.environ['MACOS_SDK_VERSION'] 134 135 # find macOS SDK paths and version 136 if not sdk: 137 sdk = __run(['xcrun', '--sdk', 'macosx', '--show-sdk-path']) 138 if not ctx.env.MACOS_SDK_VERSION: 139 # show-sdk-build-version: is not available on older command line tools, but return a build version (eg 17A360) 140 # show-sdk-version: is always available, but on older dev tools it's only the major version 141 sdk_build_version = __run(['xcrun', '--sdk', 'macosx', '--show-sdk-build-version']) 142 sdk_version = __run(['xcrun', '--sdk', 'macosx', '--show-sdk-version']) 143 144 if sdk: 145 ctx.env.MACOS_SDK = sdk 146 build_version = '10.10.0' 147 148 if not ctx.env.MACOS_SDK_VERSION: 149 # convert build version to a version string 150 # first 2 two digits are the major version, starting with 15 which is 10.11 (offset of 4) 151 # 1 char is the minor version, A => 0, B => 1 and ongoing 152 # last digits are bugfix version, which are not relevant for us 153 # eg 16E185 => 10.12.4, 17A360 => 10.13, 18B71 => 10.14.1 154 if sdk_build_version and isinstance(sdk_build_version, str): 155 verRe = re.compile("(\d+)(\D+)(\d+)") 156 version_parts = verRe.search(sdk_build_version) 157 major = int(version_parts.group(1)) - 4 158 minor = string.ascii_lowercase.index(version_parts.group(2).lower()) 159 build_version = '10.' + str(major) + '.' + str(minor) 160 # from 20 onwards macOS 11.0 starts 161 if int(version_parts.group(1)) >= 20: 162 build_version = '11.' + str(minor) 163 164 if not isinstance(sdk_version, str): 165 sdk_version = '10.10.0' 166 167 # pick the higher version, always pick sdk over build if newer 168 if StrictVersion(build_version) > StrictVersion(sdk_version): 169 ctx.env.MACOS_SDK_VERSION = build_version 170 else: 171 ctx.env.MACOS_SDK_VERSION = sdk_version 172 173 ctx.end_msg(sdk + ' (version found: ' + ctx.env.MACOS_SDK_VERSION + ')') 174 else: 175 ctx.end_msg(False) 176 177 178def __find_swift_compiler(ctx): 179 ctx.start_msg('Checking for swift (Swift compiler)') 180 swift = '' 181 182 # look for set swift paths in passed environment variables 183 if 'SWIFT' in ctx.environ: 184 swift = ctx.environ['SWIFT'] 185 186 # find swift executable 187 if not swift: 188 swift = __run(['xcrun', '-find', 'swift']) 189 190 if swift: 191 ctx.end_msg(swift) 192 ctx.env.SWIFT = swift 193 __add_swift_flags(ctx) 194 __find_swift_library(ctx) 195 else: 196 ctx.end_msg(False) 197 198 199def configure(ctx): 200 if ctx.env.DEST_OS == "darwin": 201 __find_macos_sdk(ctx) 202 if ctx.options.enable_swift is not False: 203 __find_swift_compiler(ctx) 204