Merge pull request #2703 from mesonbuild/msvc-library-search-fixes
Various MSVC library search fixes
This commit is contained in:
commit
cf76ffad14
|
@ -727,10 +727,12 @@ class CCompiler(Compiler):
|
|||
if for_darwin(env.is_cross_build(), env):
|
||||
shlibext = ['dylib']
|
||||
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':
|
||||
shlibext = ['lib']
|
||||
else:
|
||||
shlibext = ['dll', 'dll.a', 'lib']
|
||||
shlibext = ['dll.a', 'lib', 'dll']
|
||||
# Yep, static libraries can also be foo.lib
|
||||
stlibext += ['lib']
|
||||
elif for_cygwin(env.is_cross_build(), env):
|
||||
|
|
|
@ -22,6 +22,7 @@ import shlex
|
|||
import shutil
|
||||
import textwrap
|
||||
from enum import Enum
|
||||
from pathlib import PurePath
|
||||
|
||||
from .. import mlog
|
||||
from .. import mesonlib
|
||||
|
@ -156,9 +157,6 @@ class ExternalDependency(Dependency):
|
|||
self.name = type_name # default
|
||||
self.is_found = False
|
||||
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.required = kwargs.get('required', True)
|
||||
self.silent = kwargs.get('silent', False)
|
||||
|
@ -176,7 +174,20 @@ class ExternalDependency(Dependency):
|
|||
compilers = self.env.coredata.cross_compilers
|
||||
else:
|
||||
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):
|
||||
return self.compiler
|
||||
|
@ -308,8 +319,8 @@ class PkgConfigDependency(ExternalDependency):
|
|||
# multiple times in the same Meson invocation.
|
||||
class_pkgbin = None
|
||||
|
||||
def __init__(self, name, environment, kwargs):
|
||||
super().__init__('pkgconfig', environment, None, kwargs)
|
||||
def __init__(self, name, environment, kwargs, language=None):
|
||||
super().__init__('pkgconfig', environment, language, kwargs)
|
||||
self.name = name
|
||||
self.is_libtool = False
|
||||
# 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]
|
||||
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):
|
||||
ret, out = self._call_pkgbin(['--cflags', self.name])
|
||||
if ret != 0:
|
||||
raise DependencyException('Could not generate cargs for %s:\n\n%s' %
|
||||
(self.name, out))
|
||||
self.compile_args = shlex.split(out)
|
||||
self.compile_args = self._convert_mingw_paths(shlex.split(out))
|
||||
|
||||
def _set_libs(self):
|
||||
env = None
|
||||
|
@ -423,7 +462,7 @@ class PkgConfigDependency(ExternalDependency):
|
|||
(self.name, out))
|
||||
self.link_args = []
|
||||
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
|
||||
# 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
|
||||
|
|
|
@ -408,7 +408,7 @@ class MPIDependency(ExternalDependency):
|
|||
|
||||
for pkg in pkgconfig_files:
|
||||
try:
|
||||
pkgdep = PkgConfigDependency(pkg, environment, kwargs)
|
||||
pkgdep = PkgConfigDependency(pkg, environment, kwargs, language=self.language)
|
||||
if pkgdep.found():
|
||||
self.compile_args = pkgdep.get_compile_args()
|
||||
self.link_args = pkgdep.get_link_args()
|
||||
|
|
|
@ -218,7 +218,8 @@ class QtBaseDependency(ExternalDependency):
|
|||
kwargs['required'] = False
|
||||
modules = OrderedDict()
|
||||
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():
|
||||
if not m.found():
|
||||
self.is_found = False
|
||||
|
@ -232,7 +233,8 @@ class QtBaseDependency(ExternalDependency):
|
|||
core = modules['Core']
|
||||
else:
|
||||
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()
|
||||
self.bindir = self.get_pkgconfig_host_bins(core)
|
||||
if not self.bindir:
|
||||
|
|
|
@ -40,10 +40,10 @@ class VisualStudioLinker(StaticLinker):
|
|||
return []
|
||||
|
||||
def get_always_args(self):
|
||||
return VisualStudioLinker.always_args
|
||||
return VisualStudioLinker.always_args[:]
|
||||
|
||||
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):
|
||||
return []
|
||||
|
|
|
@ -155,15 +155,6 @@ def run_configure(meson_command, commandlist):
|
|||
return run_configure_external(meson_exe + 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():
|
||||
print(mlog.bold('System information.').get_text(mlog.colorize_console))
|
||||
print('Architecture:', platform.architecture())
|
||||
|
|
|
@ -40,7 +40,7 @@ from mesonbuild.environment import Environment
|
|||
from mesonbuild.dependencies import DependencyException
|
||||
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 ensure_backend_detects_changes, run_configure, meson_exe
|
||||
from run_tests import should_run_linux_cross_tests
|
||||
|
@ -1061,16 +1061,17 @@ class AllPlatformTests(BasePlatformTests):
|
|||
evalue = os.environ.pop(evar)
|
||||
# Very rough/strict heuristics. Would never work for actual
|
||||
# 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(elinker, ar)
|
||||
elif 'clang' in os.path.basename(evalue):
|
||||
elif 'clang' in ebase:
|
||||
self.assertIsInstance(ecc, clang)
|
||||
self.assertIsInstance(elinker, ar)
|
||||
elif os.path.basename(evalue).startswith('ic'):
|
||||
elif ebase.startswith('ic'):
|
||||
self.assertIsInstance(ecc, intel)
|
||||
self.assertIsInstance(elinker, ar)
|
||||
elif os.path.basename(evalue).startswith('cl'):
|
||||
elif ebase.startswith('cl'):
|
||||
self.assertIsInstance(ecc, msvc)
|
||||
self.assertIsInstance(elinker, lib)
|
||||
else:
|
||||
|
@ -1398,6 +1399,7 @@ int main(int argc, char **argv) {
|
|||
env = Environment('', self.builddir, self.meson_command,
|
||||
get_fake_options(self.prefix), [])
|
||||
cc = env.detect_c_compiler(False)
|
||||
stlinker = env.detect_static_linker(cc)
|
||||
if mesonbuild.mesonlib.is_windows():
|
||||
object_suffix = 'obj'
|
||||
shared_suffix = 'dll'
|
||||
|
@ -1410,7 +1412,7 @@ int main(int argc, char **argv) {
|
|||
else:
|
||||
object_suffix = 'o'
|
||||
shared_suffix = 'so'
|
||||
return (cc, object_suffix, shared_suffix)
|
||||
return (cc, stlinker, object_suffix, shared_suffix)
|
||||
|
||||
def pbcompile(self, compiler, source, objectfile, extra_args=[]):
|
||||
cmd = compiler.get_exelist()
|
||||
|
@ -1422,7 +1424,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
|
||||
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')
|
||||
source = os.path.join(tdir, 'source.c')
|
||||
objectfile = os.path.join(tdir, 'prebuilt.' + object_suffix)
|
||||
|
@ -1434,13 +1436,18 @@ int main(int argc, char **argv) {
|
|||
finally:
|
||||
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:
|
||||
extra_args = []
|
||||
if compiler.id == 'msvc':
|
||||
link_cmd = ['lib', '/NOLOGO', '/OUT:' + outfile, objectfile]
|
||||
else:
|
||||
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)
|
||||
try:
|
||||
subprocess.check_call(link_cmd)
|
||||
|
@ -1448,12 +1455,12 @@ int main(int argc, char **argv) {
|
|||
os.unlink(objectfile)
|
||||
|
||||
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')
|
||||
source = os.path.join(tdir, 'libdir/best.c')
|
||||
objectfile = os.path.join(tdir, 'libdir/best.' + object_suffix)
|
||||
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
|
||||
try:
|
||||
self.init(tdir)
|
||||
|
@ -1480,7 +1487,7 @@ int main(int argc, char **argv) {
|
|||
os.unlink(objectfile)
|
||||
|
||||
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')
|
||||
source = os.path.join(tdir, 'alexandria.c')
|
||||
objectfile = os.path.join(tdir, 'alexandria.' + object_suffix)
|
||||
|
@ -1514,7 +1521,7 @@ int main(int argc, char **argv) {
|
|||
'''
|
||||
if not shutil.which('pkg-config'):
|
||||
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')
|
||||
source = os.path.join(testdir, 'foo.c')
|
||||
objectfile = os.path.join(testdir, 'foo.' + objext)
|
||||
|
@ -1527,7 +1534,7 @@ int main(int argc, char **argv) {
|
|||
else:
|
||||
shlibfile = os.path.join(testdir, 'libfoo.' + shext)
|
||||
# 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)
|
||||
# Run test
|
||||
os.environ['PKG_CONFIG_LIBDIR'] = self.builddir
|
||||
|
@ -1555,7 +1562,8 @@ int main(int argc, char **argv) {
|
|||
'--libdir=' + libdir])
|
||||
# Find foo dependency
|
||||
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}
|
||||
foo_dep = PkgConfigDependency('libfoo', env, kwargs)
|
||||
# Ensure link_args are properly quoted
|
||||
|
@ -1875,7 +1883,8 @@ class LinuxlikeTests(BasePlatformTests):
|
|||
'''
|
||||
testdir = os.path.join(self.common_test_dir, '51 pkgconfig-gen')
|
||||
self.init(testdir)
|
||||
env = FakeEnvironment()
|
||||
env = Environment(testdir, self.builddir, self.meson_command,
|
||||
get_fake_options(self.prefix), [])
|
||||
kwargs = {'required': True, 'silent': True}
|
||||
os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir
|
||||
foo_dep = PkgConfigDependency('libfoo', env, kwargs)
|
||||
|
@ -2259,8 +2268,9 @@ class LinuxlikeTests(BasePlatformTests):
|
|||
raise unittest.SkipTest('gcovr not found')
|
||||
if not shutil.which('genhtml'):
|
||||
raise unittest.SkipTest('genhtml not found')
|
||||
if 'clang' in os.environ.get('CC', '') and os.environ.get('TRAVIS_OS_NAME', '') == 'linux':
|
||||
raise unittest.SkipTest('Gcovr has a bug and does not work with Clang in the CI environment.')
|
||||
if 'clang' in os.environ.get('CC', ''):
|
||||
# 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')
|
||||
self.init(testdir, ['-Db_coverage=true'])
|
||||
self.build()
|
||||
|
|
|
@ -5,8 +5,22 @@ if build_machine.system() != 'windows'
|
|||
else
|
||||
# pkg-config files should not use paths with \
|
||||
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
|
||||
message(prefix)
|
||||
|
||||
# Escape spaces
|
||||
prefix_parts = prefix.split(' ')
|
||||
|
|
Loading…
Reference in New Issue