Skip to content
Permalink
Browse files
Merge pull request #684 from mesonbuild/pdb
Create pdb files with MSVC
  • Loading branch information
jpakkane committed Sep 11, 2016
2 parents 167deda + 91c5f07 commit c334eeda76c1c4a5d7a150c7af5976ea7a73c7ad
@@ -120,6 +120,12 @@ def get_target_filename_for_linking(self, target):
return os.path.join(self.get_target_dir(target), target.get_filename())
raise AssertionError('BUG: Tried to link to something that\'s not a library')

def get_target_debug_filename(self, target):
fname = target.get_debug_filename()
if not fname:
raise AssertionError("BUG: Tried to generate debug filename when it doesn't exist")
return os.path.join(self.get_target_dir(target), fname)

def get_target_dir(self, target):
if self.environment.coredata.get_builtin_option('layout') == 'mirror':
dirname = target.get_subdir()
@@ -544,6 +544,13 @@ def generate_target_install(self, d):
else:
# XXX: Add BuildTarget-specific install dir cases here
outdir = self.environment.get_libdir()
if isinstance(t, build.SharedLibrary) or isinstance(t, build.Executable):
if t.get_debug_filename():
# Install the debug symbols file in the same place as
# the target itself. It has no aliases, should not be
# stripped, and doesn't have an install_rpath
i = [self.get_target_debug_filename(t), outdir, [], False, '']
d.targets.append(i)
i = [self.get_target_filename(t), outdir, t.get_aliaslist(),\
should_strip, t.install_rpath]
d.targets.append(i)
@@ -1482,6 +1489,66 @@ def get_cross_stdlib_args(self, target, compiler):
return []
return compiler.get_no_stdinc_args()

def get_compile_debugfile_args(self, compiler, target, objfile):
if compiler.id != 'msvc':
return []
# The way MSVC uses PDB files is documented exactly nowhere so
# the following is what we have been able to decipher via
# reverse engineering.
#
# Each object file gets the path of its PDB file written
# inside it. This can be either the final PDB (for, say,
# foo.exe) or an object pdb (for foo.obj). If the former, then
# each compilation step locks the pdb file for writing, which
# is a bottleneck and object files from one target can not be
# used in a different target. The latter seems to be the
# sensible one (and what Unix does) but there is a catch. If
# you try to use precompiled headers MSVC will error out
# because both source and pch pdbs go in the same file and
# they must be the same.
#
# This means:
#
# - pch files must be compiled anew for every object file (negating
# the entire point of having them in the first place)
# - when using pch, output must go to the target pdb
#
# Since both of these are broken in some way, use the one that
# works for each target. This unfortunately means that you
# can't combine pch and object extraction in a single target.
#
# PDB files also lead to filename collisions. A target foo.exe
# has a corresponding foo.pdb. A shared library foo.dll _also_
# has pdb file called foo.pdb. So will a static library
# foo.lib, which clobbers both foo.pdb _and_ the dll file's
# export library called foo.lib (by default, currently we name
# them libfoo.a to avoidt this issue). You can give the files
# unique names such as foo_exe.pdb but VC also generates a
# bunch of other files which take their names from the target
# basename (i.e. "foo") and stomp on each other.
#
# CMake solves this problem by doing two things. First of all
# static libraries do not generate pdb files at
# all. Presumably you don't need them and VC is smart enough
# to look up the original data when linking (speculation, not
# tested). The second solution is that you can only have
# target named "foo" as an exe, shared lib _or_ static
# lib. This makes filename collisions not happen. The downside
# is that you can't have an executable foo that uses a shared
# library libfoo.so, which is a common idiom on Unix.
#
# If you feel that the above is completely wrong and all of
# this is actually doable, please send patches.

if target.has_pch():
tfilename = self.get_target_filename_abs(target)
return compiler.get_compile_debugfile_args(tfilename)
else:
return compiler.get_compile_debugfile_args(objfile)

def get_link_debugfile_args(self, linker, target, outname):
return linker.get_link_debugfile_args(outname)

