diff options
75 files changed, 805 insertions, 239 deletions
diff --git a/ci/travis_install.sh b/ci/travis_install.sh index 5d191f1..d9d308a 100755 --- a/ci/travis_install.sh +++ b/ci/travis_install.sh @@ -7,9 +7,11 @@ msg() { echo -e "\x1b[1;32mINFO: \x1b[37m$*\x1b[0m"; } if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then msg "Running OSX setup" brew update + # Run one macOS build with pkg-config available (pulled in by qt), and the + # other (unity=on) without pkg-config brew install qt ldc llvm ninja if [[ "$MESON_ARGS" =~ .*unity=on.* ]]; then - which pkg-config || brew install pkg-config + which pkg-config && rm -f $(which pkg-config) fi python3 -m pip install jsonschema elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 1bd5ff0..9b5d657 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -1613,6 +1613,13 @@ object](#build-target-object) returned by object](#external-program-object) returned by [`find_program()`](#find_program). +*Since 0.55.0* When cross compiling, if an exe_wrapper is needed and defined +the environment variable `MESON_EXE_WRAPPER` will be set to the string value +of that wrapper (implementation detail: using `mesonlib.join_args`). Test +scripts may use this to run cross built binaries. If your test needs +`MESON_EXE_WRAPPER` in cross build situations it is your responsibility to +return code 77 to tell the harness to report "skip" + By default, environment variable [`MALLOC_PERTURB_`](http://man7.org/linux/man-pages/man3/mallopt.3.html) is automatically set by `meson test` to a random value between 1..255. diff --git a/docs/markdown/howtox.md b/docs/markdown/howtox.md index abf7519..84546b7 100644 --- a/docs/markdown/howtox.md +++ b/docs/markdown/howtox.md @@ -271,6 +271,7 @@ This can be used in cases where you want a default value, but might override it later. ```meson +# Not needed on Windows! my_dep = dependency('', required : false) if host_machine.system() in ['freebsd', 'netbsd', 'openbsd', 'dragonfly'] my_dep = dependency('some dep', required : false) @@ -278,8 +279,9 @@ elif host_machine.system() == 'linux' my_dep = dependency('some other dep', required : false) endif -# Last ditch effort! -if no my_dep.found() - my_dep = meson.get_compiler('c').find_library('dep') -endif +executable( + 'myexe', + my_sources, + deps : [my_dep] +) ``` diff --git a/docs/markdown/snippets/d-lang_n_debug.md b/docs/markdown/snippets/d-lang_n_debug.md new file mode 100644 index 0000000..59f09e4 --- /dev/null +++ b/docs/markdown/snippets/d-lang_n_debug.md @@ -0,0 +1,4 @@ +## b_ndebug support for D language compilers + +D Language compilers will now set -release/--release/-frelease (depending on +the compiler) when the b_ndebug flag is set. diff --git a/docs/markdown/snippets/exe_wrapper_for_cross_built_tests.md b/docs/markdown/snippets/exe_wrapper_for_cross_built_tests.md new file mode 100644 index 0000000..ebdd8a7 --- /dev/null +++ b/docs/markdown/snippets/exe_wrapper_for_cross_built_tests.md @@ -0,0 +1,9 @@ +## Test scripts are given the exe wrapper if needed + +Meson will now set the `MESON_EXE_WRAPPER` as the properly wrapped and joined +representation. For Unix-like OSes this means python's shelx.join, on Windows +an implementation that attempts to properly quote windows argument is used. +This allow wrapper scripts to run test binaries, instead of just skipping. + +for example, if the wrapper is `['emulator', '--script']`, it will be passed +as `MESON_EXE_WRAPPER="emulator --script"`. diff --git a/docs/markdown/snippets/rpath_behavior.md b/docs/markdown/snippets/rpath_behavior.md new file mode 100644 index 0000000..c46f0c2 --- /dev/null +++ b/docs/markdown/snippets/rpath_behavior.md @@ -0,0 +1,7 @@ +## rpath removal now more careful + +On Linux-like systems, meson adds rpath entries to allow running apps +in the build tree, and then removes those build-time-only +rpath entries when installing. Rpath entries may also come +in via LDFLAGS and via .pc files. Meson used to remove those +latter rpath entries by accident, but is now more careful. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 7f7c434..840c9a3 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -90,12 +90,13 @@ class InstallData: self.mesonintrospect = mesonintrospect class TargetInstallData: - def __init__(self, fname, outdir, aliases, strip, install_name_mappings, install_rpath, install_mode, optional=False): + def __init__(self, fname, outdir, aliases, strip, install_name_mappings, rpath_dirs_to_remove, install_rpath, install_mode, optional=False): self.fname = fname self.outdir = outdir self.aliases = aliases self.strip = strip self.install_name_mappings = install_name_mappings + self.rpath_dirs_to_remove = rpath_dirs_to_remove self.install_rpath = install_rpath self.install_mode = install_mode self.optional = optional @@ -118,7 +119,8 @@ class TestSerialisation: needs_exe_wrapper: bool, is_parallel: bool, cmd_args: T.List[str], env: build.EnvironmentVariables, should_fail: bool, timeout: T.Optional[int], workdir: T.Optional[str], - extra_paths: T.List[str], protocol: TestProtocol, priority: int): + extra_paths: T.List[str], protocol: TestProtocol, priority: int, + cmd_is_built: bool): self.name = name self.project_name = project self.suite = suite @@ -137,6 +139,8 @@ class TestSerialisation: self.protocol = protocol self.priority = priority self.needs_exe_wrapper = needs_exe_wrapper + self.cmd_is_built = cmd_is_built + def get_backend_from_name(backend: str, build: T.Optional[build.Build] = None, interpreter: T.Optional['Interpreter'] = None) -> T.Optional['Backend']: if backend == 'ninja': @@ -258,7 +262,7 @@ class Backend: return self.build_to_src def get_target_private_dir(self, target): - return os.path.join(self.get_target_dir(target), target.get_id()) + return os.path.join(self.get_target_filename(target) + '.p') def get_target_private_dir_abs(self, target): return os.path.join(self.environment.get_build_dir(), self.get_target_private_dir(target)) @@ -443,6 +447,21 @@ class Backend: return True return False + def get_external_rpath_dirs(self, target): + dirs = set() + args = [] + # FIXME: is there a better way? + for lang in ['c', 'cpp']: + try: + args.extend(self.environment.coredata.get_external_link_args(target.for_machine, lang)) + except Exception: + pass + for arg in args: + if arg.startswith('-Wl,-rpath='): + for dir in arg.replace('-Wl,-rpath=','').split(':'): + dirs.add(dir) + return dirs + def rpaths_for_bundled_shared_libraries(self, target, exclude_system=True): paths = [] for dep in target.external_deps: @@ -457,6 +476,9 @@ class Backend: if exclude_system and self._libdir_is_system(libdir, target.compilers, self.environment): # No point in adding system paths. continue + # Don't remove rpaths specified in LDFLAGS. + if libdir in self.get_external_rpath_dirs(target): + continue # Windows doesn't support rpaths, but we use this function to # emulate rpaths by setting PATH, so also accept DLLs here if os.path.splitext(libpath)[1] not in ['.dll', '.lib', '.so', '.dylib']: @@ -476,6 +498,7 @@ class Backend: result = OrderedSet() result.add('meson-out') result.update(self.rpaths_for_bundled_shared_libraries(target)) + target.rpath_dirs_to_remove.update([d.encode('utf8') for d in result]) return tuple(result) def object_filename_from_source(self, target, source): @@ -768,6 +791,15 @@ class Backend: # E.g. an external verifier or simulator program run on a generated executable. # Can always be run without a wrapper. test_for_machine = MachineChoice.BUILD + + # we allow passing compiled executables to tests, which may be cross built. + # We need to consider these as well when considering whether the target is cross or not. + for a in t.cmd_args: + if isinstance(a, build.BuildTarget): + if a.for_machine is MachineChoice.HOST: + test_for_machine = MachineChoice.HOST + break + is_cross = self.environment.is_cross_build(test_for_machine) if is_cross and self.environment.need_exe_wrapper(): exe_wrapper = self.environment.get_exe_wrapper() @@ -781,6 +813,7 @@ class Backend: extra_paths = self.determine_windows_extra_paths(exe, extra_bdeps) else: extra_paths = [] + cmd_args = [] for a in unholder(t.cmd_args): if isinstance(a, build.BuildTarget): @@ -790,6 +823,11 @@ class Backend: cmd_args.append(a) elif isinstance(a, str): cmd_args.append(a) + elif isinstance(a, build.Executable): + p = self.construct_target_rel_path(a, t.workdir) + if p == a.get_filename(): + p = './' + p + cmd_args.append(p) elif isinstance(a, build.Target): cmd_args.append(self.construct_target_rel_path(a, t.workdir)) else: @@ -798,7 +836,8 @@ class Backend: exe_wrapper, self.environment.need_exe_wrapper(), t.is_parallel, cmd_args, t.env, t.should_fail, t.timeout, t.workdir, - extra_paths, t.protocol, t.priority) + extra_paths, t.protocol, t.priority, + isinstance(exe, build.Executable)) arr.append(ts) return arr @@ -1140,6 +1179,7 @@ class Backend: mappings = t.get_link_deps_mapping(d.prefix, self.environment) i = TargetInstallData(self.get_target_filename(t), outdirs[0], t.get_aliases(), should_strip, mappings, + t.rpath_dirs_to_remove, t.install_rpath, install_mode) d.targets.append(i) @@ -1157,14 +1197,14 @@ class Backend: implib_install_dir = self.environment.get_import_lib_dir() # Install the import library; may not exist for shared modules i = TargetInstallData(self.get_target_filename_for_linking(t), - implib_install_dir, {}, False, {}, '', install_mode, + implib_install_dir, {}, False, {}, set(), '', install_mode, optional=isinstance(t, build.SharedModule)) d.targets.append(i) if not should_strip and t.get_debug_filename(): debug_file = os.path.join(self.get_target_dir(t), t.get_debug_filename()) i = TargetInstallData(debug_file, outdirs[0], - {}, False, {}, '', + {}, False, {}, set(), '', install_mode, optional=True) d.targets.append(i) # Install secondary outputs. Only used for Vala right now. @@ -1174,7 +1214,7 @@ class Backend: if outdir is False: continue f = os.path.join(self.get_target_dir(t), output) - i = TargetInstallData(f, outdir, {}, False, {}, None, install_mode) + i = TargetInstallData(f, outdir, {}, False, {}, set(), None, install_mode) d.targets.append(i) elif isinstance(t, build.CustomTarget): # If only one install_dir is specified, assume that all @@ -1187,7 +1227,7 @@ class Backend: if num_outdirs == 1 and num_out > 1: for output in t.get_outputs(): f = os.path.join(self.get_target_dir(t), output) - i = TargetInstallData(f, outdirs[0], {}, False, {}, None, install_mode, + i = TargetInstallData(f, outdirs[0], {}, False, {}, set(), None, install_mode, optional=not t.build_by_default) d.targets.append(i) else: @@ -1196,7 +1236,7 @@ class Backend: if outdir is False: continue f = os.path.join(self.get_target_dir(t), output) - i = TargetInstallData(f, outdir, {}, False, {}, None, install_mode, + i = TargetInstallData(f, outdir, {}, False, {}, set(), None, install_mode, optional=not t.build_by_default) d.targets.append(i) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 9b895c9..7300aaf 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -570,7 +570,7 @@ int dummy; generated_source_files.append(raw_src) elif self.environment.is_object(rel_src): obj_list.append(rel_src) - elif self.environment.is_library(rel_src): + elif self.environment.is_library(rel_src) or modules.is_module_library(rel_src): pass else: # Assume anything not specifically a source file is a header. This is because @@ -585,7 +585,7 @@ int dummy; o = self.generate_llvm_ir_compile(target, src) else: o = self.generate_single_compile(target, src, True, - header_deps=header_deps) + order_deps=header_deps) obj_list.append(o) use_pch = self.environment.coredata.base_options.get('b_pch', False) @@ -1348,7 +1348,8 @@ int dummy; self.get_target_dir(target)) else: target_slashname_workaround_dir = self.get_target_dir(target) - rpath_args = rustc.build_rpath_args(self.environment, + (rpath_args, target.rpath_dirs_to_remove) = \ + rustc.build_rpath_args(self.environment, self.environment.get_build_dir(), target_slashname_workaround_dir, self.determine_rpath_dirs(target), @@ -2580,12 +2581,14 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) self.get_target_dir(target)) else: target_slashname_workaround_dir = self.get_target_dir(target) - commands += linker.build_rpath_args(self.environment, + (rpath_args, target.rpath_dirs_to_remove) = \ + linker.build_rpath_args(self.environment, self.environment.get_build_dir(), target_slashname_workaround_dir, self.determine_rpath_dirs(target), target.build_rpath, target.install_rpath) + commands += rpath_args # Add libraries generated by custom targets custom_target_libraries = self.get_custom_target_provided_libraries(target) commands += extra_args diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index b5803bf..6965c42 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -98,6 +98,9 @@ class Vs2010Backend(backends.Backend): self.subdirs = {} self.handled_target_deps = {} + def get_target_private_dir(self, target): + return os.path.join(self.get_target_dir(target), target.get_id()) + def generate_custom_generator_commands(self, target, parent_node): generator_output_files = [] custom_target_include_dirs = [] @@ -821,12 +824,12 @@ class Vs2010Backend(backends.Backend): clconf = ET.SubElement(compiles, 'ClCompile') # CRT type; debug or release if vscrt_type.value == 'from_buildtype': - if self.buildtype == 'debug' or self.buildtype == 'debugoptimized': + if self.buildtype == 'debug': ET.SubElement(type_config, 'UseDebugLibraries').text = 'true' ET.SubElement(clconf, 'RuntimeLibrary').text = 'MultiThreadedDebugDLL' else: ET.SubElement(type_config, 'UseDebugLibraries').text = 'false' - ET.SubElement(clconf, 'RuntimeLibrary').text = 'MultiThreaded' + ET.SubElement(clconf, 'RuntimeLibrary').text = 'MultiThreadedDLL' elif vscrt_type.value == 'mdd': ET.SubElement(type_config, 'UseDebugLibraries').text = 'true' ET.SubElement(clconf, 'RuntimeLibrary').text = 'MultiThreadedDebugDLL' diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 67b92a5..d7f3b66 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -512,6 +512,8 @@ class BuildTarget(Target): self.d_features = {} self.pic = False self.pie = False + # Track build_rpath entries so we can remove them at install time + self.rpath_dirs_to_remove = set() # Sources can be: # 1. Pre-existing source files in the source tree # 2. Pre-existing sources generated by configure_file in the build tree diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index 1e033c7..35eb17c 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -290,10 +290,12 @@ class ConverterTarget: m = ConverterTarget.std_regex.match(j) if m: std = m.group(2) - if std not in self._all_lang_stds(i): + supported = self._all_lang_stds(i) + if std not in supported: mlog.warning( 'Unknown {0}_std "{1}" -> Ignoring. Try setting the project-' - 'level {0}_std if build errors occur.'.format(i, std), + 'level {0}_std if build errors occur. Known ' + '{0}_stds are: {2}'.format(i, std, ' '.join(supported)), once=True ) continue diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 1bc9e84..aac99b4 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -86,9 +86,10 @@ class ClangCCompiler(ClangCompiler, CCompiler): _C18_VERSION = '>=8.0.0' def __init__(self, exelist, version, for_machine: MachineChoice, - is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs): + is_cross, info: 'MachineInfo', exe_wrapper=None, + defines: T.Optional[T.List[str]] = None, **kwargs): CCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, **kwargs) - ClangCompiler.__init__(self) + ClangCompiler.__init__(self, defines) default_warn_args = ['-Wall', '-Winvalid-pch'] self.warn_args = {'0': [], '1': default_warn_args, diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 385ef5e..f3c171f 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -186,7 +186,7 @@ rust_buildtype_args = {'plain': [], d_gdc_buildtype_args = {'plain': [], 'debug': [], 'debugoptimized': ['-finline-functions'], - 'release': ['-frelease', '-finline-functions'], + 'release': ['-finline-functions'], 'minsize': [], 'custom': [], } @@ -194,7 +194,7 @@ d_gdc_buildtype_args = {'plain': [], d_ldc_buildtype_args = {'plain': [], 'debug': [], 'debugoptimized': ['-enable-inlining', '-Hkeep-all-bodies'], - 'release': ['-release', '-enable-inlining', '-Hkeep-all-bodies'], + 'release': ['-enable-inlining', '-Hkeep-all-bodies'], 'minsize': [], 'custom': [], } @@ -202,7 +202,7 @@ d_ldc_buildtype_args = {'plain': [], d_dmd_buildtype_args = {'plain': [], 'debug': [], 'debugoptimized': ['-inline'], - 'release': ['-release', '-inline'], + 'release': ['-inline'], 'minsize': [], 'custom': [], } @@ -320,7 +320,7 @@ def get_base_compile_args(options, compiler): if (options['b_ndebug'].value == 'true' or (options['b_ndebug'].value == 'if-release' and options['buildtype'].value in {'release', 'plain'})): - args += ['-DNDEBUG'] + args += compiler.get_disable_assert_args() except KeyError: pass # This does not need a try...except @@ -1077,7 +1077,7 @@ class Compiler: def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.List[str]: + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: return self.linker.build_rpath_args( env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath) @@ -1204,6 +1204,9 @@ class Compiler: def get_coverage_link_args(self) -> T.List[str]: return self.linker.get_coverage_args() + def get_disable_assert_args(self) -> T.List[str]: + return [] + def get_largefile_args(compiler): ''' diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index d30017f..478a68c 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -155,10 +155,11 @@ class CPPCompiler(CLikeCompiler, Compiler): class ClangCPPCompiler(ClangCompiler, CPPCompiler): def __init__(self, exelist, version, for_machine: MachineChoice, - is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs): + is_cross, info: 'MachineInfo', exe_wrapper=None, + defines : T.Optional[T.List[str]] = None, **kwargs): CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, **kwargs) - ClangCompiler.__init__(self) + ClangCompiler.__init__(self, defines) default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] self.warn_args = {'0': [], '1': default_warn_args, @@ -238,7 +239,9 @@ class EmscriptenCPPCompiler(EmscriptenMixin, LinkerEnvVarsMixin, ClangCPPCompile class ArmclangCPPCompiler(ArmclangCompiler, CPPCompiler): def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs): - CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrapper, **kwargs) + CPPCompiler.__init__(self, exelist=exelist, version=version, + for_machine=for_machine, is_cross=is_cross, + info=info, exe_wrapper=exe_wrapper, **kwargs) ArmclangCompiler.__init__(self) default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] self.warn_args = {'0': [], diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index e839f53..4e89f5d 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -271,9 +271,10 @@ class CudaCompiler(Compiler): def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.List[str]: - return self._cook_link_args(self.host_compiler.build_rpath_args( - env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)) + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: + (rpath_args, rpath_dirs_to_remove) = self.host_compiler.build_rpath_args( + env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath) + return (self._cook_link_args(rpath_args), rpath_dirs_to_remove) def linker_to_compiler_args(self, args): return args diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index a83e221..777fa19 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -220,7 +220,7 @@ class DmdLikeCompilerMixin: def build_rpath_args(self, env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): if self.info.is_windows(): - return [] + return ([], set()) # GNU ld, solaris ld, and lld acting like GNU ld if self.linker.id.startswith('ld'): @@ -228,15 +228,16 @@ class DmdLikeCompilerMixin: # do directly, each argument -rpath and the value to rpath, need to be # split into two separate arguments both prefaced with the -L=. args = [] - for r in super().build_rpath_args( - env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): + (rpath_args, rpath_dirs_to_remove) = super().build_rpath_args( + env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath) + for r in rpath_args: if ',' in r: a, b = r.split(',', maxsplit=1) args.append(a) args.append(self.LINKER_PREFIX + b) else: args.append(r) - return args + return (args, rpath_dirs_to_remove) return super().build_rpath_args( env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath) @@ -645,7 +646,8 @@ class GnuDCompiler(GnuCompiler, DCompiler): '1': default_warn_args, '2': default_warn_args + ['-Wextra'], '3': default_warn_args + ['-Wextra', '-Wpedantic']} - self.base_options = ['b_colorout', 'b_sanitize', 'b_staticpic', 'b_vscrt', 'b_coverage'] + self.base_options = ['b_colorout', 'b_sanitize', 'b_staticpic', + 'b_vscrt', 'b_coverage', 'b_pgo', 'b_ndebug'] self._has_color_support = version_compare(self.version, '>=4.9') # dependencies were implemented before, but broken - support was fixed in GCC 7.1+ @@ -684,6 +686,9 @@ class GnuDCompiler(GnuCompiler, DCompiler): return args return args + ['-shared-libphobos'] + def get_disable_assert_args(self): + return ['-frelease'] + class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): @@ -691,7 +696,7 @@ class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): info: 'MachineInfo', arch, **kwargs): DCompiler.__init__(self, exelist, version, for_machine, info, arch, False, None, **kwargs) self.id = 'llvm' - self.base_options = ['b_coverage', 'b_colorout', 'b_vscrt'] + self.base_options = ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug'] def get_colorout_args(self, colortype): if colortype == 'always': @@ -733,6 +738,9 @@ class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): return args return args + ['-link-defaultlib-shared'] + def get_disable_assert_args(self) -> T.List[str]: + return ['--release'] + class DmdDCompiler(DmdLikeCompilerMixin, DCompiler): @@ -740,7 +748,7 @@ class DmdDCompiler(DmdLikeCompilerMixin, DCompiler): info: 'MachineInfo', arch, **kwargs): DCompiler.__init__(self, exelist, version, for_machine, info, arch, False, None, **kwargs) self.id = 'dmd' - self.base_options = ['b_coverage', 'b_colorout', 'b_vscrt'] + self.base_options = ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug'] def get_colorout_args(self, colortype): if colortype == 'always': @@ -803,3 +811,6 @@ class DmdDCompiler(DmdLikeCompilerMixin, DCompiler): if self.info.is_windows(): return args return args + ['-defaultlib=phobos2', '-debuglib=phobos2'] + + def get_disable_assert_args(self) -> T.List[str]: + return ['-release'] diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index c155b5b..af83c0e56 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -424,7 +424,7 @@ class FlangFortranCompiler(ClangCompiler, FortranCompiler): **kwargs): FortranCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, **kwargs) - ClangCompiler.__init__(self) + ClangCompiler.__init__(self, []) self.id = 'flang' default_warn_args = ['-Minform=inform'] self.warn_args = {'0': [], diff --git a/mesonbuild/compilers/mixins/arm.py b/mesonbuild/compilers/mixins/arm.py index aa5d15d..2e2ed94 100644 --- a/mesonbuild/compilers/mixins/arm.py +++ b/mesonbuild/compilers/mixins/arm.py @@ -27,10 +27,10 @@ if T.TYPE_CHECKING: arm_buildtype_args = { 'plain': [], - 'debug': ['-O0', '--debug'], - 'debugoptimized': ['-O1', '--debug'], - 'release': ['-O3', '-Otime'], - 'minsize': ['-O3', '-Ospace'], + 'debug': [], + 'debugoptimized': [], + 'release': [], + 'minsize': [], 'custom': [], } # type: T.Dict[str, T.List[str]] @@ -38,9 +38,9 @@ arm_optimization_args = { '0': ['-O0'], 'g': ['-g'], '1': ['-O1'], - '2': ['-O2'], - '3': ['-O3'], - 's': [], + '2': [], # Compiler defaults to -O2 + '3': ['-O3', '-Otime'], + 's': ['-O3'], # Compiler defaults to -Ospace } # type: T.Dict[str, T.List[str]] armclang_buildtype_args = { @@ -181,7 +181,7 @@ class ArmclangCompiler: # Override CCompiler.get_dependency_gen_args def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: - return [] + return ['-MD', '-MT', outtarget, '-MF', outfile] def get_optimization_args(self, optimization_level: str) -> T.List[str]: return armclang_optimization_args[optimization_level] diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py index 1c0ee45..0ee10ad 100644 --- a/mesonbuild/compilers/mixins/clang.py +++ b/mesonbuild/compilers/mixins/clang.py @@ -42,9 +42,10 @@ clang_optimization_args = { } # type: T.Dict[str, T.List[str]] class ClangCompiler(GnuLikeCompiler): - def __init__(self): + def __init__(self, defines: T.Optional[T.Dict[str, str]]): super().__init__() self.id = 'clang' + self.defines = defines or {} self.base_options.append('b_colorout') # TODO: this really should be part of the linker base_options, but # linkers don't have base_options. @@ -56,6 +57,12 @@ class ClangCompiler(GnuLikeCompiler): def get_colorout_args(self, colortype: str) -> T.List[str]: return clang_color_args[colortype][:] + def has_builtin_define(self, define: str) -> bool: + return define in self.defines + + def get_builtin_define(self, define: str) -> T.Optional[str]: + return self.defines.get(define) + def get_optimization_args(self, optimization_level: str) -> T.List[str]: return clang_optimization_args[optimization_level] diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index e7b0cd2..01c984d 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -1137,3 +1137,6 @@ class CLikeCompiler: return self.compiles(self.attribute_check_func(name), env, extra_args=self.get_has_func_attribute_extra_args(name)) + + def get_disable_assert_args(self) -> T.List[str]: + return ['-DNDEBUG'] diff --git a/mesonbuild/compilers/mixins/islinker.py b/mesonbuild/compilers/mixins/islinker.py index 681c816..bf1d339 100644 --- a/mesonbuild/compilers/mixins/islinker.py +++ b/mesonbuild/compilers/mixins/islinker.py @@ -107,8 +107,8 @@ class BasicLinkerIsCompilerMixin: def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.List[str]: - return [] + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: + return ([], set()) def get_linker_debug_crt_args(self) -> T.List[str]: return [] diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py index 52d258d..d351c88 100644 --- a/mesonbuild/compilers/objc.py +++ b/mesonbuild/compilers/objc.py @@ -86,7 +86,7 @@ class ClangObjCCompiler(ClangCompiler, ObjCCompiler): **kwargs): ObjCCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, **kwargs) - ClangCompiler.__init__(self) + ClangCompiler.__init__(self, []) default_warn_args = ['-Wall', '-Winvalid-pch'] self.warn_args = {'0': [], '1': default_warn_args, diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py index c8b422b..10555b4 100644 --- a/mesonbuild/compilers/objcpp.py +++ b/mesonbuild/compilers/objcpp.py @@ -84,7 +84,7 @@ class ClangObjCPPCompiler(ClangCompiler, ObjCPPCompiler): is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs): ObjCPPCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, **kwargs) - ClangCompiler.__init__(self) + ClangCompiler.__init__(self, []) default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] self.warn_args = {'0': [], '1': default_warn_args, diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py index 2e84820..907c0c2 100644 --- a/mesonbuild/dependencies/boost.py +++ b/mesonbuild/dependencies/boost.py @@ -22,7 +22,7 @@ from .. import mlog from .. import mesonlib from ..environment import Environment -from .base import (DependencyException, ExternalDependency) +from .base import DependencyException, ExternalDependency, PkgConfigDependency from .misc import threads_factory # On windows 3 directory layouts are supported: @@ -189,13 +189,13 @@ class BoostLibraryFile(): def __lt__(self, other: T.Any) -> bool: if isinstance(other, BoostLibraryFile): return ( - self.mod_name, self.version_lib, self.arch, self.static, + self.mod_name, self.static, self.version_lib, self.arch, not self.mt, not self.runtime_static, not self.debug, self.runtime_debug, self.python_debug, self.stlport, self.deprecated_iostreams, self.name, ) < ( - other.mod_name, other.version_lib, other.arch, other.static, + other.mod_name, other.static, other.version_lib, other.arch, not other.mt, not other.runtime_static, not other.debug, other.runtime_debug, other.python_debug, other.stlport, other.deprecated_iostreams, @@ -605,6 +605,23 @@ class BoostDependency(ExternalDependency): roots += paths return roots # Do not add system paths if BOOST_ROOT is present + # Try getting the BOOST_ROOT from a boost.pc if it exists. This primarely + # allows BoostDependency to find boost from Conan. See #5438 + try: + boost_pc = PkgConfigDependency('boost', self.env, {'required': False}) + if boost_pc.found(): + boost_root = boost_pc.get_pkgconfig_variable('prefix', {'default': None}) + if boost_root: + roots += [Path(boost_root)] + except DependencyException: + pass + + # Add roots from system paths + inc_paths = [Path(x) for x in self.clib_compiler.get_default_include_dirs()] + inc_paths = [x.parent for x in inc_paths if x.exists()] + inc_paths = [x.resolve() for x in inc_paths] + roots += inc_paths + # Add system paths if self.env.machines[self.for_machine].is_windows(): # Where boost built from source actually installs it @@ -626,8 +643,6 @@ class BoostDependency(ExternalDependency): roots += [x for x in candidates if x.name.lower().startswith('boost') and x.is_dir()] else: tmp = [] # type: T.List[Path] - # Add unix paths - tmp += [Path(x).parent for x in self.clib_compiler.get_default_include_dirs()] # Homebrew brew_boost = Path('/usr/local/Cellar/boost') diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 8fad628..cb6ae7d 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -726,6 +726,28 @@ class Environment: minor = defines.get('__LCC_MINOR__', '0') return dot.join((generation, major, minor)) + @staticmethod + def get_clang_compiler_defines(compiler): + """ + Get the list of Clang pre-processor defines + """ + args = compiler + ['-E', '-dM', '-'] + p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE) + if p.returncode != 0: + raise EnvironmentException('Unable to get clang pre-processor defines:\n' + output + error) + defines = {} + for line in output.split('\n'): + if not line: + continue + d, *rest = line.split(' ', 2) + if d != '#define': + continue + if len(rest) == 1: + defines[rest] = True + if len(rest) == 2: + defines[rest[0]] = rest[1] + return defines + def _get_compilers(self, lang, for_machine): ''' The list of compilers is detected in the exact same way for @@ -1043,6 +1065,8 @@ class Environment: if 'clang' in out: linker = None + defines = self.get_clang_compiler_defines(compiler) + # Even if the for_machine is darwin, we could be using vanilla # clang. if 'Apple' in out: @@ -1063,7 +1087,7 @@ class Environment: return cls( ccache + compiler, version, for_machine, is_cross, info, - exe_wrap, full_version=full_version, linker=linker) + exe_wrap, defines, full_version=full_version, linker=linker) if 'Intel(R) C++ Intel(R)' in err: version = search_version(err) diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py index db735e7..f02c297 100644 --- a/mesonbuild/linkers.py +++ b/mesonbuild/linkers.py @@ -56,8 +56,8 @@ class StaticLinker: def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.List[str]: - return [] + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: + return ([], set()) def thread_link_flags(self, env: 'Environment') -> T.List[str]: return [] @@ -444,8 +444,8 @@ class DynamicLinker(LinkerEnvVarsMixin, metaclass=abc.ABCMeta): def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.List[str]: - return [] + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: + return ([], set()) def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], @@ -551,12 +551,12 @@ class GnuLikeDynamicLinkerMixin: def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.List[str]: + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: m = env.machines[self.for_machine] if m.is_windows() or m.is_cygwin(): - return [] + return ([], set()) if not rpath_paths and not install_rpath and not build_rpath: - return [] + return ([], set()) args = [] origin_placeholder = '$ORIGIN' processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir) @@ -564,9 +564,14 @@ class GnuLikeDynamicLinkerMixin: # is *very* allergic to duplicate -delete_rpath arguments # when calling depfixer on installation. all_paths = mesonlib.OrderedSet([os.path.join(origin_placeholder, p) for p in processed_rpaths]) + rpath_dirs_to_remove = set() + for p in all_paths: + rpath_dirs_to_remove.add(p.encode('utf8')) # Build_rpath is used as-is (it is usually absolute). if build_rpath != '': all_paths.add(build_rpath) + for p in build_rpath.split(':'): + rpath_dirs_to_remove.add(p.encode('utf8')) # TODO: should this actually be "for (dragonfly|open)bsd"? if mesonlib.is_dragonflybsd() or mesonlib.is_openbsd(): @@ -590,7 +595,7 @@ class GnuLikeDynamicLinkerMixin: # TODO: should this actually be "for solaris/sunos"? if mesonlib.is_sunos(): - return args + return (args, rpath_dirs_to_remove) # Rpaths to use while linking must be absolute. These are not # written to the binary. Needed only with GNU ld: @@ -610,7 +615,7 @@ class GnuLikeDynamicLinkerMixin: for p in rpath_paths: args.extend(self._apply_prefix('-rpath-link,' + os.path.join(build_dir, p))) - return args + return (args, rpath_dirs_to_remove) class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): @@ -676,9 +681,9 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.List[str]: + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: if not rpath_paths and not install_rpath and not build_rpath: - return [] + return ([], set()) # Ensure that there is enough space for install_name_tool in-place # editing of large RPATHs args = self._apply_prefix('-headerpad_max_install_names') @@ -692,7 +697,7 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): for rp in all_paths: args.extend(self._apply_prefix('-rpath,' + rp)) - return args + return (args, set()) class GnuDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker): @@ -763,8 +768,8 @@ class WASMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, Dyna def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.List[str]: - return [] + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: + return ([], set()) class CcrxDynamicLinker(DynamicLinker): @@ -839,8 +844,8 @@ class Xc16DynamicLinker(DynamicLinker): def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.List[str]: - return [] + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: + return ([], set()) class C2000DynamicLinker(DynamicLinker): @@ -938,10 +943,10 @@ class PGIDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.List[str]: + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: if not env.machines[self.for_machine].is_windows(): - return ['-R' + os.path.join(build_dir, p) for p in rpath_paths] - return [] + return (['-R' + os.path.join(build_dir, p) for p in rpath_paths], set()) + return ([], set()) class PGIStaticLinker(StaticLinker): @@ -1091,9 +1096,9 @@ class SolarisDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.List[str]: + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: if not rpath_paths and not install_rpath and not build_rpath: - return [] + return ([], set()) processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir) all_paths = mesonlib.OrderedSet([os.path.join('$ORIGIN', p) for p in processed_rpaths]) if build_rpath != '': @@ -1108,7 +1113,7 @@ class SolarisDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): paths = padding else: paths = paths + ':' + padding - return self._apply_prefix('-rpath,{}'.format(paths)) + return (self._apply_prefix('-rpath,{}'.format(paths)), set()) def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 9c64429..0be01fe 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -512,7 +512,7 @@ class Installer: if file_copied: self.did_install_something = True try: - depfixer.fix_rpath(outname, install_rpath, final_path, + depfixer.fix_rpath(outname, t.rpath_dirs_to_remove, install_rpath, final_path, install_name_mappings, verbose=False) except SystemExit as e: if isinstance(e.code, int) and e.code == 0: diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index dc86a1b..47be039 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -57,6 +57,17 @@ def get_include_args(include_dirs, prefix='-I'): return dirs_str +def is_module_library(fname): + ''' + Check if the file is a library-like file generated by a module-specific + target, such as GirTarget or TypelibTarget + ''' + if hasattr(fname, 'fname'): + fname = fname.fname + suffix = fname.split('.')[-1] + return suffix in ('gir', 'typelib') + + class ModuleReturnValue: def __init__(self, return_value, new_objects): self.return_value = return_value diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 4592c90..4aafe62 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -42,7 +42,7 @@ from . import build from . import environment from . import mlog from .dependencies import ExternalProgram -from .mesonlib import MesonException, get_wine_shortpath, split_args +from .mesonlib import MesonException, get_wine_shortpath, split_args, join_args from .backend.backends import TestProtocol if T.TYPE_CHECKING: @@ -311,7 +311,7 @@ class TAPParser: yield self.Version(version=version) continue - if len(line) == 0: + if not line: continue yield self.Error('unexpected input at line {}'.format((lineno,))) @@ -609,20 +609,20 @@ class SingleTestRunner: return ['java', '-jar'] + self.test.fname elif not self.test.is_cross_built and run_with_mono(self.test.fname[0]): return ['mono'] + self.test.fname - else: - if self.test.is_cross_built and self.test.needs_exe_wrapper: - if self.test.exe_runner is None: - # Can not run test on cross compiled executable - # because there is no execute wrapper. - return None - else: - if not self.test.exe_runner.found(): - msg = 'The exe_wrapper defined in the cross file {!r} was not ' \ - 'found. Please check the command and/or add it to PATH.' - raise TestException(msg.format(self.test.exe_runner.name)) - return self.test.exe_runner.get_command() + self.test.fname - else: - return self.test.fname + elif self.test.cmd_is_built and self.test.needs_exe_wrapper: + if self.test.exe_runner is None: + # Can not run test on cross compiled executable + # because there is no execute wrapper. + return None + elif self.test.cmd_is_built: + # If the command is not built (ie, its a python script), + # then we don't check for the exe-wrapper + if not self.test.exe_runner.found(): + msg = ('The exe_wrapper defined in the cross file {!r} was not ' + 'found. Please check the command and/or add it to PATH.') + raise TestException(msg.format(self.test.exe_runner.name)) + return self.test.exe_runner.get_command() + self.test.fname + return self.test.fname def run(self) -> TestRun: cmd = self._get_cmd() @@ -638,7 +638,7 @@ class SingleTestRunner: def _run_cmd(self, cmd: T.List[str]) -> TestRun: starttime = time.time() - if len(self.test.extra_paths) > 0: + if self.test.extra_paths: self.env['PATH'] = os.pathsep.join(self.test.extra_paths + ['']) + self.env['PATH'] winecmd = [] for c in cmd: @@ -867,6 +867,9 @@ class TestHarness: env = os.environ.copy() test_env = test.env.get_env(env) env.update(test_env) + if (test.is_cross_built and test.needs_exe_wrapper and + test.exe_runner and test.exe_runner.found()): + env['MESON_EXE_WRAPPER'] = join_args(test.exe_runner.get_command()) return SingleTestRunner(test, test_env, env, options) def process_test_result(self, result: TestRun) -> None: @@ -941,7 +944,7 @@ class TestHarness: self.junit.write() def print_collected_logs(self) -> None: - if len(self.collected_logs) > 0: + if self.collected_logs: if len(self.collected_logs) > 10: print('\nThe output from 10 first failed tests:\n') else: @@ -1023,7 +1026,7 @@ class TestHarness: print('No tests defined.') return [] - if len(self.options.include_suites) or len(self.options.exclude_suites): + if self.options.include_suites or self.options.exclude_suites: tests = [] for tst in self.tests: if self.test_suitable(tst): @@ -1085,7 +1088,7 @@ class TestHarness: if len(self.suites) > 1 and test.suite: rv = TestHarness.split_suite_string(test.suite[0])[0] s = "+".join(TestHarness.split_suite_string(s)[1] for s in test.suite) - if len(s): + if s: rv += ":" return rv + s + " / " + test.name else: diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py index 5ba3a97..a3a3eff 100644 --- a/mesonbuild/scripts/depfixer.py +++ b/mesonbuild/scripts/depfixer.py @@ -290,13 +290,13 @@ class Elf(DataSizes): self.bf.seek(offset) self.bf.write(newname) - def fix_rpath(self, new_rpath): + def fix_rpath(self, rpath_dirs_to_remove, new_rpath): # The path to search for can be either rpath or runpath. # Fix both of them to be sure. - self.fix_rpathtype_entry(new_rpath, DT_RPATH) - self.fix_rpathtype_entry(new_rpath, DT_RUNPATH) + self.fix_rpathtype_entry(rpath_dirs_to_remove, new_rpath, DT_RPATH) + self.fix_rpathtype_entry(rpath_dirs_to_remove, new_rpath, DT_RUNPATH) - def fix_rpathtype_entry(self, new_rpath, entrynum): + def fix_rpathtype_entry(self, rpath_dirs_to_remove, new_rpath, entrynum): if isinstance(new_rpath, str): new_rpath = new_rpath.encode('utf8') rp_off = self.get_entry_offset(entrynum) @@ -305,7 +305,23 @@ class Elf(DataSizes): print('File does not have rpath. It should be a fully static executable.') return self.bf.seek(rp_off) + old_rpath = self.read_str() + new_rpaths = [] + if new_rpath: + new_rpaths.append(new_rpath) + if old_rpath: + # Filter out build-only rpath entries + # added by get_link_dep_subdirs() or + # specified by user with build_rpath. + for dir in old_rpath.split(b':'): + if not (dir in rpath_dirs_to_remove or + dir == (b'X' * len(dir))): + new_rpaths.append(dir) + + # Prepend user-specified new entries while preserving the ones that came from pkgconfig etc. + new_rpath = b':'.join(new_rpaths) + if len(old_rpath) < len(new_rpath): sys.exit("New rpath must not be longer than the old one.") # The linker does read-only string deduplication. If there is a @@ -343,13 +359,13 @@ class Elf(DataSizes): entry.write(self.bf) return None -def fix_elf(fname, new_rpath, verbose=True): +def fix_elf(fname, rpath_dirs_to_remove, new_rpath, verbose=True): with Elf(fname, verbose) as e: if new_rpath is None: e.print_rpath() e.print_runpath() else: - e.fix_rpath(new_rpath) + e.fix_rpath(rpath_dirs_to_remove, new_rpath) def get_darwin_rpaths_to_remove(fname): out = subprocess.check_output(['otool', '-l', fname], @@ -430,7 +446,7 @@ def fix_jar(fname): f.truncate() subprocess.check_call(['jar', 'ufm', fname, 'META-INF/MANIFEST.MF']) -def fix_rpath(fname, new_rpath, final_path, install_name_mappings, verbose=True): +def fix_rpath(fname, rpath_dirs_to_remove, new_rpath, final_path, install_name_mappings, verbose=True): global INSTALL_NAME_TOOL # Static libraries, import libraries, debug information, headers, etc # never have rpaths @@ -441,7 +457,7 @@ def fix_rpath(fname, new_rpath, final_path, install_name_mappings, verbose=True) if fname.endswith('.jar'): fix_jar(fname) return - fix_elf(fname, new_rpath, verbose) + fix_elf(fname, rpath_dirs_to_remove, new_rpath, verbose) return except SystemExit as e: if isinstance(e.code, int) and e.code == 0: diff --git a/mesonbuild/scripts/symbolextractor.py b/mesonbuild/scripts/symbolextractor.py index d393f93..41cca26 100644 --- a/mesonbuild/scripts/symbolextractor.py +++ b/mesonbuild/scripts/symbolextractor.py @@ -113,7 +113,10 @@ def gnu_syms(libfilename: str, outfilename: str): continue line_split = line.split() entry = line_split[0:2] - if len(line_split) >= 4: + # Store the size of symbols pointing to data objects so we relink + # when those change, which is needed because of copy relocations + # https://github.com/mesonbuild/meson/pull/7132#issuecomment-628353702 + if line_split[1].upper() in ('B', 'G', 'D') and len(line_split) >= 4: entry += [line_split[3]] result += [' '.join(entry)] write_if_changed('\n'.join(result) + '\n', outfilename) @@ -139,6 +142,23 @@ def osx_syms(libfilename: str, outfilename: str): result += [' '.join(x.split()[0:2]) for x in output.split('\n')] write_if_changed('\n'.join(result) + '\n', outfilename) +def openbsd_syms(libfilename: str, outfilename: str): + # Get the name of the library + output = call_tool('readelf', ['-d', libfilename]) + if not output: + dummy_syms(outfilename) + return + result = [x for x in output.split('\n') if 'SONAME' in x] + assert(len(result) <= 1) + # Get a list of all symbols exported + output = call_tool('nm', ['-D', '-P', '-g', libfilename]) + if not output: + dummy_syms(outfilename) + return + # U = undefined (cope with the lack of --defined-only option) + result += [' '.join(x.split()[0:2]) for x in output.split('\n') if x and not x.endswith('U ')] + write_if_changed('\n'.join(result) + '\n', outfilename) + def cygwin_syms(impfilename: str, outfilename: str): # Get the name of the library output = call_tool('dlltool', ['-I', impfilename]) @@ -234,6 +254,8 @@ def gen_symbols(libfilename: str, impfilename: str, outfilename: str, cross_host gnu_syms(libfilename, outfilename) elif mesonlib.is_osx(): osx_syms(libfilename, outfilename) + elif mesonlib.is_openbsd(): + openbsd_syms(libfilename, outfilename) elif mesonlib.is_windows(): if os.path.isfile(impfilename): windows_syms(impfilename, outfilename) diff --git a/msi/createmsi.py b/msi/createmsi.py index f80d1dc..4d03593 100644 --- a/msi/createmsi.py +++ b/msi/createmsi.py @@ -153,6 +153,7 @@ class PackageGenerator: if os.path.exists(sdir): shutil.rmtree(sdir) main_stage, ninja_stage = self.staging_dirs + dep_data_dir = 'mesonbuild/dependencies/data' modules = self.get_all_modules_from_dir('mesonbuild/modules') modules += self.get_all_modules_from_dir('mesonbuild/scripts') modules += self.get_more_modules() @@ -174,6 +175,7 @@ class PackageGenerator: pyinst_cmd += ['meson.py'] subprocess.check_call(pyinst_cmd) shutil.move(pyinstaller_tmpdir + '/meson', main_stage) + shutil.copytree(dep_data_dir, main_stage + '/mesonbuild/dependencies/data') if not os.path.exists(os.path.join(main_stage, 'meson.exe')): sys.exit('Meson exe missing from staging dir.') os.mkdir(ninja_stage) diff --git a/run_project_tests.py b/run_project_tests.py index bcfe05c..22c0205 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -594,18 +594,16 @@ def _run_test(test: TestDef, test_build_dir: str, install_dir: str, extra_args, return testresult -def gather_tests(testdir: Path) -> T.List[TestDef]: +def gather_tests(testdir: Path, stdout_mandatory: bool) -> T.List[TestDef]: tests = [t.name for t in testdir.iterdir() if t.is_dir()] tests = [t for t in tests if not t.startswith('.')] # Filter non-tests files (dot files, etc) test_defs = [TestDef(testdir / t, None, []) for t in tests] all_tests = [] # type: T.List[TestDef] for t in test_defs: + test_def = {} test_def_file = t.path / 'test.json' - if not test_def_file.is_file(): - all_tests += [t] - continue - - test_def = json.loads(test_def_file.read_text()) + if test_def_file.is_file(): + test_def = json.loads(test_def_file.read_text()) # Handle additional environment variables env = {} # type: T.Dict[str, str] @@ -623,6 +621,8 @@ def gather_tests(testdir: Path) -> T.List[TestDef]: # Handle expected output stdout = test_def.get('stdout', []) + if stdout_mandatory and not stdout: + raise RuntimeError("{} must contain a non-empty stdout key".format(test_def_file)) # Handle the do_not_set_opts list do_not_set_opts = test_def.get('do_not_set_opts', []) # type: T.List[str] @@ -897,45 +897,50 @@ def detect_tests_to_run(only: T.List[str], use_tmp: bool) -> T.List[T.Tuple[str, shutil.which('pgfortran') or shutil.which('ifort')) - # Name, subdirectory, skip condition. + class TestCategory: + def __init__(self, category: str, subdir: str, skip: bool = False, stdout_mandatory: bool = False): + self.category = category # category name + self.subdir = subdir # subdirectory + self.skip = skip # skip condition + self.stdout_mandatory = stdout_mandatory # expected stdout is mandatory for tests in this categroy + all_tests = [ - ('cmake', 'cmake', not shutil.which('cmake') or (os.environ.get('compiler') == 'msvc2015' and under_ci)), - ('common', 'common', False), - ('warning-meson', 'warning', False), - ('failing-meson', 'failing', False), - ('failing-build', 'failing build', False), - ('failing-test', 'failing test', False), - ('keyval', 'keyval', False), - - ('platform-osx', 'osx', not mesonlib.is_osx()), - ('platform-windows', 'windows', not mesonlib.is_windows() and not mesonlib.is_cygwin()), - ('platform-linux', 'linuxlike', mesonlib.is_osx() or mesonlib.is_windows()), - - ('java', 'java', backend is not Backend.ninja or mesonlib.is_osx() or not have_java()), - ('C#', 'csharp', skip_csharp(backend)), - ('vala', 'vala', backend is not Backend.ninja or not shutil.which(os.environ.get('VALAC', 'valac'))), - ('rust', 'rust', should_skip_rust(backend)), - ('d', 'd', backend is not Backend.ninja or not have_d_compiler()), - ('objective c', 'objc', backend not in (Backend.ninja, Backend.xcode) or not have_objc_compiler(options.use_tmpdir)), - ('objective c++', 'objcpp', backend not in (Backend.ninja, Backend.xcode) or not have_objcpp_compiler(options.use_tmpdir)), - ('fortran', 'fortran', skip_fortran or backend != Backend.ninja), - ('swift', 'swift', backend not in (Backend.ninja, Backend.xcode) or not shutil.which('swiftc')), + TestCategory('cmake', 'cmake', not shutil.which('cmake') or (os.environ.get('compiler') == 'msvc2015' and under_ci)), + TestCategory('common', 'common'), + TestCategory('warning-meson', 'warning', stdout_mandatory=True), + TestCategory('failing-meson', 'failing', stdout_mandatory=True), + TestCategory('failing-build', 'failing build'), + TestCategory('failing-test', 'failing test'), + TestCategory('keyval', 'keyval'), + TestCategory('platform-osx', 'osx', not mesonlib.is_osx()), + TestCategory('platform-windows', 'windows', not mesonlib.is_windows() and not mesonlib.is_cygwin()), + TestCategory('platform-linux', 'linuxlike', mesonlib.is_osx() or mesonlib.is_windows()), + TestCategory('java', 'java', backend is not Backend.ninja or mesonlib.is_osx() or not have_java()), + TestCategory('C#', 'csharp', skip_csharp(backend)), + TestCategory('vala', 'vala', backend is not Backend.ninja or not shutil.which(os.environ.get('VALAC', 'valac'))), + TestCategory('rust', 'rust', should_skip_rust(backend)), + TestCategory('d', 'd', backend is not Backend.ninja or not have_d_compiler()), + TestCategory('objective c', 'objc', backend not in (Backend.ninja, Backend.xcode) or not have_objc_compiler(options.use_tmpdir)), + TestCategory('objective c++', 'objcpp', backend not in (Backend.ninja, Backend.xcode) or not have_objcpp_compiler(options.use_tmpdir)), + TestCategory('fortran', 'fortran', skip_fortran or backend != Backend.ninja), + TestCategory('swift', 'swift', backend not in (Backend.ninja, Backend.xcode) or not shutil.which('swiftc')), # CUDA tests on Windows: use Ninja backend: python run_project_tests.py --only cuda --backend ninja - ('cuda', 'cuda', backend not in (Backend.ninja, Backend.xcode) or not shutil.which('nvcc')), - ('python3', 'python3', backend is not Backend.ninja), - ('python', 'python', backend is not Backend.ninja), - ('fpga', 'fpga', shutil.which('yosys') is None), - ('frameworks', 'frameworks', False), - ('nasm', 'nasm', False), - ('wasm', 'wasm', shutil.which('emcc') is None or backend is not Backend.ninja), + TestCategory('cuda', 'cuda', backend not in (Backend.ninja, Backend.xcode) or not shutil.which('nvcc')), + TestCategory('python3', 'python3', backend is not Backend.ninja), + TestCategory('python', 'python', backend is not Backend.ninja), + TestCategory('fpga', 'fpga', shutil.which('yosys') is None), + TestCategory('frameworks', 'frameworks'), + TestCategory('nasm', 'nasm'), + TestCategory('wasm', 'wasm', shutil.which('emcc') is None or backend is not Backend.ninja), ] - names = [t[0] for t in all_tests] - assert names == ALL_TESTS, 'argparse("--only", choices=ALL_TESTS) need to be updated to match all_tests names' + categories = [t.category for t in all_tests] + assert categories == ALL_TESTS, 'argparse("--only", choices=ALL_TESTS) need to be updated to match all_tests categories' + if only: - ind = [names.index(o) for o in only] - all_tests = [all_tests[i] for i in ind] - gathered_tests = [(name, gather_tests(Path('test cases', subdir)), skip) for name, subdir, skip in all_tests] + all_tests = [t for t in all_tests if t.category in only] + + gathered_tests = [(t.category, gather_tests(Path('test cases', t.subdir), t.stdout_mandatory), t.skip) for t in all_tests] return gathered_tests def run_tests(all_tests: T.List[T.Tuple[str, T.List[TestDef], bool]], diff --git a/run_unittests.py b/run_unittests.py index e088467..651e366 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -40,6 +40,7 @@ from contextlib import contextmanager from glob import glob from pathlib import (PurePath, Path) from distutils.dir_util import copy_tree +import typing import mesonbuild.mlog import mesonbuild.depfile @@ -56,7 +57,7 @@ from mesonbuild.mesonlib import ( BuildDirLock, LibType, MachineChoice, PerMachine, Version, is_windows, is_osx, is_cygwin, is_dragonflybsd, is_openbsd, is_haiku, is_sunos, windows_proof_rmtree, python_command, version_compare, split_args, - quote_arg, relpath + quote_arg, relpath, is_linux ) from mesonbuild.environment import detect_ninja from mesonbuild.mesonlib import MesonException, EnvironmentException @@ -2446,9 +2447,12 @@ class AllPlatformTests(BasePlatformTests): # Check include order for 'someexe' incs = [a for a in split_args(execmd) if a.startswith("-I")] self.assertEqual(len(incs), 9) - # target private dir - someexe_id = Target.construct_id_from_path("sub4", "someexe", "@exe") - self.assertPathEqual(incs[0], "-I" + os.path.join("sub4", someexe_id)) + # Need to run the build so the private dir is created. + self.build() + pdirs = glob(os.path.join(self.builddir, 'sub4/someexe*.p')) + self.assertEqual(len(pdirs), 1) + privdir = pdirs[0][len(self.builddir)+1:] + self.assertPathEqual(incs[0], "-I" + privdir) # target build subdir self.assertPathEqual(incs[1], "-Isub4") # target source subdir @@ -2469,7 +2473,10 @@ class AllPlatformTests(BasePlatformTests): incs = [a for a in split_args(fxecmd) if a.startswith('-I')] self.assertEqual(len(incs), 9) # target private dir - self.assertPathEqual(incs[0], '-Isomefxe@exe') + pdirs = glob(os.path.join(self.builddir, 'somefxe*.p')) + self.assertEqual(len(pdirs), 1) + privdir = pdirs[0][len(self.builddir)+1:] + self.assertPathEqual(incs[0], '-I' + privdir) # target build dir self.assertPathEqual(incs[1], '-I.') # target source dir @@ -3417,67 +3424,6 @@ int main(int argc, char **argv) { f.write('public class Foo { public static void main() {} }') self._run(self.meson_command + ['init', '-b'], workdir=tmpdir) - # The test uses mocking and thus requires that - # the current process is the one to run the Meson steps. - # If we are using an external test executable (most commonly - # in Debian autopkgtests) then the mocking won't work. - @unittest.skipIf('MESON_EXE' in os.environ, 'MESON_EXE is defined, can not use mocking.') - def test_cross_file_system_paths(self): - if is_windows(): - raise unittest.SkipTest('system crossfile paths not defined for Windows (yet)') - if is_sunos(): - cc = 'gcc' - else: - cc = 'cc' - - testdir = os.path.join(self.common_test_dir, '1 trivial') - cross_content = textwrap.dedent("""\ - [binaries] - c = '/usr/bin/{}' - ar = '/usr/bin/ar' - strip = '/usr/bin/ar' - - [properties] - - [host_machine] - system = 'linux' - cpu_family = 'x86' - cpu = 'i686' - endian = 'little' - """.format(cc)) - - with tempfile.TemporaryDirectory() as d: - dir_ = os.path.join(d, 'meson', 'cross') - os.makedirs(dir_) - with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f: - f.write(cross_content) - name = os.path.basename(f.name) - - with mock.patch.dict(os.environ, {'XDG_DATA_HOME': d}): - self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True) - self.wipe() - - with mock.patch.dict(os.environ, {'XDG_DATA_DIRS': d}): - os.environ.pop('XDG_DATA_HOME', None) - self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True) - self.wipe() - - with tempfile.TemporaryDirectory() as d: - dir_ = os.path.join(d, '.local', 'share', 'meson', 'cross') - os.makedirs(dir_) - with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f: - f.write(cross_content) - name = os.path.basename(f.name) - - # If XDG_DATA_HOME is set in the environment running the - # tests this test will fail, os mock the environment, pop - # it, then test - with mock.patch.dict(os.environ): - os.environ.pop('XDG_DATA_HOME', None) - with mock.patch('mesonbuild.coredata.os.path.expanduser', lambda x: x.replace('~', d)): - self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True) - self.wipe() - def test_compiler_run_command(self): ''' The test checks that the compiler object can be passed to @@ -4533,7 +4479,7 @@ recommended as it is not supported on some platforms''') self._run(self.mconf_command + [self.builddir]) def test_summary(self): - testdir = os.path.join(self.unit_test_dir, '72 summary') + testdir = os.path.join(self.unit_test_dir, '73 summary') out = self.init(testdir) expected = textwrap.dedent(r''' Some Subproject 2.0 @@ -4587,7 +4533,7 @@ recommended as it is not supported on some platforms''') self.assertPathDoesNotExist(os.path.join(self.builddir, prog)) def test_spurious_reconfigure_built_dep_file(self): - testdir = os.path.join(self.unit_test_dir, '74 dep files') + testdir = os.path.join(self.unit_test_dir, '75 dep files') # Regression test: Spurious reconfigure was happening when build # directory is inside source directory. @@ -5574,6 +5520,10 @@ class LinuxlikeTests(BasePlatformTests): self.assertRegex('\n'.join(mesonlog), r'Run-time dependency qt5 \(modules: Core\) found: YES .* \((qmake|qmake-qt5)\)\n') + def glob_sofiles_without_privdir(self, g): + files = glob(g) + return [f for f in files if not f.endswith('.p')] + def _test_soname_impl(self, libpath, install): if is_cygwin() or is_osx(): raise unittest.SkipTest('Test only applicable to ELF and linuxlike sonames') @@ -5589,28 +5539,28 @@ class LinuxlikeTests(BasePlatformTests): self.assertPathExists(nover) self.assertFalse(os.path.islink(nover)) self.assertEqual(get_soname(nover), 'libnover.so') - self.assertEqual(len(glob(nover[:-3] + '*')), 1) + self.assertEqual(len(self.glob_sofiles_without_privdir(nover[:-3] + '*')), 1) # File with version set verset = os.path.join(libpath, 'libverset.so') self.assertPathExists(verset + '.4.5.6') self.assertEqual(os.readlink(verset), 'libverset.so.4') self.assertEqual(get_soname(verset), 'libverset.so.4') - self.assertEqual(len(glob(verset[:-3] + '*')), 3) + self.assertEqual(len(self.glob_sofiles_without_privdir(verset[:-3] + '*')), 3) # File with soversion set soverset = os.path.join(libpath, 'libsoverset.so') self.assertPathExists(soverset + '.1.2.3') self.assertEqual(os.readlink(soverset), 'libsoverset.so.1.2.3') self.assertEqual(get_soname(soverset), 'libsoverset.so.1.2.3') - self.assertEqual(len(glob(soverset[:-3] + '*')), 2) + self.assertEqual(len(self.glob_sofiles_without_privdir(soverset[:-3] + '*')), 2) # File with version and soversion set to same values settosame = os.path.join(libpath, 'libsettosame.so') self.assertPathExists(settosame + '.7.8.9') self.assertEqual(os.readlink(settosame), 'libsettosame.so.7.8.9') self.assertEqual(get_soname(settosame), 'libsettosame.so.7.8.9') - self.assertEqual(len(glob(settosame[:-3] + '*')), 2) + self.assertEqual(len(self.glob_sofiles_without_privdir(settosame[:-3] + '*')), 2) # File with version and soversion set to different values bothset = os.path.join(libpath, 'libbothset.so') @@ -5618,7 +5568,7 @@ class LinuxlikeTests(BasePlatformTests): self.assertEqual(os.readlink(bothset), 'libbothset.so.1.2.3') self.assertEqual(os.readlink(bothset + '.1.2.3'), 'libbothset.so.4.5.6') self.assertEqual(get_soname(bothset), 'libbothset.so.1.2.3') - self.assertEqual(len(glob(bothset[:-3] + '*')), 3) + self.assertEqual(len(self.glob_sofiles_without_privdir(bothset[:-3] + '*')), 3) def test_soname(self): self._test_soname_impl(self.builddir, False) @@ -5738,10 +5688,12 @@ class LinuxlikeTests(BasePlatformTests): def test_unity_subproj(self): testdir = os.path.join(self.common_test_dir, '45 subproject') self.init(testdir, extra_args='--unity=subprojects') - simpletest_id = Target.construct_id_from_path('subprojects/sublib', 'simpletest', '@exe') - self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib', simpletest_id, 'simpletest-unity0.c')) - sublib_id = Target.construct_id_from_path('subprojects/sublib', 'sublib', '@sha') - self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib', sublib_id, 'sublib-unity0.c')) + pdirs = glob(os.path.join(self.builddir, 'subprojects/sublib/simpletest*.p')) + self.assertEqual(len(pdirs), 1) + self.assertPathExists(os.path.join(pdirs[0], 'simpletest-unity0.c')) + sdirs = glob(os.path.join(self.builddir, 'subprojects/sublib/*sublib*.p')) + self.assertEqual(len(sdirs), 1) + self.assertPathExists(os.path.join(sdirs[0], 'sublib-unity0.c')) self.assertPathDoesNotExist(os.path.join(self.builddir, 'user@exe/user-unity.c')) self.build() @@ -6077,6 +6029,39 @@ class LinuxlikeTests(BasePlatformTests): install_rpath = get_rpath(os.path.join(self.installdir, 'usr/bin/progcxx')) self.assertEqual(install_rpath, 'baz') + def test_global_rpath(self): + if is_cygwin(): + raise unittest.SkipTest('Windows PE/COFF binaries do not use RPATH') + if is_osx(): + raise unittest.SkipTest('Global RPATHs via LDFLAGS not yet supported on MacOS (does anybody need it?)') + + testdir = os.path.join(self.unit_test_dir, '77 global-rpath') + oldinstalldir = self.installdir + + # Build and install an external library without DESTDIR. + # The external library generates a .pc file without an rpath. + yonder_dir = os.path.join(testdir, 'yonder') + yonder_prefix = os.path.join(oldinstalldir, 'yonder') + yonder_libdir = os.path.join(yonder_prefix, self.libdir) + self.prefix = yonder_prefix + self.installdir = yonder_prefix + self.init(yonder_dir) + self.build() + self.install(use_destdir=False) + self.new_builddir() + + # Build an app that uses that installed library. + # Supply the rpath to the installed library via LDFLAGS + # (as systems like buildroot and guix are wont to do) + # and verify install preserves that rpath. + env = {'LDFLAGS': '-Wl,-rpath=' + yonder_libdir, + 'PKG_CONFIG_PATH': os.path.join(yonder_libdir, 'pkgconfig')} + self.init(testdir, override_envvars=env) + self.build() + self.install(use_destdir=False) + got_rpath = get_rpath(os.path.join(yonder_prefix, 'bin/rpathified')) + self.assertEqual(got_rpath, yonder_libdir) + @skip_if_not_base_option('b_sanitize') def test_pch_with_address_sanitizer(self): if is_cygwin(): @@ -6388,13 +6373,15 @@ class LinuxlikeTests(BasePlatformTests): self.build(override_envvars=env) # test uninstalled self.run_tests(override_envvars=env) - if not is_osx(): - # Rest of the workflow only works on macOS + if not (is_osx() or is_linux()): return # test running after installation self.install(use_destdir=False) prog = os.path.join(self.installdir, 'bin', 'prog') self._run([prog]) + if not is_osx(): + # Rest of the workflow only works on macOS + return out = self._run(['otool', '-L', prog]) self.assertNotIn('@rpath', out) ## New builddir for testing that DESTDIR is not added to install_name @@ -6411,6 +6398,57 @@ class LinuxlikeTests(BasePlatformTests): # Ensure that the otool output does not contain self.installdir self.assertNotRegex(out, self.installdir + '.*dylib ') + @skipIfNoPkgconfig + def test_usage_pkgconfig_prefixes(self): + ''' + Build and install two external libraries, to different prefixes, + then build and install a client program that finds them via pkgconfig, + and verify the installed client program runs. + ''' + oldinstalldir = self.installdir + + # Build and install both external libraries without DESTDIR + val1dir = os.path.join(self.unit_test_dir, '76 pkgconfig prefixes', 'val1') + val1prefix = os.path.join(oldinstalldir, 'val1') + self.prefix = val1prefix + self.installdir = val1prefix + self.init(val1dir) + self.build() + self.install(use_destdir=False) + self.new_builddir() + + env1 = {} + env1['PKG_CONFIG_PATH'] = os.path.join(val1prefix, self.libdir, 'pkgconfig') + val2dir = os.path.join(self.unit_test_dir, '76 pkgconfig prefixes', 'val2') + val2prefix = os.path.join(oldinstalldir, 'val2') + self.prefix = val2prefix + self.installdir = val2prefix + self.init(val2dir, override_envvars=env1) + self.build() + self.install(use_destdir=False) + self.new_builddir() + + # Build, install, and run the client program + env2 = {} + env2['PKG_CONFIG_PATH'] = os.path.join(val2prefix, self.libdir, 'pkgconfig') + testdir = os.path.join(self.unit_test_dir, '76 pkgconfig prefixes', 'client') + testprefix = os.path.join(oldinstalldir, 'client') + self.prefix = testprefix + self.installdir = testprefix + self.init(testdir, override_envvars=env2) + self.build() + self.install(use_destdir=False) + prog = os.path.join(self.installdir, 'bin', 'client') + env3 = {} + if is_cygwin(): + env3['PATH'] = os.path.join(val1prefix, 'bin') + \ + os.pathsep + \ + os.path.join(val2prefix, 'bin') + \ + os.pathsep + os.environ['PATH'] + out = self._run([prog], override_envvars=env3).strip() + # Expected output is val1 + val2 = 3 + self.assertEqual(out, '3') + def install_subdir_invalid_symlinks(self, testdir, subdir_path): ''' Test that installation of broken symlinks works fine. @@ -6601,7 +6639,7 @@ c = ['{0}'] return hashlib.sha256(f.read()).hexdigest() def test_wrap_with_file_url(self): - testdir = os.path.join(self.unit_test_dir, '73 wrap file url') + testdir = os.path.join(self.unit_test_dir, '74 wrap file url') source_filename = os.path.join(testdir, 'subprojects', 'foo.tar.xz') patch_filename = os.path.join(testdir, 'subprojects', 'foo-patch.tar.xz') wrap_filename = os.path.join(testdir, 'subprojects', 'foo.wrap') @@ -6692,7 +6730,7 @@ class LinuxCrossArmTests(BaseLinuxCrossTests): def test_cross_libdir_subproject(self): # Guard against a regression where calling "subproject" # would reset the value of libdir to its default value. - testdir = os.path.join(self.unit_test_dir, '75 subdir libdir') + testdir = os.path.join(self.unit_test_dir, '76 subdir libdir') self.init(testdir, extra_args=['--libdir=fuf']) for i in self.introspect('--buildoptions'): if i['name'] == 'libdir': @@ -7517,6 +7555,134 @@ class CrossFileTests(BasePlatformTests): This is mainly aimed to testing overrides from cross files. """ + def _cross_file_generator(self, *, needs_exe_wrapper: bool = False, + exe_wrapper: typing.Optional[typing.List[str]] = None) -> str: + if is_windows(): + raise unittest.SkipTest('Cannot run this test on non-mingw/non-cygwin windows') + if is_sunos(): + cc = 'gcc' + else: + cc = 'cc' + + return textwrap.dedent("""\ + [binaries] + c = '/usr/bin/{}' + ar = '/usr/bin/ar' + strip = '/usr/bin/ar' + {} + + [properties] + needs_exe_wrapper = {} + + [host_machine] + system = 'linux' + cpu_family = 'x86' + cpu = 'i686' + endian = 'little' + """.format(cc, + 'exe_wrapper = {}'.format(str(exe_wrapper)) if exe_wrapper is not None else '', + needs_exe_wrapper)) + + def _stub_exe_wrapper(self) -> str: + return textwrap.dedent('''\ + #!/usr/bin/env python3 + import subprocess + import sys + + sys.exit(subprocess.run(sys.argv[1:]).returncode) + ''') + + def test_needs_exe_wrapper_true(self): + testdir = os.path.join(self.unit_test_dir, '72 cross test passed') + with tempfile.TemporaryDirectory() as d: + p = Path(d) / 'crossfile' + with p.open('wt') as f: + f.write(self._cross_file_generator(needs_exe_wrapper=True)) + self.init(testdir, extra_args=['--cross-file=' + str(p)]) + out = self.run_target('test') + self.assertRegex(out, r'Skipped:\s*1\s*\n') + + def test_needs_exe_wrapper_false(self): + testdir = os.path.join(self.unit_test_dir, '72 cross test passed') + with tempfile.TemporaryDirectory() as d: + p = Path(d) / 'crossfile' + with p.open('wt') as f: + f.write(self._cross_file_generator(needs_exe_wrapper=False)) + self.init(testdir, extra_args=['--cross-file=' + str(p)]) + out = self.run_target('test') + self.assertNotRegex(out, r'Skipped:\s*1\n') + + def test_needs_exe_wrapper_true_wrapper(self): + testdir = os.path.join(self.unit_test_dir, '72 cross test passed') + with tempfile.TemporaryDirectory() as d: + s = Path(d) / 'wrapper.py' + with s.open('wt') as f: + f.write(self._stub_exe_wrapper()) + s.chmod(0o774) + p = Path(d) / 'crossfile' + with p.open('wt') as f: + f.write(self._cross_file_generator( + needs_exe_wrapper=True, + exe_wrapper=[str(s)])) + + self.init(testdir, extra_args=['--cross-file=' + str(p), '-Dexpect=true']) + out = self.run_target('test') + self.assertRegex(out, r'Ok:\s*3\s*\n') + + def test_cross_exe_passed_no_wrapper(self): + testdir = os.path.join(self.unit_test_dir, '72 cross test passed') + with tempfile.TemporaryDirectory() as d: + p = Path(d) / 'crossfile' + with p.open('wt') as f: + f.write(self._cross_file_generator(needs_exe_wrapper=True)) + + self.init(testdir, extra_args=['--cross-file=' + str(p)]) + self.build() + out = self.run_target('test') + self.assertRegex(out, r'Skipped:\s*1\s*\n') + + # The test uses mocking and thus requires that the current process is the + # one to run the Meson steps. If we are using an external test executable + # (most commonly in Debian autopkgtests) then the mocking won't work. + @unittest.skipIf('MESON_EXE' in os.environ, 'MESON_EXE is defined, can not use mocking.') + def test_cross_file_system_paths(self): + if is_windows(): + raise unittest.SkipTest('system crossfile paths not defined for Windows (yet)') + + testdir = os.path.join(self.common_test_dir, '1 trivial') + cross_content = self._cross_file_generator() + with tempfile.TemporaryDirectory() as d: + dir_ = os.path.join(d, 'meson', 'cross') + os.makedirs(dir_) + with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f: + f.write(cross_content) + name = os.path.basename(f.name) + + with mock.patch.dict(os.environ, {'XDG_DATA_HOME': d}): + self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True) + self.wipe() + + with mock.patch.dict(os.environ, {'XDG_DATA_DIRS': d}): + os.environ.pop('XDG_DATA_HOME', None) + self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True) + self.wipe() + + with tempfile.TemporaryDirectory() as d: + dir_ = os.path.join(d, '.local', 'share', 'meson', 'cross') + os.makedirs(dir_) + with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f: + f.write(cross_content) + name = os.path.basename(f.name) + + # If XDG_DATA_HOME is set in the environment running the + # tests this test will fail, os mock the environment, pop + # it, then test + with mock.patch.dict(os.environ): + os.environ.pop('XDG_DATA_HOME', None) + with mock.patch('mesonbuild.coredata.os.path.expanduser', lambda x: x.replace('~', d)): + self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True) + self.wipe() + def test_cross_file_dirs(self): testcase = os.path.join(self.unit_test_dir, '60 native file override') self.init(testcase, default_args=False, diff --git a/test cases/failing/100 fallback consistency/test.json b/test cases/failing/100 fallback consistency/test.json new file mode 100644 index 0000000..a783d8c --- /dev/null +++ b/test cases/failing/100 fallback consistency/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/100 fallback consistency/meson.build:7:0: ERROR: Inconsistency: Subproject has overridden the dependency with another variable than 'dep2'" + } + ] +} diff --git a/test cases/failing/101 no native compiler/test.json b/test cases/failing/101 no native compiler/test.json new file mode 100644 index 0000000..c7b5d1c --- /dev/null +++ b/test cases/failing/101 no native compiler/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/101 no native compiler/meson.build:12:0: ERROR: No host machine compiler for \"main.c\"" + } + ] +} diff --git a/test cases/failing/102 subdir parse error/test.json b/test cases/failing/102 subdir parse error/test.json new file mode 100644 index 0000000..06fd4d3 --- /dev/null +++ b/test cases/failing/102 subdir parse error/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/102 subdir parse error/subdir/meson.build:1:0: ERROR: Plusassignment target must be an id." + } + ] +} diff --git a/test cases/failing/103 invalid option file/test.json b/test cases/failing/103 invalid option file/test.json new file mode 100644 index 0000000..20dbec3 --- /dev/null +++ b/test cases/failing/103 invalid option file/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/103 invalid option file/meson_options.txt:1:0: ERROR: lexer" + } + ] +} diff --git a/test cases/failing/104 no lang/test.json b/test cases/failing/104 no lang/test.json new file mode 100644 index 0000000..62999be --- /dev/null +++ b/test cases/failing/104 no lang/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/104 no lang/meson.build:2:0: ERROR: No host machine compiler for \"main.c\"" + } + ] +} diff --git a/test cases/failing/105 no glib-compile-resources/test.json b/test cases/failing/105 no glib-compile-resources/test.json new file mode 100644 index 0000000..67dc7e4 --- /dev/null +++ b/test cases/failing/105 no glib-compile-resources/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/105 no glib-compile-resources/meson.build:8:0: ERROR: Could not execute glib-compile-resources." + } + ] +} diff --git a/test cases/failing/34 dependency not-required then required/test.json b/test cases/failing/34 dependency not-required then required/test.json index bed1a45..3cf35f5 100644 --- a/test cases/failing/34 dependency not-required then required/test.json +++ b/test cases/failing/34 dependency not-required then required/test.json @@ -2,7 +2,7 @@ "stdout": [ { "match": "re", - "line": "test cases/failing/34 dependency not\\-required then required/meson\\.build:4:0: ERROR: Dependency \"foo\\-bar\\-xyz\\-12\\.3\" not found, tried .*" + "line": ".*/meson\\.build:4:0: ERROR: (Pkg-config binary for machine MachineChoice\\.HOST not found\\. Giving up\\.|Dependency \"foo\\-bar\\-xyz\\-12\\.3\" not found, tried .*)" } ] } diff --git a/test cases/failing/36 pkgconfig dependency impossible conditions/test.json b/test cases/failing/36 pkgconfig dependency impossible conditions/test.json new file mode 100644 index 0000000..2ce62ac --- /dev/null +++ b/test cases/failing/36 pkgconfig dependency impossible conditions/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/36 pkgconfig dependency impossible conditions/meson.build:7:0: ERROR: Dependency 'zlib' was already checked and was not found" + } + ] +} diff --git a/test cases/failing/67 subproj different versions/test.json b/test cases/failing/67 subproj different versions/test.json new file mode 100644 index 0000000..d16daf9 --- /dev/null +++ b/test cases/failing/67 subproj different versions/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/67 subproj different versions/subprojects/b/meson.build:3:0: ERROR: Dependency 'c' was already checked and was not found" + } + ] +} diff --git a/test cases/failing/84 gtest dependency with version/test.json b/test cases/failing/84 gtest dependency with version/test.json new file mode 100644 index 0000000..e1bbcac --- /dev/null +++ b/test cases/failing/84 gtest dependency with version/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/84 gtest dependency with version/meson.build:8:0: ERROR: Dependency 'gtest' was already checked and was not found" + } + ] +} diff --git a/test cases/failing/98 fallback consistency/test.json b/test cases/failing/98 fallback consistency/test.json new file mode 100644 index 0000000..fd77bad --- /dev/null +++ b/test cases/failing/98 fallback consistency/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/98 fallback consistency/meson.build:4:0: ERROR: Inconsistency: Subproject has overridden the dependency with another variable than 'dep2'" + } + ] +} diff --git a/test cases/unit/40 external, internal library rpath/built library/meson.build b/test cases/unit/40 external, internal library rpath/built library/meson.build index f633996..07fe7bb 100644 --- a/test cases/unit/40 external, internal library rpath/built library/meson.build +++ b/test cases/unit/40 external, internal library rpath/built library/meson.build @@ -18,4 +18,9 @@ l = shared_library('bar_built', 'bar.c', if host_machine.system() == 'darwin' e = executable('prog', 'prog.c', link_with: l, install: true) test('testprog', e) +elif host_machine.system() == 'linux' + e = executable('prog', 'prog.c', link_with: l, install: true, + install_rpath: '$ORIGIN/..' / get_option('libdir'), + ) + test('testprog', e) endif diff --git a/test cases/unit/40 external, internal library rpath/external library/meson.build b/test cases/unit/40 external, internal library rpath/external library/meson.build index 3c311f5..06ffa0f 100644 --- a/test cases/unit/40 external, internal library rpath/external library/meson.build +++ b/test cases/unit/40 external, internal library rpath/external library/meson.build @@ -4,16 +4,16 @@ shared_library('foo_in_system', 'foo.c', install : true) l = shared_library('faa_pkg', 'faa.c', install: true) if host_machine.system() == 'darwin' - frameworks = ['-framework', 'CoreFoundation', '-framework', 'CoreMedia'] + ldflags = ['-framework', 'CoreFoundation', '-framework', 'CoreMedia'] allow_undef_args = ['-Wl,-undefined,dynamic_lookup'] else - frameworks = [] + ldflags = ['-Wl,-rpath,${libdir}'] allow_undef_args = [] endif pkg = import('pkgconfig') pkg.generate(name: 'faa_pkg', - libraries: [l] + frameworks, + libraries: [l] + ldflags, description: 'FAA, a pkg-config test library') # cygwin DLLs can't have undefined symbols diff --git a/test cases/unit/72 cross test passed/exewrapper.py b/test cases/unit/72 cross test passed/exewrapper.py new file mode 100755 index 0000000..2c15ed6 --- /dev/null +++ b/test cases/unit/72 cross test passed/exewrapper.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# Test that the MESON_EXE_WRAPPER environment variable is set + +import argparse +import os +import sys + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('binary') # unused, but needed for test behavior + parser.add_argument('--expected', action='store_true') + args = parser.parse_args() + + defined = 'MESON_EXE_WRAPPER' in os.environ + + if args.expected != defined: + print(os.environ, file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/test cases/unit/72 cross test passed/meson.build b/test cases/unit/72 cross test passed/meson.build new file mode 100644 index 0000000..4deb74b --- /dev/null +++ b/test cases/unit/72 cross test passed/meson.build @@ -0,0 +1,19 @@ +project( + 'cross test passed', + 'c', + version : '>= 0.51' +) + +e = executable('exec', 'src/main.c') + +py = import('python').find_installation() + +test('root', e) +test('main', py, args : [meson.current_source_dir() / 'script.py', e]) + +wrapper_args = [] +if get_option('expect') + wrapper_args += '--expected' +endif + +test('exe_wrapper in env', py, args : [meson.current_source_dir() / 'exewrapper.py', e, wrapper_args]) diff --git a/test cases/unit/72 cross test passed/meson_options.txt b/test cases/unit/72 cross test passed/meson_options.txt new file mode 100644 index 0000000..084c776 --- /dev/null +++ b/test cases/unit/72 cross test passed/meson_options.txt @@ -0,0 +1,5 @@ +option( + 'expect', + type : 'boolean', + value : false, +) diff --git a/test cases/unit/72 cross test passed/script.py b/test cases/unit/72 cross test passed/script.py new file mode 100644 index 0000000..257cd30 --- /dev/null +++ b/test cases/unit/72 cross test passed/script.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 + +import subprocess +import sys + +if __name__ == "__main__": + sys.exit(subprocess.run(sys.argv[1:]).returncode) diff --git a/test cases/unit/72 cross test passed/src/main.c b/test cases/unit/72 cross test passed/src/main.c new file mode 100644 index 0000000..490b4a6 --- /dev/null +++ b/test cases/unit/72 cross test passed/src/main.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +int main(int argc, char const *argv[]) +{ + return 0; +} diff --git a/test cases/unit/72 summary/meson.build b/test cases/unit/73 summary/meson.build index df4540d..df4540d 100644 --- a/test cases/unit/72 summary/meson.build +++ b/test cases/unit/73 summary/meson.build diff --git a/test cases/unit/72 summary/subprojects/sub/meson.build b/test cases/unit/73 summary/subprojects/sub/meson.build index e7d7833..e7d7833 100644 --- a/test cases/unit/72 summary/subprojects/sub/meson.build +++ b/test cases/unit/73 summary/subprojects/sub/meson.build diff --git a/test cases/unit/72 summary/subprojects/sub2/meson.build b/test cases/unit/73 summary/subprojects/sub2/meson.build index 86b9cfd..86b9cfd 100644 --- a/test cases/unit/72 summary/subprojects/sub2/meson.build +++ b/test cases/unit/73 summary/subprojects/sub2/meson.build diff --git a/test cases/unit/73 wrap file url/meson.build b/test cases/unit/74 wrap file url/meson.build index 3bd3b25..3bd3b25 100644 --- a/test cases/unit/73 wrap file url/meson.build +++ b/test cases/unit/74 wrap file url/meson.build diff --git a/test cases/unit/73 wrap file url/subprojects/foo-patch.tar.xz b/test cases/unit/74 wrap file url/subprojects/foo-patch.tar.xz Binary files differindex fdb026c..fdb026c 100644 --- a/test cases/unit/73 wrap file url/subprojects/foo-patch.tar.xz +++ b/test cases/unit/74 wrap file url/subprojects/foo-patch.tar.xz diff --git a/test cases/unit/73 wrap file url/subprojects/foo.tar.xz b/test cases/unit/74 wrap file url/subprojects/foo.tar.xz Binary files differindex 2ed6ab4..2ed6ab4 100644 --- a/test cases/unit/73 wrap file url/subprojects/foo.tar.xz +++ b/test cases/unit/74 wrap file url/subprojects/foo.tar.xz diff --git a/test cases/unit/74 dep files/foo.c b/test cases/unit/75 dep files/foo.c index e69de29..e69de29 100644 --- a/test cases/unit/74 dep files/foo.c +++ b/test cases/unit/75 dep files/foo.c diff --git a/test cases/unit/74 dep files/meson.build b/test cases/unit/75 dep files/meson.build index 4829f56..4829f56 100644 --- a/test cases/unit/74 dep files/meson.build +++ b/test cases/unit/75 dep files/meson.build diff --git a/test cases/unit/76 pkgconfig prefixes/client/client.c b/test cases/unit/76 pkgconfig prefixes/client/client.c new file mode 100644 index 0000000..be9bead --- /dev/null +++ b/test cases/unit/76 pkgconfig prefixes/client/client.c @@ -0,0 +1,8 @@ +#include <val2.h> +#include <stdio.h> + +int main(int argc, char **argv) +{ + printf("%d\n", val2()); + return 0; +} diff --git a/test cases/unit/76 pkgconfig prefixes/client/meson.build b/test cases/unit/76 pkgconfig prefixes/client/meson.build new file mode 100644 index 0000000..491937b --- /dev/null +++ b/test cases/unit/76 pkgconfig prefixes/client/meson.build @@ -0,0 +1,3 @@ +project('client', 'c') +val2_dep = dependency('val2') +executable('client', 'client.c', dependencies : [val2_dep], install: true) diff --git a/test cases/unit/76 pkgconfig prefixes/val1/meson.build b/test cases/unit/76 pkgconfig prefixes/val1/meson.build new file mode 100644 index 0000000..cc63e31 --- /dev/null +++ b/test cases/unit/76 pkgconfig prefixes/val1/meson.build @@ -0,0 +1,5 @@ +project('val1', 'c') +val1 = shared_library('val1', 'val1.c', install: true) +install_headers('val1.h') +pkgconfig = import('pkgconfig') +pkgconfig.generate(val1, libraries : ['-Wl,-rpath,${libdir}']) diff --git a/test cases/unit/76 pkgconfig prefixes/val1/val1.c b/test cases/unit/76 pkgconfig prefixes/val1/val1.c new file mode 100644 index 0000000..591e521 --- /dev/null +++ b/test cases/unit/76 pkgconfig prefixes/val1/val1.c @@ -0,0 +1,3 @@ +#include "val1.h" + +int val1(void) { return 1; } diff --git a/test cases/unit/76 pkgconfig prefixes/val1/val1.h b/test cases/unit/76 pkgconfig prefixes/val1/val1.h new file mode 100644 index 0000000..6bd435e --- /dev/null +++ b/test cases/unit/76 pkgconfig prefixes/val1/val1.h @@ -0,0 +1 @@ +int val1(void); diff --git a/test cases/unit/76 pkgconfig prefixes/val2/meson.build b/test cases/unit/76 pkgconfig prefixes/val2/meson.build new file mode 100644 index 0000000..ce69481 --- /dev/null +++ b/test cases/unit/76 pkgconfig prefixes/val2/meson.build @@ -0,0 +1,8 @@ +project('val2', 'c') +val1_dep = dependency('val1') +val2 = shared_library('val2', 'val2.c', + dependencies : [val1_dep], + install: true) +install_headers('val2.h') +pkgconfig = import('pkgconfig') +pkgconfig.generate(val2, libraries : ['-Wl,-rpath,${libdir}']) diff --git a/test cases/unit/76 pkgconfig prefixes/val2/val2.c b/test cases/unit/76 pkgconfig prefixes/val2/val2.c new file mode 100644 index 0000000..d7d4857 --- /dev/null +++ b/test cases/unit/76 pkgconfig prefixes/val2/val2.c @@ -0,0 +1,4 @@ +#include "val1.h" +#include "val2.h" + +int val2(void) { return val1() + 2; } diff --git a/test cases/unit/76 pkgconfig prefixes/val2/val2.h b/test cases/unit/76 pkgconfig prefixes/val2/val2.h new file mode 100644 index 0000000..995023d --- /dev/null +++ b/test cases/unit/76 pkgconfig prefixes/val2/val2.h @@ -0,0 +1 @@ +int val2(void); diff --git a/test cases/unit/75 subdir libdir/meson.build b/test cases/unit/76 subdir libdir/meson.build index 5099c91..5099c91 100644 --- a/test cases/unit/75 subdir libdir/meson.build +++ b/test cases/unit/76 subdir libdir/meson.build diff --git a/test cases/unit/75 subdir libdir/subprojects/flub/meson.build b/test cases/unit/76 subdir libdir/subprojects/flub/meson.build index 7bfd2c5..7bfd2c5 100644 --- a/test cases/unit/75 subdir libdir/subprojects/flub/meson.build +++ b/test cases/unit/76 subdir libdir/subprojects/flub/meson.build diff --git a/test cases/unit/77 global-rpath/meson.build b/test cases/unit/77 global-rpath/meson.build new file mode 100644 index 0000000..c67d9e0 --- /dev/null +++ b/test cases/unit/77 global-rpath/meson.build @@ -0,0 +1,3 @@ +project('global-rpath', 'cpp') +yonder_dep = dependency('yonder') +executable('rpathified', 'rpathified.cpp', dependencies: [yonder_dep], install: true) diff --git a/test cases/unit/77 global-rpath/rpathified.cpp b/test cases/unit/77 global-rpath/rpathified.cpp new file mode 100644 index 0000000..3788906 --- /dev/null +++ b/test cases/unit/77 global-rpath/rpathified.cpp @@ -0,0 +1,6 @@ +#include <yonder.h> +#include <string.h> +int main(int argc, char **argv) +{ + return strcmp(yonder(), "AB54 6BR"); +} diff --git a/test cases/unit/77 global-rpath/yonder/meson.build b/test cases/unit/77 global-rpath/yonder/meson.build new file mode 100644 index 0000000..e32f383 --- /dev/null +++ b/test cases/unit/77 global-rpath/yonder/meson.build @@ -0,0 +1,5 @@ +project('yonder', 'cpp') +yonder = shared_library('yonder', 'yonder.cpp', install: true) +install_headers('yonder.h') +pkgconfig = import('pkgconfig') +pkgconfig.generate(yonder) diff --git a/test cases/unit/77 global-rpath/yonder/yonder.cpp b/test cases/unit/77 global-rpath/yonder/yonder.cpp new file mode 100644 index 0000000..b182d34 --- /dev/null +++ b/test cases/unit/77 global-rpath/yonder/yonder.cpp @@ -0,0 +1,3 @@ +#include "yonder.h" + +char *yonder(void) { return "AB54 6BR"; } diff --git a/test cases/unit/77 global-rpath/yonder/yonder.h b/test cases/unit/77 global-rpath/yonder/yonder.h new file mode 100644 index 0000000..9d9ad16 --- /dev/null +++ b/test cases/unit/77 global-rpath/yonder/yonder.h @@ -0,0 +1 @@ +char *yonder(void); |