diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2017-12-03 22:48:56 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-12-03 22:48:56 +0200 |
commit | cf76ffad145eb83a0bbfce89e05b7610637ff293 (patch) | |
tree | 9d76dd2d2f2398fbca00bdfad434297246338213 | |
parent | 2cf1e8da15b954725fa9c9467bfb35a516814c89 (diff) | |
parent | bccb7a8eb8f13fc747f5198ee0d77ecaf1b77be7 (diff) | |
download | meson-cf76ffad145eb83a0bbfce89e05b7610637ff293.zip meson-cf76ffad145eb83a0bbfce89e05b7610637ff293.tar.gz meson-cf76ffad145eb83a0bbfce89e05b7610637ff293.tar.bz2 |
Merge pull request #2703 from mesonbuild/msvc-library-search-fixes
Various MSVC library search fixes
-rw-r--r-- | mesonbuild/compilers/c.py | 4 | ||||
-rw-r--r-- | mesonbuild/dependencies/base.py | 55 | ||||
-rw-r--r-- | mesonbuild/dependencies/misc.py | 2 | ||||
-rw-r--r-- | mesonbuild/dependencies/ui.py | 6 | ||||
-rw-r--r-- | mesonbuild/linkers.py | 4 | ||||
-rwxr-xr-x | run_tests.py | 9 | ||||
-rwxr-xr-x | run_unittests.py | 44 | ||||
-rw-r--r-- | test cases/unit/17 pkgconfig static/meson.build | 16 |
8 files changed, 99 insertions, 41 deletions
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 317a4d7..2d12314 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -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): diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index a720232..cdeaf5e 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -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 diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 41666a3..5bd9d93 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -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() diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index dd04580..bf74029 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -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: diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py index e0554e0..de788b7 100644 --- a/mesonbuild/linkers.py +++ b/mesonbuild/linkers.py @@ -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 [] diff --git a/run_tests.py b/run_tests.py index b287a1a..1cc3983 100755 --- a/run_tests.py +++ b/run_tests.py @@ -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()) diff --git a/run_unittests.py b/run_unittests.py index f378d70..8c61111 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -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() diff --git a/test cases/unit/17 pkgconfig static/meson.build b/test cases/unit/17 pkgconfig static/meson.build index caeb4aa..d1b0fd5 100644 --- a/test cases/unit/17 pkgconfig static/meson.build +++ b/test cases/unit/17 pkgconfig static/meson.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(' ') |