diff options
77 files changed, 868 insertions, 332 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 3ae740d..6d2b2da 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -13,13 +13,22 @@ afterwards](#returned-objects). void add_global_arguments(arg1, arg2, ...) ``` -Adds the positional arguments to the compiler command line for the -language specified in `language` keyword argument. If a list of -languages is given, the arguments are added to each of the -corresponding compiler command lines. Note that there is no way to -remove an argument set in this way. If you have an argument that is -only used in a subset of targets, you have to specify it in per-target -flags. +Adds the positional arguments to the compiler command line. This +function has two keyword arguments: + +- `language` specifies the language(s) that the arguments should be +applied to. If a list of languages is given, the arguments are added +to each of the corresponding compiler command lines. Note that there +is no way to remove an argument set in this way. If you have an +argument that is only used in a subset of targets, you have to specify +it in per-target flags. + +- `native` is a boolean specifying whether the arguments should be + applied to the native or cross compilation. If `true` the arguments + will only be used for native compilations. If `false` the arguments + will only be used in cross compilations. If omitted, the flags are + added to native compilations if compiling natively and cross + compilations (only) when cross compiling. Available since 0.48.0 The arguments are used in all compiler invocations with the exception of compile tests, because you might need to run a compile test with @@ -60,8 +69,9 @@ endif Takes one keyword argument, `required`. It defaults to `true`, which means that if any of the languages specified is not found, Meson will halt. Returns true if all languages specified were found and false -otherwise. Since *0.47.0* the value of a [`feature`](Build-options.md#features) -option can also be passed to the `required` keyword argument. +otherwise. Since *0.47.0* the value of a +[`feature`](Build-options.md#features) option can also be passed to +the `required` keyword argument. ### add_project_arguments() @@ -370,11 +380,12 @@ otherwise. This function supports the following keyword arguments: - `static` tells the dependency provider to try to get static libraries instead of dynamic ones (note that this is not supported by all dependency backends) -- `version`, specifies the required version, a string containing a +- `version` specifies the required version, a string containing a comparison operator followed by the version string, examples include `>1.0.0`, `<=2.3.5` or `3.1.4` for exact matching. (*Added 0.37.0*) You can also specify multiple restrictions by passing a list to this keyword argument, such as: `['>=3.14.0', '<=4.1.0']`. + These requirements are never met if the version is unknown. - other [library-specific](Dependencies.md#dependencies-with-custom-lookup-functionality) keywords may also be accepted (e.g. `modules` specifies submodules to use for @@ -1976,7 +1987,9 @@ an external dependency with the following methods: with `declare_dependency()` and `pkgconfig` for system dependencies obtained with Pkg-config. - - `version()` is the version number as a string, for example `1.2.8` + - `version()` is the version number as a string, for example `1.2.8`. + `unknown` if the dependency provider doesn't support determining the + version. - `partial_dependency(compile_args : false, link_args : false, links : false, includes : false, source : false)` (*added 0.46.0*) returns diff --git a/docs/markdown/snippets/dependency_version.md b/docs/markdown/snippets/dependency_version.md new file mode 100644 index 0000000..4bbf346 --- /dev/null +++ b/docs/markdown/snippets/dependency_version.md @@ -0,0 +1,14 @@ +## `dependency(version:)` now applies to all dependency types + +Previously, version constraints were only enforced for dependencies found using +the pkg-config dependency provider. These constraints now apply to dependencies +found using any dependency provider. + +Some combinations of dependency, host and method do not currently support +discovery of the version. In these cases, the dependency will not be found if a +version constraint is applied, otherwise the `version()` method for the +dependency object will return `'unknown'`. + +(If discovering the version in one of these combinations is important to you, +and a method exists to determine the version in that case, please file an issue +with as much information as possible.) diff --git a/docs/markdown/snippets/native_args.md b/docs/markdown/snippets/native_args.md new file mode 100644 index 0000000..54c6de2 --- /dev/null +++ b/docs/markdown/snippets/native_args.md @@ -0,0 +1,34 @@ +## Projects args can be set separately for cross and native builds (potentially breaking change) + +It has been a longstanding bug (or let's call it a "delayed bug fix") +that if yo do this: + +```meson +add_project_arguments('-DFOO', language : 'c') +``` + +Then the flag is used both in native and cross compilations. This is +very confusing and almost never what you want. To fix this a new +keyword `native` has been added to all functions that add arguments, +namely `add_global_arguments`, `add_global_link_arguments`, +`add_project_arguments` and `add_project_link_arguments` that behaves +like the following: + +``` +## Added to native builds when compiling natively and to cross +## compilations when doing cross compiles. +add_project_arguments(...) + +## Added only to native compilations, not used in cross compilations. +add_project_arguments(..., native : true) + +## Added only to cross compilations, not used in native compilations. +add_project_arguments(..., native : false) +``` + +Also remember that cross compilation is a property of each +target. There can be target that are compiled with the native compiler +and some which are compiled with the cross compiler. + +Unfortunately this change is backwards incompatible and may cause some +projects to fail building. However this should be very rare in practice. @@ -22,10 +22,11 @@ import urllib.request, json, sys, os, shutil, subprocess import configparser, hashlib +req_timeout = 600.0 private_repos = {'meson', 'wrapweb', 'meson-ci'} def gh_get(url): - r = urllib.request.urlopen(url) + r = urllib.request.urlopen(url, timeout=req_timeout) jd = json.loads(r.read().decode('utf-8')) return jd @@ -45,7 +46,7 @@ def unpack(sproj, branch, outdir): config = configparser.ConfigParser() config.read(usfile) us_url = config['wrap-file']['source_url'] - us = urllib.request.urlopen(us_url).read() + us = urllib.request.urlopen(us_url, timeout=req_timeout).read() h = hashlib.sha256() h.update(us) dig = h.hexdigest() diff --git a/manual tests/4 standalone binaries/build_windows_package.py b/manual tests/4 standalone binaries/build_windows_package.py index b30ec4d..0932eac 100755 --- a/manual tests/4 standalone binaries/build_windows_package.py +++ b/manual tests/4 standalone binaries/build_windows_package.py @@ -11,7 +11,7 @@ shutil.rmtree('build', ignore_errors=True) os.mkdir('build') if not os.path.exists(sdl_filename): - response = urllib.request.urlopen(sdl_url) + response = urllib.request.urlopen(sdl_url, timeout=600.0) data = response.read() open(sdl_filename, 'wb').write(data) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 7ed97b2..bdc3fad 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -355,9 +355,9 @@ class Backend: return l, stdlib_args @staticmethod - def _libdir_is_system(libdir, compilers): + def _libdir_is_system(libdir, compilers, env): for cc in compilers.values(): - if libdir in cc.get_library_dirs(): + if libdir in cc.get_library_dirs(env): return True return False @@ -372,7 +372,7 @@ class Backend: # The only link argument is an absolute path to a library file. libpath = la[0] libdir = os.path.dirname(libpath) - if exclude_system and self._libdir_is_system(libdir, target.compilers): + if exclude_system and self._libdir_is_system(libdir, target.compilers, self.environment): # No point in adding system paths. continue # Windows doesn't support rpaths, but we use this function to @@ -535,10 +535,10 @@ class Backend: commands += compiler.get_optimization_args(self.get_option_for_target('optimization', target)) commands += compiler.get_debug_args(self.get_option_for_target('debug', target)) # Add compile args added using add_project_arguments() - commands += self.build.get_project_args(compiler, target.subproject) + commands += self.build.get_project_args(compiler, target.subproject, target.is_cross) # Add compile args added using add_global_arguments() # These override per-project arguments - commands += self.build.get_global_args(compiler) + commands += self.build.get_global_args(compiler, target.is_cross) if not target.is_cross: # Compile args added from the env: CFLAGS/CXXFLAGS, etc. We want these # to override all the defaults, but not the per-target compile args. @@ -612,8 +612,8 @@ class Backend: # Get program and library dirs from all target compilers if isinstance(target, build.BuildTarget): for cc in target.compilers.values(): - paths.update(cc.get_program_dirs()) - paths.update(cc.get_library_dirs()) + paths.update(cc.get_program_dirs(self.environment)) + paths.update(cc.get_library_dirs(self.environment)) return list(paths) def determine_windows_extra_paths(self, target, extra_bdeps, is_cross=False): diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 6b2b130..acbeed3 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -855,8 +855,8 @@ int dummy; for dep in target.get_external_deps(): commands.extend_direct(dep.get_link_args()) - commands += self.build.get_project_args(compiler, target.subproject) - commands += self.build.get_global_args(compiler) + commands += self.build.get_project_args(compiler, target.subproject, target.is_cross) + commands += self.build.get_global_args(compiler, target.is_cross) elem = NinjaBuildElement(self.all_outputs, outputs, 'cs_COMPILER', rel_srcs) elem.add_dep(deps) @@ -869,8 +869,8 @@ int dummy; deps = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets] args = [] args += compiler.get_buildtype_args(self.get_option_for_target('buildtype', target)) - args += self.build.get_global_args(compiler) - args += self.build.get_project_args(compiler, target.subproject) + args += self.build.get_global_args(compiler, target.is_cross) + args += self.build.get_project_args(compiler, target.subproject, target.is_cross) args += target.get_java_args() args += compiler.get_output_args(self.get_target_private_dir(target)) args += target.get_classpath_args() @@ -1247,8 +1247,8 @@ int dummy; compile_args += swiftc.get_optimization_args(self.get_option_for_target('optimization', target)) compile_args += swiftc.get_debug_args(self.get_option_for_target('debug', target)) compile_args += swiftc.get_module_args(module_name) - compile_args += self.build.get_project_args(swiftc, target.subproject) - compile_args += self.build.get_global_args(swiftc) + compile_args += self.build.get_project_args(swiftc, target.subproject, target.is_cross) + compile_args += self.build.get_global_args(swiftc, target.is_cross) for i in reversed(target.get_include_dirs()): basedir = i.get_curdir() for d in i.get_incdirs(): @@ -1260,8 +1260,8 @@ int dummy; sargs = swiftc.get_include_args(srctreedir) compile_args += sargs link_args = swiftc.get_output_args(os.path.join(self.environment.get_build_dir(), self.get_target_filename(target))) - link_args += self.build.get_project_link_args(swiftc, target.subproject) - link_args += self.build.get_global_link_args(swiftc) + link_args += self.build.get_project_link_args(swiftc, target.subproject, target.is_cross) + link_args += self.build.get_global_link_args(swiftc, target.is_cross) rundir = self.get_target_private_dir(target) out_module_name = self.swift_module_file_name(target) in_module_files = self.determine_swift_dep_modules(target) @@ -2333,7 +2333,7 @@ rule FORTRAN_DEP_HACK%s guessed_dependencies = [] # TODO The get_library_naming requirement currently excludes link targets that use d or fortran as their main linker if hasattr(linker, 'get_library_naming'): - search_dirs = list(search_dirs) + linker.get_library_dirs() + search_dirs = list(search_dirs) + linker.get_library_dirs(self.environment) static_patterns = linker.get_library_naming(self.environment, 'static', strict=True) shared_patterns = linker.get_library_naming(self.environment, 'shared', strict=True) for libname in libs: @@ -2395,10 +2395,10 @@ rule FORTRAN_DEP_HACK%s if not isinstance(target, build.StaticLibrary): # Add link args added using add_project_link_arguments() - commands += self.build.get_project_link_args(linker, target.subproject) + commands += self.build.get_project_link_args(linker, target.subproject, target.is_cross) # Add link args added using add_global_link_arguments() # These override per-project link arguments - commands += self.build.get_global_link_args(linker) + commands += self.build.get_global_link_args(linker, target.is_cross) if not target.is_cross: # Link args added from the env: LDFLAGS. We want these to # override all the defaults but not the per-target link args. diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 4c799d0..2e86ca9 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -998,10 +998,10 @@ class Vs2010Backend(backends.Backend): options = self.environment.coredata.base_options extra_link_args += compiler.get_std_shared_module_link_args(options) # Add link args added using add_project_link_arguments() - extra_link_args += self.build.get_project_link_args(compiler, target.subproject) + extra_link_args += self.build.get_project_link_args(compiler, target.subproject, target.is_cross) # Add link args added using add_global_link_arguments() # These override per-project link arguments - extra_link_args += self.build.get_global_link_args(compiler) + extra_link_args += self.build.get_global_link_args(compiler, target.is_cross) if not target.is_cross: # Link args added from the env: LDFLAGS. We want these to # override all the defaults but not the per-target link args. diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 96c64b8..b34ae2f 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -108,6 +108,10 @@ class Build: self.projects_args = {} self.global_link_args = {} self.projects_link_args = {} + self.cross_global_args = {} + self.cross_projects_args = {} + self.cross_global_link_args = {} + self.cross_projects_link_args = {} self.tests = [] self.benchmarks = [] self.headers = [] @@ -168,20 +172,25 @@ class Build: def get_install_subdirs(self): return self.install_dirs - def get_global_args(self, compiler): - return self.global_args.get(compiler.get_language(), []) + def get_global_args(self, compiler, for_cross): + d = self.cross_global_args if for_cross else self.global_args + return d.get(compiler.get_language(), []) - def get_project_args(self, compiler, project): - args = self.projects_args.get(project) + def get_project_args(self, compiler, project, for_cross): + d = self.cross_projects_args if for_cross else self.projects_args + args = d.get(project) if not args: return [] return args.get(compiler.get_language(), []) - def get_global_link_args(self, compiler): - return self.global_link_args.get(compiler.get_language(), []) + def get_global_link_args(self, compiler, for_cross): + d = self.cross_global_link_args if for_cross else self.global_link_args + return d.get(compiler.get_language(), []) - def get_project_link_args(self, compiler, project): - link_args = self.projects_link_args.get(project) + def get_project_link_args(self, compiler, project, for_cross): + d = self.cross_projects_link_args if for_cross else self.projects_link_args + + link_args = d.get(project) if not link_args: return [] diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index c7092b8..436f699 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -16,6 +16,8 @@ import re import glob import os.path import subprocess +import functools +import itertools from pathlib import Path from .. import mlog @@ -49,6 +51,7 @@ gnu_compiler_internal_libs = ('m', 'c', 'pthread', 'dl', 'rt') class CCompiler(Compiler): + # TODO: Replace this manual cache with functools.lru_cache library_dirs_cache = {} program_dirs_cache = {} find_library_cache = {} @@ -144,6 +147,30 @@ class CCompiler(Compiler): ''' return self.get_no_optimization_args() + def get_allow_undefined_link_args(self): + ''' + Get args for allowing undefined symbols when linking to a shared library + ''' + if self.id == 'clang': + if self.clang_type == compilers.CLANG_OSX: + # Apple ld + return ['-Wl,-undefined,dynamic_lookup'] + else: + # GNU ld and LLVM lld + return ['-Wl,--allow-shlib-undefined'] + elif self.id == 'gcc': + if self.gcc_type == compilers.GCC_OSX: + # Apple ld + return ['-Wl,-undefined,dynamic_lookup'] + else: + # GNU ld and LLVM lld + return ['-Wl,--allow-shlib-undefined'] + elif self.id == 'msvc': + # link.exe + return ['/FORCE:UNRESOLVED'] + # FIXME: implement other linkers + return [] + def get_output_args(self, target): return ['-o', target] @@ -172,42 +199,47 @@ class CCompiler(Compiler): def get_std_shared_lib_link_args(self): return ['-shared'] - def get_library_dirs_real(self): - env = os.environ.copy() - env['LC_ALL'] = 'C' - stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=env)[1] + @functools.lru_cache() + def _get_search_dirs(self, env): + extra_args = ['--print-search-dirs'] + stdo = None + with self._build_wrapper('', env, extra_args, None, 'compile', True) as p: + stdo = p.stdo + return stdo + + @staticmethod + def _split_fetch_real_dirs(pathstr, sep=':'): paths = [] - for line in stdo.split('\n'): - if line.startswith('libraries:'): - libstr = line.split('=', 1)[1] - paths = [os.path.realpath(p) for p in libstr.split(':') if os.path.exists(os.path.realpath(p))] + for p in pathstr.split(sep): + p = Path(p) + if p.exists(): + paths.append(p.resolve().as_posix()) return paths - def get_library_dirs(self): - key = tuple(self.exelist) + def get_compiler_dirs(self, env, name): + ''' + Get dirs from the compiler, either `libraries:` or `programs:` + ''' + stdo = self._get_search_dirs(env) + for line in stdo.split('\n'): + if line.startswith(name + ':'): + return CCompiler._split_fetch_real_dirs(line.split('=', 1)[1]) + return [] + + def get_library_dirs(self, env): + key = (tuple(self.exelist), env) if key not in self.library_dirs_cache: - self.library_dirs_cache[key] = self.get_library_dirs_real() + self.library_dirs_cache[key] = self.get_compiler_dirs(env, 'libraries') return self.library_dirs_cache[key][:] - def get_program_dirs_real(self): - env = os.environ.copy() - env['LC_ALL'] = 'C' - stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=env)[1] - paths = [] - for line in stdo.split('\n'): - if line.startswith('programs:'): - libstr = line.split('=', 1)[1] - paths = [os.path.realpath(p) for p in libstr.split(':')] - return paths - - def get_program_dirs(self): + def get_program_dirs(self, env): ''' Programs used by the compiler. Also where toolchain DLLs such as libstdc++-6.dll are found with MinGW. ''' - key = tuple(self.exelist) + key = (tuple(self.exelist), env) if key not in self.program_dirs_cache: - self.program_dirs_cache[key] = self.get_program_dirs_real() + self.program_dirs_cache[key] = self.get_compiler_dirs(env, 'programs') return self.program_dirs_cache[key][:] def get_pic_args(self): @@ -908,7 +940,8 @@ class CCompiler(Compiler): # Only try to find std libs if no extra dirs specified. if not extra_dirs or libname in self.internal_libs: args = ['-l' + libname] - if self.links(code, env, extra_args=args): + largs = self.linker_to_compiler_args(self.get_allow_undefined_link_args()) + if self.links(code, env, extra_args=(args + largs)): return args # Don't do a manual search for internal libs if libname in self.internal_libs: @@ -916,35 +949,19 @@ class CCompiler(Compiler): # Not found or we want to use a specific libtype? Try to find the # library file itself. patterns = self.get_library_naming(env, libtype) - for d in extra_dirs: + # Search in the specified dirs, and then in the system libraries + for d in itertools.chain(extra_dirs, self.get_library_dirs(env)): for p in patterns: trial = self._get_trials_from_pattern(p, d, libname) if not trial: continue + # We just check whether the library exists. We can't do a link + # check because the library might have unresolved symbols that + # require other libraries. trial = self._get_file_from_list(trial) if not trial: continue return [trial] - # Search in the system libraries too - for d in self.get_library_dirs(): - for p in patterns: - trial = self._get_trials_from_pattern(p, d, libname) - if not trial: - continue - trial = self._get_file_from_list(trial) - if not trial: - continue - # When searching the system paths used by the compiler, we - # need to check linking with link-whole, as static libs - # (.a) need to be checked to ensure they are the right - # architecture, e.g. 32bit or 64-bit. - # Just a normal test link won't work as the .a file doesn't - # seem to be checked by linker if there are no unresolved - # symbols from the main C file. - extra_link_args = self.get_link_whole_for([trial]) - extra_link_args = self.linker_to_compiler_args(extra_link_args) - if self.links(code, env, extra_args=extra_link_args): - return [trial] return None def find_library_impl(self, libname, env, extra_dirs, code, libtype): @@ -1230,7 +1247,10 @@ class VisualStudioCCompiler(CCompiler): return ['/MDd'] def get_buildtype_args(self, buildtype): - return compilers.msvc_buildtype_args[buildtype] + args = compilers.msvc_buildtype_args[buildtype] + if version_compare(self.version, '<18.0'): + args = [arg for arg in args if arg != '/Gw'] + return args def get_buildtype_linker_args(self, buildtype): return compilers.msvc_buildtype_linker_args[buildtype] diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 47c222d..352c49e 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -145,8 +145,8 @@ arm_buildtype_args = {'plain': [], msvc_buildtype_args = {'plain': [], 'debug': ["/ZI", "/Ob0", "/Od", "/RTC1"], 'debugoptimized': ["/Zi", "/Ob1"], - 'release': ["/Ob2"], - 'minsize': ["/Zi", "/Ob1"], + 'release': ["/Ob2", "/Gw"], + 'minsize': ["/Zi", "/Gw"], } apple_buildtype_linker_args = {'plain': [], @@ -267,7 +267,7 @@ msvc_optimization_args = {'0': [], '1': ['/O1'], '2': ['/O2'], '3': ['/O3'], - 's': ['/Os'], + 's': ['/O1'], # Implies /Os. } clike_debug_args = {False: [], @@ -611,27 +611,32 @@ class CompilerArgs(list): return True return False - def to_native(self): + def to_native(self, copy=False): # Check if we need to add --start/end-group for circular dependencies # between static libraries, and for recursively searching for symbols # needed by static libraries that are provided by object files or # shared libraries. + if copy: + new = self.copy() + else: + new = self if get_compiler_uses_gnuld(self.compiler): global soregex group_start = -1 - for each in self: + group_end = -1 + for i, each in enumerate(new): if not each.startswith('-l') and not each.endswith('.a') and \ not soregex.match(each): continue - i = self.index(each) + group_end = i if group_start < 0: # First occurrence of a library group_start = i if group_start >= 0: # Last occurrence of a library - self.insert(i + 1, '-Wl,--end-group') - self.insert(group_start, '-Wl,--start-group') - return self.compiler.unix_args_to_native(self) + new.insert(group_end + 1, '-Wl,--end-group') + new.insert(group_start, '-Wl,--start-group') + return self.compiler.unix_args_to_native(new) def append_direct(self, arg): ''' @@ -867,11 +872,11 @@ class Compiler: self.language + '_args': coredata.UserArrayOption( self.language + '_args', description + ' compiler', - compile_args, shlex_split=True, user_input=True), + compile_args, shlex_split=True, user_input=True, allow_dups=True), self.language + '_link_args': coredata.UserArrayOption( self.language + '_link_args', description + ' linker', - link_args, shlex_split=True, user_input=True), + link_args, shlex_split=True, user_input=True, allow_dups=True), }) return opts @@ -917,7 +922,7 @@ class Compiler: def find_library(self, *args, **kwargs): raise EnvironmentException('Language {} does not support library finding.'.format(self.get_display_language())) - def get_library_dirs(self): + def get_library_dirs(self, *args, **kwargs): return [] def has_multi_arguments(self, args, env): @@ -1004,7 +1009,9 @@ class Compiler: mlog.debug('Working directory: ', tmpdirname) mlog.debug('Command line: ', ' '.join(commands), '\n') mlog.debug('Code:\n', code) - p, p.stdo, p.stde = Popen_safe(commands, cwd=tmpdirname) + os_env = os.environ.copy() + os_env['LC_ALL'] = 'C' + p, p.stdo, p.stde = Popen_safe(commands, cwd=tmpdirname, env=os_env) mlog.debug('Compiler stdout:\n', p.stdo) mlog.debug('Compiler stderr:\n', p.stde) p.commands = commands @@ -1362,10 +1369,12 @@ class ElbrusCompiler(GnuCompiler): 'b_ndebug', 'b_staticpic', 'b_lundef', 'b_asneeded'] - def get_library_dirs(self): - env = os.environ.copy() - env['LC_ALL'] = 'C' - stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=env)[1] + # FIXME: use _build_wrapper to call this so that linker flags from the env + # get applied + def get_library_dirs(self, env): + os_env = os.environ.copy() + os_env['LC_ALL'] = 'C' + stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=os_env)[1] paths = [] for line in stdo.split('\n'): if line.startswith('libraries:'): @@ -1375,10 +1384,10 @@ class ElbrusCompiler(GnuCompiler): break return paths - def get_program_dirs(self): - env = os.environ.copy() - env['LC_ALL'] = 'C' - stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=env)[1] + def get_program_dirs(self, env): + os_env = os.environ.copy() + os_env['LC_ALL'] = 'C' + stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=os_env)[1] paths = [] for line in stdo.split('\n'): if line.startswith('programs:'): diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 7d682ec..a03af3e 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -269,7 +269,7 @@ class DCompiler(Compiler): continue dcargs.append('-L' + la.strip()) continue - elif arg.startswith('-install-name'): + elif arg.startswith('-install_name'): dcargs.append('-L' + arg) continue elif arg.startswith('-link-defaultlib') or arg.startswith('-linker'): diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 5648a6f..1ee1fcb 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -144,6 +144,9 @@ end program prog def get_compiler_check_args(self): return CCompiler.get_compiler_check_args(self) + def get_allow_undefined_link_args(self): + return CCompiler.get_allow_undefined_link_args(self) + def get_output_args(self, target): return CCompiler.get_output_args(self, target) @@ -177,11 +180,14 @@ end program prog def get_std_shared_lib_link_args(self): return CCompiler.get_std_shared_lib_link_args(self) - def get_library_dirs_real(self): - return CCompiler.get_library_dirs_real(self) + def _get_search_dirs(self, *args, **kwargs): + return CCompiler._get_search_dirs(self, *args, **kwargs) + + def get_compiler_dirs(self, *args, **kwargs): + return CCompiler.get_compiler_dirs(self, *args, **kwargs) - def get_library_dirs(self): - return CCompiler.get_library_dirs(self) + def get_library_dirs(self, *args, **kwargs): + return CCompiler.get_library_dirs(self, *args, **kwargs) def get_pic_args(self): return CCompiler.get_pic_args(self) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 7520e06..4b32c19 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -19,6 +19,7 @@ from pathlib import PurePath from collections import OrderedDict from .mesonlib import MesonException from .mesonlib import default_libdir, default_libexecdir, default_prefix +from .wrap import WrapMode import ast import argparse @@ -138,9 +139,10 @@ class UserComboOption(UserOption): return value class UserArrayOption(UserOption): - def __init__(self, name, description, value, shlex_split=False, user_input=False, **kwargs): + def __init__(self, name, description, value, shlex_split=False, user_input=False, allow_dups=False, **kwargs): super().__init__(name, description, kwargs.get('choices', []), yielding=kwargs.get('yielding', None)) self.shlex_split = shlex_split + self.allow_dups = allow_dups self.value = self.validate_value(value, user_input=user_input) def validate_value(self, value, user_input=True): @@ -166,7 +168,7 @@ class UserArrayOption(UserOption): else: raise MesonException('"{0}" should be a string array, but it is not'.format(str(newvalue))) - if len(set(newvalue)) != len(newvalue): + if not self.allow_dups and len(set(newvalue)) != len(newvalue): msg = 'Duplicated values in array option "%s" is deprecated. ' \ 'This will become a hard error in the future.' % (self.name) mlog.deprecation(msg) @@ -221,7 +223,7 @@ class CoreData: self.base_options = {} self.external_preprocess_args = {} # CPPFLAGS only self.cross_file = self.__load_cross_file(options.cross_file) - self.wrap_mode = options.wrap_mode + self.wrap_mode = options.wrap_mode if options.wrap_mode is not None else WrapMode.default self.compilers = OrderedDict() self.cross_compilers = OrderedDict() self.deps = OrderedDict() diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 20ba06c..04c4701 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -98,7 +98,7 @@ class Dependency: def __init__(self, type_name, kwargs): self.name = "null" - self.version = 'none' + self.version = None self.language = None # None means C-like self.is_found = False self.type_name = type_name @@ -138,7 +138,10 @@ class Dependency: return self.name def get_version(self): - return self.version + if self.version: + return self.version + else: + return 'unknown' def get_exe_args(self, compiler): return [] @@ -218,6 +221,8 @@ class ExternalDependency(Dependency): self.is_found = False self.language = language self.version_reqs = kwargs.get('version', None) + if isinstance(self.version_reqs, str): + self.version_reqs = [self.version_reqs] self.required = kwargs.get('required', True) self.silent = kwargs.get('silent', False) self.static = kwargs.get('static', False) @@ -275,6 +280,41 @@ class ExternalDependency(Dependency): def log_tried(self): return '' + # Check if dependency version meets the requirements + def _check_version(self): + if not self.is_found: + return + + if self.version_reqs: + # an unknown version can never satisfy any requirement + if not self.version: + found_msg = ['Dependency', mlog.bold(self.name), 'found:'] + found_msg += [mlog.red('NO'), 'unknown version, but need:', + self.version_reqs] + mlog.log(*found_msg) + + if self.required: + m = 'Unknown version of dependency {!r}, but need {!r}.' + raise DependencyException(m.format(self.name, self.version_reqs)) + + else: + (self.is_found, not_found, found) = \ + version_compare_many(self.version, self.version_reqs) + if not self.is_found: + found_msg = ['Dependency', mlog.bold(self.name), 'found:'] + found_msg += [mlog.red('NO'), + 'found {!r} but need:'.format(self.version), + ', '.join(["'{}'".format(e) for e in not_found])] + if found: + found_msg += ['; matched:', + ', '.join(["'{}'".format(e) for e in found])] + mlog.log(*found_msg) + + if self.required: + m = 'Invalid version of dependency, need {!r} {!r} found {!r}.' + raise DependencyException(m.format(self.name, not_found, self.version)) + return + class NotFoundDependency(Dependency): def __init__(self, environment): @@ -375,7 +415,7 @@ class ConfigToolDependency(ExternalDependency): # don't fail with --version, in that case just assume that there is # only one version and return it. if not out: - return (tool, 'none') + return (tool, None) if versions: is_found = version_compare_many(out, versions)[0] # This allows returning a found version without a config tool, @@ -480,20 +520,6 @@ class PkgConfigDependency(ExternalDependency): ret, self.version = self._call_pkgbin(['--modversion', name]) if ret != 0: return - if self.version_reqs is None: - self.is_found = True - else: - if not isinstance(self.version_reqs, (str, list)): - raise DependencyException('Version argument must be string or list.') - if isinstance(self.version_reqs, str): - self.version_reqs = [self.version_reqs] - (self.is_found, not_found, found) = \ - version_compare_many(self.version, self.version_reqs) - if not self.is_found: - if self.required: - m = 'Invalid version of dependency, need {!r} {!r} found {!r}.' - raise DependencyException(m.format(name, not_found, self.version)) - return try: # Fetch cargs to be used while using this dependency @@ -509,6 +535,8 @@ class PkgConfigDependency(ExternalDependency): self.is_found = False self.reason = e + self.is_found = True + def __repr__(self): s = '<{0} {1}: {2} {3}>' return s.format(self.__class__.__name__, self.name, self.is_found, @@ -576,29 +604,59 @@ class PkgConfigDependency(ExternalDependency): self.compile_args = self._convert_mingw_paths(shlex.split(out)) def _search_libs(self, out, out_raw): - link_args = [] - raw_link_args = [] + ''' + @out: PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 pkg-config --libs + @out_raw: pkg-config --libs + + We always look for the file ourselves instead of depending on the + compiler to find it with -lfoo or foo.lib (if possible) because: + 1. We want to be able to select static or shared + 2. We need the full path of the library to calculate RPATH values + 3. De-dup of libraries is easier when we have absolute paths + + Libraries that are provided by the toolchain or are not found by + find_library() will be added with -L -l pairs. + ''' # Library paths should be safe to de-dup - libpaths = OrderedSet() - raw_libpaths = OrderedSet() + # + # First, figure out what library paths to use. Originally, we were + # doing this as part of the loop, but due to differences in the order + # of -L values between pkg-config and pkgconf, we need to do that as + # a separate step. See: + # https://github.com/mesonbuild/meson/issues/3951 + # https://github.com/mesonbuild/meson/issues/4023 + # + # Separate system and prefix paths, and ensure that prefix paths are + # always searched first. + prefix_libpaths = OrderedSet() + # We also store this raw_link_args on the object later + raw_link_args = self._convert_mingw_paths(shlex.split(out_raw)) + for arg in raw_link_args: + if arg.startswith('-L') and not arg.startswith(('-L-l', '-L-L')): + prefix_libpaths.add(arg[2:]) + system_libpaths = OrderedSet() + full_args = self._convert_mingw_paths(shlex.split(out)) + for arg in full_args: + if arg.startswith(('-L-l', '-L-L')): + # These are D language arguments, not library paths + continue + if arg.startswith('-L') and arg[2:] not in prefix_libpaths: + system_libpaths.add(arg[2:]) + # Use this re-ordered path list for library resolution + libpaths = list(prefix_libpaths) + list(system_libpaths) # Track -lfoo libraries to avoid duplicate work libs_found = OrderedSet() # Track not-found libraries to know whether to add library paths libs_notfound = [] libtype = 'static' if self.static else 'default' - # We always look for the file ourselves instead of depending on the - # compiler to find it with -lfoo or foo.lib (if possible) because: - # 1. We want to be able to select static or shared - # 2. We need the full path of the library to calculate RPATH values - # - # Libraries that are provided by the toolchain or are not found by - # find_library() will be added with -L -l pairs. - for lib in self._convert_mingw_paths(shlex.split(out)): + # Generate link arguments for this library + link_args = [] + for lib in full_args: if lib.startswith(('-L-l', '-L-L')): # These are D language arguments, add them as-is pass elif lib.startswith('-L'): - libpaths.add(lib[2:]) + # We already handled library paths above continue elif lib.startswith('-l'): # Don't resolve the same -lfoo argument again @@ -606,7 +664,7 @@ class PkgConfigDependency(ExternalDependency): continue if self.clib_compiler: args = self.clib_compiler.find_library(lib[2:], self.env, - list(reversed(libpaths)), libtype) + libpaths, libtype) # If the project only uses a non-clib language such as D, Rust, # C#, Python, etc, all we can do is limp along by adding the # arguments as-is and then adding the libpaths at the end. @@ -650,16 +708,11 @@ class PkgConfigDependency(ExternalDependency): if lib in link_args: continue link_args.append(lib) - # Also store the raw link arguments, and store raw_libpaths - for lib in self._convert_mingw_paths(shlex.split(out_raw)): - if lib.startswith('-L') and not lib.startswith(('-L-l', '-L-L')): - raw_libpaths.add(lib[2:]) - raw_link_args.append(lib) # Add all -Lbar args if we have -lfoo args in link_args if libs_notfound: # Order of -L flags doesn't matter with ld, but it might with other # linkers such as MSVC, so prepend them. - link_args = ['-L' + lp for lp in raw_libpaths] + link_args + link_args = ['-L' + lp for lp in prefix_libpaths] + link_args return link_args, raw_link_args def _set_libs(self): @@ -837,22 +890,6 @@ class DubDependency(ExternalDependency): self.pkg = package break - # Check if package version meets the requirements - if self.version_reqs is None: - self.is_found = True - else: - if not isinstance(self.version_reqs, (str, list)): - raise DependencyException('Version argument must be string or list.') - if isinstance(self.version_reqs, str): - self.version_reqs = [self.version_reqs] - (self.is_found, not_found, found) = \ - version_compare_many(self.version, self.version_reqs) - if not self.is_found: - if self.required: - m = 'Invalid version of dependency, need {!r} {!r} found {!r}.' - raise DependencyException(m.format(name, not_found, self.version)) - return - if self.pkg['targetFileName'].endswith('.a'): self.static = True @@ -872,9 +909,7 @@ class DubDependency(ExternalDependency): for file in res: self.link_args.append(file) - if not found: - self.is_found = False - return + self.is_found = found def get_compiler(self): return self.compiler @@ -1216,9 +1251,6 @@ class ExtraFrameworkDependency(ExternalDependency): self.is_found = True return - def get_version(self): - return 'unknown' - def log_info(self): return os.path.join(self.path, self.name) @@ -1266,6 +1298,8 @@ def find_external_dependency(name, env, kwargs): lname = name.lower() if lname not in _packages_accept_language and 'language' in kwargs: raise DependencyException('%s dependency does not accept "language" keyword argument' % (name, )) + if not isinstance(kwargs.get('version', ''), (str, list)): + raise DependencyException('Keyword "Version" must be string or list.') # display the dependency name with correct casing display_name = display_name_map.get(lname, lname) @@ -1288,6 +1322,7 @@ def find_external_dependency(name, env, kwargs): # try this dependency method try: d = c() + d._check_version() pkgdep.append(d) except Exception as e: mlog.debug(str(e)) @@ -1308,7 +1343,7 @@ def find_external_dependency(name, env, kwargs): if info: info = ', ' + info - mlog.log(type_text, mlog.bold(display_name), details + 'found:', mlog.green('YES'), d.version + info) + mlog.log(type_text, mlog.bold(display_name), details + 'found:', mlog.green('YES'), (d.version if d.version else '') + info) return d diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py index 17f9240..b06f62d 100644 --- a/mesonbuild/dependencies/boost.py +++ b/mesonbuild/dependencies/boost.py @@ -443,7 +443,7 @@ class BoostDependency(ExternalDependency): if self.libdir: libdirs = [self.libdir] elif self.boost_root is None: - libdirs = mesonlib.get_library_dirs() + libdirs = mesonlib.get_library_dirs(self.env) else: libdirs = [os.path.join(self.boost_root, 'lib')] for libdir in libdirs: diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index 0cd3c2b..0876391 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -35,7 +35,6 @@ class GTestDependency(ExternalDependency): self.detect() def detect(self): - self.version = '1.something_maybe' gtest_detect = self.clib_compiler.find_library("gtest", self.env, []) gtest_main_detect = self.clib_compiler.find_library("gtest_main", self.env, []) if gtest_detect and (not self.main or gtest_main_detect): @@ -85,7 +84,6 @@ class GTestDependency(ExternalDependency): class GMockDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('gmock', environment, 'cpp', kwargs) - self.version = '1.something_maybe' # GMock may be a library or just source. # Work with both. gmock_detect = self.clib_compiler.find_library("gmock", self.env, []) diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 78ce51b..014be84 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -180,7 +180,7 @@ class MPIDependency(ExternalDependency): if version: version = version.group(0) else: - version = 'none' + version = None return version, cargs, libs @@ -197,7 +197,7 @@ class MPIDependency(ExternalDependency): return args = shlex.split(o) - version = 'none' + version = None return version, args, args @@ -222,11 +222,11 @@ class MPIDependency(ExternalDependency): else: return if self.language == 'fortran': - return ('none', + return (None, ['-I' + incdir, '-I' + os.path.join(incdir, post)], [os.path.join(libdir, 'msmpi.lib'), os.path.join(libdir, 'msmpifec.lib')]) else: - return ('none', + return (None, ['-I' + incdir, '-I' + os.path.join(incdir, post)], [os.path.join(libdir, 'msmpi.lib')]) @@ -267,17 +267,13 @@ class OpenMPDependency(ExternalDependency): class ThreadDependency(ExternalDependency): def __init__(self, environment, kwargs): - super().__init__('threads', environment, None, {}) + super().__init__('threads', environment, None, kwargs) self.name = 'threads' self.is_found = True - mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES')) def need_threads(self): return True - def get_version(self): - return 'unknown' - class Python3Dependency(ExternalDependency): def __init__(self, environment, kwargs): @@ -447,8 +443,11 @@ class PcapDependency(ExternalDependency): @staticmethod def get_pcap_lib_version(ctdep): - return ctdep.clib_compiler.get_return_value('pcap_lib_version', 'string', - '#include <pcap.h>', ctdep.env, [], [ctdep]) + v = ctdep.clib_compiler.get_return_value('pcap_lib_version', 'string', + '#include <pcap.h>', ctdep.env, [], [ctdep]) + v = re.sub(r'libpcap version ', '', v) + v = re.sub(r' -- Apple version.*$', '', v) + return v class CupsDependency(ExternalDependency): diff --git a/mesonbuild/dependencies/platform.py b/mesonbuild/dependencies/platform.py index 5f89ccb..0c05156 100644 --- a/mesonbuild/dependencies/platform.py +++ b/mesonbuild/dependencies/platform.py @@ -33,8 +33,4 @@ class AppleFrameworks(ExternalDependency): for f in self.frameworks: self.link_args += ['-framework', f] - def found(self): - return mesonlib.is_osx() - - def get_version(self): - return 'unknown' + self.is_found = mesonlib.is_osx() diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index c877f51..f19a76d 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -44,14 +44,12 @@ class GLDependency(ExternalDependency): # FIXME: Use AppleFrameworks dependency self.link_args = ['-framework', 'OpenGL'] # FIXME: Detect version using self.clib_compiler - self.version = '1' return if mesonlib.is_windows(): self.is_found = True # FIXME: Use self.clib_compiler.find_library() self.link_args = ['-lopengl32'] # FIXME: Detect version using self.clib_compiler - self.version = '1' return @classmethod @@ -63,7 +61,7 @@ class GLDependency(ExternalDependency): candidates.append(functools.partial(PkgConfigDependency, 'gl', environment, kwargs)) if DependencyMethods.SYSTEM in methods: - candidates.append(functools.partial(GLDependency), environment, kwargs) + candidates.append(functools.partial(GLDependency, environment, kwargs)) return candidates @@ -224,7 +222,7 @@ class QtBaseDependency(ExternalDependency): self.compile_args = [] self.link_args = [] self.from_text = mlog.format_list(methods) - self.version = 'none' + self.version = None def compilers_detect(self): "Detect Qt (4 or 5) moc, uic, rcc in the specified bindir or in PATH" @@ -557,7 +555,6 @@ class VulkanDependency(ExternalDependency): # TODO: find a way to retrieve the version from the sdk? # Usually it is a part of the path to it (but does not have to be) - self.version = '1' return else: # simply try to guess it, usually works on linux @@ -565,7 +562,6 @@ class VulkanDependency(ExternalDependency): if libs is not None and self.clib_compiler.has_header('vulkan/vulkan.h', '', environment): self.type_name = 'system' self.is_found = True - self.version = 1 # TODO for lib in libs: self.link_args.append(lib) return diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 9777540..38f66e9 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -814,6 +814,12 @@ This is probably wrong, it should always point to the native compiler.''' % evar # up to date language version at time (2016). if 'DC' in os.environ: exelist = shlex.split(os.environ['DC']) + for dc in exelist[:]: + if os.path.basename(dc).startswith(('ldmd', 'gdmd')): + mlog.log('Meson doesn\'t support', mlog.bold(dc), 'as it\'s only a DMD frontend for another compiler, skipping.') + exelist.remove(dc) + if not exelist: + raise EnvironmentException('Couldn\'t find any compatible D compiler in the DC environment variable. Please provide a valid value for DC or unset it so that Meson resolve the compiler by itself.') elif self.is_cross_build() and want_cross: exelist = mesonlib.stringlistify(self.cross_info.config['binaries']['d']) is_cross = True diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index c0f3745..e001232 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -383,6 +383,9 @@ class DependencyHolder(InterpreterObject, ObjectHolder): 'partial_dependency': self.partial_dependency_method, }) + def found(self): + return self.found_method([], {}) + @noPosargs @permittedKwargs({}) def type_name_method(self, args, kwargs): @@ -916,16 +919,30 @@ class Test(InterpreterObject): class SubprojectHolder(InterpreterObject, ObjectHolder): - def __init__(self, subinterpreter): + def __init__(self, subinterpreter, subproject_dir, name): InterpreterObject.__init__(self) ObjectHolder.__init__(self, subinterpreter) + self.name = name + self.subproject_dir = subproject_dir self.methods.update({'get_variable': self.get_variable_method, + 'found': self.found_method, }) + @noPosargs + @permittedKwargs({}) + def found_method(self, args, kwargs): + return self.found() + + def found(self): + return self.held_object is not None + @permittedKwargs({}) def get_variable_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Get_variable takes one argument.') + if not self.found(): + raise InterpreterException('Subproject "%s/%s" disabled can\'t get_variable on it.' % ( + self.subproject_dir, self.name)) varname = args[0] if not isinstance(varname, str): raise InterpreterException('Get_variable takes a string argument.') @@ -1808,11 +1825,11 @@ known_build_target_kwargs = ( {'target_type'} ) -permitted_kwargs = {'add_global_arguments': {'language'}, - 'add_global_link_arguments': {'language'}, - 'add_project_link_arguments': {'language'}, +permitted_kwargs = {'add_global_arguments': {'language', 'native'}, + 'add_global_link_arguments': {'language', 'native'}, 'add_languages': {'required'}, - 'add_project_arguments': {'language'}, + 'add_project_link_arguments': {'language', 'native'}, + 'add_project_arguments': {'language', 'native'}, 'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'}, 'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'}, 'build_target': known_build_target_kwargs, @@ -1838,7 +1855,7 @@ permitted_kwargs = {'add_global_arguments': {'language'}, 'both_libraries': known_library_kwargs, 'library': known_library_kwargs, 'subdir': {'if_found'}, - 'subproject': {'version', 'default_options'}, + 'subproject': {'version', 'default_options', 'required'}, 'test': {'args', 'depends', 'env', 'is_parallel', 'should_fail', 'timeout', 'workdir', 'suite'}, 'vcs_tag': {'input', 'output', 'fallback', 'command', 'replace_string'}, } @@ -2219,7 +2236,16 @@ external dependencies (including libraries) must go to "dependencies".''') dirname = args[0] return self.do_subproject(dirname, kwargs) + def disabled_subproject(self, dirname): + self.subprojects[dirname] = SubprojectHolder(None, self.subproject_dir, dirname) + return self.subprojects[dirname] + def do_subproject(self, dirname, kwargs): + disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) + if disabled: + mlog.log('\nSubproject', mlog.bold(dirname), ':', 'skipped: feature', mlog.bold(feature), 'disabled') + return self.disabled_subproject(dirname) + default_options = mesonlib.stringlistify(kwargs.get('default_options', [])) default_options = coredata.create_options_dict(default_options) if dirname == '': @@ -2237,7 +2263,13 @@ external dependencies (including libraries) must go to "dependencies".''') incpath = ' => '.join(fullstack) raise InvalidCode('Recursive include of subprojects: %s.' % incpath) if dirname in self.subprojects: - return self.subprojects[dirname] + subproject = self.subprojects[dirname] + + if required and not subproject.found(): + raise InterpreterException('Subproject "%s/%s" required but not found.' % ( + self.subproject_dir, dirname)) + + return subproject subproject_dir_abs = os.path.join(self.environment.get_source_dir(), self.subproject_dir) r = wrap.Resolver(subproject_dir_abs, self.coredata.wrap_mode) try: @@ -2249,22 +2281,35 @@ external dependencies (including libraries) must go to "dependencies".''') # promotion... self.print_nested_info(dirname) - msg = 'Subproject directory {!r} does not exist and cannot be downloaded:\n{}' - raise InterpreterException(msg.format(os.path.join(self.subproject_dir, dirname), e)) + if required: + msg = 'Subproject directory {!r} does not exist and cannot be downloaded:\n{}' + raise InterpreterException(msg.format(os.path.join(self.subproject_dir, dirname), e)) + + mlog.log('\nSubproject ', mlog.bold(dirname), 'is buildable:', mlog.red('NO'), '(disabling)\n') + return self.disabled_subproject(dirname) + subdir = os.path.join(self.subproject_dir, resolved) os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True) self.global_args_frozen = True mlog.log() with mlog.nested(): - mlog.log('\nExecuting subproject', mlog.bold(dirname), '\n') - subi = Interpreter(self.build, self.backend, dirname, subdir, self.subproject_dir, - self.modules, default_options) - subi.subprojects = self.subprojects - - subi.subproject_stack = self.subproject_stack + [dirname] - current_active = self.active_projectname - subi.run() - mlog.log('\nSubproject', mlog.bold(dirname), 'finished.') + try: + mlog.log('\nExecuting subproject', mlog.bold(dirname), '\n') + subi = Interpreter(self.build, self.backend, dirname, subdir, self.subproject_dir, + self.modules, default_options) + subi.subprojects = self.subprojects + + subi.subproject_stack = self.subproject_stack + [dirname] + current_active = self.active_projectname + subi.run() + mlog.log('\nSubproject', mlog.bold(dirname), 'finished.') + except Exception as e: + if not required: + mlog.log(e) + mlog.log('\nSubproject', mlog.bold(dirname), 'is buildable:', mlog.red('NO'), '(disabling)') + return self.disabled_subproject(dirname) + else: + raise e if 'version' in kwargs: pv = subi.project_version @@ -2274,26 +2319,24 @@ external dependencies (including libraries) must go to "dependencies".''') self.active_projectname = current_active self.build.subprojects[dirname] = subi.project_version self.subprojects.update(subi.subprojects) - self.subprojects[dirname] = SubprojectHolder(subi) + self.subprojects[dirname] = SubprojectHolder(subi, self.subproject_dir, dirname) self.build_def_files += subi.build_def_files return self.subprojects[dirname] def get_option_internal(self, optname): + # Some base options are not defined in some environments, return the + # default value from compilers.base_options in that case. + for d in [self.coredata.base_options, compilers.base_options, + self.coredata.builtins, self.coredata.compiler_options]: + try: + return d[optname] + except KeyError: + pass + raw_optname = optname - try: - return self.coredata.base_options[optname] - except KeyError: - pass - try: - return self.coredata.builtins[optname] - except KeyError: - pass - try: - return self.coredata.compiler_options[optname] - except KeyError: - pass - if not coredata.is_builtin_option(optname) and self.is_subproject(): + if self.is_subproject(): optname = self.subproject + ':' + optname + try: opt = self.coredata.user_options[optname] if opt.yielding and ':' in optname and raw_optname in self.coredata.user_options: @@ -2313,11 +2356,7 @@ external dependencies (including libraries) must go to "dependencies".''') return opt except KeyError: pass - # Some base options are not defined in some environments, return the default value. - try: - return compilers.base_options[optname] - except KeyError: - pass + raise InterpreterException('Tried to access unknown option "%s".' % optname) @stringArgs @@ -2805,6 +2844,13 @@ external dependencies (including libraries) must go to "dependencies".''') def get_subproject_dep(self, name, dirname, varname, required): try: + subproject = self.subprojects[dirname] + if not subproject.found(): + if not required: + return DependencyHolder(NotFoundDependency(self.environment), self.subproject) + + raise DependencyException('Subproject %s was not found.' % (name)) + dep = self.subprojects[dirname].get_variable_method([varname], {}) except InvalidArguments as e: if required: @@ -2826,9 +2872,12 @@ external dependencies (including libraries) must go to "dependencies".''') dep = self.get_subproject_dep(name, dirname, varname, required) if not dep: return False + if not dep.found(): + return dep + found = dep.version_method([], {}) # Don't do a version check if the dependency is not found and not required - if found == 'none' and not required: + if not dep.found_method([], {}) and not required: subproj_path = os.path.join(self.subproject_dir, dirname) mlog.log('Dependency', mlog.bold(name), 'from subproject', mlog.bold(subproj_path), 'found:', mlog.red('NO'), '(cached)') @@ -3653,25 +3702,45 @@ different subdirectory. timeout_multiplier=timeout_multiplier, env=env) + def get_argdict_on_crossness(self, native_dict, cross_dict, kwargs): + for_native = kwargs.get('native', not self.environment.is_cross_build()) + if not isinstance(for_native, bool): + raise InterpreterException('Keyword native must be a boolean.') + if for_native: + return native_dict + else: + return cross_dict + @permittedKwargs(permitted_kwargs['add_global_arguments']) @stringArgs def func_add_global_arguments(self, node, args, kwargs): - self.add_global_arguments(node, self.build.global_args, args, kwargs) + argdict = self.get_argdict_on_crossness(self.build.global_args, + self.build.cross_global_args, + kwargs) + self.add_global_arguments(node, argdict, args, kwargs) @permittedKwargs(permitted_kwargs['add_global_link_arguments']) @stringArgs def func_add_global_link_arguments(self, node, args, kwargs): - self.add_global_arguments(node, self.build.global_link_args, args, kwargs) + argdict = self.get_argdict_on_crossness(self.build.global_link_args, + self.build.cross_global_link_args, + kwargs) + self.add_global_arguments(node, argdict, args, kwargs) @permittedKwargs(permitted_kwargs['add_project_arguments']) @stringArgs def func_add_project_arguments(self, node, args, kwargs): - self.add_project_arguments(node, self.build.projects_args, args, kwargs) + argdict = self.get_argdict_on_crossness(self.build.projects_args, + self.build.cross_projects_args, + kwargs) + self.add_project_arguments(node, argdict, args, kwargs) @permittedKwargs(permitted_kwargs['add_project_link_arguments']) @stringArgs def func_add_project_link_arguments(self, node, args, kwargs): - self.add_project_arguments(node, self.build.projects_link_args, args, kwargs) + argdict = self.get_argdict_on_crossness(self.build.projects_link_args, + self.build.cross_projects_link_args, kwargs) + self.add_project_arguments(node, argdict, args, kwargs) def add_global_arguments(self, node, argsdict, args, kwargs): if self.is_subproject(): diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 513c238..2fd69b0 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -13,7 +13,6 @@ # limitations under the License. import os -import sys import argparse from . import (coredata, mesonlib, build) @@ -172,7 +171,3 @@ def run(args): print('Meson configurator encountered an error:') raise e return 0 - - -if __name__ == '__main__': - sys.exit(run(sys.argv[1:])) diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 68a2ddb..e8b7b30 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -36,7 +36,7 @@ def create_parser(): p.add_argument('-v', '--version', action='version', version=coredata.version) # See the mesonlib.WrapMode enum for documentation - p.add_argument('--wrap-mode', default=WrapMode.default, + p.add_argument('--wrap-mode', default=None, type=wrapmodetype, choices=WrapMode, help='Special wrap mode to use') p.add_argument('--profile-self', action='store_true', dest='profile', @@ -71,18 +71,18 @@ class MesonApp: if not os.path.exists(ndir2): os.makedirs(ndir2) if not stat.S_ISDIR(os.stat(ndir1).st_mode): - raise RuntimeError('%s is not a directory' % dir1) + raise MesonException('%s is not a directory' % dir1) if not stat.S_ISDIR(os.stat(ndir2).st_mode): - raise RuntimeError('%s is not a directory' % dir2) + raise MesonException('%s is not a directory' % dir2) if os.path.samefile(dir1, dir2): - raise RuntimeError('Source and build directories must not be the same. Create a pristine build directory.') + raise MesonException('Source and build directories must not be the same. Create a pristine build directory.') if self.has_build_file(ndir1): if self.has_build_file(ndir2): - raise RuntimeError('Both directories contain a build file %s.' % environment.build_filename) + raise MesonException('Both directories contain a build file %s.' % environment.build_filename) return ndir1, ndir2 if self.has_build_file(ndir2): return ndir2, ndir1 - raise RuntimeError('Neither directory contains a build file %s.' % environment.build_filename) + raise MesonException('Neither directory contains a build file %s.' % environment.build_filename) def validate_dirs(self, dir1, dir2, handshake): (src_dir, build_dir) = self.validate_core_dirs(dir1, dir2) diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 748f06b..1d72179 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -27,6 +27,10 @@ except ImportError: # This is only used for pkexec which is not, so this is fine. main_file = None +symlink_warning = '''Warning: trying to copy a symlink that points to a file. This will copy the file, +but this will be changed in a future version of Meson to copy the symlink as is. Please update your +build definitions so that it will not break when the change happens.''' + selinux_updates = [] def buildparser(): @@ -242,7 +246,15 @@ class Installer: os.remove(to_file) print('Installing %s to %s' % (from_file, outdir)) if os.path.islink(from_file): - shutil.copy(from_file, outdir, follow_symlinks=False) + if not os.path.exists(from_file): + # Dangling symlink. Replicate as is. + shutil.copy(from_file, outdir, follow_symlinks=False) + else: + # Remove this entire branch when changing the behaviour to duplicate + # symlinks rather than copying what they point to. + print(symlink_warning) + shutil.copyfile(from_file, to_file) + shutil.copystat(from_file, to_file) else: shutil.copyfile(from_file, to_file) shutil.copystat(from_file, to_file) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 94bc00b..188459a 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -249,6 +249,3 @@ def run(args): print('No command specified') return 1 return 0 - -if __name__ == '__main__': - sys.exit(run(sys.argv[1:])) diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index 1654824..b8d3ccc 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -59,9 +59,12 @@ def set_timestamp_start(start): def shutdown(): global log_file if log_file is not None: + path = log_file.name exception_around_goer = log_file log_file = None exception_around_goer.close() + return path + return None class AnsiDecorator: plain_code = "\033[0m" diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index 19f3e2b..87209d6 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -88,35 +88,38 @@ class WindowsModule(ExtensionModule): if hasattr(src, 'held_object'): src = src.held_object - res_kwargs = { - 'output': '@BASENAME@.' + suffix, - 'input': [src], - 'command': [rescomp] + res_args, - 'depend_files': wrc_depend_files, - 'depends': wrc_depends, - } - if isinstance(src, str): - name = 'file {!r}'.format(os.path.join(state.subdir, src)) + name_format = 'file {!r}' + name = os.path.join(state.subdir, src) elif isinstance(src, mesonlib.File): - name = 'file {!r}'.format(src.relative_name()) + name_format = 'file {!r}' + name = src.relative_name() elif isinstance(src, build.CustomTarget): if len(src.get_outputs()) > 1: raise MesonException('windows.compile_resources does not accept custom targets with more than 1 output.') - name = 'target {!r}'.format(src.get_id()) + name_format = 'target {!r}' + name = src.get_id() else: raise MesonException('Unexpected source type {!r}. windows.compile_resources accepts only strings, files, custom targets, and lists thereof.'.format(src)) # Path separators are not allowed in target names name = name.replace('/', '_').replace('\\', '_') + res_kwargs = { + 'output': name + '_@BASENAME@.' + suffix, + 'input': [src], + 'command': [rescomp] + res_args, + 'depend_files': wrc_depend_files, + 'depends': wrc_depends, + } + # instruct binutils windres to generate a preprocessor depfile if comp.id != 'msvc': res_kwargs['depfile'] = res_kwargs['output'] + '.d' res_kwargs['command'] += ['--preprocessor-arg=-MD', '--preprocessor-arg=-MQ@OUTPUT@', '--preprocessor-arg=-MF@DEPFILE@'] - res_targets.append(build.CustomTarget('Windows resource for ' + name, state.subdir, state.subproject, res_kwargs)) + res_targets.append(build.CustomTarget('Windows resource for ' + name_format.format(name), state.subdir, state.subproject, res_kwargs)) add_target(args) diff --git a/mesonbuild/wrap/__init__.py b/mesonbuild/wrap/__init__.py index 6e2bc83..b792dfa 100644 --- a/mesonbuild/wrap/__init__.py +++ b/mesonbuild/wrap/__init__.py @@ -33,4 +33,11 @@ from enum import Enum # Note that these options do not affect subprojects that # are git submodules since those are only usable in git # repositories, and you almost always want to download them. -WrapMode = Enum('WrapMode', 'default nofallback nodownload forcefallback') +class WrapMode(Enum): + default = 1 + nofallback = 2 + nodownload = 3 + forcefallback = 4 + + def __str__(self): + return self.name diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index a3f8ab1..412097c 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -29,6 +29,7 @@ except ImportError: has_ssl = False API_ROOT = 'http://wrapdb.mesonbuild.com/v1/' +req_timeout = 600.0 ssl_warning_printed = False def build_ssl_context(): @@ -51,7 +52,7 @@ def open_wrapdburl(urlstring): global ssl_warning_printed if has_ssl: try: - return urllib.request.urlopen(urlstring)# , context=build_ssl_context()) + return urllib.request.urlopen(urlstring, timeout=req_timeout)# , context=build_ssl_context()) except urllib.error.URLError: if not ssl_warning_printed: print('SSL connection failed. Falling back to unencrypted connections.') @@ -64,7 +65,7 @@ def open_wrapdburl(urlstring): # certificate is not known. if urlstring.startswith('https'): urlstring = 'http' + urlstring[5:] - return urllib.request.urlopen(urlstring) + return urllib.request.urlopen(urlstring, timeout=req_timeout) class PackageDefinition: @@ -270,7 +271,7 @@ class Resolver: if url.startswith('https://wrapdb.mesonbuild.com'): resp = open_wrapdburl(url) else: - resp = urllib.request.urlopen(url) + resp = urllib.request.urlopen(url, timeout=req_timeout) with contextlib.closing(resp) as resp: try: dlsize = int(resp.info()['Content-Length']) diff --git a/mesonbuild/wrap/wraptool.py b/mesonbuild/wrap/wraptool.py index 570e691..364452d 100644 --- a/mesonbuild/wrap/wraptool.py +++ b/mesonbuild/wrap/wraptool.py @@ -213,6 +213,4 @@ def run(args): add_arguments(parser) options = parser.parse_args(args) options.wrap_func(options) - -if __name__ == '__main__': - sys.exit(run(sys.argv[1:])) + return 0 diff --git a/run_tests.py b/run_tests.py index 6e441d3..a5fd7a5 100755 --- a/run_tests.py +++ b/run_tests.py @@ -31,7 +31,32 @@ from mesonbuild import mesonlib from mesonbuild import mesonmain from mesonbuild import mtest from mesonbuild import mlog -from mesonbuild.environment import detect_ninja +from mesonbuild.environment import Environment, detect_ninja + + +# Fake classes and objects for mocking +class FakeBuild: + def __init__(self, env): + self.environment = env + +class FakeCompilerOptions: + def __init__(self): + self.value = [] + +def get_fake_options(prefix): + import argparse + opts = argparse.Namespace() + opts.cross_file = None + opts.wrap_mode = None + opts.prefix = prefix + opts.cmd_line_options = {} + return opts + +def get_fake_env(sdir, bdir, prefix): + env = Environment(sdir, bdir, get_fake_options(prefix)) + env.coredata.compiler_options['c_args'] = FakeCompilerOptions() + return env + Backend = Enum('Backend', 'ninja vs xcode') @@ -148,15 +173,6 @@ def ensure_backend_detects_changes(backend): if backend is Backend.ninja: time.sleep(1) -def get_fake_options(prefix): - import argparse - opts = argparse.Namespace() - opts.cross_file = None - opts.wrap_mode = None - opts.prefix = prefix - opts.cmd_line_options = {} - return opts - def run_mtest_inprocess(commandlist): old_stdout = sys.stdout sys.stdout = mystdout = StringIO() diff --git a/run_unittests.py b/run_unittests.py index 2e29af7..ebead66 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -41,24 +41,16 @@ from mesonbuild.mesonlib import ( windows_proof_rmtree, python_command, version_compare, grab_leading_numbers, BuildDirLock ) -from mesonbuild.environment import Environment, detect_ninja +from mesonbuild.environment import detect_ninja from mesonbuild.mesonlib import MesonException, EnvironmentException from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram import mesonbuild.modules.pkgconfig -from run_tests import exe_suffix, get_fake_options, get_meson_script +from run_tests import exe_suffix, get_fake_env, get_meson_script from run_tests import get_builddir_target_args, get_backend_commands, Backend from run_tests import ensure_backend_detects_changes, run_configure_inprocess from run_tests import run_mtest_inprocess - -# Fake classes for mocking -class FakeBuild: - def __init__(self, env): - self.environment = env - -class FakeCompilerOptions: - def __init__(self): - self.value = [] +from run_tests import FakeBuild, FakeCompilerOptions def get_dynamic_section_entry(fname, entry): if is_cygwin() or is_osx(): @@ -176,19 +168,19 @@ class InternalTests(unittest.TestCase): def test_compiler_args_class(self): cargsfunc = mesonbuild.compilers.CompilerArgs - c = mesonbuild.compilers.CCompiler([], 'fake', False) + cc = mesonbuild.compilers.CCompiler([], 'fake', False) # Test that bad initialization fails self.assertRaises(TypeError, cargsfunc, []) self.assertRaises(TypeError, cargsfunc, [], []) - self.assertRaises(TypeError, cargsfunc, c, [], []) + self.assertRaises(TypeError, cargsfunc, cc, [], []) # Test that empty initialization works - a = cargsfunc(c) + a = cargsfunc(cc) self.assertEqual(a, []) # Test that list initialization works - a = cargsfunc(['-I.', '-I..'], c) + a = cargsfunc(['-I.', '-I..'], cc) self.assertEqual(a, ['-I.', '-I..']) # Test that there is no de-dup on initialization - self.assertEqual(cargsfunc(['-I.', '-I.'], c), ['-I.', '-I.']) + self.assertEqual(cargsfunc(['-I.', '-I.'], cc), ['-I.', '-I.']) ## Test that appending works a.append('-I..') @@ -234,7 +226,7 @@ class InternalTests(unittest.TestCase): self.assertEqual(a, ['-Ibar', '-Ifoo', '-Ibaz', '-I..', '-I.', '-Ldir', '-Lbah', '-Werror', '-O3', '-O2', '-Wall']) ## Test that adding libraries works - l = cargsfunc(c, ['-Lfoodir', '-lfoo']) + l = cargsfunc(cc, ['-Lfoodir', '-lfoo']) self.assertEqual(l, ['-Lfoodir', '-lfoo']) # Adding a library and a libpath appends both correctly l += ['-Lbardir', '-lbar'] @@ -244,7 +236,7 @@ class InternalTests(unittest.TestCase): self.assertEqual(l, ['-Lbardir', '-Lfoodir', '-lfoo', '-lbar']) ## Test that 'direct' append and extend works - l = cargsfunc(c, ['-Lfoodir', '-lfoo']) + l = cargsfunc(cc, ['-Lfoodir', '-lfoo']) self.assertEqual(l, ['-Lfoodir', '-lfoo']) # Direct-adding a library and a libpath appends both correctly l.extend_direct(['-Lbardir', '-lbar']) @@ -259,6 +251,29 @@ class InternalTests(unittest.TestCase): l.append_direct('/libbaz.a') self.assertEqual(l, ['-Lfoodir', '-lfoo', '-Lbardir', '-lbar', '-lbar', '/libbaz.a']) + def test_compiler_args_class_gnuld(self): + cargsfunc = mesonbuild.compilers.CompilerArgs + ## Test --start/end-group + gcc = mesonbuild.compilers.GnuCCompiler([], 'fake', 0, False) + ## Test that 'direct' append and extend works + l = cargsfunc(gcc, ['-Lfoodir', '-lfoo']) + self.assertEqual(l.to_native(copy=True), ['-Lfoodir', '-Wl,--start-group', '-lfoo', '-Wl,--end-group']) + # Direct-adding a library and a libpath appends both correctly + l.extend_direct(['-Lbardir', '-lbar']) + self.assertEqual(l.to_native(copy=True), ['-Lfoodir', '-Wl,--start-group', '-lfoo', '-Lbardir', '-lbar', '-Wl,--end-group']) + # Direct-adding the same library again still adds it + l.append_direct('-lbar') + self.assertEqual(l.to_native(copy=True), ['-Lfoodir', '-Wl,--start-group', '-lfoo', '-Lbardir', '-lbar', '-lbar', '-Wl,--end-group']) + # Direct-adding with absolute path deduplicates + l.append_direct('/libbaz.a') + self.assertEqual(l.to_native(copy=True), ['-Lfoodir', '-Wl,--start-group', '-lfoo', '-Lbardir', '-lbar', '-lbar', '/libbaz.a', '-Wl,--end-group']) + # Adding libbaz again does nothing + l.append_direct('/libbaz.a') + self.assertEqual(l.to_native(copy=True), ['-Lfoodir', '-Wl,--start-group', '-lfoo', '-Lbardir', '-lbar', '-lbar', '/libbaz.a', '-Wl,--end-group']) + # Adding a non-library argument doesn't include it in the group + l += ['-Lfoo', '-Wl,--export-dynamic'] + self.assertEqual(l.to_native(copy=True), ['-Lfoo', '-Lfoodir', '-Wl,--start-group', '-lfoo', '-Lbardir', '-lbar', '-lbar', '/libbaz.a', '-Wl,--end-group', '-Wl,--export-dynamic']) + def test_string_templates_substitution(self): dictfunc = mesonbuild.mesonlib.get_filenames_templates_dict substfunc = mesonbuild.mesonlib.substitute_values @@ -563,7 +578,7 @@ class InternalTests(unittest.TestCase): 'windows-mingw': {'shared': ['lib{}.dll.a', 'lib{}.lib', 'lib{}.dll', '{}.dll.a', '{}.lib', '{}.dll'], 'static': msvc_static}} - env = Environment('', '', get_fake_options('')) + env = get_fake_env('', '', '') cc = env.detect_c_compiler(False) if is_osx(): self._test_all_naming(cc, env, patterns, 'darwin') @@ -606,7 +621,7 @@ class InternalTests(unittest.TestCase): ''' with tempfile.TemporaryDirectory() as tmpdir: pkgbin = ExternalProgram('pkg-config', command=['pkg-config'], silent=True) - env = Environment('', '', get_fake_options('')) + env = get_fake_env('', '', '') compiler = env.detect_c_compiler(False) env.coredata.compilers = {'c': compiler} env.coredata.compiler_options['c_link_args'] = FakeCompilerOptions() @@ -630,7 +645,7 @@ class InternalTests(unittest.TestCase): if '--libs' not in args: return 0, '' if args[0] == 'foo': - return 0, '-L{} -lfoo -L{} -lbar'.format(p1.as_posix(), p2.as_posix()) + return 0, '-L{} -lfoo -L{} -lbar'.format(p2.as_posix(), p1.as_posix()) if args[0] == 'bar': return 0, '-L{} -lbar'.format(p2.as_posix()) if args[0] == 'internal': @@ -672,6 +687,8 @@ class DataTests(unittest.TestCase): self.assertTrue(snippet_dir.is_dir()) for f in snippet_dir.glob('*'): self.assertTrue(f.is_file()) + if f.parts[-1].endswith('~'): + continue if f.suffix == '.md': with f.open() as snippet: for line in snippet: @@ -692,7 +709,7 @@ class DataTests(unittest.TestCase): with open('docs/markdown/Builtin-options.md') as f: md = f.read() self.assertIsNotNone(md) - env = Environment('', '', get_fake_options('')) + env = get_fake_env('', '', '') # FIXME: Support other compilers cc = env.detect_c_compiler(False) cpp = env.detect_cpp_compiler(False) @@ -738,7 +755,7 @@ class DataTests(unittest.TestCase): Ensure that syntax highlighting files were updated for new functions in the global namespace in build files. ''' - env = Environment('', '', get_fake_options('')) + env = get_fake_env('', '', '') interp = Interpreter(FakeBuild(env), mock=True) with open('data/syntax-highlighting/vim/syntax/meson.vim') as f: res = re.search(r'syn keyword mesonBuiltin(\s+\\\s\w+)+', f.read(), re.MULTILINE) @@ -1172,7 +1189,7 @@ class AllPlatformTests(BasePlatformTests): https://github.com/mesonbuild/meson/issues/1355 ''' testdir = os.path.join(self.common_test_dir, '3 static') - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) cc = env.detect_c_compiler(False) static_linker = env.detect_static_linker(cc) if is_windows(): @@ -1459,7 +1476,7 @@ class AllPlatformTests(BasePlatformTests): if not is_windows(): langs += [('objc', 'OBJC'), ('objcpp', 'OBJCXX')] testdir = os.path.join(self.unit_test_dir, '5 compiler detection') - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) for lang, evar in langs: # Detect with evar and do sanity checks on that if evar in os.environ: @@ -1561,7 +1578,7 @@ class AllPlatformTests(BasePlatformTests): def test_always_prefer_c_compiler_for_asm(self): testdir = os.path.join(self.common_test_dir, '138 c cpp and asm') # Skip if building with MSVC - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) if env.detect_c_compiler(False).get_id() == 'msvc': raise unittest.SkipTest('MSVC can\'t compile assembly') self.init(testdir) @@ -1819,7 +1836,7 @@ int main(int argc, char **argv) { self.assertPathExists(os.path.join(testdir, i)) def detect_prebuild_env(self): - env = Environment('', self.builddir, get_fake_options(self.prefix)) + env = get_fake_env('', self.builddir, self.prefix) cc = env.detect_c_compiler(False) stlinker = env.detect_static_linker(cc) if mesonbuild.mesonlib.is_windows(): @@ -1985,7 +2002,7 @@ int main(int argc, char **argv) { '--libdir=' + libdir]) # Find foo dependency os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) kwargs = {'required': True, 'silent': True} foo_dep = PkgConfigDependency('libfoo', env, kwargs) # Ensure link_args are properly quoted @@ -2292,7 +2309,7 @@ recommended as it is not supported on some platforms''') testdirbase = os.path.join(self.unit_test_dir, '29 guessed linker dependencies') testdirlib = os.path.join(testdirbase, 'lib') extra_args = None - env = Environment(testdirlib, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdirlib, self.builddir, self.prefix) if env.detect_c_compiler(False).get_id() != 'msvc': # static libraries are not linkable with -l with msvc because meson installs them # as .a files which unix_args_to_native will not know as it expects libraries to use @@ -2746,7 +2763,7 @@ class FailureTests(BasePlatformTests): ''' Test that when we can't detect objc or objcpp, we fail gracefully. ''' - env = Environment('', self.builddir, get_fake_options(self.prefix)) + env = get_fake_env('', self.builddir, self.prefix) try: env.detect_objc_compiler(False) env.detect_objcpp_compiler(False) @@ -2831,6 +2848,16 @@ class FailureTests(BasePlatformTests): msg = '.*WARNING:.*feature.*build_always_stale.*custom_target.*' self.assertMesonDoesNotOutput(vcs_tag, msg, meson_version='>=0.43') + def test_missing_subproject_not_required_and_required(self): + self.assertMesonRaises("sub1 = subproject('not-found-subproject', required: false)\n" + + "sub2 = subproject('not-found-subproject', required: true)", + """.*Subproject "subprojects/not-found-subproject" required but not found.*""") + + def test_get_variable_on_not_found_project(self): + self.assertMesonRaises("sub1 = subproject('not-found-subproject', required: false)\n" + + "sub1.get_variable('naaa')", + """Subproject "subprojects/not-found-subproject" disabled can't get_variable on it.""") + class WindowsTests(BasePlatformTests): ''' @@ -2883,7 +2910,7 @@ class WindowsTests(BasePlatformTests): ExternalLibraryHolder from build files. ''' testdir = os.path.join(self.platform_test_dir, '1 basic') - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) cc = env.detect_c_compiler(False) if cc.id != 'msvc': raise unittest.SkipTest('Not using MSVC') @@ -2896,7 +2923,7 @@ class WindowsTests(BasePlatformTests): testdir = os.path.join(self.platform_test_dir, '5 resources') # resource compiler depfile generation is not yet implemented for msvc - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) depfile_works = env.detect_c_compiler(False).get_id() != 'msvc' self.init(testdir) @@ -2985,7 +3012,7 @@ class LinuxlikeTests(BasePlatformTests): ''' testdir = os.path.join(self.common_test_dir, '48 pkgconfig-gen') self.init(testdir) - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) kwargs = {'required': True, 'silent': True} os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir foo_dep = PkgConfigDependency('libfoo', env, kwargs) @@ -3241,7 +3268,7 @@ class LinuxlikeTests(BasePlatformTests): an ordinary test because it requires passing options to meson. ''' testdir = os.path.join(self.common_test_dir, '1 trivial') - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) cc = env.detect_c_compiler(False) self._test_stds_impl(testdir, cc, 'c') @@ -3251,7 +3278,7 @@ class LinuxlikeTests(BasePlatformTests): be an ordinary test because it requires passing options to meson. ''' testdir = os.path.join(self.common_test_dir, '2 cpp') - env = Environment(testdir, self.builddir, get_fake_options(self.prefix)) + env = get_fake_env(testdir, self.builddir, self.prefix) cpp = env.detect_cpp_compiler(False) self._test_stds_impl(testdir, cpp, 'cpp') @@ -3796,8 +3823,6 @@ endian = 'little' The system library is found with cc.find_library() and pkg-config deps. ''' - if not is_osx(): - raise unittest.SkipTest('workflow currently only works on macOS') oldprefix = self.prefix # Install external library so we can find it testdir = os.path.join(self.unit_test_dir, '39 external, internal library rpath', 'external library') @@ -3820,6 +3845,9 @@ endian = 'little' self.build() # test uninstalled self.run_tests() + if not is_osx(): + # Rest of the workflow only works on macOS + return # test running after installation self.install(use_destdir=False) prog = os.path.join(self.installdir, 'bin', 'prog') diff --git a/test cases/common/190 openmp/meson.build b/test cases/common/190 openmp/meson.build index a05ca59..eb270ab 100644 --- a/test cases/common/190 openmp/meson.build +++ b/test cases/common/190 openmp/meson.build @@ -38,3 +38,6 @@ if add_languages('fortran', required : false) test('OpenMP Fortran', execpp, env : env) endif + +# Check we can apply a version constraint +dependency('openmp', version: '>=@0@'.format(openmp.version())) diff --git a/test cases/common/206 subproject with features/meson.build b/test cases/common/206 subproject with features/meson.build new file mode 100644 index 0000000..5bdfefb --- /dev/null +++ b/test cases/common/206 subproject with features/meson.build @@ -0,0 +1,17 @@ +project('proj', 'c') + +auto_subproj = subproject('sub', required: get_option('use-subproject')) +assert(auto_subproj.found(), 'Subproject should always be buildable and thus found') + +auto_dep = dependency('', fallback: ['sub', 'libSub'], required: true) +assert(auto_dep.found() == true, 'Subproject is required and foundable, dependency should be found.') + +disabled_subproj = subproject('disabled_sub', required: get_option('disabled-subproject')) +assert(disabled_subproj.found() == false, 'Disabled subproject should be NOT found') + +disabled_dep = dependency('', fallback: ['disabled_sub', 'libSub'], required: false) +assert(disabled_dep.found() == false, 'Subprojetc was disabled, it should never be built.') +nothing = executable('nothing', 'nothing.c', dependencies: [disabled_dep]) + +subproj_with_missing_dep = subproject('auto_sub_with_missing_dep', required: get_option('auto-sub-with-missing-dep')) +assert(subproj_with_missing_dep.found() == false, 'Subproject with required=auto and missing dependency should be NOT found') diff --git a/test cases/common/206 subproject with features/meson_options.txt b/test cases/common/206 subproject with features/meson_options.txt new file mode 100644 index 0000000..a46e5fb --- /dev/null +++ b/test cases/common/206 subproject with features/meson_options.txt @@ -0,0 +1,3 @@ +option('use-subproject', type : 'feature', value : 'auto') +option('disabled-subproject', type : 'feature', value : 'disabled') +option('auto-sub-with-missing-dep', type : 'feature', value : 'auto') diff --git a/test cases/common/206 subproject with features/nothing.c b/test cases/common/206 subproject with features/nothing.c new file mode 100644 index 0000000..77750c2 --- /dev/null +++ b/test cases/common/206 subproject with features/nothing.c @@ -0,0 +1,4 @@ +int main(int argc, char const *argv[]) +{ + return 0; +}
\ No newline at end of file diff --git a/test cases/common/206 subproject with features/subprojects/auto_sub_with_missing_dep/meson.build b/test cases/common/206 subproject with features/subprojects/auto_sub_with_missing_dep/meson.build new file mode 100644 index 0000000..fa6b011 --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/auto_sub_with_missing_dep/meson.build @@ -0,0 +1,3 @@ +project('sub', 'c') + +dependency('no_way_this_exists', required: true)
\ No newline at end of file diff --git a/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/meson.build b/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/meson.build new file mode 100644 index 0000000..933001a --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/meson.build @@ -0,0 +1,3 @@ +lib = static_library('sub', 'sub.c') + +libSub = declare_dependency(include_directories: include_directories('.'), link_with: lib)
\ No newline at end of file diff --git a/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/sub.c b/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/sub.c new file mode 100644 index 0000000..068a5b8 --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/sub.c @@ -0,0 +1,5 @@ +#include "sub.h" + +int sub() { + return 0; +} diff --git a/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/sub.h b/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/sub.h new file mode 100644 index 0000000..f1ab0e1 --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/sub.h @@ -0,0 +1,6 @@ +#ifndef SUB_H +#define SUB_H + +int sub(); + +#endif diff --git a/test cases/common/206 subproject with features/subprojects/disabled_sub/meson.build b/test cases/common/206 subproject with features/subprojects/disabled_sub/meson.build new file mode 100644 index 0000000..65fef03 --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/disabled_sub/meson.build @@ -0,0 +1,3 @@ +project('disabled_sub', 'c') + +subdir('lib')
\ No newline at end of file diff --git a/test cases/common/206 subproject with features/subprojects/sub/lib/meson.build b/test cases/common/206 subproject with features/subprojects/sub/lib/meson.build new file mode 100644 index 0000000..731d22b --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/sub/lib/meson.build @@ -0,0 +1,2 @@ +lib = static_library('sub', 'sub.c') +libSub = declare_dependency(include_directories: include_directories('.'), link_with: lib) diff --git a/test cases/common/206 subproject with features/subprojects/sub/lib/sub.c b/test cases/common/206 subproject with features/subprojects/sub/lib/sub.c new file mode 100644 index 0000000..ed78306 --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/sub/lib/sub.c @@ -0,0 +1,5 @@ +#include "sub.h" + +int sub() { + return 0; +} diff --git a/test cases/common/206 subproject with features/subprojects/sub/lib/sub.h b/test cases/common/206 subproject with features/subprojects/sub/lib/sub.h new file mode 100644 index 0000000..f1ab0e1 --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/sub/lib/sub.h @@ -0,0 +1,6 @@ +#ifndef SUB_H +#define SUB_H + +int sub(); + +#endif diff --git a/test cases/common/206 subproject with features/subprojects/sub/meson.build b/test cases/common/206 subproject with features/subprojects/sub/meson.build new file mode 100644 index 0000000..31882ac --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/sub/meson.build @@ -0,0 +1,3 @@ +project('sub', 'c') + +subdir('lib')
\ No newline at end of file diff --git a/test cases/common/21 global arg/meson.build b/test cases/common/21 global arg/meson.build index d7fd428..699dae6 100644 --- a/test cases/common/21 global arg/meson.build +++ b/test cases/common/21 global arg/meson.build @@ -3,9 +3,18 @@ project('global arg test', 'cpp', 'c') add_global_arguments('-DMYTHING', language : 'c') add_global_arguments('-DMYCPPTHING', language : 'cpp') +add_global_arguments('-DGLOBAL_NATIVE', language : 'c', native : true) +add_global_arguments('-DGLOBAL_CROSS', language : 'c', native : false) + +if meson.is_cross_build() + c_args = ['-DARG_CROSS'] +else + c_args = ['-DARG_NATIVE'] +endif + add_global_arguments('-DMYCANDCPPTHING', language: ['c', 'cpp']) -exe1 = executable('prog', 'prog.c') +exe1 = executable('prog', 'prog.c', c_args : c_args) exe2 = executable('prog2', 'prog.cc') test('prog1', exe1) diff --git a/test cases/common/21 global arg/prog.c b/test cases/common/21 global arg/prog.c index ace5a0a..fb014c7 100644 --- a/test cases/common/21 global arg/prog.c +++ b/test cases/common/21 global arg/prog.c @@ -10,6 +10,42 @@ #error "Global argument not set" #endif +#ifdef GLOBAL_NATIVE + #ifndef ARG_NATIVE + #error "Global is native but arg_native is not set." + #endif + + #ifdef GLOBAL_CROSS + #error "Both global native and global cross set." + #endif +#else + #ifndef GLOBAL_CROSS + #error "Neither global_cross nor glogal_native is set." + #endif + + #ifndef ARG_CROSS + #error "Global is cross but arg_cross is not set." + #endif + + #ifdef ARG_NATIVE + #error "Global is cross but arg_native is set." + #endif +#endif + +#ifdef GLOBAL_CROSS + #ifndef ARG_CROSS + #error "Global is cross but arg_cross is not set." + #endif +#else + #ifdef ARG_CROSS + #error "Global is cross but arg_native is set." + #endif + + #ifdef ARG_CROSS + #error "Global is native but arg cross is set." + #endif +#endif + int main(int argc, char **argv) { return 0; } diff --git a/test cases/failing/81 framework dependency with version/meson.build b/test cases/failing/81 framework dependency with version/meson.build new file mode 100644 index 0000000..714ad3b --- /dev/null +++ b/test cases/failing/81 framework dependency with version/meson.build @@ -0,0 +1,4 @@ +project('framework dependency with version') +# do individual frameworks have a meaningful version to test? And multiple frameworks might be listed... +# otherwise we're not on OSX and this will definitely fail +dep = dependency('appleframeworks', modules: 'foundation', version: '>0') diff --git a/test cases/failing/82 gl dependency with version/meson.build b/test cases/failing/82 gl dependency with version/meson.build new file mode 100644 index 0000000..3014d43 --- /dev/null +++ b/test cases/failing/82 gl dependency with version/meson.build @@ -0,0 +1,9 @@ +project('gl dependency with version', 'c') + +host_system = host_machine.system() +if host_system != 'windows' and host_system != 'darwin' + error('Test only fails on Windows and OSX') +endif + +# gl dependency found via system method doesn't have a meaningful version to check +dep = dependency('gl', method: 'system', version: '>0') diff --git a/test cases/failing/83 threads dependency with version/meson.build b/test cases/failing/83 threads dependency with version/meson.build new file mode 100644 index 0000000..6023fae --- /dev/null +++ b/test cases/failing/83 threads dependency with version/meson.build @@ -0,0 +1,3 @@ +project('threads dependency with version', 'c') +# threads dependency doesn't have a meaningful version to check +dep = dependency('threads', version: '>0') diff --git a/test cases/failing/84 gtest dependency with version/meson.build b/test cases/failing/84 gtest dependency with version/meson.build new file mode 100644 index 0000000..5115f27 --- /dev/null +++ b/test cases/failing/84 gtest dependency with version/meson.build @@ -0,0 +1,3 @@ +project('gtest dependency with version', ['c', 'cpp']) +# discovering gtest version is not yet implemented +dep = dependency('gtest', version: '>0') diff --git a/test cases/frameworks/1 boost/meson.build b/test cases/frameworks/1 boost/meson.build index 9399598..d1e1da4 100644 --- a/test cases/frameworks/1 boost/meson.build +++ b/test cases/frameworks/1 boost/meson.build @@ -33,3 +33,6 @@ test('Boost nomod', nomodexe) test('Boost extralib test', extralibexe) subdir('partial_dep') + +# check we can apply a version constraint +dependency('boost', version: '>=@0@'.format(dep.version())) diff --git a/test cases/frameworks/15 llvm/meson.build b/test cases/frameworks/15 llvm/meson.build index b5505eb..e05fddd 100644 --- a/test cases/frameworks/15 llvm/meson.build +++ b/test cases/frameworks/15 llvm/meson.build @@ -41,3 +41,6 @@ foreach static : [true, false] ) endif endforeach + +# Check we can apply a version constraint +dependency('llvm', version: '>=@0@'.format(d.version())) diff --git a/test cases/frameworks/16 sdl2/meson.build b/test cases/frameworks/16 sdl2/meson.build index 1bbf09f..fd90e36 100644 --- a/test cases/frameworks/16 sdl2/meson.build +++ b/test cases/frameworks/16 sdl2/meson.build @@ -15,3 +15,7 @@ configdep = dependency('sdl2', method : 'sdlconfig') # And the modern method name configdep = dependency('sdl2', method : 'config-tool') + +# Check we can apply a version constraint +dependency('sdl2', version: '>=@0@'.format(sdl2_dep.version()), method: 'pkg-config') +dependency('sdl2', version: '>=@0@'.format(sdl2_dep.version()), method: 'config-tool') diff --git a/test cases/frameworks/17 mpi/meson.build b/test cases/frameworks/17 mpi/meson.build index f3eacac..1085d40 100644 --- a/test cases/frameworks/17 mpi/meson.build +++ b/test cases/frameworks/17 mpi/meson.build @@ -42,3 +42,6 @@ if uburesult.returncode() != 0 and add_languages('fortran', required : false) test('MPI Fortran', exef) endif + +# Check we can apply a version constraint +dependency('mpi', version: '>=@0@'.format(mpic.version())) diff --git a/test cases/frameworks/18 vulkan/meson.build b/test cases/frameworks/18 vulkan/meson.build index e98854e..5cfe89f 100644 --- a/test cases/frameworks/18 vulkan/meson.build +++ b/test cases/frameworks/18 vulkan/meson.build @@ -8,3 +8,6 @@ endif e = executable('vulkanprog', 'vulkanprog.c', dependencies : vulkan_dep) test('vulkantest', e) + +# Check we can apply a version constraint +dependency('vulkan', version: '>=@0@'.format(vulkan_dep.version())) diff --git a/test cases/frameworks/19 pcap/meson.build b/test cases/frameworks/19 pcap/meson.build index eb6fc2c..051e49e 100644 --- a/test cases/frameworks/19 pcap/meson.build +++ b/test cases/frameworks/19 pcap/meson.build @@ -16,3 +16,7 @@ test('pcaptest', e) # Ensure discovery via the configuration tools work also pcap_dep = dependency('pcap', version : '>=1.0', method : 'pcap-config') pcap_dep = dependency('pcap', version : '>=1.0', method : 'config-tool') + +# Check we can apply a version constraint +dependency('pcap', version: '>=@0@'.format(pcap_dep.version()), method: 'pkg-config', required: false) +dependency('pcap', version: '>=@0@'.format(pcap_dep.version()), method: 'config-tool') diff --git a/test cases/frameworks/20 cups/meson.build b/test cases/frameworks/20 cups/meson.build index 9040de6..d50c4a8 100644 --- a/test cases/frameworks/20 cups/meson.build +++ b/test cases/frameworks/20 cups/meson.build @@ -14,3 +14,7 @@ test('cupstest', e) # options dep = dependency('cups', version : '>=1.4', method : 'cups-config') dep = dependency('cups', version : '>=1.4', method : 'config-tool') + +# check we can apply a version constraint +dependency('cups', version: '>=@0@'.format(dep.version()), method: 'pkg-config', required: false) +dependency('cups', version: '>=@0@'.format(dep.version()), method: 'config-tool') diff --git a/test cases/frameworks/21 libwmf/meson.build b/test cases/frameworks/21 libwmf/meson.build index 1fdce2e..ab0ebf6 100644 --- a/test cases/frameworks/21 libwmf/meson.build +++ b/test cases/frameworks/21 libwmf/meson.build @@ -17,3 +17,7 @@ test('libwmftest', e) dependency('libwmf', method : 'config-tool') dependency('libwmf', method : 'libwmf-config') + +# Check we can apply a version constraint +dependency('libwmf', version: '>=@0@'.format(libwmf_dep.version()), method: 'pkg-config', required: false) +dependency('libwmf', version: '>=@0@'.format(libwmf_dep.version()), method: 'config-tool') diff --git a/test cases/frameworks/4 qt/meson.build b/test cases/frameworks/4 qt/meson.build index 16fe564..1d7ff4e 100644 --- a/test cases/frameworks/4 qt/meson.build +++ b/test cases/frameworks/4 qt/meson.build @@ -103,5 +103,9 @@ foreach qt : ['qt4', 'qt5'] if qt == 'qt5' subdir('subfolder') endif + + # Check we can apply a version constraint + dependency(qt, modules: qt_modules, version: '>=@0@'.format(qtdep.version()), method : get_option('method')) + endif endforeach diff --git a/test cases/frameworks/9 wxwidgets/meson.build b/test cases/frameworks/9 wxwidgets/meson.build index d815a2d..0c7ecaa 100644 --- a/test cases/frameworks/9 wxwidgets/meson.build +++ b/test cases/frameworks/9 wxwidgets/meson.build @@ -12,4 +12,8 @@ if wxd.found() wx_stc = dependency('wxwidgets', version : '>=3.0.0', modules : ['std', 'stc']) stc_exe = executable('wxstc', 'wxstc.cpp', dependencies : wx_stc) test('wxstctest', stc_exe) + + # Check we can apply a version constraint + dependency('wxwidgets', version: '>=@0@'.format(wxd.version())) + endif diff --git a/test cases/python3/2 extmodule/meson.build b/test cases/python3/2 extmodule/meson.build index 0ecc813..4916a69 100644 --- a/test cases/python3/2 extmodule/meson.build +++ b/test cases/python3/2 extmodule/meson.build @@ -14,6 +14,10 @@ if py3_dep.found() py3, args : files('blaster.py'), env : ['PYTHONPATH=' + pypathdir]) + + # Check we can apply a version constraint + dependency('python3', version: '>=@0@'.format(py3_dep.version())) + else error('MESON_SKIP_TEST: Python3 libraries not found, skipping test.') endif diff --git a/test cases/unit/39 external, internal library rpath/built library/meson.build b/test cases/unit/39 external, internal library rpath/built library/meson.build index 2b422f4..f633996 100644 --- a/test cases/unit/39 external, internal library rpath/built library/meson.build +++ b/test cases/unit/39 external, internal library rpath/built library/meson.build @@ -1,12 +1,21 @@ project('built library', 'c') cc = meson.get_compiler('c') + +if host_machine.system() != 'cygwin' + # bar_in_system has undefined symbols, but still must be found + bar_system_dep = cc.find_library('bar_in_system') +endif + foo_system_dep = cc.find_library('foo_in_system') + faa_pkg_dep = dependency('faa_pkg') l = shared_library('bar_built', 'bar.c', install: true, dependencies : [foo_system_dep, faa_pkg_dep]) -e = executable('prog', 'prog.c', link_with: l, install: true) -test('testprog', e) +if host_machine.system() == 'darwin' + e = executable('prog', 'prog.c', link_with: l, install: true) + test('testprog', e) +endif diff --git a/test cases/unit/39 external, internal library rpath/external library/bar.c b/test cases/unit/39 external, internal library rpath/external library/bar.c new file mode 100644 index 0000000..c6f42d6 --- /dev/null +++ b/test cases/unit/39 external, internal library rpath/external library/bar.c @@ -0,0 +1,6 @@ +int some_undefined_func (void); + +int bar_system_value (void) +{ + return some_undefined_func (); +} diff --git a/test cases/unit/39 external, internal library rpath/external library/meson.build b/test cases/unit/39 external, internal library rpath/external library/meson.build index 6dcc97e..3c311f5 100644 --- a/test cases/unit/39 external, internal library rpath/external library/meson.build +++ b/test cases/unit/39 external, internal library rpath/external library/meson.build @@ -1,9 +1,22 @@ -project('system library', 'c') +project('system library', 'c', default_options : ['b_lundef=false']) 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'] + allow_undef_args = ['-Wl,-undefined,dynamic_lookup'] +else + frameworks = [] + allow_undef_args = [] +endif + pkg = import('pkgconfig') pkg.generate(name: 'faa_pkg', - libraries: [l, '-framework', 'CoreFoundation', '-framework', 'CoreMedia'], + libraries: [l] + frameworks, description: 'FAA, a pkg-config test library') + +# cygwin DLLs can't have undefined symbols +if host_machine.system() != 'cygwin' + shared_library('bar_in_system', 'bar.c', install : true, link_args : allow_undef_args) +endif diff --git a/test cases/windows/15 resource scripts with duplicate filenames/exe3/meson.build b/test cases/windows/15 resource scripts with duplicate filenames/exe3/meson.build new file mode 100644 index 0000000..1b97435 --- /dev/null +++ b/test cases/windows/15 resource scripts with duplicate filenames/exe3/meson.build @@ -0,0 +1,5 @@ +dll_res = win.compile_resources('src_dll/version.rc') +shared_library('lib3', 'src_dll/main.c', dll_res) + +exe_res = win.compile_resources('src_exe/version.rc') +executable('exe3', 'src_exe/main.c', exe_res) diff --git a/test cases/windows/15 resource scripts with duplicate filenames/exe3/src_dll/main.c b/test cases/windows/15 resource scripts with duplicate filenames/exe3/src_dll/main.c new file mode 100644 index 0000000..673b5e4 --- /dev/null +++ b/test cases/windows/15 resource scripts with duplicate filenames/exe3/src_dll/main.c @@ -0,0 +1,6 @@ +#include <windows.h> + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + return TRUE; +} diff --git a/test cases/windows/15 resource scripts with duplicate filenames/exe3/src_dll/version.rc b/test cases/windows/15 resource scripts with duplicate filenames/exe3/src_dll/version.rc new file mode 100644 index 0000000..abdbaaa --- /dev/null +++ b/test cases/windows/15 resource scripts with duplicate filenames/exe3/src_dll/version.rc @@ -0,0 +1,11 @@ + #include <windows.h> + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP +BEGIN +END diff --git a/test cases/windows/15 resource scripts with duplicate filenames/exe3/src_exe/main.c b/test cases/windows/15 resource scripts with duplicate filenames/exe3/src_exe/main.c new file mode 100644 index 0000000..11b7fad --- /dev/null +++ b/test cases/windows/15 resource scripts with duplicate filenames/exe3/src_exe/main.c @@ -0,0 +1,3 @@ +int main(int argc, char **argv) { + return 0; +} diff --git a/test cases/windows/15 resource scripts with duplicate filenames/exe3/src_exe/version.rc b/test cases/windows/15 resource scripts with duplicate filenames/exe3/src_exe/version.rc new file mode 100644 index 0000000..abdbaaa --- /dev/null +++ b/test cases/windows/15 resource scripts with duplicate filenames/exe3/src_exe/version.rc @@ -0,0 +1,11 @@ + #include <windows.h> + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP +BEGIN +END diff --git a/test cases/windows/15 resource scripts with duplicate filenames/exe4/meson.build b/test cases/windows/15 resource scripts with duplicate filenames/exe4/meson.build new file mode 100644 index 0000000..2ae3a71 --- /dev/null +++ b/test cases/windows/15 resource scripts with duplicate filenames/exe4/meson.build @@ -0,0 +1,5 @@ +dll_res = win.compile_resources(files('src_dll/version.rc')) +shared_library('lib4', 'src_dll/main.c', dll_res) + +exe_res = win.compile_resources(files('src_exe/version.rc')) +executable('exe4', 'src_exe/main.c', exe_res) diff --git a/test cases/windows/15 resource scripts with duplicate filenames/exe4/src_dll/main.c b/test cases/windows/15 resource scripts with duplicate filenames/exe4/src_dll/main.c new file mode 100644 index 0000000..673b5e4 --- /dev/null +++ b/test cases/windows/15 resource scripts with duplicate filenames/exe4/src_dll/main.c @@ -0,0 +1,6 @@ +#include <windows.h> + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + return TRUE; +} diff --git a/test cases/windows/15 resource scripts with duplicate filenames/exe4/src_dll/version.rc b/test cases/windows/15 resource scripts with duplicate filenames/exe4/src_dll/version.rc new file mode 100644 index 0000000..abdbaaa --- /dev/null +++ b/test cases/windows/15 resource scripts with duplicate filenames/exe4/src_dll/version.rc @@ -0,0 +1,11 @@ + #include <windows.h> + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP +BEGIN +END diff --git a/test cases/windows/15 resource scripts with duplicate filenames/exe4/src_exe/main.c b/test cases/windows/15 resource scripts with duplicate filenames/exe4/src_exe/main.c new file mode 100644 index 0000000..11b7fad --- /dev/null +++ b/test cases/windows/15 resource scripts with duplicate filenames/exe4/src_exe/main.c @@ -0,0 +1,3 @@ +int main(int argc, char **argv) { + return 0; +} diff --git a/test cases/windows/15 resource scripts with duplicate filenames/exe4/src_exe/version.rc b/test cases/windows/15 resource scripts with duplicate filenames/exe4/src_exe/version.rc new file mode 100644 index 0000000..abdbaaa --- /dev/null +++ b/test cases/windows/15 resource scripts with duplicate filenames/exe4/src_exe/version.rc @@ -0,0 +1,11 @@ + #include <windows.h> + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP +BEGIN +END diff --git a/test cases/windows/15 resource scripts with duplicate filenames/meson.build b/test cases/windows/15 resource scripts with duplicate filenames/meson.build index 4073a8e..9fa3525 100644 --- a/test cases/windows/15 resource scripts with duplicate filenames/meson.build +++ b/test cases/windows/15 resource scripts with duplicate filenames/meson.build @@ -5,6 +5,8 @@ win = import('windows') subdir('a') subdir('b') subdir('c') +subdir('exe3') +subdir('exe4') main = win.compile_resources('rsrc.rc') |