diff options
25 files changed, 223 insertions, 26 deletions
@@ -8,7 +8,7 @@ build system. [](https://pypi.python.org/pypi/meson) [](https://travis-ci.org/mesonbuild/meson) -[](https://ci.appveyor.com/project/jpakkane/meson) +[](https://ci.appveyor.com/project/mesonbuild/meson) [](https://codecov.io/gh/mesonbuild/meson/branch/master) #### Dependencies diff --git a/docs/markdown/Icestorm-module.md b/docs/markdown/Icestorm-module.md index 6aa8481..bc2ad61 100644 --- a/docs/markdown/Icestorm-module.md +++ b/docs/markdown/Icestorm-module.md @@ -1,6 +1,6 @@ # Unstable IceStorm module -This module provides is available since version 0.45.0. +This module is available since version 0.45.0. **Note**:Â this module is unstable. It is only provided as a technology preview. Its API may change in arbitrary ways between releases or it @@ -8,7 +8,7 @@ might be removed from Meson altogether. ## Usage -This module provides an experimental to create FPGA bitstreams using +This module provides an experimental method to create FPGA bitstreams using the [IceStorm](http://www.clifford.at/icestorm/) suite of tools. The module exposes only one method called `project` and it is used @@ -24,4 +24,4 @@ constraint file. This produces output files called `projname.asc`, `projname.blif` and `projname.bin`. In addition it creates two run targets called `projname-time` for running timing analysis and `projname-upload` that uploads the generated bitstream to an FPGA -devide using the `iceprog` programming executable. +device using the `iceprog` programming executable. diff --git a/docs/markdown/Project-templates.md b/docs/markdown/Project-templates.md index d8459c6..5f323bd 100644 --- a/docs/markdown/Project-templates.md +++ b/docs/markdown/Project-templates.md @@ -25,6 +25,6 @@ $ ninja -C builddir ``` The generator has many different projects and settings. They can all -be listed by invoking the command `meson test --help`. +be listed by invoking the command `meson init --help`. This feature is available since Meson version 0.45.0. diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 408e4d4..f0ea09b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -27,7 +27,7 @@ from .. import compilers from ..compilers import CompilerArgs from ..linkers import ArLinker from ..mesonlib import File, MesonException, OrderedSet -from ..mesonlib import get_compiler_for_source +from ..mesonlib import get_compiler_for_source, has_path_sep from .backends import CleanTrees, InstallData from ..build import InvalidArguments @@ -1334,7 +1334,7 @@ int dummy; # Set runtime-paths so we can run executables without needing to set # LD_LIBRARY_PATH, etc in the environment. Doesn't work on Windows. - if '/' in target.name or '\\' in target.name: + if has_path_sep(target.name): # Target names really should not have slashes in them, but # unfortunately we did not check for that and some downstream projects # now have them. Once slashes are forbidden, remove this bit. @@ -2323,7 +2323,7 @@ rule FORTRAN_DEP_HACK # FIXME FIXME: The usage of this is a terrible and unreliable hack if isinstance(fname, File): return fname.subdir != '' - return '/' in fname or '\\' in fname + return has_path_sep(fname) # Fortran is a bit weird (again). When you link against a library, just compiling a source file # requires the mod files that are output when single files are built. To do this right we would need to @@ -2369,7 +2369,7 @@ rule FORTRAN_DEP_HACK pch = target.get_pch(lang) if not pch: continue - if '/' not in pch[0] or '/' not in pch[-1]: + if not has_path_sep(pch[0]) or not has_path_sep(pch[-1]): msg = 'Precompiled header of {!r} must not be in the same ' \ 'directory as source, please put it in a subdirectory.' \ ''.format(target.get_basename()) @@ -2546,7 +2546,7 @@ rule FORTRAN_DEP_HACK commands += linker.get_option_link_args(self.environment.coredata.compiler_options) # Set runtime-paths so we can run executables without needing to set # LD_LIBRARY_PATH, etc in the environment. Doesn't work on Windows. - if '/' in target.name or '\\' in target.name: + if has_path_sep(target.name): # Target names really should not have slashes in them, but # unfortunately we did not check for that and some downstream projects # now have them. Once slashes are forbidden, remove this bit. diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 515fd83..c2f547b 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -23,7 +23,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 .mesonlib import for_windows, for_darwin, for_cygwin, for_android +from .mesonlib import for_windows, for_darwin, for_cygwin, for_android, has_path_sep from .compilers import is_object, clike_langs, sort_clike, lang_suffixes known_basic_kwargs = {'install': True, @@ -286,7 +286,7 @@ class EnvironmentVariables: class Target: def __init__(self, name, subdir, subproject, build_by_default): - if '/' in name or '\\' in name: + if has_path_sep(name): # Fix failing test 53 when this becomes an error. mlog.warning('''Target "%s" has a path separator in its name. This is not supported, it can cause unexpected failures and will become @@ -1067,7 +1067,7 @@ class Generator: raise InvalidArguments('"output" may only contain strings.') if '@BASENAME@' not in rule and '@PLAINNAME@' not in rule: raise InvalidArguments('Every element of "output" must contain @BASENAME@ or @PLAINNAME@.') - if '/' in rule or '\\' in rule: + if has_path_sep(rule): raise InvalidArguments('"outputs" must not contain a directory separator.') if len(outputs) > 1: for o in outputs: @@ -1666,7 +1666,7 @@ class CustomTarget(Target): raise InvalidArguments('Output must not be empty.') if i.strip() == '': raise InvalidArguments('Output must not consist only of whitespace.') - if '/' in i: + if has_path_sep(i): raise InvalidArguments('Output must not contain a path segment.') if '@INPUT@' in i or '@INPUT0@' in i: m = 'Output cannot contain @INPUT@ or @INPUT0@, did you ' \ diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 034fef4..f8dfc96 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -348,7 +348,7 @@ def get_base_link_args(options, linker, is_shared_module): pass try: if 'b_asneeded' in linker.base_options and options['b_asneeded'].value: - args.append('-Wl,--as-needed') + args.append(linker.get_asneeded_args()) except KeyError: pass try: @@ -900,6 +900,13 @@ ICC_STANDARD = 0 ICC_OSX = 1 ICC_WIN = 2 +# GNU ld cannot be installed on macOS +# https://github.com/Homebrew/homebrew-core/issues/17794#issuecomment-328174395 +# Hence, we don't need to differentiate between OS and ld +# for the sake of adding as-needed support +GNU_LD_AS_NEEDED = '-Wl,--as-needed' +APPLE_LD_AS_NEEDED = '-Wl,-dead_strip_dylibs' + def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module): if soversion is None: sostr = '' @@ -1002,10 +1009,18 @@ class GnuCompiler: 'b_colorout', 'b_ndebug', 'b_staticpic'] if self.gcc_type != GCC_OSX: self.base_options.append('b_lundef') - self.base_options.append('b_asneeded') + self.base_options.append('b_asneeded') # All GCC backends can do assembly self.can_compile_suffixes.add('s') + # TODO: centralise this policy more globally, instead + # of fragmenting it into GnuCompiler and ClangCompiler + def get_asneeded_args(self): + if self.gcc_type == GCC_OSX: + return APPLE_LD_AS_NEEDED + else: + return GNU_LD_AS_NEEDED + def get_colorout_args(self, colortype): if mesonlib.version_compare(self.version, '>=4.9.0'): return gnu_color_args[colortype][:] @@ -1084,10 +1099,18 @@ class ClangCompiler: 'b_ndebug', 'b_staticpic', 'b_colorout'] if self.clang_type != CLANG_OSX: self.base_options.append('b_lundef') - self.base_options.append('b_asneeded') + self.base_options.append('b_asneeded') # All Clang backends can do assembly and LLVM IR self.can_compile_suffixes.update(['ll', 's']) + # TODO: centralise this policy more globally, instead + # of fragmenting it into GnuCompiler and ClangCompiler + def get_asneeded_args(self): + if self.clang_type == CLANG_OSX: + return APPLE_LD_AS_NEEDED + else: + return GNU_LD_AS_NEEDED + def get_pic_args(self): if self.clang_type in (CLANG_WIN, CLANG_OSX): return [] # On Window and OS X, pic is always on. diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 11e058c..029fe59 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -21,7 +21,7 @@ from . import optinterpreter from . import compilers from .wrap import wrap, WrapMode from . import mesonlib -from .mesonlib import FileMode, Popen_safe, listify, extract_as_list +from .mesonlib import FileMode, Popen_safe, listify, extract_as_list, has_path_sep from .dependencies import ExternalProgram from .dependencies import InternalDependency, Dependency, DependencyException from .interpreterbase import InterpreterBase @@ -1863,7 +1863,7 @@ external dependencies (including libraries) must go to "dependencies".''') raise InterpreterException('Subproject name must not contain a ".." path segment.') if os.path.isabs(dirname): raise InterpreterException('Subproject name must not be an absolute path.') - if '\\' in dirname or '/' in dirname: + if has_path_sep(dirname): mlog.warning('Subproject name has a path separator. This may cause unexpected behaviour.') if dirname in self.subproject_stack: fullstack = self.subproject_stack + [dirname] diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 4e72600..247b4f9 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -519,6 +519,12 @@ def get_library_dirs(): unixdirs += glob('/lib/' + plat + '*') return unixdirs +def has_path_sep(name, sep='/\\'): + 'Checks if any of the specified @sep path separators are in @name' + for each in sep: + if each in name: + return True + return False def do_replacement(regex, line, confdata): missing_variables = set() diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 1805e6c..23e666c 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -186,6 +186,7 @@ def list_tests(testdata): to['workdir'] = t.workdir to['timeout'] = t.timeout to['suite'] = t.suite + to['is_parallel'] = t.is_parallel result.append(to) print(json.dumps(result)) diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 074fc5a..c89f657 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -96,7 +96,20 @@ class DependenciesHelper: if obj.found(): processed_libs += obj.get_link_args() processed_cflags += obj.get_compile_args() - elif isinstance(obj, (build.SharedLibrary, build.StaticLibrary)): + elif isinstance(obj, build.SharedLibrary): + processed_libs.append(obj) + if public: + if not hasattr(obj, 'generated_pc'): + obj.generated_pc = self.name + elif isinstance(obj, build.StaticLibrary): + # Due to a "feature" in pkgconfig, it leaks out private dependencies. + # Thus we will not add them to the pc file unless the target + # we are processing is a static library. + # + # This way (hopefully) "pkgconfig --libs --static foobar" works + # and "pkgconfig --cflags/--libs foobar" does not have any trace + # of dependencies that the build file creator has not explicitly + # added to the dependency list. processed_libs.append(obj) if public: if not hasattr(obj, 'generated_pc'): diff --git a/mesonbuild/scripts/yelphelper.py b/mesonbuild/scripts/yelphelper.py index ab99267..0f8b0b8 100644 --- a/mesonbuild/scripts/yelphelper.py +++ b/mesonbuild/scripts/yelphelper.py @@ -17,6 +17,7 @@ import subprocess import shutil import argparse from .. import mlog +from ..mesonlib import has_path_sep from . import destdir_join from .gettext import read_linguas @@ -79,7 +80,7 @@ def install_help(srcdir, blddir, sources, media, langs, install_dir, destdir, pr elif symlinks: srcfile = os.path.join(c_install_dir, m) mlog.log('Symlinking %s to %s.' % (outfile, srcfile)) - if '/' in m or '\\' in m: + if has_path_sep(m): os.makedirs(os.path.dirname(outfile), exist_ok=True) try: try: @@ -94,7 +95,7 @@ def install_help(srcdir, blddir, sources, media, langs, install_dir, destdir, pr # Lang doesn't have media file so copy it over 'C' one infile = os.path.join(srcdir, 'C', m) mlog.log('Installing %s to %s' % (infile, outfile)) - if '/' in m or '\\' in m: + if has_path_sep(m): os.makedirs(os.path.dirname(outfile), exist_ok=True) shutil.copyfile(infile, outfile) shutil.copystat(infile, outfile) diff --git a/run_unittests.py b/run_unittests.py index 6e9970e..3ea0412 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -513,7 +513,8 @@ class BasePlatformTests(unittest.TestCase): windows_proof_rmtree(path) except FileNotFoundError: pass - os.environ = self.orig_env + os.environ.clear() + os.environ.update(self.orig_env) super().tearDown() def _run(self, command, workdir=None): @@ -588,10 +589,11 @@ class BasePlatformTests(unittest.TestCase): def run_tests(self): self._run(self.test_command, workdir=self.builddir) - def install(self): + def install(self, *, use_destdir=True): if self.backend is not Backend.ninja: raise unittest.SkipTest('{!r} backend can\'t install files'.format(self.backend.name)) - os.environ['DESTDIR'] = self.installdir + if use_destdir: + os.environ['DESTDIR'] = self.installdir self._run(self.install_command, workdir=self.builddir) def uninstall(self): @@ -2735,6 +2737,42 @@ endian = 'little' self.build() mesonbuild.modules.gnome.native_glib_version = None + @unittest.skipIf(shutil.which('pkg-config') is None, 'Pkg-config not found.') + def test_pkgconfig_usage(self): + testdir1 = os.path.join(self.unit_test_dir, '24 pkgconfig usage/dependency') + testdir2 = os.path.join(self.unit_test_dir, '24 pkgconfig usage/dependee') + if subprocess.call(['pkg-config', '--cflags', 'glib-2.0'], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) != 0: + raise unittest.SkipTest('Glib 2.0 dependency not available.') + with tempfile.TemporaryDirectory() as tempdirname: + self.init(testdir1, ['--prefix=' + tempdirname, '--libdir=lib'], default_args=False) + self.install(use_destdir=False) + shutil.rmtree(self.builddir) + os.mkdir(self.builddir) + pkg_dir = os.path.join(tempdirname, 'lib/pkgconfig') + self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'libpkgdep.pc'))) + lib_dir = os.path.join(tempdirname, 'lib') + os.environ['PKG_CONFIG_PATH'] = pkg_dir + # Private internal libraries must not leak out. + pkg_out = subprocess.check_output(['pkg-config', '--static', '--libs', 'libpkgdep']) + self.assertFalse(b'libpkgdep-int' in pkg_out, 'Internal library leaked out.') + # Dependencies must not leak to cflags when building only a shared library. + pkg_out = subprocess.check_output(['pkg-config', '--cflags', 'libpkgdep']) + self.assertFalse(b'glib' in pkg_out, 'Internal dependency leaked to headers.') + # Test that the result is usable. + self.init(testdir2) + self.build() + myenv = os.environ.copy() + myenv['LD_LIBRARY_PATH'] = lib_dir + if is_cygwin(): + bin_dir = os.path.join(tempdirname, 'bin') + myenv['PATH'] = bin_dir + os.pathsep + myenv['PATH'] + self.assertTrue(os.path.isdir(lib_dir)) + test_exe = os.path.join(self.builddir, 'pkguser') + self.assertTrue(os.path.isfile(test_exe)) + subprocess.check_call(test_exe, env=myenv) + class LinuxArmCrossCompileTests(BasePlatformTests): ''' diff --git a/test cases/common/183 as-needed/config.h b/test cases/common/183 as-needed/config.h new file mode 100644 index 0000000..b8fb60f --- /dev/null +++ b/test cases/common/183 as-needed/config.h @@ -0,0 +1,14 @@ +#if defined _WIN32 || defined __CYGWIN__ + #if defined BUILDING_DLL + #define DLL_PUBLIC __declspec(dllexport) + #else + #define DLL_PUBLIC __declspec(dllimport) + #endif +#else + #if defined __GNUC__ + #define DLL_PUBLIC __attribute__ ((visibility("default"))) + #else + #pragma message ("Compiler does not support symbol visibility.") + #define DLL_PUBLIC + #endif +#endif diff --git a/test cases/common/183 as-needed/libA.cpp b/test cases/common/183 as-needed/libA.cpp new file mode 100644 index 0000000..5f45bc0 --- /dev/null +++ b/test cases/common/183 as-needed/libA.cpp @@ -0,0 +1,7 @@ +#define BUILDING_DLL + +#include "libA.h" + +namespace meson_test_as_needed { + DLL_PUBLIC bool linked = false; +} diff --git a/test cases/common/183 as-needed/libA.h b/test cases/common/183 as-needed/libA.h new file mode 100644 index 0000000..8e76d22 --- /dev/null +++ b/test cases/common/183 as-needed/libA.h @@ -0,0 +1,5 @@ +#include "config.h" + +namespace meson_test_as_needed { + DLL_PUBLIC extern bool linked; +} diff --git a/test cases/common/183 as-needed/libB.cpp b/test cases/common/183 as-needed/libB.cpp new file mode 100644 index 0000000..a872394 --- /dev/null +++ b/test cases/common/183 as-needed/libB.cpp @@ -0,0 +1,19 @@ +#include "libA.h" + +#undef DLL_PUBLIC +#define BUILDING_DLL +#include "config.h" + +namespace meson_test_as_needed { + namespace { + bool set_linked() { + linked = true; + return true; + } + bool stub = set_linked(); + } + + DLL_PUBLIC int libB_unused_func() { + return 0; + } +} diff --git a/test cases/common/183 as-needed/main.cpp b/test cases/common/183 as-needed/main.cpp new file mode 100644 index 0000000..cecb2ff --- /dev/null +++ b/test cases/common/183 as-needed/main.cpp @@ -0,0 +1,7 @@ +#include <cstdlib> + +#include "libA.h" + +int main() { + return (meson_test_as_needed::linked == false ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/test cases/common/183 as-needed/meson.build b/test cases/common/183 as-needed/meson.build new file mode 100644 index 0000000..3b54aaa --- /dev/null +++ b/test cases/common/183 as-needed/meson.build @@ -0,0 +1,13 @@ +project('as-needed test', 'cpp') + +# Idea behind this test is to have -Wl,--as-needed prune +# away unneeded linkages, which would otherwise cause global +# static initialiser side-effects to set a boolean to true. + +# Credits for portable ISO C++ idea go to sarum9in + +libA = library('A', 'libA.cpp') +libB = library('B', 'libB.cpp', link_with : libA) + +main_exe = executable('C', 'main.cpp', link_with : [libA, libB]) +test('main test', main_exe) diff --git a/test cases/common/51 pkgconfig-gen/dependencies/meson.build b/test cases/common/51 pkgconfig-gen/dependencies/meson.build index 018b72f..2e00943 100644 --- a/test cases/common/51 pkgconfig-gen/dependencies/meson.build +++ b/test cases/common/51 pkgconfig-gen/dependencies/meson.build @@ -5,7 +5,7 @@ pkgg = import('pkgconfig') # libmain internally use libinternal and expose libexpose in its API exposed_lib = shared_library('libexposed', 'exposed.c') internal_lib = shared_library('libinternal', 'internal.c') -main_lib = shared_library('libmain', link_with : [exposed_lib, internal_lib]) +main_lib = static_library('libmain', link_with : [exposed_lib, internal_lib]) pkgg.generate(libraries : exposed_lib, version : '1.0', diff --git a/test cases/unit/24 pkgconfig usage/dependee/meson.build b/test cases/unit/24 pkgconfig usage/dependee/meson.build new file mode 100644 index 0000000..beb446c --- /dev/null +++ b/test cases/unit/24 pkgconfig usage/dependee/meson.build @@ -0,0 +1,7 @@ +project('pkgconfig user', 'c') + +pkgdep = dependency('libpkgdep') + +executable('pkguser', 'pkguser.c', + dependencies : pkgdep) + diff --git a/test cases/unit/24 pkgconfig usage/dependee/pkguser.c b/test cases/unit/24 pkgconfig usage/dependee/pkguser.c new file mode 100644 index 0000000..2bff316 --- /dev/null +++ b/test cases/unit/24 pkgconfig usage/dependee/pkguser.c @@ -0,0 +1,6 @@ +#include<pkgdep.h> + +int main(int argc, char **argv) { + int res = pkgdep(); + return res != 99; +} diff --git a/test cases/unit/24 pkgconfig usage/dependency/meson.build b/test cases/unit/24 pkgconfig usage/dependency/meson.build new file mode 100644 index 0000000..89fae8e --- /dev/null +++ b/test cases/unit/24 pkgconfig usage/dependency/meson.build @@ -0,0 +1,24 @@ +project('pkgconfig dep', 'c', + version : '1.0.0') + +# This is not used in the code, only to check that it does not +# leak out to --libs. +glib_dep = dependency('glib-2.0') + +pkgconfig = import('pkgconfig') + +intlib = static_library('libpkgdep-int', 'privatelib.c') +intdep = declare_dependency(link_with : intlib) + +lib = shared_library('pkgdep', 'pkgdep.c', + dependencies : [glib_dep, intdep], + install : true) + +install_headers('pkgdep.h') + +pkgconfig.generate( + filebase : 'libpkgdep', + name : 'Libpkgdep', + description : 'Sample pkgconfig dependency library', + version : meson.project_version(), + libraries : lib) diff --git a/test cases/unit/24 pkgconfig usage/dependency/pkgdep.c b/test cases/unit/24 pkgconfig usage/dependency/pkgdep.c new file mode 100644 index 0000000..bd5c3f4 --- /dev/null +++ b/test cases/unit/24 pkgconfig usage/dependency/pkgdep.c @@ -0,0 +1,7 @@ +#include<pkgdep.h> + +int internal_thingy(); + +int pkgdep() { + return internal_thingy(); +} diff --git a/test cases/unit/24 pkgconfig usage/dependency/pkgdep.h b/test cases/unit/24 pkgconfig usage/dependency/pkgdep.h new file mode 100644 index 0000000..16d622e --- /dev/null +++ b/test cases/unit/24 pkgconfig usage/dependency/pkgdep.h @@ -0,0 +1,3 @@ +#pragma once + +int pkgdep(); diff --git a/test cases/unit/24 pkgconfig usage/dependency/privatelib.c b/test cases/unit/24 pkgconfig usage/dependency/privatelib.c new file mode 100644 index 0000000..71d2179 --- /dev/null +++ b/test cases/unit/24 pkgconfig usage/dependency/privatelib.c @@ -0,0 +1,3 @@ +int internal_thingy() { + return 99; +} |