aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/backend/backends.py6
-rw-r--r--mesonbuild/backend/ninjabackend.py71
-rw-r--r--mesonbuild/build.py37
-rw-r--r--mesonbuild/compilers.py33
-rwxr-xr-xrun_tests.py2
-rw-r--r--test cases/common/86 same basename/meson.build8
-rw-r--r--test cases/common/86 same basename/sharedsub/meson.build1
-rw-r--r--test cases/common/86 same basename/staticsub/meson.build3
8 files changed, 149 insertions, 12 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 54be8ec..6f4501c 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -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()
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 0b2b304..db9e1b0 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -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):
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 50abd52..f95922e 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -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
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index 5d3dc99..d1f3896 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -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 []
diff --git a/run_tests.py b/run_tests.py
index 48f1230..b57dd39 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -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
diff --git a/test cases/common/86 same basename/meson.build b/test cases/common/86 same basename/meson.build
index 3dc384e..856c536 100644
--- a/test cases/common/86 same basename/meson.build
+++ b/test cases/common/86 same basename/meson.build
@@ -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)
diff --git a/test cases/common/86 same basename/sharedsub/meson.build b/test cases/common/86 same basename/sharedsub/meson.build
new file mode 100644
index 0000000..29654a9
--- /dev/null
+++ b/test cases/common/86 same basename/sharedsub/meson.build
@@ -0,0 +1 @@
+shlib = shared_library('name', '../lib.c', c_args : '-DSHAR')
diff --git a/test cases/common/86 same basename/staticsub/meson.build b/test cases/common/86 same basename/staticsub/meson.build
new file mode 100644
index 0000000..5e5242e
--- /dev/null
+++ b/test cases/common/86 same basename/staticsub/meson.build
@@ -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')