@@ -1196,33 +1196,156 @@ def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]:
11961196 def name_string (self ) -> str :
11971197 return ' ' .join (self .exelist )
11981198
1199- @abc .abstractmethod
1200- def sanity_check (self , work_dir : str , environment : 'Environment' ) -> None :
1199+ def sanity_check (self , work_dir : str , env : Environment ) -> None :
12011200 """Check that this compiler actually works.
12021201
12031202 This should provide a simple compile/link test. Something as simple as:
12041203 ```python
12051204 main(): return 0
12061205 ```
12071206 is good enough here.
1207+
1208+ :param work_dir: A directory to put temporary artifacts
1209+ :param env: The :class:`environment.Environment` instance to use with
1210+ this check
1211+ :raises mesonlib.EnvironmentException: If building the binary fails
1212+ :raises mesonlib.EnvironmentException: If running the binary is attempted and fails
1213+ """
1214+ sourcename , transpiled , binname = self ._sanity_check_filenames (env )
1215+
1216+ with open (os .path .join (work_dir , sourcename ), 'w' , encoding = 'utf-8' ) as f :
1217+ f .write (self ._sanity_check_source_code ())
1218+
1219+ if transpiled :
1220+ cmdlist = self ._sanity_check_compile_args (env , sourcename , transpiled )
1221+ pc , stdo , stde = mesonlib .Popen_safe (cmdlist , cwd = work_dir )
1222+ mlog .debug ('Sanity check transpiler command line:' , mesonlib .join_args (cmdlist ))
1223+ mlog .debug ('Sanity check transpiler stdout:' )
1224+ mlog .debug (stdo )
1225+ mlog .debug ('-----\n Sanity check transpiler stderr:' )
1226+ mlog .debug (stde )
1227+ mlog .debug ('-----' )
1228+ if pc .returncode != 0 :
1229+ raise mesonlib .EnvironmentException (f'Compiler { self .name_string ()} cannot transpile programs.' )
1230+
1231+ sourcename = transpiled
1232+ comp_lang = SUFFIX_TO_LANG [transpiled .rsplit ('.' , maxsplit = 1 )[1 ]]
1233+ comp = env .coredata .compilers [self .for_machine ].get (comp_lang )
1234+ if not comp :
1235+ raise mesonlib .MesonBugException (f'Need a { comp_lang } compiler for { self .language } compiler test, but one doesnt exist' )
1236+
1237+ cmdlist = self ._transpiled_sanity_check_compile_args (comp , env , transpiled , binname )
1238+
1239+ mlog .debug ('checking compilation of transpiled source:' )
1240+ else :
1241+ cmdlist = self ._sanity_check_compile_args (env , sourcename , binname )
1242+
1243+ pc , stdo , stde = mesonlib .Popen_safe (cmdlist , cwd = work_dir )
1244+ mlog .debug ('Sanity check compiler command line:' , mesonlib .join_args (cmdlist ))
1245+ mlog .debug ('Sanity check compile stdout:' )
1246+ mlog .debug (stdo )
1247+ mlog .debug ('-----\n Sanity check compile stderr:' )
1248+ mlog .debug (stde )
1249+ mlog .debug ('-----' )
1250+ if pc .returncode != 0 :
1251+ raise mesonlib .EnvironmentException (f'Compiler { self .name_string ()} cannot compile programs.' )
1252+
1253+ self ._run_sanity_check (env , [os .path .join (work_dir , binname )], work_dir )
1254+
1255+ def _sanity_check_filenames (self , env : Environment ) -> T .Tuple [str , T .Optional [str ], str ]:
1256+ """Generate the name of the source and binary file for the sanity check.
1257+
1258+ The returned names should be just the names of the files with
1259+ extensions, but no paths.
1260+
1261+ The return value consists of a source name, a transpiled source name (if
1262+ there is one), and the final binary name.
1263+
1264+ :param env: The :class:`environment.Environment` instance to use
1265+ :return: A tuple of (sourcename, transpiled, binaryname)
1266+ """
1267+ default_ext = lang_suffixes [self .language ][0 ]
1268+ template = f'sanity_check_for_{ self .language } '
1269+ sourcename = f'{ template } .{ default_ext } '
1270+ cross_or_not = "_cross" if self .is_cross else ""
1271+ binaryname = f'{ template } { cross_or_not } .exe'
1272+ return sourcename , None , binaryname
1273+
1274+ def _transpiled_sanity_check_compile_args (self , compiler : Compiler , env : Environment ,
1275+ sourcename : str , binname : str ) -> T .List [str ]:
1276+ """Get arguments to run compiler for sanity check.
1277+
1278+ By default this will just return the compile arguments for the compiler in question.
1279+
1280+ Overriding this is useful when needing to change the kind of output
1281+ produced, or adding extra arguments.
1282+
1283+ :param compiler: The :class:`Compiler` that is used for compiling the transpiled sources
1284+ :param env: The :class:`environment.Environment` instance to use
1285+ :param sourcename: the name of the source file to generate
1286+ :param binname: the name of the binary file to generate
1287+ :return: a list of strings to pass to :func:`subprocess.run` or equivalent
12081288 """
1289+ return compiler ._sanity_check_compile_args (env , sourcename , binname )
12091290
1210- def run_sanity_check (self , environment : Environment , cmdlist : T .List [str ], work_dir : str , use_exe_wrapper_for_cross : bool = True ) -> T .Tuple [str , str ]:
1211- # Run sanity check
1212- if self .is_cross and use_exe_wrapper_for_cross :
1213- if not environment .has_exe_wrapper ():
1214- # Can't check if the binaries run so we have to assume they do
1215- return ('' , '' )
1216- cmdlist = environment .exe_wrapper .get_command () + cmdlist
1217- mlog .debug ('Running test binary command: ' , mesonlib .join_args (cmdlist ))
1291+ def _sanity_check_compile_args (self , env : Environment , sourcename : str , binname : str ) -> T .List [str ]:
1292+ """Get arguments to run compiler for sanity check.
1293+
1294+ :param env: The :class:`environment.Environment` instance to use
1295+ :param sourcename: the name of the source file to generate
1296+ :param binname: the name of the binary file to generate
1297+ :return: a list of strings to pass to :func:`subprocess.run` or equivalent
1298+ """
1299+ return self .exelist_no_ccache + self .get_always_args () + self .get_output_args (binname ) + [sourcename ]
1300+
1301+ @abc .abstractmethod
1302+ def _sanity_check_source_code (self ) -> str :
1303+ """Get the source code to run for a sanity check
1304+
1305+ :return: A string to be written into a file and ran.
1306+ """
1307+
1308+ def _sanity_check_run_with_exe_wrapper (self , env : Environment , command : T .List [str ]) -> T .List [str ]:
1309+ """Wrap the binary to run in the test with the exe_wrapper if necessary
1310+
1311+ Languages that do no want to use an exe_wrapper (or always want to use
1312+ some kind of wrapper) should override this method
1313+
1314+ :param env: the :class:`environment.Environment` instance to use
1315+ :param command: The string list of commands to run
1316+ :return: The list of commands wrapped by the exe_wrapper if it is needed, otherwise the original commands
1317+ """
1318+ if self .is_cross and env .has_exe_wrapper ():
1319+ assert env .exe_wrapper is not None , 'for mypy'
1320+ return env .exe_wrapper .get_command () + command
1321+ return command
1322+
1323+ def _run_sanity_check (self , env : Environment , cmdlist : T .List [str ], work_dir : str ) -> None :
1324+ """Run a sanity test binary
1325+
1326+ :param env: the :class:`environment.Environment` instance to use
1327+ :param cmdlist: A list of strings to pass to :func:`subprocess.run` or equivalent to run the test
1328+ :param work_dir: A directory to place temporary artifacts
1329+ :raises mesonlib.EnvironmentException: If the binary cannot be run or if it returns a non-zero exit code
1330+ """
1331+ # Can't check binaries, so we have to assume they work
1332+ if self .is_cross and not env .has_exe_wrapper ():
1333+ mlog .debug ('Cannot run cross check' )
1334+ return
1335+
1336+ cmdlist = self ._sanity_check_run_with_exe_wrapper (env , cmdlist )
1337+ mlog .debug ('Sanity check built target output for' , self .for_machine , self .language , 'compiler' )
1338+ mlog .debug (' -- Running test binary command: ' , mesonlib .join_args (cmdlist ))
12181339 try :
12191340 pe , stdo , stde = Popen_safe_logged (cmdlist , 'Sanity check' , cwd = work_dir )
1341+ mlog .debug (' -- stdout:\n ' , stdo )
1342+ mlog .debug (' -- stderr:\n ' , stde )
1343+ mlog .debug (' -- returncode:' , pe .returncode )
12201344 except Exception as e :
12211345 raise mesonlib .EnvironmentException (f'Could not invoke sanity check executable: { e !s} .' )
12221346
12231347 if pe .returncode != 0 :
12241348 raise mesonlib .EnvironmentException (f'Executables created by { self .language } compiler { self .name_string ()} are not runnable.' )
1225- return stdo , stde
12261349
12271350 def split_shlib_to_parts (self , fname : str ) -> T .Tuple [T .Optional [str ], str ]:
12281351 return None , fname
0 commit comments