aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/build.py2
-rw-r--r--mesonbuild/compilers/c.py59
-rw-r--r--mesonbuild/dependencies/base.py39
-rw-r--r--mesonbuild/dependencies/ui.py5
-rw-r--r--mesonbuild/environment.py44
-rw-r--r--mesonbuild/mesonlib.py36
-rw-r--r--mesonbuild/modules/pkgconfig.py33
7 files changed, 146 insertions, 72 deletions
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 12f4bdb..5f552c2 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -22,7 +22,7 @@ from . import mlog
from .mesonlib import File, MesonException, listify, extract_as_list
from .mesonlib import typeslistify, stringlistify, classify_unity_sources
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
known_basic_kwargs = {'install': True,
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index 3f9ba5c..9e85712 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -17,6 +17,7 @@ import subprocess, os.path, tempfile
from .. import mlog
from .. import coredata
from ..mesonlib import EnvironmentException, version_compare, Popen_safe, listify
+from ..mesonlib import for_windows, for_darwin, for_cygwin
from .compilers import (
GCC_MINGW,
@@ -710,7 +711,45 @@ class CCompiler(Compiler):
return False
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
if libname in self.ignore_libs:
return []
@@ -720,21 +759,21 @@ class CCompiler(Compiler):
extra_dirs = [extra_dirs]
# Gcc + co seem to prefer builtin lib dirs to -L dirs.
# 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]
if self.links(code, env, extra_args=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()
- suffixes = ['so', 'dylib', 'lib', 'dll', 'a']
+ prefixes, suffixes = self.get_library_naming(env, libtype)
+ # Triply-nested loop!
for d in extra_dirs:
for suffix in suffixes:
- trial = os.path.join(d, 'lib' + libname + '.' + suffix)
- if os.path.isfile(trial):
- return [trial]
- trial2 = os.path.join(d, libname + '.' + suffix)
- if os.path.isfile(trial2):
- return [trial2]
+ for prefix in prefixes:
+ trial = os.path.join(d, prefix + libname + '.' + suffix)
+ if os.path.isfile(trial):
+ return [trial]
return None
def thread_flags(self):
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index 2ac345f..fcc74b5 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -16,9 +16,10 @@
# Custom logic for several other packages are in separate files.
import os
-import shutil
-import stat
import sys
+import stat
+import shlex
+import shutil
from enum import Enum
from .. import mlog
@@ -258,8 +259,10 @@ class PkgConfigDependency(ExternalDependency):
return s.format(self.__class__.__name__, self.name, self.is_found,
self.version_reqs)
- def _call_pkgbin(self, args):
- p, out = Popen_safe([self.pkgbin] + args, env=os.environ)[0:2]
+ def _call_pkgbin(self, args, env=None):
+ if not env:
+ env = os.environ
+ p, out = Popen_safe([self.pkgbin] + args, env=env)[0:2]
return p.returncode, out.strip()
def _set_cargs(self):
@@ -267,19 +270,39 @@ class PkgConfigDependency(ExternalDependency):
if ret != 0:
raise DependencyException('Could not generate cargs for %s:\n\n%s' %
(self.name, out))
- self.compile_args = out.split()
+ self.compile_args = shlex.split(out)
def _set_libs(self):
+ env = None
libcmd = [self.name, '--libs']
if self.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:
raise DependencyException('Could not generate libs for %s:\n\n%s' %
(self.name, out))
self.link_args = []
- for lib in out.split():
- if lib.endswith(".la"):
+ libpaths = []
+ 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_lib = os.path.join(os.path.dirname(lib), shared_libname)
if not os.path.exists(shared_lib):
diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py
index 8f183e5..3412dc6 100644
--- a/mesonbuild/dependencies/ui.py
+++ b/mesonbuild/dependencies/ui.py
@@ -23,8 +23,9 @@ from collections import OrderedDict
from .. import mlog
from .. import mesonlib
-from ..mesonlib import MesonException, Popen_safe, version_compare, extract_as_list
-from ..environment import for_windows, detect_cpu
+from ..mesonlib import MesonException, Popen_safe, version_compare
+from ..mesonlib import extract_as_list, for_windows
+from ..environment import detect_cpu
from .base import DependencyException, DependencyMethods
from .base import ExternalDependency, ExternalProgram
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 7f07c8d..d9146eb 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -219,46 +219,6 @@ def detect_system():
return 'cygwin'
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):
# Usually of the type 4.1.4 but compiler output may contain
# stuff like this:
@@ -550,9 +510,9 @@ class Environment:
cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler
return cls(ccache + compiler, version, gtype, is_cross, exe_wrap, defines)
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
- elif 'windows' in out or for_windows(want_cross, self):
+ elif 'windows' in out or mesonlib.for_windows(want_cross, self):
cltype = CLANG_WIN
else:
cltype = CLANG_STANDARD
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 5c4c374..f74c6c1 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -219,6 +219,42 @@ def is_cygwin():
def is_debianlike():
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):
try:
p = subprocess.Popen(arglist, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index 8383a3a..52f3a50 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -13,6 +13,7 @@
# limitations under the License.
import os
+from pathlib import PurePath
from .. import build
from .. import mesonlib
@@ -42,20 +43,34 @@ class PkgConfigModule(ExtensionModule):
mlog.warning(msg.format(l.name, 'name_prefix', l.name, pcfile))
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,
url, version, pcfile, pub_reqs, priv_reqs,
conflicts, priv_libs, extra_cflags, variables):
coredata = state.environment.get_coredata()
outdir = state.environment.scratch_dir
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:
- ofile.write('prefix=%s\n' % coredata.get_builtin_option('prefix'))
- # '${prefix}' is ignored if the second path is absolute (see
- # 'os.path.join' for details)
- 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')))
+ ofile.write('prefix={}\n'.format(self._escape(prefix)))
+ ofile.write('libdir={}\n'.format(self._escape('${prefix}' / libdir)))
+ ofile.write('includedir={}\n'.format(self._escape('${prefix}' / incdir)))
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('Name: %s\n' % name)
if len(description) > 0:
@@ -83,7 +98,7 @@ class PkgConfigModule(ExtensionModule):
if install_dir is False:
continue
if isinstance(install_dir, str):
- yield '-L${prefix}/%s ' % install_dir
+ yield '-L${prefix}/%s ' % self._escape(install_dir)
else: # install_dir is True
yield '-L${libdir}'
lname = self._get_lname(l, msg, pcfile)
@@ -103,10 +118,10 @@ class PkgConfigModule(ExtensionModule):
if h == '.':
ofile.write('-I${includedir}')
else:
- ofile.write(os.path.join('-I${includedir}', h))
+ ofile.write(self._escape(PurePath('-I${includedir}') / h))
for f in extra_cflags:
ofile.write(' ')
- ofile.write(f)
+ ofile.write(self._escape(f))
ofile.write('\n')
def process_libs(self, libs):