def generate_single_compile(self, target, outfile, src, is_generated=False, header_deps=[], order_deps=[]):
if(isinstance(src, str) and src.endswith('.h')):
raise RuntimeError('Fug')
@@ -1568,6 +1635,8 @@ def generate_single_compile(self, target, outfile, src, is_generated=False, head
commands+= compiler.get_include_args(i, False)
if self.environment.coredata.base_options.get('b_pch', False):
commands += self.get_pch_include_args(compiler, target)

commands += self.get_compile_debugfile_args(compiler, target, rel_obj)
crstr = ''
if target.is_cross:
crstr = '_CROSS'
@@ -1636,6 +1705,7 @@ def generate_msvc_pch_command(self, target, compiler, pch):
just_name = os.path.split(header)[1]
(objname, pch_args) = compiler.gen_pch_args(just_name, source, dst)
commands += pch_args
commands += self.get_compile_debugfile_args(compiler, target, objname)
dep = dst + '.' + compiler.get_depfile_suffix()
return (commands, dep, dst, [objname])

@@ -1715,6 +1785,7 @@ def generate_link(self, target, outfile, outname, obj_list, linker, extra_args=[
linker)
commands += linker.get_buildtype_linker_args(self.environment.coredata.get_builtin_option('buildtype'))
commands += linker.get_option_link_args(self.environment.coredata.compiler_options)
commands += self.get_link_debugfile_args(linker, target, outname)
if not(isinstance(target, build.StaticLibrary)):
commands += self.environment.coredata.external_link_args[linker.get_language()]
if isinstance(target, build.Executable):
@@ -206,6 +206,8 @@ def __init__(self, name, subdir, subproject, is_cross, sources, objects, environ
self.link_targets = []
self.link_depends = []
self.filename = 'no_name'
# The file with debugging symbols
self.debug_filename = None
self.need_install = False
self.pch = {}
self.extra_args = {}
@@ -467,6 +469,15 @@ def get_subdir(self):
def get_filename(self):
return self.filename

def get_debug_filename(self):
"""
The name of the file that contains debugging symbols for this target
Returns None if there are no debugging symbols or if they are embedded
in the filename itself
"""
return self.debug_filename

def get_extra_args(self, language):
return self.extra_args.get(language, [])

@@ -729,6 +740,11 @@ def __init__(self, name, subdir, subproject, is_cross, sources, objects, environ
self.filename = self.name
if self.suffix:
self.filename += '.' + self.suffix
# See determine_debug_filenames() in build.SharedLibrary
buildtype = environment.coredata.get_builtin_option('buildtype')
if compiler_is_msvc(self.sources, is_cross, environment) and \
buildtype.startswith('debug'):
self.debug_filename = self.prefix + self.name + '.pdb'

def type_suffix(self):
return "@exe"
@@ -754,6 +770,11 @@ def __init__(self, name, subdir, subproject, is_cross, sources, objects, environ
else:
self.suffix = 'a'
self.filename = self.prefix + self.name + '.' + self.suffix
# See determine_debug_filenames() in build.SharedLibrary
buildtype = environment.coredata.get_builtin_option('buildtype')
if compiler_is_msvc(self.sources, is_cross, environment) and \
buildtype.startswith('debug'):
self.debug_filename = self.prefix + self.name + '.pdb'

def type_suffix(self):
return "@sta"
@@ -776,6 +797,7 @@ def __init__(self, name, subdir, subproject, is_cross, sources, objects, environ
self.suffix = None
self.basic_filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
self.determine_filenames(is_cross, environment)
self.determine_debug_filenames(is_cross, environment)

def determine_filenames(self, is_cross, env):
"""
@@ -865,6 +887,21 @@ def determine_filenames(self, is_cross, env):
self.suffix = suffix
self.filename = self.filename_tpl.format(self)

def determine_debug_filenames(self, is_cross, env):
"""
Determine the debug filename(s) using the prefix/name/etc detected in
determine_filenames() above.
"""
buildtype = env.coredata.get_builtin_option('buildtype')
if compiler_is_msvc(self.sources, is_cross, env) and buildtype.startswith('debug'):
# Currently we only implement separate debug symbol files for MSVC
# since the toolchain does it for us. Other toolchains embed the
# debugging symbols in the file itself by default.
if self.soversion:
self.debug_filename = '{0.prefix}{0.name}-{0.soversion}.pdb'.format(self)
else:
self.debug_filename = '{0.prefix}{0.name}.pdb'.format(self)

def process_kwargs(self, kwargs, environment):
super().process_kwargs(kwargs, environment)
# Shared library version
@@ -373,6 +373,14 @@ def get_cross_extra_flags(self, environment, *, compile, link):
def get_colorout_args(self, colortype):
return []

# Some compilers (msvc) write debug info to a separate file.
# These args specify where it should be written.
def get_compile_debugfile_args(self, rel_obj):
return []

def get_link_debugfile_args(self, rel_obj):
return []

class CCompiler(Compiler):
def __init__(self, exelist, version, is_cross, exe_wrapper=None):
super().__init__(exelist, version)
@@ -1708,16 +1716,11 @@ def unix_compile_flags_to_native(self, args):
class VisualStudioCCompiler(CCompiler):
std_warn_args = ['/W3']
std_opt_args= ['/O2']
vs2010_always_args = ['/nologo', '/showIncludes']
vs2013_always_args = ['/nologo', '/showIncludes', '/FS']

def __init__(self, exelist, version, is_cross, exe_wrap):
CCompiler.__init__(self, exelist, version, is_cross, exe_wrap)
self.id = 'msvc'
if int(version.split('.')[0]) > 17:
self.always_args = VisualStudioCCompiler.vs2013_always_args
else:
self.always_args = VisualStudioCCompiler.vs2010_always_args
self.always_args = ['/nologo', '/showIncludes']
self.warn_args = {'1': ['/W2'],
'2': ['/W3'],
'3': ['/w4']}
@@ -1878,6 +1881,16 @@ def has_argument(self, arg, env):
raise MesonException('Compiling test app failed.')
return not(warning_text in stde or warning_text in stdo)

def get_compile_debugfile_args(self, rel_obj):
pdbarr = rel_obj.split('.')[:-1]
pdbarr += ['pdb']
return ['/Fd' + '.'.join(pdbarr)]

def get_link_debugfile_args(self, targetfile):
pdbarr = targetfile.split('.')[:-1]
pdbarr += ['pdb']
return ['/DEBUG', '/PDB:' + '.'.join(pdbarr)]

class VisualStudioCPPCompiler(VisualStudioCCompiler):
def __init__(self, exelist, version, is_cross, exe_wrap):
VisualStudioCCompiler.__init__(self, exelist, version, is_cross, exe_wrap)
@@ -2564,6 +2577,11 @@ def unix_link_flags_to_native(self, args):
def unix_compile_flags_to_native(self, args):
return args[:]

def get_link_debugfile_args(self, targetfile):
pdbarr = targetfile.split('.')[:-1]
pdbarr += ['pdb']
return ['/DEBUG', '/PDB:' + '.'.join(pdbarr)]

class ArLinker():

def __init__(self, exelist):
@@ -2612,3 +2630,6 @@ def unix_link_flags_to_native(self, args):

def unix_compile_flags_to_native(self, args):
return args[:]

def get_link_debugfile_args(self, targetfile):
return []
@@ -169,7 +169,7 @@ def validate_install(srcdir, installdir):
# Check if there are any unexpected files
found = get_relative_files_list_from_dir(installdir)
for fname in found:
if fname not in expected:
if fname not in expected and not fname.endswith('.pdb'):
ret_msg += 'Extra file {0} found.\n'.format(fname)
return ret_msg

@@ -1,13 +1,11 @@
project('same basename', 'c')

subdir('sharedsub')
subdir('staticsub')

# Use the same source file to check that each top level target
# has its own unique working directory. If they don't
# then the .o files will clobber each other.
shlib = shared_library('name', 'lib.c', c_args : '-DSHAR')

# On Windows a static lib is now libfoo.a, so it does not conflict with foo.lib
# from the shared library above
stlib = static_library('name', 'lib.c', c_args : '-DSTAT')

exe1 = executable('name', 'exe1.c', link_with : stlib)
exe2 = executable('name2', 'exe2.c', link_with : shlib)
@@ -0,0 +1 @@
shlib = shared_library('name', '../lib.c', c_args : '-DSHAR')
@@ -0,0 +1,3 @@
# On Windows a static lib is now libfoo.a, so it does not conflict with foo.lib
# from the shared library above
stlib = static_library('name', '../lib.c', c_args : '-DSTAT')

0 comments on commit c334eed

Please sign in to comment.