Merge pull request #2703 from mesonbuild/msvc-library-search-fixes

Various MSVC library search fixes
pull/2615/head
Jussi Pakkanen 7 years ago committed by GitHub
commit cf76ffad14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      mesonbuild/compilers/c.py
  2. 55
      mesonbuild/dependencies/base.py
  3. 2
      mesonbuild/dependencies/misc.py
  4. 6
      mesonbuild/dependencies/ui.py
  5. 4
      mesonbuild/linkers.py
  6. 9
      run_tests.py
  7. 44
      run_unittests.py
  8. 16
      test cases/unit/17 pkgconfig static/meson.build

@ -727,10 +727,12 @@ class CCompiler(Compiler):
if for_darwin(env.is_cross_build(), env): if for_darwin(env.is_cross_build(), env):
shlibext = ['dylib'] shlibext = ['dylib']
elif for_windows(env.is_cross_build(), env): elif for_windows(env.is_cross_build(), env):
# FIXME: .lib files can be import or static so we should read the
# file, figure out which one it is, and reject the wrong kind.
if self.id == 'msvc': if self.id == 'msvc':
shlibext = ['lib'] shlibext = ['lib']
else: else:
shlibext = ['dll', 'dll.a', 'lib'] shlibext = ['dll.a', 'lib', 'dll']
# Yep, static libraries can also be foo.lib # Yep, static libraries can also be foo.lib
stlibext += ['lib'] stlibext += ['lib']
elif for_cygwin(env.is_cross_build(), env): elif for_cygwin(env.is_cross_build(), env):

@ -22,6 +22,7 @@ import shlex
import shutil import shutil
import textwrap import textwrap
from enum import Enum from enum import Enum
from pathlib import PurePath
from .. import mlog from .. import mlog
from .. import mesonlib from .. import mesonlib
@ -156,9 +157,6 @@ class ExternalDependency(Dependency):
self.name = type_name # default self.name = type_name # default
self.is_found = False self.is_found = False
self.language = language self.language = language
if language and language not in self.env.coredata.compilers:
m = self.name.capitalize() + ' requires a {} compiler'
raise DependencyException(m.format(language.capitalize()))
self.version_reqs = kwargs.get('version', None) self.version_reqs = kwargs.get('version', None)
self.required = kwargs.get('required', True) self.required = kwargs.get('required', True)
self.silent = kwargs.get('silent', False) self.silent = kwargs.get('silent', False)
@ -176,7 +174,20 @@ class ExternalDependency(Dependency):
compilers = self.env.coredata.cross_compilers compilers = self.env.coredata.cross_compilers
else: else:
compilers = self.env.coredata.compilers compilers = self.env.coredata.compilers
self.compiler = compilers.get(self.language or 'c', None) # Set the compiler for this dependency if a language is specified,
# else try to pick something that looks usable.
if self.language:
if self.language not in compilers:
m = self.name.capitalize() + ' requires a {} compiler'
raise DependencyException(m.format(self.language.capitalize()))
self.compiler = compilers[self.language]
else:
# Try to find a compiler that this dependency can use for compiler
# checks. It's ok if we don't find one.
for lang in ('c', 'cpp', 'objc', 'objcpp', 'fortran', 'd'):
self.compiler = compilers.get(lang, None)
if self.compiler:
break
def get_compiler(self): def get_compiler(self):
return self.compiler return self.compiler
@ -308,8 +319,8 @@ class PkgConfigDependency(ExternalDependency):
# multiple times in the same Meson invocation. # multiple times in the same Meson invocation.
class_pkgbin = None class_pkgbin = None
def __init__(self, name, environment, kwargs): def __init__(self, name, environment, kwargs, language=None):
super().__init__('pkgconfig', environment, None, kwargs) super().__init__('pkgconfig', environment, language, kwargs)
self.name = name self.name = name
self.is_libtool = False self.is_libtool = False
# Store a copy of the pkg-config path on the object itself so it is # Store a copy of the pkg-config path on the object itself so it is
@ -401,12 +412,40 @@ class PkgConfigDependency(ExternalDependency):
p, out = Popen_safe([self.pkgbin] + args, env=env)[0:2] p, out = Popen_safe([self.pkgbin] + args, env=env)[0:2]
return p.returncode, out.strip() return p.returncode, out.strip()
def _convert_mingw_paths(self, args):
'''
Both MSVC and native Python on Windows cannot handle MinGW-esque /c/foo
paths so convert them to C:/foo. We cannot resolve other paths starting
with / like /home/foo so leave them as-is so that the user gets an
error/warning from the compiler/linker.
'''
if not mesonlib.is_windows():
return args
converted = []
for arg in args:
pargs = []
# Library search path
if arg.startswith('-L/'):
pargs = PurePath(arg[2:]).parts
tmpl = '-L{}:/{}'
elif arg.startswith('-I/'):
pargs = PurePath(arg[2:]).parts
tmpl = '-I{}:/{}'
# Full path to library or .la file
elif arg.startswith('/'):
pargs = PurePath(arg).parts
tmpl = '{}:/{}'
if len(pargs) > 1 and len(pargs[1]) == 1:
arg = tmpl.format(pargs[1], '/'.join(pargs[2:]))
converted.append(arg)
return converted
def _set_cargs(self): def _set_cargs(self):
ret, out = self._call_pkgbin(['--cflags', self.name]) ret, out = self._call_pkgbin(['--cflags', self.name])
if ret != 0: if ret != 0:
raise DependencyException('Could not generate cargs for %s:\n\n%s' % raise DependencyException('Could not generate cargs for %s:\n\n%s' %
(self.name, out)) (self.name, out))
self.compile_args = shlex.split(out) self.compile_args = self._convert_mingw_paths(shlex.split(out))
def _set_libs(self): def _set_libs(self):
env = None env = None
@ -423,7 +462,7 @@ class PkgConfigDependency(ExternalDependency):
(self.name, out)) (self.name, out))
self.link_args = [] self.link_args = []
libpaths = [] libpaths = []
for lib in shlex.split(out): for lib in self._convert_mingw_paths(shlex.split(out)):
# If we want to use only static libraries, we have to look for the # If we want to use only static libraries, we have to look for the
# file ourselves instead of depending on the compiler to find it # file ourselves instead of depending on the compiler to find it
# with -lfoo or foo.lib. However, we can only do this if we already # with -lfoo or foo.lib. However, we can only do this if we already

