class TestMsvcStaticLinker(CrossPlatformTestCase): def __init__(self, *args, **kwargs): super().__init__(clear_variables=True, *args, **kwargs) def setUp(self): with mock.patch('bfg9000.shell.which', mock_which): self.linker = MsvcBuilder(self.env, known_langs['c++'], ['cl'], True, 'version').linker('static_library') def test_call(self): self.assertEqual(self.linker(['in'], 'out'), [self.linker, 'in', '/OUT:out']) self.assertEqual(self.linker(['in'], 'out', flags=['flags']), [self.linker, 'flags', 'in', '/OUT:out']) def test_output_file(self): fmt = self.env.target_platform.object_format self.assertEqual( self.linker.output_file('lib', AttrDict(input_langs=['c++'])), StaticLibrary(Path('lib.lib'), fmt, ['c++']) ) def test_can_link(self): fmt = self.env.target_platform.object_format self.assertTrue(self.linker.can_link(fmt, ['c', 'c++'])) self.assertTrue(self.linker.can_link(fmt, ['goofy'])) self.assertFalse(self.linker.can_link('goofy', ['c'])) def test_flags_empty(self): self.assertEqual(self.linker.flags(opts.option_list()), []) def test_flags_string(self): self.assertEqual(self.linker.flags(opts.option_list('-v')), ['-v']) def test_flags_invalid(self): with self.assertRaises(TypeError): self.linker.flags(opts.option_list(123)) def test_parse_flags(self): self.assertEqual(self.linker.parse_flags([]), {'extra': []}) self.assertEqual(self.linker.parse_flags(['/foo', 'bar']), {'extra': ['/foo', 'bar']})
class TestMsvcCompiler(CrossPlatformTestCase): def __init__(self, *args, **kwargs): super().__init__(clear_variables=True, *args, **kwargs) def setUp(self): with mock.patch('bfg9000.shell.which', mock_which): self.compiler = MsvcBuilder(self.env, known_langs['c++'], ['cl'], True, 'version').compiler def test_call(self): extra = self.compiler._always_flags self.assertEqual(self.compiler('in', 'out'), [self.compiler] + extra + ['/c', 'in', '/Foout']) self.assertEqual(self.compiler('in', 'out', flags=['flags']), [self.compiler] + extra + ['flags', '/c', 'in', '/Foout']) self.assertEqual(self.compiler('in', 'out', 'out.d'), [self.compiler] + extra + ['/showIncludes', '/c', 'in', '/Foout']) self.assertEqual(self.compiler('in', 'out', 'out.d', ['flags']), [self.compiler] + extra + ['flags', '/showIncludes', '/c', 'in', '/Foout']) def test_default_name(self): src = SourceFile(Path('file.cpp', Root.srcdir), 'c++') self.assertEqual(self.compiler.default_name(src, None), 'file') def test_output_file(self): fmt = self.env.target_platform.object_format self.assertEqual(self.compiler.output_file('file', None), ObjectFile(Path('file.obj'), fmt, 'c++')) def test_flags_empty(self): self.assertEqual(self.compiler.flags(opts.option_list()), ['/MD']) def test_flags_include_dir(self): p = self.Path('/path/to/include') self.assertEqual( self.compiler.flags( opts.option_list(opts.include_dir(HeaderDirectory(p)))), ['/I' + p, '/MD']) self.assertEqual( self.compiler.flags(opts.option_list( opts.include_dir(HeaderDirectory(p))), mode='pkg-config'), ['-I' + p]) def test_flags_define(self): self.assertEqual( self.compiler.flags(opts.option_list(opts.define('NAME'))), ['/DNAME', '/MD']) self.assertEqual( self.compiler.flags(opts.option_list(opts.define('NAME')), mode='pkg-config'), ['-DNAME']) self.assertEqual( self.compiler.flags(opts.option_list(opts.define('NAME', 'value'))), ['/DNAME=value', '/MD']) self.assertEqual( self.compiler.flags(opts.option_list(opts.define('NAME', 'value')), mode='pkg-config'), ['-DNAME=value']) def test_flags_std(self): self.assertEqual( self.compiler.flags(opts.option_list(opts.std('c++14'))), ['/std:c++14', '/MD']) def test_flags_static(self): self.assertEqual(self.compiler.flags(opts.option_list(opts.static())), ['/MT']) self.assertEqual( self.compiler.flags( opts.option_list(), opts.option_list(opts.static()), ), ['/MT']) def test_flags_debug(self): self.assertEqual(self.compiler.flags(opts.option_list(opts.debug())), ['/Zi', '/MDd']) self.assertEqual( self.compiler.flags(opts.option_list(opts.debug(), opts.static())), ['/Zi', '/MTd']) self.assertEqual( self.compiler.flags( opts.option_list(), opts.option_list(opts.debug()), ), ['/MDd']) self.assertEqual( self.compiler.flags( opts.option_list(opts.static()), opts.option_list(opts.debug()), ), ['/MTd']) def test_flags_warning(self): self.assertEqual( self.compiler.flags(opts.option_list(opts.warning('disable'))), ['/w', '/MD']) self.assertEqual( self.compiler.flags(opts.option_list(opts.warning('all'))), ['/W3', '/MD']) self.assertEqual( self.compiler.flags(opts.option_list(opts.warning('extra'))), ['/W4', '/MD']) self.assertEqual( self.compiler.flags(opts.option_list(opts.warning('error'))), ['/WX', '/MD']) self.assertEqual( self.compiler.flags( opts.option_list(opts.warning('all', 'extra', 'error'))), ['/W3', '/W4', '/WX', '/MD']) with self.assertRaises(ValueError): self.compiler.flags(opts.option_list(opts.warning('unknown'))) def test_flags_optimize(self): self.assertEqual( self.compiler.flags(opts.option_list(opts.optimize('disable'))), ['/Od', '/MD']) self.assertEqual( self.compiler.flags(opts.option_list(opts.optimize('size'))), ['/O1', '/MD']) self.assertEqual( self.compiler.flags(opts.option_list(opts.optimize('speed'))), ['/O2', '/MD']) self.assertEqual( self.compiler.flags(opts.option_list(opts.optimize('linktime'))), ['/GL', '/MD']) self.assertEqual( self.compiler.flags( opts.option_list(opts.optimize('speed', 'linktime'))), ['/O2', '/GL', '/MD']) def test_flags_include_pch(self): p = self.Path('/path/to/header.hpp') self.assertEqual( self.compiler.flags( opts.option_list( opts.pch( MsvcPrecompiledHeader(p, p, 'header', 'native', 'c++')))), ['/Yuheader', '/MD']) def test_flags_sanitize(self): self.assertEqual( self.compiler.flags(opts.option_list(opts.sanitize())), ['/RTC1', '/MD']) def test_flags_string(self): self.assertEqual(self.compiler.flags(opts.option_list('-v')), ['-v', '/MD']) def test_flags_invalid(self): with self.assertRaises(TypeError): self.compiler.flags(opts.option_list(123)) def test_parse_flags(self): default = { 'debug': None, 'defines': [], 'extra': [], 'includes': [], 'nologo': None, 'pch': { 'create': None, 'use': None }, 'runtime': None, 'warnings': { 'as_error': None, 'level': None } } def assertFlags(flags, extra={}): self.assertEqual(self.compiler.parse_flags(flags), merge_dicts(default, extra)) assertFlags([]) assertFlags(['/un', 'known'], {'extra': ['/un', 'known']}) assertFlags(['/nologo'], {'nologo': True}) assertFlags(['/Dfoo'], {'defines': ['foo']}) assertFlags(['/Idir'], {'includes': ['dir']}) assertFlags(['/Z7'], {'debug': 'old'}) assertFlags(['/Zi'], {'debug': 'pdb'}) assertFlags(['/ZI'], {'debug': 'edit'}) assertFlags(['/W0'], {'warnings': {'level': '0'}}) assertFlags(['/Wall'], {'warnings': {'level': 'all'}}) assertFlags(['/WX'], {'warnings': {'as_error': True}}) assertFlags(['/WX-'], {'warnings': {'as_error': False}}) assertFlags(['/w'], {'warnings': {'level': '0'}}) assertFlags(['/Yufoo'], {'pch': {'use': 'foo'}}) assertFlags(['/Ycfoo'], {'pch': {'create': 'foo'}})
class TestMsvcLinker(CrossPlatformTestCase): def __init__(self, *args, **kwargs): CrossPlatformTestCase.__init__(self, clear_variables=True, *args, **kwargs) def setUp(self): version = ('Microsoft (R) C/C++ Optimizing Compiler Version ' + '19.12.25831 for x86') with mock.patch('bfg9000.shell.which', mock_which): self.linker = MsvcBuilder(self.env, known_langs['c++'], ['cl'], version).linker('executable') def test_call(self): extra = self.linker._always_flags self.assertEqual(self.linker(['in'], 'out'), [self.linker] + extra + ['in', '/OUT:out']) self.assertEqual(self.linker(['in'], 'out', flags=['flags']), [self.linker] + extra + ['flags', 'in', '/OUT:out']) self.assertEqual(self.linker(['in'], 'out', ['lib']), [self.linker] + extra + ['in', 'lib', '/OUT:out']) self.assertEqual(self.linker(['in'], 'out', ['lib'], ['flags']), [self.linker] + extra + ['flags', 'in', 'lib', '/OUT:out']) def test_output_file(self): fmt = self.env.target_platform.object_format ext = self.env.target_platform.executable_ext self.assertEqual(self.linker.output_file('prog', None), file_types.Executable(Path('prog' + ext), fmt, 'c++')) def test_flags_empty(self): self.assertEqual(self.linker.flags(opts.option_list()), []) def test_flags_lib_dir(self): libdir = self.Path('/path/to/lib') lib = self.Path('/path/to/lib/foo.so') # Lib dir self.assertEqual( self.linker.flags( opts.option_list(opts.lib_dir(file_types.Directory(libdir)))), ['/LIBPATH:' + libdir]) self.assertEqual( self.linker.flags(opts.option_list( opts.lib_dir(file_types.Directory(libdir))), mode='pkg-config'), ['-L' + libdir]) # Shared library self.assertEqual( self.linker.flags( opts.option_list( opts.lib(file_types.SharedLibrary(lib, 'native')))), []) # Static library self.assertEqual( self.linker.flags( opts.option_list( opts.lib(file_types.StaticLibrary(lib, 'native')))), []) # Mixed self.assertEqual( self.linker.flags( opts.option_list( opts.lib_dir(file_types.Directory(libdir)), opts.lib(file_types.SharedLibrary(lib, 'native')))), ['/LIBPATH:' + libdir]) def test_flags_module_def(self): path = self.Path('/path/to/module.def') self.assertEqual( self.linker.flags( opts.option_list( opts.module_def(file_types.ModuleDefFile(path)))), ['/DEF:' + path]) def test_flags_debug(self): self.assertEqual(self.linker.flags(opts.option_list(opts.debug())), ['/DEBUG']) def test_flags_optimize(self): self.assertEqual( self.linker.flags(opts.option_list(opts.optimize('disable'))), []) self.assertEqual( self.linker.flags(opts.option_list(opts.optimize('size'))), []) self.assertEqual( self.linker.flags(opts.option_list(opts.optimize('speed'))), []) self.assertEqual( self.linker.flags(opts.option_list(opts.optimize('linktime'))), ['/LTCG']) self.assertEqual( self.linker.flags( opts.option_list(opts.optimize('speed', 'linktime'))), ['/LTCG']) def test_flags_string(self): self.assertEqual(self.linker.flags(opts.option_list('-v')), ['-v']) def test_flags_lib_literal(self): self.assertEqual( self.linker.flags(opts.option_list(opts.lib_literal('-lfoo'))), []) def test_flags_invalid(self): with self.assertRaises(TypeError): self.linker.flags(opts.option_list(123)) def test_lib_flags_empty(self): self.assertEqual(self.linker.lib_flags(opts.option_list()), []) def test_lib_flags_lib(self): lib = self.Path('/path/to/lib/foo.lib') self.assertEqual( self.linker.lib_flags( opts.option_list( opts.lib(file_types.SharedLibrary(lib, 'native')))), [lib]) self.assertEqual( self.linker.lib_flags(opts.option_list( opts.lib(file_types.SharedLibrary(lib, 'native'))), mode='pkg-config'), ['-lfoo']) self.assertEqual( self.linker.lib_flags( opts.option_list( opts.lib( file_types.WholeArchive( file_types.SharedLibrary(lib, 'native'))))), ['/WHOLEARCHIVE:' + lib]) version = ('Microsoft (R) C/C++ Optimizing Compiler Version ' + '18.00.25831 for x86') with mock.patch('bfg9000.shell.which', mock_which): linker = MsvcBuilder(self.env, known_langs['c++'], ['cl'], version).linker('executable') with self.assertRaises(TypeError): linker.lib_flags( opts.option_list( opts.lib( file_types.WholeArchive( file_types.StaticLibrary(lib, 'native'))))) with self.assertRaises(TypeError): self.linker.lib_flags( opts.option_list(opts.lib(Framework('cocoa')))) def test_lib_flags_lib_literal(self): self.assertEqual( self.linker.lib_flags(opts.option_list(opts.lib_literal('/?'))), ['/?']) def test_lib_flags_ignored(self): self.assertEqual(self.linker.lib_flags(opts.option_list('-Lfoo')), []) def test_parse_flags(self): default = { 'extra': [], 'libs': [], 'nologo': None, } def assertFlags(flags, libflags, extra={}): self.assertEqual(self.linker.parse_flags(flags, libflags), merge_dicts(default, extra)) assertFlags([], []) assertFlags(['/foo', 'bar'], ['/baz', 'quux'], { 'libs': ['quux'], 'extra': ['/foo', 'bar', '/baz'] }) assertFlags(['/nologo'], [], {'nologo': True}) assertFlags([], ['/nologo'], {'nologo': True}) assertFlags(['/nologo'], ['/nologo'], {'nologo': True})