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
|
# SPDX-License-Identifier: Apache-2.0
# Copyright 2012-2017 The Meson development team
from __future__ import annotations
import re
import subprocess, os.path
import typing as T
from .. import mlog, options
from ..mesonlib import first, MesonException, version_compare
from .compilers import Compiler, clike_debug_args
if T.TYPE_CHECKING:
from .. import build
from ..options import MutableKeyedOptionDictType
from ..dependencies import Dependency
from ..environment import Environment
from ..linkers.linkers import DynamicLinker
from ..mesonlib import MachineChoice
swift_optimization_args: T.Dict[str, T.List[str]] = {
'plain': [],
'0': [],
'g': [],
'1': ['-O'],
'2': ['-O'],
'3': ['-O'],
's': ['-O'],
}
class SwiftCompiler(Compiler):
LINKER_PREFIX = ['-Xlinker']
language = 'swift'
id = 'llvm'
def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
env: Environment, full_version: T.Optional[str] = None,
linker: T.Optional['DynamicLinker'] = None):
super().__init__([], exelist, version, for_machine, env,
full_version=full_version, linker=linker)
self.version = version
if self.info.is_darwin():
try:
self.sdk_path = subprocess.check_output(['xcrun', '--show-sdk-path'],
universal_newlines=True,
encoding='utf-8', stderr=subprocess.STDOUT).strip()
except subprocess.CalledProcessError as e:
mlog.error("Failed to get Xcode SDK path: " + e.output)
raise MesonException('Xcode license not accepted yet. Run `sudo xcodebuild -license`.')
except FileNotFoundError:
mlog.error('xcrun not found. Install Xcode to compile Swift code.')
raise MesonException('Could not detect Xcode. Please install it to compile Swift code.')
def get_pic_args(self) -> T.List[str]:
return []
def get_pie_args(self) -> T.List[str]:
return []
def needs_static_linker(self) -> bool:
return True
def get_werror_args(self) -> T.List[str]:
return ['-warnings-as-errors']
def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
return ['-emit-dependencies']
def get_dependency_compile_args(self, dep: Dependency) -> T.List[str]:
args = dep.get_compile_args()
# Some deps might sneak in a hardcoded path to an older macOS SDK, which can
# cause compilation errors. Let's replace all .sdk paths with the current one.
# SwiftPM does it this way: https://github.com/swiftlang/swift-package-manager/pull/6772
# Not tested on anything else than macOS for now.
if not self.info.is_darwin():
return args
pattern = re.compile(r'.*\/MacOSX[^\/]*\.sdk(\/.*|$)')
for i, arg in enumerate(args):
if arg.startswith('-I'):
match = pattern.match(arg)
if match:
args[i] = '-I' + self.sdk_path + match.group(1)
return args
def depfile_for_object(self, objfile: str) -> T.Optional[str]:
return os.path.splitext(objfile)[0] + '.' + self.get_depfile_suffix()
def get_depfile_suffix(self) -> str:
return 'd'
def get_output_args(self, target: str) -> T.List[str]:
return ['-o', target]
def get_header_import_args(self, headername: str) -> T.List[str]:
return ['-import-objc-header', headername]
def get_warn_args(self, level: str) -> T.List[str]:
return []
def get_std_exe_link_args(self) -> T.List[str]:
return ['-emit-executable']
def get_module_args(self, modname: str) -> T.List[str]:
return ['-module-name', modname]
def get_mod_gen_args(self) -> T.List[str]:
return ['-emit-module']
def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
return ['-I' + path]
def get_compile_only_args(self) -> T.List[str]:
return ['-c']
def get_options(self) -> MutableKeyedOptionDictType:
opts = super().get_options()
key = self.form_compileropt_key('std')
opts[key] = options.UserComboOption(
self.make_option_name(key),
'Swift language version.',
'none',
# List them with swiftc -frontend -swift-version ''
choices=['none', '4', '4.2', '5', '6'])
return opts
def get_option_std_args(self, target: build.BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args += ['-swift-version', std]
# Pass C compiler -std=... arg to swiftc
c_langs = ['objc', 'c']
if target.uses_swift_cpp_interop():
c_langs = ['objcpp', 'cpp', *c_langs]
c_lang = first(c_langs, lambda x: x in target.compilers)
if c_lang is not None:
cc = target.compilers[c_lang]
args.extend(arg for c_arg in cc.get_option_std_args(target, subproject) for arg in ['-Xcc', c_arg])
return args
def get_working_directory_args(self, path: str) -> T.Optional[T.List[str]]:
if version_compare(self.version, '<4.2'):
return None
return ['-working-directory', path]
def get_cxx_interoperability_args(self, target: T.Optional[build.BuildTarget] = None) -> T.List[str]:
if target is not None and not target.uses_swift_cpp_interop():
return []
if version_compare(self.version, '<5.9'):
raise MesonException(f'Compiler {self} does not support C++ interoperability')
return ['-cxx-interoperability-mode=default']
def get_library_args(self) -> T.List[str]:
return ['-parse-as-library']
def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
build_dir: str) -> T.List[str]:
for idx, i in enumerate(parameter_list):
if i[:2] == '-I' or i[:2] == '-L':
parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
return parameter_list
def sanity_check(self, work_dir: str) -> None:
src = 'swifttest.swift'
source_name = os.path.join(work_dir, src)
output_name = os.path.join(work_dir, 'swifttest')
extra_flags: T.List[str] = []
extra_flags += self.environment.coredata.get_external_args(self.for_machine, self.language)
if self.is_cross:
extra_flags += self.get_compile_only_args()
else:
extra_flags += self.environment.coredata.get_external_link_args(self.for_machine, self.language)
with open(source_name, 'w', encoding='utf-8') as ofile:
ofile.write('''print("Swift compilation is working.")
''')
pc = subprocess.Popen(self.exelist + extra_flags + ['-emit-executable', '-o', output_name, src], cwd=work_dir)
pc.wait()
self.run_sanity_check([output_name], work_dir)
def get_debug_args(self, is_debug: bool) -> T.List[str]:
return clike_debug_args[is_debug]
def get_optimization_args(self, optimization_level: str) -> T.List[str]:
return swift_optimization_args[optimization_level]
|