diff options
Diffstat (limited to 'mesonbuild')
-rw-r--r-- | mesonbuild/build.py | 2 | ||||
-rw-r--r-- | mesonbuild/compilers/c.py | 59 | ||||
-rw-r--r-- | mesonbuild/dependencies/base.py | 39 | ||||
-rw-r--r-- | mesonbuild/dependencies/ui.py | 5 | ||||
-rw-r--r-- | mesonbuild/environment.py | 44 | ||||
-rw-r--r-- | mesonbuild/mesonlib.py | 36 | ||||
-rw-r--r-- | mesonbuild/modules/pkgconfig.py | 33 |
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): |