@ -408,7 +408,7 @@ class MPIDependency(ExternalDependency):
for pkg in pkgconfig_files: for pkg in pkgconfig_files:
try: try:
pkgdep = PkgConfigDependency(pkg, environment, kwargs) pkgdep = PkgConfigDependency(pkg, environment, kwargs, language=self.language)
if pkgdep.found(): if pkgdep.found():
self.compile_args = pkgdep.get_compile_args() self.compile_args = pkgdep.get_compile_args()
self.link_args = pkgdep.get_link_args() self.link_args = pkgdep.get_link_args()

@ -218,7 +218,8 @@ class QtBaseDependency(ExternalDependency):
kwargs['required'] = False kwargs['required'] = False
modules = OrderedDict() modules = OrderedDict()
for module in mods: for module in mods:
modules[module] = PkgConfigDependency(self.qtpkgname + module, self.env, kwargs) modules[module] = PkgConfigDependency(self.qtpkgname + module, self.env,
kwargs, language=self.language)
for m in modules.values(): for m in modules.values():
if not m.found(): if not m.found():
self.is_found = False self.is_found = False
@ -232,7 +233,8 @@ class QtBaseDependency(ExternalDependency):
core = modules['Core'] core = modules['Core']
else: else:
corekwargs = {'required': 'false', 'silent': 'true'} corekwargs = {'required': 'false', 'silent': 'true'}
core = PkgConfigDependency(self.qtpkgname + 'Core', self.env, corekwargs) core = PkgConfigDependency(self.qtpkgname + 'Core', self.env, corekwargs,
language=self.language)
# Used by self.compilers_detect() # Used by self.compilers_detect()
self.bindir = self.get_pkgconfig_host_bins(core) self.bindir = self.get_pkgconfig_host_bins(core)
if not self.bindir: if not self.bindir:

@ -40,10 +40,10 @@ class VisualStudioLinker(StaticLinker):
return [] return []
def get_always_args(self): def get_always_args(self):
return VisualStudioLinker.always_args return VisualStudioLinker.always_args[:]
def get_linker_always_args(self): def get_linker_always_args(self):
return VisualStudioLinker.always_args return VisualStudioLinker.always_args[:]
def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath):
return [] return []

