1# Copyright 2012-2017 The Meson development team 2 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6 7# http://www.apache.org/licenses/LICENSE-2.0 8 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import subprocess, os.path 16import textwrap 17import typing as T 18 19from .. import coredata 20from ..mesonlib import ( 21 EnvironmentException, MachineChoice, MesonException, Popen_safe, 22 OptionKey, 23) 24from .compilers import Compiler, rust_buildtype_args, clike_debug_args 25 26if T.TYPE_CHECKING: 27 from ..coredata import KeyedOptionDictType 28 from ..envconfig import MachineInfo 29 from ..environment import Environment # noqa: F401 30 from ..linkers import DynamicLinker 31 from ..programs import ExternalProgram 32 33 34rust_optimization_args = { 35 '0': [], 36 'g': ['-C', 'opt-level=0'], 37 '1': ['-C', 'opt-level=1'], 38 '2': ['-C', 'opt-level=2'], 39 '3': ['-C', 'opt-level=3'], 40 's': ['-C', 'opt-level=s'], 41} # type: T.Dict[str, T.List[str]] 42 43class RustCompiler(Compiler): 44 45 # rustc doesn't invoke the compiler itself, it doesn't need a LINKER_PREFIX 46 language = 'rust' 47 48 _WARNING_LEVELS: T.Dict[str, T.List[str]] = { 49 '0': ['-A', 'warnings'], 50 '1': [], 51 '2': [], 52 '3': ['-W', 'warnings'], 53 } 54 55 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, 56 is_cross: bool, info: 'MachineInfo', 57 exe_wrapper: T.Optional['ExternalProgram'] = None, 58 full_version: T.Optional[str] = None, 59 linker: T.Optional['DynamicLinker'] = None): 60 super().__init__(exelist, version, for_machine, info, 61 is_cross=is_cross, full_version=full_version, 62 linker=linker) 63 self.exe_wrapper = exe_wrapper 64 self.id = 'rustc' 65 self.base_options.add(OptionKey('b_colorout')) 66 if 'link' in self.linker.id: 67 self.base_options.add(OptionKey('b_vscrt')) 68 69 def needs_static_linker(self) -> bool: 70 return False 71 72 def sanity_check(self, work_dir: str, environment: 'Environment') -> None: 73 source_name = os.path.join(work_dir, 'sanity.rs') 74 output_name = os.path.join(work_dir, 'rusttest') 75 with open(source_name, 'w', encoding='utf-8') as ofile: 76 ofile.write(textwrap.dedent( 77 '''fn main() { 78 } 79 ''')) 80 pc = subprocess.Popen(self.exelist + ['-o', output_name, source_name], 81 stdout=subprocess.PIPE, 82 stderr=subprocess.PIPE, 83 cwd=work_dir) 84 _stdo, _stde = pc.communicate() 85 assert isinstance(_stdo, bytes) 86 assert isinstance(_stde, bytes) 87 stdo = _stdo.decode('utf-8', errors='replace') 88 stde = _stde.decode('utf-8', errors='replace') 89 if pc.returncode != 0: 90 raise EnvironmentException('Rust compiler {} can not compile programs.\n{}\n{}'.format( 91 self.name_string(), 92 stdo, 93 stde)) 94 if self.is_cross: 95 if self.exe_wrapper is None: 96 # Can't check if the binaries run so we have to assume they do 97 return 98 cmdlist = self.exe_wrapper.get_command() + [output_name] 99 else: 100 cmdlist = [output_name] 101 pe = subprocess.Popen(cmdlist, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) 102 pe.wait() 103 if pe.returncode != 0: 104 raise EnvironmentException('Executables created by Rust compiler %s are not runnable.' % self.name_string()) 105 106 def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: 107 return ['--dep-info', outfile] 108 109 def get_buildtype_args(self, buildtype: str) -> T.List[str]: 110 return rust_buildtype_args[buildtype] 111 112 def get_sysroot(self) -> str: 113 cmd = self.exelist + ['--print', 'sysroot'] 114 p, stdo, stde = Popen_safe(cmd) 115 return stdo.split('\n')[0] 116 117 def get_debug_args(self, is_debug: bool) -> T.List[str]: 118 return clike_debug_args[is_debug] 119 120 def get_optimization_args(self, optimization_level: str) -> T.List[str]: 121 return rust_optimization_args[optimization_level] 122 123 def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], 124 build_dir: str) -> T.List[str]: 125 for idx, i in enumerate(parameter_list): 126 if i[:2] == '-L': 127 for j in ['dependency', 'crate', 'native', 'framework', 'all']: 128 combined_len = len(j) + 3 129 if i[:combined_len] == f'-L{j}=': 130 parameter_list[idx] = i[:combined_len] + os.path.normpath(os.path.join(build_dir, i[combined_len:])) 131 break 132 133 return parameter_list 134 135 def get_output_args(self, outputname: str) -> T.List[str]: 136 return ['-o', outputname] 137 138 @classmethod 139 def use_linker_args(cls, linker: str) -> T.List[str]: 140 return ['-C', f'linker={linker}'] 141 142 # Rust does not have a use_linker_args because it dispatches to a gcc-like 143 # C compiler for dynamic linking, as such we invoke the C compiler's 144 # use_linker_args method instead. 145 146 def get_options(self) -> 'KeyedOptionDictType': 147 key = OptionKey('std', machine=self.for_machine, lang=self.language) 148 return { 149 key: coredata.UserComboOption( 150 'Rust edition to use', 151 ['none', '2015', '2018', '2021'], 152 'none', 153 ), 154 } 155 156 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 157 args = [] 158 key = OptionKey('std', machine=self.for_machine, lang=self.language) 159 std = options[key] 160 if std.value != 'none': 161 args.append('--edition=' + std.value) 162 return args 163 164 def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]: 165 # Rust handles this for us, we don't need to do anything 166 return [] 167 168 def get_colorout_args(self, colortype: str) -> T.List[str]: 169 if colortype in {'always', 'never', 'auto'}: 170 return [f'--color={colortype}'] 171 raise MesonException(f'Invalid color type for rust {colortype}') 172 173 def get_linker_always_args(self) -> T.List[str]: 174 args: T.List[str] = [] 175 for a in super().get_linker_always_args(): 176 args.extend(['-C', f'link-arg={a}']) 177 return args 178 179 def get_werror_args(self) -> T.List[str]: 180 # Use -D warnings, which makes every warning not explicitly allowed an 181 # error 182 return ['-D', 'warnings'] 183 184 def get_warn_args(self, level: str) -> T.List[str]: 185 # TODO: I'm not really sure what to put here, Rustc doesn't have warning 186 return self._WARNING_LEVELS[level] 187 188 def get_no_warn_args(self) -> T.List[str]: 189 return self._WARNING_LEVELS["0"] 190 191 def get_pic_args(self) -> T.List[str]: 192 # This defaults to 193 return ['-C', 'relocation-model=pic'] 194 195 def get_pie_args(self) -> T.List[str]: 196 # Rustc currently has no way to toggle this, it's controlled by whether 197 # pic is on by rustc 198 return [] 199 200 201class ClippyRustCompiler(RustCompiler): 202 203 """Clippy is a linter that wraps Rustc. 204 205 This just provides us a different id 206 """ 207 208 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, 209 is_cross: bool, info: 'MachineInfo', 210 exe_wrapper: T.Optional['ExternalProgram'] = None, 211 full_version: T.Optional[str] = None, 212 linker: T.Optional['DynamicLinker'] = None): 213 super().__init__(exelist, version, for_machine, is_cross, info, 214 exe_wrapper, full_version, linker) 215 self.id = 'clippy-driver rustc' 216