Merge pull request #684 from mesonbuild/pdb

Create pdb files with MSVC
pull/780/head
Jussi Pakkanen 8 years ago committed by GitHub
commit c334eeda76
  1. 6
      mesonbuild/backend/backends.py
  2. 71
      mesonbuild/backend/ninjabackend.py
  3. 37
      mesonbuild/build.py
  4. 33
      mesonbuild/compilers.py
  5. 2
      run_tests.py
  6. 8
      test cases/common/86 same basename/meson.build
  7. 1
      test cases/common/86 same basename/sharedsub/meson.build
  8. 3
      test cases/common/86 same basename/staticsub/meson.build

@ -120,6 +120,12 @@ class Backend():
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 @@ int dummy;
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 @@ rule FORTRAN_DEP_HACK
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 @@ rule FORTRAN_DEP_HACK
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 @@ rule FORTRAN_DEP_HACK
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 @@ rule FORTRAN_DEP_HACK
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 @@ class BuildTarget():
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 @@ class BuildTarget():
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 @@ class Executable(BuildTarget):
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 @@ class StaticLibrary(BuildTarget):
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 @@ class SharedLibrary(BuildTarget):
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 @@ class SharedLibrary(BuildTarget):
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 @@ class Compiler():
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 @@ class DmdDCompiler(DCompiler):
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 @@ class VisualStudioCCompiler(CCompiler):
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 @@ class VisualStudioLinker():
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 @@ class ArLinker():
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')
Loading…
Cancel
Save