def test_link_environment_variable_d(self): env = get_fake_env() comp = detect_d_compiler(env, MachineChoice.HOST) if comp.id == 'dmd': raise SkipTest( 'meson cannot reliably make DMD use a different linker.') self._check_ld('lld-link', 'd', 'lld-link')
def wrapped(*args, **kwargs): try: compiler_from_language(get_fake_env(), lang, MachineChoice.HOST) except EnvironmentException: raise unittest.SkipTest(f'No {lang} compiler found.') return func(*args, **kwargs)
def test_pefile_checksum(self): try: import pefile except ImportError: if is_ci(): raise raise SkipTest('pefile module not found') testdir = os.path.join(self.common_test_dir, '6 linkshared') self.init(testdir, extra_args=['--buildtype=release']) self.build() # Test that binaries have a non-zero checksum env = get_fake_env() cc = detect_c_compiler(env, MachineChoice.HOST) cc_id = cc.get_id() ld_id = cc.get_linker_id() dll = glob(os.path.join(self.builddir, '*mycpplib.dll'))[0] exe = os.path.join(self.builddir, 'cppprog.exe') for f in (dll, exe): pe = pefile.PE(f) msg = f'PE file: {f!r}, compiler: {cc_id!r}, linker: {ld_id!r}' if cc_id == 'clang-cl': # Latest clang-cl tested (7.0) does not write checksums out self.assertFalse(pe.verify_checksum(), msg=msg) else: # Verify that a valid checksum was written by all other compilers self.assertTrue(pe.verify_checksum(), msg=msg)
def test_qt5dependency_vscrt(self): ''' Test that qt5 dependencies use the debug module suffix when b_vscrt is set to 'mdd' ''' # Verify that the `b_vscrt` option is available env = get_fake_env() cc = detect_c_compiler(env, MachineChoice.HOST) if OptionKey('b_vscrt') not in cc.base_options: raise SkipTest('Compiler does not support setting the VS CRT') # Verify that qmake is for Qt5 if not shutil.which('qmake-qt5'): if not shutil.which('qmake') and not is_ci(): raise SkipTest('QMake not found') output = subprocess.getoutput('qmake --version') if 'Qt version 5' not in output and not is_ci(): raise SkipTest('Qmake found, but it is not for Qt 5.') # Setup with /MDd testdir = os.path.join(self.framework_test_dir, '4 qt') self.init(testdir, extra_args=['-Db_vscrt=mdd']) # Verify that we're linking to the debug versions of Qt DLLs build_ninja = os.path.join(self.builddir, 'build.ninja') with open(build_ninja, encoding='utf-8') as f: contents = f.read() m = re.search('build qt5core.exe: cpp_LINKER.*Qt5Cored.lib', contents) self.assertIsNotNone(m, msg=contents)
def test_rc_depends_files(self): testdir = os.path.join(self.platform_test_dir, '5 resources') # resource compiler depfile generation is not yet implemented for msvc env = get_fake_env(testdir, self.builddir, self.prefix) depfile_works = detect_c_compiler(env, MachineChoice.HOST).get_id() not in { 'msvc', 'clang-cl', 'intel-cl' } self.init(testdir) self.build() # Immediately rebuilding should not do anything self.assertBuildIsNoop() # Test compile_resources(depend_file:) # Changing mtime of sample.ico should rebuild prog self.utime(os.path.join(testdir, 'res', 'sample.ico')) self.assertRebuiltTarget('prog') # Test depfile generation by compile_resources # Changing mtime of resource.h should rebuild myres.rc and then prog if depfile_works: self.utime(os.path.join(testdir, 'inc', 'resource', 'resource.h')) self.assertRebuiltTarget('prog') self.wipe() if depfile_works: testdir = os.path.join(self.platform_test_dir, '12 resources with custom targets') self.init(testdir) self.build() # Immediately rebuilding should not do anything self.assertBuildIsNoop() # Changing mtime of resource.h should rebuild myres_1.rc and then prog_1 self.utime(os.path.join(testdir, 'res', 'resource.h')) self.assertRebuiltTarget('prog_1')
def wrapped(*args, **kwargs): env = get_fake_env() cc = detect_c_compiler(env, MachineChoice.HOST) key = OptionKey(feature) if key not in cc.base_options: raise unittest.SkipTest( f'{feature} not available with {cc.id}') return f(*args, **kwargs)
def test_all_functions_defined_in_ast_interpreter(self): ''' Ensure that the all functions defined in the Interpreter are also defined in the AstInterpreter (and vice versa). ''' env = get_fake_env() interp = Interpreter(FakeBuild(env), mock=True) astint = AstInterpreter('.', '', '') self.assertEqual(set(interp.funcs.keys()), set(astint.funcs.keys()))
def test_swift_compiler(self): wrapper = self.helper_create_binary_wrapper( 'swiftc', version='Swift 1.2345', outfile='stderr', extra_args={'Xlinker': 'macosx_version. PROJECT:ld - 1.2.3'}) env = get_fake_env() env.binaries.host.binaries['swift'] = [wrapper] compiler = detect_swift_compiler(env, MachineChoice.HOST) self.assertEqual(compiler.version, '1.2345')
def helper_for_compiler(self, lang, cb, for_machine=MachineChoice.HOST): """Helper for generating tests for overriding compilers for langaugages with more than one implementation, such as C, C++, ObjC, ObjC++, and D. """ env = get_fake_env() getter = lambda: compiler_from_language(env, lang, for_machine) cc = getter() binary, newid = cb(cc) env.binaries[for_machine].binaries[lang] = binary compiler = getter() self.assertEqual(compiler.id, newid)
def test_vim_syntax_highlighting(self): ''' Ensure that vim syntax highlighting files were updated for new functions in the global namespace in build files. ''' env = get_fake_env() interp = Interpreter(FakeBuild(env), mock=True) with open('data/syntax-highlighting/vim/syntax/meson.vim', encoding='utf-8') as f: res = re.search(r'syn keyword mesonBuiltin(\s+\\\s\w+)+', f.read(), re.MULTILINE) defined = set([a.strip() for a in res.group().split('\\')][1:]) self.assertEqual(defined, set(chain(interp.funcs.keys(), interp.builtin.keys())))
def test_objc_cpp_detection(self): ''' Test that when we can't detect objc or objcpp, we fail gracefully. ''' env = get_fake_env() try: detect_objc_compiler(env, MachineChoice.HOST) detect_objcpp_compiler(env, MachineChoice.HOST) except EnvironmentException: code = "add_languages('objc')\nadd_languages('objcpp')" self.assertMesonRaises(code, "Unknown compiler") return raise unittest.SkipTest("objc and objcpp found, can't test detection failure")
def _single_implementation_compiler(self, lang: str, binary: str, version_str: str, version: str) -> None: """Helper for languages with a single (supported) implementation. Builds a wrapper around the compiler to override the version. """ wrapper = self.helper_create_binary_wrapper(binary, version=version_str) env = get_fake_env() env.binaries.host.binaries[lang] = [wrapper] compiler = compiler_from_language(env, lang, MachineChoice.HOST) self.assertEqual(compiler.version, version)
def test_non_utf8_fails(self): # FIXME: VS backend does not use flags from compiler.get_always_args() # and thus it's missing /utf-8 argument. Was that intentional? This needs # to be revisited. if self.backend is not Backend.ninja: raise SkipTest(f'This test only pass with ninja backend (not {self.backend.name}).') testdir = os.path.join(self.platform_test_dir, '18 msvc charset') env = get_fake_env(testdir, self.builddir, self.prefix) cc = detect_c_compiler(env, MachineChoice.HOST) if cc.get_argument_syntax() != 'msvc': raise SkipTest('Not using MSVC') self.init(testdir, extra_args=['-Dtest-failure=true']) self.assertRaises(subprocess.CalledProcessError, self.build)
def test_install_pdb_introspection(self): testdir = os.path.join(self.platform_test_dir, '1 basic') env = get_fake_env(testdir, self.builddir, self.prefix) cc = detect_c_compiler(env, MachineChoice.HOST) if cc.get_argument_syntax() != 'msvc': raise SkipTest('Test only applies to MSVC-like compilers') self.init(testdir) installed = self.introspect('--installed') files = [os.path.basename(path) for path in installed.values()] self.assertIn('prog.pdb', files)
def test_ignore_libs(self): ''' Test that find_library on libs that are to be ignored returns an empty array of arguments. Must be a unit test because we cannot inspect ExternalLibraryHolder from build files. ''' testdir = os.path.join(self.platform_test_dir, '1 basic') env = get_fake_env(testdir, self.builddir, self.prefix) cc = detect_c_compiler(env, MachineChoice.HOST) if cc.get_argument_syntax() != 'msvc': raise SkipTest('Not using MSVC') # To force people to update this test, and also test self.assertEqual(set(cc.ignore_libs), {'c', 'm', 'pthread', 'dl', 'rt', 'execinfo'}) for l in cc.ignore_libs: self.assertEqual(cc.find_library(l, env, []), [])
def test_msvc_cpp17(self): testdir = os.path.join(self.unit_test_dir, '45 vscpp17') env = get_fake_env(testdir, self.builddir, self.prefix) cc = detect_c_compiler(env, MachineChoice.HOST) if cc.get_argument_syntax() != 'msvc': raise SkipTest('Test only applies to MSVC-like compilers') try: self.init(testdir) except subprocess.CalledProcessError: # According to Python docs, output is only stored when # using check_output. We don't use it, so we can't check # that the output is correct (i.e. that it failed due # to the right reason). return self.build()
def _check_ld(self, name: str, lang: str, expected: str) -> None: if not shutil.which(name): raise SkipTest(f'Could not find {name}.') envvars = [mesonbuild.envconfig.ENV_VAR_PROG_MAP[f'{lang}_ld']] # Also test a deprecated variable if there is one. if f'{lang}_ld' in mesonbuild.envconfig.DEPRECATED_ENV_PROG_MAP: envvars.append( mesonbuild.envconfig.DEPRECATED_ENV_PROG_MAP[f'{lang}_ld']) for envvar in envvars: with mock.patch.dict(os.environ, {envvar: name}): env = get_fake_env() try: comp = compiler_from_language(env, lang, MachineChoice.HOST) except EnvironmentException: raise SkipTest(f'Could not find a compiler for {lang}') self.assertEqual(comp.linker.id, expected)
def test_compiler_checks_vscrt(self): ''' Test that the correct VS CRT is used when running compiler checks ''' # Verify that the `b_vscrt` option is available env = get_fake_env() cc = detect_c_compiler(env, MachineChoice.HOST) if OptionKey('b_vscrt') not in cc.base_options: raise SkipTest('Compiler does not support setting the VS CRT') def sanitycheck_vscrt(vscrt): checks = self.get_meson_log_sanitychecks() self.assertTrue(len(checks) > 0) for check in checks: self.assertIn(vscrt, check) testdir = os.path.join(self.common_test_dir, '1 trivial') self.init(testdir) sanitycheck_vscrt('/MDd') self.new_builddir() self.init(testdir, extra_args=['-Dbuildtype=debugoptimized']) sanitycheck_vscrt('/MD') self.new_builddir() self.init(testdir, extra_args=['-Dbuildtype=release']) sanitycheck_vscrt('/MD') self.new_builddir() self.init(testdir, extra_args=['-Db_vscrt=md']) sanitycheck_vscrt('/MD') self.new_builddir() self.init(testdir, extra_args=['-Db_vscrt=mdd']) sanitycheck_vscrt('/MDd') self.new_builddir() self.init(testdir, extra_args=['-Db_vscrt=mt']) sanitycheck_vscrt('/MT') self.new_builddir() self.init(testdir, extra_args=['-Db_vscrt=mtd']) sanitycheck_vscrt('/MTd')
def test_compiler_options_documented(self): ''' Test that C and C++ compiler options and base options are documented in Builtin-Options.md. Only tests the default compiler for the current platform on the CI. ''' md = None with open('docs/markdown/Builtin-options.md', encoding='utf-8') as f: md = f.read() self.assertIsNotNone(md) env = get_fake_env() # FIXME: Support other compilers cc = detect_c_compiler(env, MachineChoice.HOST) cpp = detect_cpp_compiler(env, MachineChoice.HOST) for comp in (cc, cpp): for opt in comp.get_options(): self.assertIn(str(opt), md) for opt in comp.base_options: self.assertIn(str(opt), md) self.assertNotIn('b_unknown', md)
def test_apple_bitcode(self): ''' Test that -fembed-bitcode is correctly added while compiling and -bitcode_bundle is added while linking when b_bitcode is true and not when it is false. This can't be an ordinary test case because we need to inspect the compiler database. ''' testdir = os.path.join(self.platform_test_dir, '7 bitcode') env = get_fake_env(testdir, self.builddir, self.prefix) cc = detect_c_compiler(env, MachineChoice.HOST) if cc.id != 'clang': raise unittest.SkipTest('Not using Clang on OSX') # Try with bitcode enabled out = self.init(testdir, extra_args='-Db_bitcode=true') # Warning was printed self.assertRegex(out, 'WARNING:.*b_bitcode') # Compiler options were added for compdb in self.get_compdb(): if 'module' in compdb['file']: self.assertNotIn('-fembed-bitcode', compdb['command']) else: self.assertIn('-fembed-bitcode', compdb['command']) build_ninja = os.path.join(self.builddir, 'build.ninja') # Linker options were added with open(build_ninja, encoding='utf-8') as f: contents = f.read() m = re.search('LINK_ARGS =.*-bitcode_bundle', contents) self.assertIsNotNone(m, msg=contents) # Try with bitcode disabled self.setconf('-Db_bitcode=false') # Regenerate build self.build() for compdb in self.get_compdb(): self.assertNotIn('-fembed-bitcode', compdb['command']) build_ninja = os.path.join(self.builddir, 'build.ninja') with open(build_ninja, encoding='utf-8') as f: contents = f.read() m = re.search('LINK_ARGS =.*-bitcode_bundle', contents) self.assertIsNone(m, msg=contents)
def test_link_environment_variable_optlink(self): env = get_fake_env() comp = detect_c_compiler(env, MachineChoice.HOST) if isinstance(comp, GnuLikeCompiler): raise SkipTest('GCC cannot be used with link compatible linkers.') self._check_ld('optlink', 'c', 'optlink')
def test_builtin_options_documented(self): ''' Test that universal options and base options are documented in Builtin-Options.md. ''' from itertools import tee md = None with open('docs/markdown/Builtin-options.md', encoding='utf-8') as f: md = f.read() self.assertIsNotNone(md) found_entries = set() sections = re.finditer(r"^## (.+)$", md, re.MULTILINE) # Extract the content for this section content = self._get_section_content("Universal options", sections, md) subsections = tee(re.finditer(r"^### (.+)$", content, re.MULTILINE)) subcontent1 = self._get_section_content("Directories", subsections[0], content) subcontent2 = self._get_section_content("Core options", subsections[1], content) subcontent3 = self._get_section_content("Module options", sections, md) for subcontent in (subcontent1, subcontent2, subcontent3): # Find the option names options = set() # Match either a table row or a table heading separator: | ------ | rows = re.finditer(r"^\|(?: (\w+) .* | *-+ *)\|", subcontent, re.MULTILINE) # Skip the header of the first table next(rows) # Skip the heading separator of the first table next(rows) for m in rows: value = m.group(1) # End when the `buildtype` table starts if value is None: break options.add(value) self.assertEqual(len(found_entries & options), 0) found_entries |= options self.assertEqual(found_entries, { *(str(k.evolve(module=None)) for k in mesonbuild.coredata.BUILTIN_OPTIONS), *(str(k.evolve(module=None)) for k in mesonbuild.coredata.BUILTIN_OPTIONS_PER_MACHINE), }) # Check that `buildtype` table inside `Core options` matches how # setting of builtin options behaves # # Find all tables inside this subsection tables = re.finditer(r"^\| (\w+) .* \|\n\| *[-|\s]+ *\|$", subcontent2, re.MULTILINE) # Get the table we want using the header of the first column table = self._get_section_content('buildtype', tables, subcontent2) # Get table row data rows = re.finditer(r"^\|(?: (\w+)\s+\| (\w+)\s+\| (\w+) .* | *-+ *)\|", table, re.MULTILINE) env = get_fake_env() for m in rows: buildtype, debug, opt = m.groups() if debug == 'true': debug = True elif debug == 'false': debug = False else: raise RuntimeError(f'Invalid debug value {debug!r} in row:\n{m.group()}') env.coredata.set_option(OptionKey('buildtype'), buildtype) self.assertEqual(env.coredata.options[OptionKey('buildtype')].value, buildtype) self.assertEqual(env.coredata.options[OptionKey('optimization')].value, opt) self.assertEqual(env.coredata.options[OptionKey('debug')].value, debug)