Merge pull request #2611 from mesonbuild/nirbheek/pkgconfig-msvc-static

Several fixes to pkg-config dependencies and the pkg-config module (try 3)
pull/2630/head
Jussi Pakkanen 7 years ago committed by GitHub
commit e51da1a34d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .appveyor.yml
  2. 2
      mesonbuild/build.py
  3. 55
      mesonbuild/compilers/c.py
  4. 39
      mesonbuild/dependencies/base.py
  5. 5
      mesonbuild/dependencies/ui.py
  6. 44
      mesonbuild/environment.py
  7. 36
      mesonbuild/mesonlib.py
  8. 33
      mesonbuild/modules/pkgconfig.py
  9. 169
      run_unittests.py
  10. 8
      test cases/unit/17 pkgconfig static/foo.c
  11. 10
      test cases/unit/17 pkgconfig static/foo.pc.in
  12. 3
      test cases/unit/17 pkgconfig static/include/foo.h
  13. 14
      test cases/unit/17 pkgconfig static/main.c
  14. 23
      test cases/unit/17 pkgconfig static/meson.build

@ -89,6 +89,8 @@ install:
# but they have a broken certificate (on Windows) and an HSTS policy that # but they have a broken certificate (on Windows) and an HSTS policy that
# forces usage of HTTPS, so we mirror our own copy. # forces usage of HTTPS, so we mirror our own copy.
- ps: If($Env:compiler -eq 'msys2-mingw') {(new-object Net.WebClient).DownloadFile('https://nirbheek.in/files/meson/get-pip.py', 'C:\projects\meson\get-pip.py')} - ps: If($Env:compiler -eq 'msys2-mingw') {(new-object Net.WebClient).DownloadFile('https://nirbheek.in/files/meson/get-pip.py', 'C:\projects\meson\get-pip.py')}
# pkg-config is needed for the pkg-config tests on msvc
- ps: If($Env:compiler.StartsWith('msvc')) {(new-object net.webclient).DownloadFile('http://nirbheek.in/files/binaries/pkg-config/win32/pkg-config.exe', 'C:\projects\meson\pkg-config.exe')}
- cmd: if %compiler%==msys2-mingw ( %PYTHON% "C:\projects\meson\get-pip.py" ) - cmd: if %compiler%==msys2-mingw ( %PYTHON% "C:\projects\meson\get-pip.py" )
- cmd: if %compiler%==cygwin ( call ci\appveyor-install.bat ) - cmd: if %compiler%==cygwin ( call ci\appveyor-install.bat )
- ps: | - ps: |