@ -155,15 +155,6 @@ def run_configure(meson_command, commandlist):
return run_configure_external(meson_exe + commandlist) return run_configure_external(meson_exe + commandlist)
return run_configure_inprocess(meson_command, commandlist) return run_configure_inprocess(meson_command, commandlist)
class FakeEnvironment(object):
def __init__(self):
self.cross_info = None
self.coredata = lambda: None
self.coredata.compilers = {}
def is_cross_build(self):
return False
def print_system_info(): def print_system_info():
print(mlog.bold('System information.').get_text(mlog.colorize_console)) print(mlog.bold('System information.').get_text(mlog.colorize_console))
print('Architecture:', platform.architecture()) print('Architecture:', platform.architecture())

@ -40,7 +40,7 @@ from mesonbuild.environment import Environment
from mesonbuild.dependencies import DependencyException from mesonbuild.dependencies import DependencyException
from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram
from run_tests import exe_suffix, get_fake_options, FakeEnvironment from run_tests import exe_suffix, get_fake_options
from run_tests import get_builddir_target_args, get_backend_commands, Backend from run_tests import get_builddir_target_args, get_backend_commands, Backend
from run_tests import ensure_backend_detects_changes, run_configure, meson_exe from run_tests import ensure_backend_detects_changes, run_configure, meson_exe
from run_tests import should_run_linux_cross_tests from run_tests import should_run_linux_cross_tests
@ -1061,16 +1061,17 @@ class AllPlatformTests(BasePlatformTests):
evalue = os.environ.pop(evar) evalue = os.environ.pop(evar)
# Very rough/strict heuristics. Would never work for actual # Very rough/strict heuristics. Would never work for actual
# compiler detection, but should be ok for the tests. # compiler detection, but should be ok for the tests.
if os.path.basename(evalue).startswith('g'): ebase = os.path.basename(evalue)
if ebase.startswith('g') or ebase.endswith(('-gcc', '-g++')):
self.assertIsInstance(ecc, gnu) self.assertIsInstance(ecc, gnu)
self.assertIsInstance(elinker, ar) self.assertIsInstance(elinker, ar)
elif 'clang' in os.path.basename(evalue): elif 'clang' in ebase:
self.assertIsInstance(ecc, clang) self.assertIsInstance(ecc, clang)
self.assertIsInstance(elinker, ar) self.assertIsInstance(elinker, ar)
elif os.path.basename(evalue).startswith('ic'): elif ebase.startswith('ic'):
self.assertIsInstance(ecc, intel) self.assertIsInstance(ecc, intel)
self.assertIsInstance(elinker, ar) self.assertIsInstance(elinker, ar)
elif os.path.basename(evalue).startswith('cl'): elif ebase.startswith('cl'):
self.assertIsInstance(ecc, msvc) self.assertIsInstance(ecc, msvc)
self.assertIsInstance(elinker, lib) self.assertIsInstance(elinker, lib)
else: else:
@ -1398,6 +1399,7 @@ int main(int argc, char **argv) {
env = Environment('', self.builddir, self.meson_command, env = Environment('', self.builddir, self.meson_command,
get_fake_options(self.prefix), []) get_fake_options(self.prefix), [])
cc = env.detect_c_compiler(False) cc = env.detect_c_compiler(False)
stlinker = env.detect_static_linker(cc)
if mesonbuild.mesonlib.is_windows(): if mesonbuild.mesonlib.is_windows():
object_suffix = 'obj' object_suffix = 'obj'
shared_suffix = 'dll' shared_suffix = 'dll'
@ -1410,7 +1412,7 @@ int main(int argc, char **argv) {
else: else:
object_suffix = 'o' object_suffix = 'o'
shared_suffix = 'so' shared_suffix = 'so'
return (cc, object_suffix, shared_suffix) return (cc, stlinker, object_suffix, shared_suffix)
def pbcompile(self, compiler, source, objectfile, extra_args=[]): def pbcompile(self, compiler, source, objectfile, extra_args=[]):
cmd = compiler.get_exelist() cmd = compiler.get_exelist()
@ -1422,7 +1424,7 @@ int main(int argc, char **argv) {
def test_prebuilt_object(self): def test_prebuilt_object(self):
(compiler, object_suffix, _) = self.detect_prebuild_env() (compiler, _, object_suffix, _) = self.detect_prebuild_env()
tdir = os.path.join(self.unit_test_dir, '14 prebuilt object') tdir = os.path.join(self.unit_test_dir, '14 prebuilt object')
source = os.path.join(tdir, 'source.c') source = os.path.join(tdir, 'source.c')
objectfile = os.path.join(tdir, 'prebuilt.' + object_suffix) objectfile = os.path.join(tdir, 'prebuilt.' + object_suffix)
@ -1434,13 +1436,18 @@ int main(int argc, char **argv) {
finally: finally:
os.unlink(objectfile) os.unlink(objectfile)
def build_static_lib(self, compiler, source, objectfile, outfile, extra_args=None): def build_static_lib(self, compiler, linker, source, objectfile, outfile, extra_args=None):
if extra_args is None: if extra_args is None:
extra_args = [] extra_args = []
if compiler.id == 'msvc': if compiler.id == 'msvc':
link_cmd = ['lib', '/NOLOGO', '/OUT:' + outfile, objectfile] link_cmd = ['lib', '/NOLOGO', '/OUT:' + outfile, objectfile]
else: else:
link_cmd = ['ar', 'csr', outfile, objectfile] link_cmd = ['ar', 'csr', outfile, objectfile]
link_cmd = linker.get_exelist()
link_cmd += linker.get_always_args()
link_cmd += linker.get_std_link_args()
link_cmd += linker.get_output_args(outfile)
link_cmd += [objectfile]
self.pbcompile(compiler, source, objectfile, extra_args=extra_args) self.pbcompile(compiler, source, objectfile, extra_args=extra_args)
try: try:
subprocess.check_call(link_cmd) subprocess.check_call(link_cmd)
@ -1448,12 +1455,12 @@ int main(int argc, char **argv) {
os.unlink(objectfile) os.unlink(objectfile)
def test_prebuilt_static_lib(self): def test_prebuilt_static_lib(self):
(cc, object_suffix, _) = self.detect_prebuild_env() (cc, stlinker, object_suffix, _) = self.detect_prebuild_env()
tdir = os.path.join(self.unit_test_dir, '15 prebuilt static') tdir = os.path.join(self.unit_test_dir, '15 prebuilt static')
source = os.path.join(tdir, 'libdir/best.c') source = os.path.join(tdir, 'libdir/best.c')
objectfile = os.path.join(tdir, 'libdir/best.' + object_suffix) objectfile = os.path.join(tdir, 'libdir/best.' + object_suffix)
stlibfile = os.path.join(tdir, 'libdir/libbest.a') stlibfile = os.path.join(tdir, 'libdir/libbest.a')
self.build_static_lib(cc, source, objectfile, stlibfile) self.build_static_lib(cc, stlinker, source, objectfile, stlibfile)
# Run the test # Run the test
try: try:
self.init(tdir) self.init(tdir)
@ -1480,7 +1487,7 @@ int main(int argc, char **argv) {
os.unlink(objectfile) os.unlink(objectfile)
def test_prebuilt_shared_lib(self): def test_prebuilt_shared_lib(self):
(cc, object_suffix, shared_suffix) = self.detect_prebuild_env() (cc, _, object_suffix, shared_suffix) = self.detect_prebuild_env()
tdir = os.path.join(self.unit_test_dir, '16 prebuilt shared') tdir = os.path.join(self.unit_test_dir, '16 prebuilt shared')
source = os.path.join(tdir, 'alexandria.c') source = os.path.join(tdir, 'alexandria.c')
objectfile = os.path.join(tdir, 'alexandria.' + object_suffix) objectfile = os.path.join(tdir, 'alexandria.' + object_suffix)
@ -1514,7 +1521,7 @@ int main(int argc, char **argv) {
''' '''
if not shutil.which('pkg-config'): if not shutil.which('pkg-config'):
raise unittest.SkipTest('pkg-config not found') raise unittest.SkipTest('pkg-config not found')
(cc, objext, shext) = self.detect_prebuild_env() (cc, stlinker, objext, shext) = self.detect_prebuild_env()
testdir = os.path.join(self.unit_test_dir, '17 pkgconfig static') testdir = os.path.join(self.unit_test_dir, '17 pkgconfig static')
source = os.path.join(testdir, 'foo.c') source = os.path.join(testdir, 'foo.c')
objectfile = os.path.join(testdir, 'foo.' + objext) objectfile = os.path.join(testdir, 'foo.' + objext)
@ -1527,7 +1534,7 @@ int main(int argc, char **argv) {
else: else:
shlibfile = os.path.join(testdir, 'libfoo.' + shext) shlibfile = os.path.join(testdir, 'libfoo.' + shext)
# Build libs # Build libs
self.build_static_lib(cc, source, objectfile, stlibfile, extra_args=['-DFOO_STATIC']) self.build_static_lib(cc, stlinker, source, objectfile, stlibfile, extra_args=['-DFOO_STATIC'])
self.build_shared_lib(cc, source, objectfile, shlibfile, impfile) self.build_shared_lib(cc, source, objectfile, shlibfile, impfile)
# Run test # Run test
os.environ['PKG_CONFIG_LIBDIR'] = self.builddir os.environ['PKG_CONFIG_LIBDIR'] = self.builddir
@ -1555,7 +1562,8 @@ int main(int argc, char **argv) {
'--libdir=' + libdir]) '--libdir=' + libdir])
# Find foo dependency # Find foo dependency
os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir
env = FakeEnvironment() env = Environment(testdir, self.builddir, self.meson_command,
get_fake_options(self.prefix), [])
kwargs = {'required': True, 'silent': True} kwargs = {'required': True, 'silent': True}
foo_dep = PkgConfigDependency('libfoo', env, kwargs) foo_dep = PkgConfigDependency('libfoo', env, kwargs)
# Ensure link_args are properly quoted # Ensure link_args are properly quoted
@ -1875,7 +1883,8 @@ class LinuxlikeTests(BasePlatformTests):
''' '''
testdir = os.path.join(self.common_test_dir, '51 pkgconfig-gen') testdir = os.path.join(self.common_test_dir, '51 pkgconfig-gen')
self.init(testdir) self.init(testdir)
env = FakeEnvironment() env = Environment(testdir, self.builddir, self.meson_command,
get_fake_options(self.prefix), [])
kwargs = {'required': True, 'silent': True} kwargs = {'required': True, 'silent': True}
os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir
foo_dep = PkgConfigDependency('libfoo', env, kwargs) foo_dep = PkgConfigDependency('libfoo', env, kwargs)
@ -2259,8 +2268,9 @@ class LinuxlikeTests(BasePlatformTests):
raise unittest.SkipTest('gcovr not found') raise unittest.SkipTest('gcovr not found')
if not shutil.which('genhtml'): if not shutil.which('genhtml'):
raise unittest.SkipTest('genhtml not found') raise unittest.SkipTest('genhtml not found')
if 'clang' in os.environ.get('CC', '') and os.environ.get('TRAVIS_OS_NAME', '') == 'linux': if 'clang' in os.environ.get('CC', ''):
raise unittest.SkipTest('Gcovr has a bug and does not work with Clang in the CI environment.') # We need to use llvm-cov instead of gcovr with clang
raise unittest.SkipTest('Coverage does not work with clang right now, help wanted!')
testdir = os.path.join(self.common_test_dir, '1 trivial') testdir = os.path.join(self.common_test_dir, '1 trivial')
self.init(testdir, ['-Db_coverage=true']) self.init(testdir, ['-Db_coverage=true'])
self.build() self.build()

@ -5,8 +5,22 @@ if build_machine.system() != 'windows'
else else
# pkg-config files should not use paths with \ # pkg-config files should not use paths with \
prefix_parts = meson.source_root().split('\\') prefix_parts = meson.source_root().split('\\')
prefix = '/'.join(prefix_parts) # If the path is C:/foo/bar, convert it to /c/foo/bar so we can test if our
# automatic conversion to C:/foo/bar inside PkgConfigDependency is working.
if prefix_parts[0][1] == ':'
drive = prefix_parts[0][0]
else
drive = prefix_parts[0]
endif
new_parts = []
foreach part : prefix_parts
if part != prefix_parts[0]
new_parts += part
endif
endforeach
prefix = '/@0@/@1@'.format(drive, '/'.join(new_parts))
endif endif
message(prefix)
# Escape spaces # Escape spaces
prefix_parts = prefix.split(' ') prefix_parts = prefix.split(' ')

Loading…
Cancel
Save