@ -22,7 +22,7 @@ from . import mlog
from .mesonlib import File, MesonException, listify, extract_as_list from .mesonlib import File, MesonException, listify, extract_as_list
from .mesonlib import typeslistify, stringlistify, classify_unity_sources from .mesonlib import typeslistify, stringlistify, classify_unity_sources
from .mesonlib import get_filenames_templates_dict, substitute_values from .mesonlib import get_filenames_templates_dict, substitute_values
from .environment import for_windows, for_darwin, for_cygwin from .mesonlib import for_windows, for_darwin, for_cygwin
from .compilers import is_object, clike_langs, sort_clike, lang_suffixes from .compilers import is_object, clike_langs, sort_clike, lang_suffixes
known_basic_kwargs = {'install': True, known_basic_kwargs = {'install': True,

@ -17,6 +17,7 @@ import subprocess, os.path, tempfile
from .. import mlog from .. import mlog
from .. import coredata from .. import coredata
from ..mesonlib import EnvironmentException, version_compare, Popen_safe, listify from ..mesonlib import EnvironmentException, version_compare, Popen_safe, listify
from ..mesonlib import for_windows, for_darwin, for_cygwin
from .compilers import ( from .compilers import (
GCC_MINGW, GCC_MINGW,
@ -710,7 +711,45 @@ class CCompiler(Compiler):
return False return False
raise RuntimeError('BUG: {!r} check failed unexpectedly'.format(n)) raise RuntimeError('BUG: {!r} check failed unexpectedly'.format(n))
def find_library(self, libname, env, extra_dirs): def get_library_naming(self, env, libtype):
'''
Get library prefixes and suffixes for the target platform ordered by
priority
'''
stlibext = ['a']
# We've always allowed libname to be both `foo` and `libfoo`,
# and now people depend on it
prefixes = ['lib', '']
# Library suffixes and prefixes
if for_darwin(env.is_cross_build(), env):
shlibext = ['dylib']
elif for_windows(env.is_cross_build(), env):
if self.id == 'msvc':
shlibext = ['lib']
else:
shlibext = ['dll', 'dll.a', 'lib']
# Yep, static libraries can also be foo.lib
stlibext += ['lib']
elif for_cygwin(env.is_cross_build(), env):
shlibext = ['dll', 'dll.a']
prefixes = ['cyg'] + prefixes
else:
# Linux/BSDs
shlibext = ['so']
# Search priority
if libtype in ('default', 'shared-static'):
suffixes = shlibext + stlibext
elif libtype == 'static-shared':
suffixes = stlibext + shlibext
elif libtype == 'shared':
suffixes = shlibext
elif libtype == 'static':
suffixes = stlibext
else:
raise AssertionError('BUG: unknown libtype {!r}'.format(libtype))
return prefixes, suffixes
def find_library(self, libname, env, extra_dirs, libtype='default'):
# These libraries are either built-in or invalid # These libraries are either built-in or invalid
if libname in self.ignore_libs: if libname in self.ignore_libs:
return [] return []
@ -720,21 +759,21 @@ class CCompiler(Compiler):
extra_dirs = [extra_dirs] extra_dirs = [extra_dirs]
# Gcc + co seem to prefer builtin lib dirs to -L dirs. # Gcc + co seem to prefer builtin lib dirs to -L dirs.
# Only try to find std libs if no extra dirs specified. # Only try to find std libs if no extra dirs specified.
if not extra_dirs: if not extra_dirs and libtype == 'default':
args = ['-l' + libname] args = ['-l' + libname]
if self.links(code, env, extra_args=args): if self.links(code, env, extra_args=args):
return args return args
# Not found? Try to find the library file itself. # Not found or we want to use a specific libtype? Try to find the
# library file itself.
extra_dirs += self.get_library_dirs() extra_dirs += self.get_library_dirs()
suffixes = ['so', 'dylib', 'lib', 'dll', 'a'] prefixes, suffixes = self.get_library_naming(env, libtype)
# Triply-nested loop!
for d in extra_dirs: for d in extra_dirs:
for suffix in suffixes: for suffix in suffixes:
trial = os.path.join(d, 'lib' + libname + '.' + suffix) for prefix in prefixes:
trial = os.path.join(d, prefix + libname + '.' + suffix)
if os.path.isfile(trial): if os.path.isfile(trial):
return [trial] return [trial]
trial2 = os.path.join(d, libname + '.' + suffix)
if os.path.isfile(trial2):
return [trial2]
return None return None
def thread_flags(self): def thread_flags(self):

@ -16,9 +16,10 @@
# Custom logic for several other packages are in separate files. # Custom logic for several other packages are in separate files.
import os import os
import shutil
import stat
import sys import sys
import stat
import shlex
import shutil
from enum import Enum from enum import Enum
from .. import mlog from .. import mlog
@ -258,8 +259,10 @@ class PkgConfigDependency(ExternalDependency):
return s.format(self.__class__.__name__, self.name, self.is_found, return s.format(self.__class__.__name__, self.name, self.is_found,
self.version_reqs) self.version_reqs)
def _call_pkgbin(self, args): def _call_pkgbin(self, args, env=None):
p, out = Popen_safe([self.pkgbin] + args, env=os.environ)[0:2] if not env:
env = os.environ
p, out = Popen_safe([self.pkgbin] + args, env=env)[0:2]
return p.returncode, out.strip() return p.returncode, out.strip()
def _set_cargs(self): def _set_cargs(self):
@ -267,19 +270,39 @@ class PkgConfigDependency(ExternalDependency):
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 = out.split() self.compile_args = shlex.split(out)
def _set_libs(self): def _set_libs(self):
env = None
libcmd = [self.name, '--libs'] libcmd = [self.name, '--libs']
if self.static: if self.static:
libcmd.append('--static') libcmd.append('--static')
ret, out = self._call_pkgbin(libcmd) # Force pkg-config to output -L fields even if they are system
# paths so we can do manual searching with cc.find_library() later.
env = os.environ.copy()
env['PKG_CONFIG_ALLOW_SYSTEM_LIBS'] = '1'
ret, out = self._call_pkgbin(libcmd, env=env)
if ret != 0: if ret != 0:
raise DependencyException('Could not generate libs for %s:\n\n%s' % raise DependencyException('Could not generate libs for %s:\n\n%s' %
(self.name, out)) (self.name, out))
self.link_args = [] self.link_args = []
for lib in out.split(): libpaths = []
if lib.endswith(".la"): for lib in 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
# have some library paths gathered.
if self.static:
if lib.startswith('-L'):
libpaths.append(lib[2:])
continue
elif lib.startswith('-l') and libpaths:
args = self.compiler.find_library(lib[2:], self.env, libpaths, libtype='static')
if not args or len(args) < 1:
raise DependencyException('Static library not found for {!r}'
''.format(lib[2:]))
lib = args[0]
elif lib.endswith(".la"):
shared_libname = self.extract_libtool_shlib(lib) shared_libname = self.extract_libtool_shlib(lib)
shared_lib = os.path.join(os.path.dirname(lib), shared_libname) shared_lib = os.path.join(os.path.dirname(lib), shared_libname)
if not os.path.exists(shared_lib): if not os.path.exists(shared_lib):

@ -23,8 +23,9 @@ from collections import OrderedDict
from .. import mlog from .. import mlog
from .. import mesonlib from .. import mesonlib
from ..mesonlib import MesonException, Popen_safe, version_compare, extract_as_list from ..mesonlib import MesonException, Popen_safe, version_compare
from ..environment import for_windows, detect_cpu from ..mesonlib import extract_as_list, for_windows
from ..environment import detect_cpu
from .base import DependencyException, DependencyMethods from .base import DependencyException, DependencyMethods
from .base import ExternalDependency, ExternalProgram from .base import ExternalDependency, ExternalProgram

@ -219,46 +219,6 @@ def detect_system():
return 'cygwin' return 'cygwin'
return system return system
def for_windows(is_cross, env):
"""
Host machine is windows?
Note: 'host' is the machine on which compiled binaries will run
"""
if not is_cross:
return mesonlib.is_windows()
elif env.cross_info.has_host():
return env.cross_info.config['host_machine']['system'] == 'windows'
return False
def for_cygwin(is_cross, env):
"""
Host machine is cygwin?
Note: 'host' is the machine on which compiled binaries will run
"""
if not is_cross:
return mesonlib.is_cygwin()
elif env.cross_info.has_host():
return env.cross_info.config['host_machine']['system'] == 'cygwin'
return False
def for_darwin(is_cross, env):
"""
Host machine is Darwin (iOS/OS X)?
Note: 'host' is the machine on which compiled binaries will run
"""
if not is_cross:
return mesonlib.is_osx()
elif env.cross_info.has_host():
return env.cross_info.config['host_machine']['system'] == 'darwin'
return False
def search_version(text): def search_version(text):
# Usually of the type 4.1.4 but compiler output may contain # Usually of the type 4.1.4 but compiler output may contain
# stuff like this: # stuff like this:
@ -550,9 +510,9 @@ class Environment:
cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler
return cls(ccache + compiler, version, gtype, is_cross, exe_wrap, defines) return cls(ccache + compiler, version, gtype, is_cross, exe_wrap, defines)
if 'clang' in out: if 'clang' in out:
if 'Apple' in out or for_darwin(want_cross, self): if 'Apple' in out or mesonlib.for_darwin(want_cross, self):
cltype = CLANG_OSX cltype = CLANG_OSX
elif 'windows' in out or for_windows(want_cross, self): elif 'windows' in out or mesonlib.for_windows(want_cross, self):
cltype = CLANG_WIN cltype = CLANG_WIN
else: else:
cltype = CLANG_STANDARD cltype = CLANG_STANDARD

@ -219,6 +219,42 @@ def is_cygwin():
def is_debianlike(): def is_debianlike():
return os.path.isfile('/etc/debian_version') return os.path.isfile('/etc/debian_version')
def for_windows(is_cross, env):
"""
Host machine is windows?
Note: 'host' is the machine on which compiled binaries will run
"""
if not is_cross:
return is_windows()
elif env.cross_info.has_host():
return env.cross_info.config['host_machine']['system'] == 'windows'
return False
def for_cygwin(is_cross, env):
"""
Host machine is cygwin?
Note: 'host' is the machine on which compiled binaries will run
"""
if not is_cross:
return is_cygwin()
elif env.cross_info.has_host():
return env.cross_info.config['host_machine']['system'] == 'cygwin'
return False
def for_darwin(is_cross, env):
"""
Host machine is Darwin (iOS/OS X)?
Note: 'host' is the machine on which compiled binaries will run
"""
if not is_cross:
return is_osx()
elif env.cross_info.has_host():
return env.cross_info.config['host_machine']['system'] == 'darwin'
return False
def exe_exists(arglist): def exe_exists(arglist):
try: try:
p = subprocess.Popen(arglist, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p = subprocess.Popen(arglist, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

@ -13,6 +13,7 @@
# limitations under the License. # limitations under the License.
import os import os
from pathlib import PurePath
from .. import build from .. import build
from .. import mesonlib from .. import mesonlib
@ -42,20 +43,34 @@ class PkgConfigModule(ExtensionModule):
mlog.warning(msg.format(l.name, 'name_prefix', l.name, pcfile)) mlog.warning(msg.format(l.name, 'name_prefix', l.name, pcfile))
return l.name return l.name
def _escape(self, value):
'''
We cannot use shlex.quote because it quotes with ' and " which does not
work with pkg-config and pkgconf at all.
'''
# We should always write out paths with / because pkg-config requires
# spaces to be quoted with \ and that messes up on Windows:
# https://bugs.freedesktop.org/show_bug.cgi?id=103203
if isinstance(value, PurePath):
value = value.as_posix()
return value.replace(' ', '\ ')
def generate_pkgconfig_file(self, state, libraries, subdirs, name, description, def generate_pkgconfig_file(self, state, libraries, subdirs, name, description,
url, version, pcfile, pub_reqs, priv_reqs, url, version, pcfile, pub_reqs, priv_reqs,
conflicts, priv_libs, extra_cflags, variables): conflicts, priv_libs, extra_cflags, variables):
coredata = state.environment.get_coredata() coredata = state.environment.get_coredata()
outdir = state.environment.scratch_dir outdir = state.environment.scratch_dir
fname = os.path.join(outdir, pcfile) fname = os.path.join(outdir, pcfile)
prefix = PurePath(coredata.get_builtin_option('prefix'))
# These always return paths relative to prefix
libdir = PurePath(coredata.get_builtin_option('libdir'))
incdir = PurePath(coredata.get_builtin_option('includedir'))
with open(fname, 'w') as ofile: with open(fname, 'w') as ofile:
ofile.write('prefix=%s\n' % coredata.get_builtin_option('prefix')) ofile.write('prefix={}\n'.format(self._escape(prefix)))
# '${prefix}' is ignored if the second path is absolute (see ofile.write('libdir={}\n'.format(self._escape('${prefix}' / libdir)))
# 'os.path.join' for details) ofile.write('includedir={}\n'.format(self._escape('${prefix}' / incdir)))
ofile.write('libdir=%s\n' % os.path.join('${prefix}', coredata.get_builtin_option('libdir')))
ofile.write('includedir=%s\n' % os.path.join('${prefix}', coredata.get_builtin_option('includedir')))
for k, v in variables: for k, v in variables:
ofile.write('%s=%s\n' % (k, v)) ofile.write('{}={}\n'.format(k, self._escape(v)))
ofile.write('\n') ofile.write('\n')
ofile.write('Name: %s\n' % name) ofile.write('Name: %s\n' % name)
if len(description) > 0: if len(description) > 0:
@ -83,7 +98,7 @@ class PkgConfigModule(ExtensionModule):
if install_dir is False: if install_dir is False:
continue continue
if isinstance(install_dir, str): if isinstance(install_dir, str):
yield '-L${prefix}/%s ' % install_dir yield '-L${prefix}/%s ' % self._escape(install_dir)
else: # install_dir is True else: # install_dir is True
yield '-L${libdir}' yield '-L${libdir}'
lname = self._get_lname(l, msg, pcfile) lname = self._get_lname(l, msg, pcfile)
@ -103,10 +118,10 @@ class PkgConfigModule(ExtensionModule):
if h == '.': if h == '.':
ofile.write('-I${includedir}') ofile.write('-I${includedir}')
else: else:
ofile.write(os.path.join('-I${includedir}', h)) ofile.write(self._escape(PurePath('-I${includedir}') / h))
for f in extra_cflags: for f in extra_cflags:
ofile.write(' ') ofile.write(' ')
ofile.write(f) ofile.write(self._escape(f))
ofile.write('\n') ofile.write('\n')
def process_libs(self, libs): def process_libs(self, libs):

@ -1389,34 +1389,34 @@ int main(int argc, char **argv) {
self.assertPathExists(os.path.join(testdir, i)) self.assertPathExists(os.path.join(testdir, i))
def detect_prebuild_env(self): def detect_prebuild_env(self):
env = Environment('', self.builddir, self.meson_command,
get_fake_options(self.prefix), [])
cc = env.detect_c_compiler(False)
if mesonbuild.mesonlib.is_windows(): if mesonbuild.mesonlib.is_windows():
object_suffix = 'obj' object_suffix = 'obj'
else: shared_suffix = 'dll'
elif mesonbuild.mesonlib.is_cygwin():
object_suffix = 'o' object_suffix = 'o'
static_suffix = 'a'
shared_suffix = 'so'
if shutil.which('cl'):
compiler = 'cl'
static_suffix = 'lib'
shared_suffix = 'dll' shared_suffix = 'dll'
elif shutil.which('cc'): elif mesonbuild.mesonlib.is_osx():
compiler = 'cc' object_suffix = 'o'
elif shutil.which('gcc'): shared_suffix = 'dylib'
compiler = 'gcc'
else: else:
raise RuntimeError("Could not find C compiler.") object_suffix = 'o'
return (compiler, object_suffix, static_suffix, shared_suffix) shared_suffix = 'so'
return (cc, object_suffix, shared_suffix)
def pbcompile(self, compiler, source, objectfile, extra_args=[]): def pbcompile(self, compiler, source, objectfile, extra_args=[]):
if compiler == 'cl': cmd = compiler.get_exelist()
cmd = [compiler, '/nologo', '/Fo' + objectfile, '/c', source] + extra_args if compiler.id == 'msvc':
cmd += ['/nologo', '/Fo' + objectfile, '/c', source] + extra_args
else: else:
cmd = [compiler, '-c', source, '-o', objectfile] + extra_args cmd += ['-c', source, '-o', objectfile] + extra_args
subprocess.check_call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) subprocess.check_call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
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)
@ -1428,21 +1428,27 @@ int main(int argc, char **argv) {
finally: finally:
os.unlink(objectfile) os.unlink(objectfile)
def test_prebuilt_static_lib(self): def build_static_lib(self, compiler, source, objectfile, outfile, extra_args=None):
(compiler, object_suffix, static_suffix, _) = self.detect_prebuild_env() if extra_args is None:
tdir = os.path.join(self.unit_test_dir, '15 prebuilt static') extra_args = []
source = os.path.join(tdir, 'libdir/best.c') if compiler.id == 'msvc':
objectfile = os.path.join(tdir, 'libdir/best.' + object_suffix) link_cmd = ['lib', '/NOLOGO', '/OUT:' + outfile, objectfile]
stlibfile = os.path.join(tdir, 'libdir/libbest.' + static_suffix)
if compiler == 'cl':
link_cmd = ['lib', '/NOLOGO', '/OUT:' + stlibfile, objectfile]
else: else:
link_cmd = ['ar', 'csr', stlibfile, objectfile] link_cmd = ['ar', 'csr', outfile, objectfile]
self.pbcompile(compiler, source, objectfile) self.pbcompile(compiler, source, objectfile, extra_args=extra_args)
try: try:
subprocess.check_call(link_cmd) subprocess.check_call(link_cmd)
finally: finally:
os.unlink(objectfile) os.unlink(objectfile)
def test_prebuilt_static_lib(self):
(cc, 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)
# Run the test
try: try:
self.init(tdir) self.init(tdir)
self.build() self.build()
@ -1450,26 +1456,37 @@ int main(int argc, char **argv) {
finally: finally:
os.unlink(stlibfile) os.unlink(stlibfile)
def test_prebuilt_shared_lib(self): def build_shared_lib(self, compiler, source, objectfile, outfile, impfile, extra_args=None):
(compiler, object_suffix, _, shared_suffix) = self.detect_prebuild_env() if extra_args is None:
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)
if compiler == 'cl':
extra_args = [] extra_args = []
shlibfile = os.path.join(tdir, 'alexandria.' + shared_suffix) if compiler.id == 'msvc':
link_cmd = ['link', '/NOLOGO','/DLL', '/DEBUG', '/IMPLIB:' + os.path.join(tdir, 'alexandria.lib'), '/OUT:' + shlibfile, objectfile] link_cmd = ['link', '/NOLOGO', '/DLL', '/DEBUG',
'/IMPLIB:' + impfile, '/OUT:' + outfile, objectfile]
else: else:
extra_args = ['-fPIC'] extra_args += ['-fPIC']
shlibfile = os.path.join(tdir, 'libalexandria.' + shared_suffix) link_cmd = compiler.get_exelist() + ['-shared', '-o', outfile, objectfile]
link_cmd = [compiler, '-shared', '-o', shlibfile, objectfile]
if not mesonbuild.mesonlib.is_osx(): if not mesonbuild.mesonlib.is_osx():
link_cmd += ['-Wl,-soname=libalexandria.so'] link_cmd += ['-Wl,-soname=' + os.path.basename(outfile)]
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)
finally: finally:
os.unlink(objectfile) os.unlink(objectfile)
def test_prebuilt_shared_lib(self):
(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)
impfile = os.path.join(tdir, 'alexandria.lib')
if cc.id == 'msvc':
shlibfile = os.path.join(tdir, 'alexandria.' + shared_suffix)
elif is_cygwin():
shlibfile = os.path.join(tdir, 'cygalexandria.' + shared_suffix)
else:
shlibfile = os.path.join(tdir, 'libalexandria.' + shared_suffix)
self.build_shared_lib(cc, source, objectfile, shlibfile, impfile)
# Run the test
try: try:
self.init(tdir) self.init(tdir)
self.build() self.build()
@ -1483,6 +1500,68 @@ int main(int argc, char **argv) {
if os.path.splitext(fname)[1] not in ['.c', '.h']: if os.path.splitext(fname)[1] not in ['.c', '.h']:
os.unlink(fname) os.unlink(fname)
def test_pkgconfig_static(self):
'''
Test that the we only use static libraries when `static: true` is
passed to dependency() with pkg-config. Can't be an ordinary test
because we need to build libs and try to find them from meson.build
'''
if not shutil.which('pkg-config'):
raise unittest.SkipTest('pkg-config not found')
(cc, 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)
stlibfile = os.path.join(testdir, 'libfoo.a')
impfile = os.path.join(testdir, 'foo.lib')
if cc.id == 'msvc':
shlibfile = os.path.join(testdir, 'foo.' + shext)
elif is_cygwin():
shlibfile = os.path.join(testdir, 'cygfoo.' + shext)
else:
shlibfile = os.path.join(testdir, 'libfoo.' + shext)
# Build libs
self.build_static_lib(cc, 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
try:
self.init(testdir)
self.build()
self.run_tests()
finally:
os.unlink(stlibfile)
os.unlink(shlibfile)
if mesonbuild.mesonlib.is_windows():
# Clean up all the garbage MSVC writes in the
# source tree.
for fname in glob(os.path.join(testdir, 'foo.*')):
if os.path.splitext(fname)[1] not in ['.c', '.h', '.in']:
os.unlink(fname)
def test_pkgconfig_gen_escaping(self):
if not shutil.which('pkg-config'):
raise unittest.SkipTest('pkg-config not found')
testdir = os.path.join(self.common_test_dir, '51 pkgconfig-gen')
prefix = '/usr/with spaces'
libdir = 'lib'
self.init(testdir, extra_args=['--prefix=' + prefix,
'--libdir=' + libdir])
# Find foo dependency
os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir
env = FakeEnvironment()
kwargs = {'required': True, 'silent': True}
foo_dep = PkgConfigDependency('libfoo', env, kwargs)
# Ensure link_args are properly quoted
libdir = PurePath(prefix) / PurePath(libdir)
link_args = ['-L' + libdir.as_posix(), '-lfoo']
self.assertEqual(foo_dep.get_link_args(), link_args)
# Ensure include args are properly quoted
incdir = PurePath(prefix) / PurePath('include')
cargs = ['-I' + incdir.as_posix()]
self.assertEqual(foo_dep.get_compile_args(), cargs)
class FailureTests(BasePlatformTests): class FailureTests(BasePlatformTests):
''' '''
Tests that test failure conditions. Build files here should be dynamically Tests that test failure conditions. Build files here should be dynamically
@ -1539,6 +1618,8 @@ class FailureTests(BasePlatformTests):
def test_dependency(self): def test_dependency(self):
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')
if subprocess.call(['pkg-config', '--exists', 'zlib']) != 0:
raise unittest.SkipTest('zlib not found with pkg-config')
a = (("dependency('zlib', method : 'fail')", "'fail' is invalid"), a = (("dependency('zlib', method : 'fail')", "'fail' is invalid"),
("dependency('zlib', static : '1')", "[Ss]tatic.*boolean"), ("dependency('zlib', static : '1')", "[Ss]tatic.*boolean"),
("dependency('zlib', version : 1)", "[Vv]ersion.*string or list"), ("dependency('zlib', version : 1)", "[Vv]ersion.*string or list"),
@ -1722,12 +1803,12 @@ class LinuxlikeTests(BasePlatformTests):
env = FakeEnvironment() env = FakeEnvironment()
kwargs = {'required': True, 'silent': True} kwargs = {'required': True, 'silent': True}
os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir
simple_dep = PkgConfigDependency('libfoo', env, kwargs) foo_dep = PkgConfigDependency('libfoo', env, kwargs)
self.assertTrue(simple_dep.found()) self.assertTrue(foo_dep.found())
self.assertEqual(simple_dep.get_version(), '1.0') self.assertEqual(foo_dep.get_version(), '1.0')
self.assertIn('-lfoo', simple_dep.get_link_args()) self.assertIn('-lfoo', foo_dep.get_link_args())
self.assertEqual(simple_dep.get_pkgconfig_variable('foo'), 'bar') self.assertEqual(foo_dep.get_pkgconfig_variable('foo'), 'bar')
self.assertPathEqual(simple_dep.get_pkgconfig_variable('datadir'), '/usr/data') self.assertPathEqual(foo_dep.get_pkgconfig_variable('datadir'), '/usr/data')
def test_vala_c_warnings(self): def test_vala_c_warnings(self):
''' '''

@ -0,0 +1,8 @@
int power_level (void)
{
#ifdef FOO_STATIC
return 9001;
#else
return 8999;
#endif
}

@ -0,0 +1,10 @@
prefix=@PREFIX@
libdir=${prefix}
includedir=${prefix}/include
datadir=${prefix}/data
Name: libfoo
Description: A foo library.
Version: 1.0
Libs: -L${libdir} -lfoo
Cflags: -I${includedir}

@ -0,0 +1,3 @@
#pragma once
int power_level (void);

@ -0,0 +1,14 @@
#include <foo.h>
#include <stdio.h>
int
main (int argc, char * argv[])
{
int value = power_level ();
if (value < 9000) {
printf ("Power level is %i\n", value);
return 1;
}
printf ("IT'S OVER 9000!!!\n");
return 0;
}

@ -0,0 +1,23 @@
project('pkg-config static', 'c')
if build_machine.system() != 'windows'
prefix = meson.source_root()
else
# pkg-config files should not use paths with \
prefix_parts = meson.source_root().split('\\')
prefix = '/'.join(prefix_parts)
endif
# Escape spaces
prefix_parts = prefix.split(' ')
prefix = '\ '.join(prefix_parts)
conf = configuration_data()
conf.set('PREFIX', prefix)
configure_file(input : 'foo.pc.in',
output : 'foo.pc',
configuration : conf)
foo_dep = dependency('foo', static : true)
test('footest', executable('foomain', 'main.c', dependencies : foo_dep))
Loading…
Cancel
Save